diff --git a/exchanges/coinbasepro/coinbasepro.go b/exchanges/coinbasepro/coinbasepro.go index f9109574908..b2ecca9c796 100644 --- a/exchanges/coinbasepro/coinbasepro.go +++ b/exchanges/coinbasepro/coinbasepro.go @@ -93,9 +93,8 @@ const ( startDateString = "start_date" endDateString = "end_date" - errIntervalNotSupported = "interval not supported" - warnSequenceIssue = "Out of order sequence number. Received %v, expected %v" - warnAuth = "%v authenticated request failed, attempting unauthenticated" + warnSequenceIssue = "Out of order sequence number. Received %v, expected %v" + warnAuth = "%v authenticated request failed, attempting unauthenticated" manyFills = 65535 manyOrds = 2147483647 @@ -155,6 +154,7 @@ var ( errUnrecognisedOrderType = errors.New("unrecognised order type") errUnrecognisedAssetType = errors.New("unrecognised asset type") errUnrecognisedStrategyType = errors.New("unrecognised strategy type") + errIntervalNotSupported = errors.New("interval not supported") allowedGranularities = []string{granOneMin, granFiveMin, granFifteenMin, granThirtyMin, granOneHour, granTwoHour, granSixHour, granOneDay} closedStatuses = []string{"FILLED", "CANCELLED", "EXPIRED", "FAILED"} @@ -199,23 +199,24 @@ func (c *CoinbasePro) GetBestBidAsk(ctx context.Context, products []string) ([]P } // GetProductBookV3 returns a list of bids/asks for a single product -func (c *CoinbasePro) GetProductBookV3(ctx context.Context, productID string, limit uint16, authenticated bool) (*ProductBook, error) { - if productID == "" { +func (c *CoinbasePro) GetProductBookV3(ctx context.Context, productID currency.Pair, limit uint16, aggregationIncrement float64, authenticated bool) (*ProductBookResp, error) { + if productID.IsEmpty() { return nil, errProductIDEmpty } vals := url.Values{} - vals.Set("product_id", productID) + vals.Set("product_id", productID.String()) if limit != 0 { vals.Set("limit", strconv.FormatInt(int64(limit), 10)) } - resp := struct { - Pricebook ProductBook `json:"pricebook"` - }{} + if aggregationIncrement != 0 { + vals.Set("aggregation_price_increment", strconv.FormatFloat(aggregationIncrement, 'f', -1, 64)) + } + var resp *ProductBookResp if authenticated { - return &resp.Pricebook, c.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, coinbaseV3+coinbaseProductBook, vals, nil, true, &resp, nil) + return resp, c.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, coinbaseV3+coinbaseProductBook, vals, nil, true, &resp, nil) } path := coinbaseV3 + coinbaseMarket + "/" + coinbaseProductBook - return &resp.Pricebook, c.SendHTTPRequest(ctx, exchange.RestSpot, path, vals, &resp) + return resp, c.SendHTTPRequest(ctx, exchange.RestSpot, path, vals, &resp) } // GetAllProducts returns information on all currency pairs that are available for trading @@ -1659,53 +1660,3 @@ func (f FiatTransferType) String() string { } return "deposit" } - -// UnmarshalJSON unmarshals the JSON input into a UnixTimestamp type -func (t *UnixTimestamp) UnmarshalJSON(b []byte) error { - var timestampStr string - err := json.Unmarshal(b, ×tampStr) - if err != nil { - return err - } - timestamp, err := strconv.ParseInt(timestampStr, 10, 64) - if err != nil { - return err - } - *t = UnixTimestamp(time.Unix(timestamp, 0).UTC()) - return nil -} - -// String implements the stringer interface -func (t *UnixTimestamp) String() string { - return t.Time().String() -} - -// Time returns the time.Time representation of the UnixTimestamp -func (t *UnixTimestamp) Time() time.Time { - return time.Time(*t) -} - -// UnmarshalJSON unmarshals the JSON input into a UnixTimestamp type -func (t *UnixTimestampMilli) UnmarshalJSON(b []byte) error { - var timestampStr string - err := json.Unmarshal(b, ×tampStr) - if err != nil { - return err - } - timestamp, err := strconv.ParseInt(timestampStr, 10, 64) - if err != nil { - return err - } - *t = UnixTimestampMilli(time.UnixMilli(timestamp).UTC()) - return nil -} - -// String implements the stringer interface -func (t *UnixTimestampMilli) String() string { - return t.Time().String() -} - -// Time returns the time.Time representation of the UnixTimestamp -func (t *UnixTimestampMilli) Time() time.Time { - return time.Time(*t) -} diff --git a/exchanges/coinbasepro/coinbasepro_test.go b/exchanges/coinbasepro/coinbasepro_test.go index c1361835e22..85110eaff57 100644 --- a/exchanges/coinbasepro/coinbasepro_test.go +++ b/exchanges/coinbasepro/coinbasepro_test.go @@ -170,13 +170,13 @@ func TestGetBestBidAsk(t *testing.T) { func TestGetProductBookV3(t *testing.T) { t.Parallel() - _, err := c.GetProductBookV3(context.Background(), "", 0, false) + _, err := c.GetProductBookV3(context.Background(), currency.Pair{}, 0, 0, false) assert.ErrorIs(t, err, errProductIDEmpty) - resp, err := c.GetProductBookV3(context.Background(), testPair.String(), 2, false) + resp, err := c.GetProductBookV3(context.Background(), testPair, 4, -1, false) assert.NoError(t, err) assert.NotEmpty(t, resp, errExpectedNonEmpty) sharedtestvalues.SkipTestIfCredentialsUnset(t, c) - resp, err = c.GetProductBookV3(context.Background(), testPair.String(), 2, true) + resp, err = c.GetProductBookV3(context.Background(), testPair, 4, -1, true) assert.NoError(t, err) assert.NotEmpty(t, resp, errExpectedNonEmpty) } @@ -970,7 +970,7 @@ func TestSendAuthenticatedHTTPRequest(t *testing.T) { err = c.SendAuthenticatedHTTPRequest(context.Background(), exchange.EdgeCase3, "", "", nil, nil, false, nil, nil) assert.ErrorIs(t, err, exchange.ErrEndpointPathNotFound) ch := make(chan struct{}) - body := map[string]interface{}{"Unmarshalable": ch} + body := map[string]any{"Unmarshalable": ch} err = c.SendAuthenticatedHTTPRequest(context.Background(), exchange.RestSpot, "", "", nil, body, false, nil, nil) var targetErr *json.UnsupportedTypeError assert.ErrorAs(t, err, &targetErr) @@ -1072,6 +1072,8 @@ func TestUpdateTicker(t *testing.T) { func TestFetchTicker(t *testing.T) { t.Parallel() + _, err := c.FetchTicker(context.Background(), currency.Pair{}, asset.Spot) + assert.ErrorIs(t, err, currency.ErrCurrencyPairEmpty) resp, err := c.FetchTicker(context.Background(), testPair, asset.Spot) assert.NoError(t, err) assert.NotEmpty(t, resp, errExpectedNonEmpty) @@ -1079,6 +1081,8 @@ func TestFetchTicker(t *testing.T) { func TestFetchOrderbook(t *testing.T) { t.Parallel() + _, err := c.FetchOrderbook(context.Background(), currency.Pair{}, asset.Empty) + assert.ErrorIs(t, err, currency.ErrCurrencyPairEmpty) resp, err := c.FetchOrderbook(context.Background(), testPair, asset.Spot) assert.NoError(t, err) assert.NotEmpty(t, resp, errExpectedNonEmpty) @@ -1427,29 +1431,6 @@ func TestFiatTransferTypeString(t *testing.T) { } } -func TestUnixTimestampUnmarshalJSON(t *testing.T) { - t.Parallel() - var u UnixTimestamp - err := u.UnmarshalJSON([]byte("0")) - var targetErr *json.UnmarshalTypeError - assert.ErrorAs(t, err, &targetErr) - err = u.UnmarshalJSON([]byte("\"922337203685477580700\"")) - assert.ErrorIs(t, err, strconv.ErrRange) - err = u.UnmarshalJSON([]byte("\"1234\"")) - assert.NoError(t, err) -} - -func TestUnixTimestampString(t *testing.T) { - t.Parallel() - var u UnixTimestamp - err := u.UnmarshalJSON([]byte("\"1234\"")) - assert.NoError(t, err) - s := u.String() - if s != expectedTimestamp { - t.Errorf(errExpectMismatch, s, expectedTimestamp) - } -} - func TestFormatExchangeKlineIntervalV3(t *testing.T) { t.Parallel() testSequence := map[kline.Interval]string{ @@ -1460,12 +1441,15 @@ func TestFormatExchangeKlineIntervalV3(t *testing.T) { kline.TwoHour: granTwoHour, kline.SixHour: granSixHour, kline.OneDay: granOneDay, - kline.OneWeek: errIntervalNotSupported} + kline.OneWeek: ""} for k := range testSequence { - resp := FormatExchangeKlineIntervalV3(k) + resp, err := FormatExchangeKlineIntervalV3(k) if resp != testSequence[k] { t.Errorf(errExpectMismatch, resp, testSequence[k]) } + if resp == "" { + assert.ErrorIs(t, err, errIntervalNotSupported) + } } } @@ -1524,7 +1508,6 @@ func TestCancelPendingFuturesSweep(t *testing.T) { // TestWsAuth dials websocket, sends login request. func TestWsAuth(t *testing.T) { - // t.Parallel() p := currency.Pairs{testPair} if c.Websocket.IsEnabled() && !c.API.AuthenticatedWebsocketSupport || !sharedtestvalues.AreAPICredentialsSet(c) { t.Skip(stream.ErrWebsocketNotEnabled.Error()) diff --git a/exchanges/coinbasepro/coinbasepro_types.go b/exchanges/coinbasepro/coinbasepro_types.go index bbed46b1824..2eea519200d 100644 --- a/exchanges/coinbasepro/coinbasepro_types.go +++ b/exchanges/coinbasepro/coinbasepro_types.go @@ -26,10 +26,10 @@ type Version bool // endpoints under version 2 of the API type FiatTransferType bool -// ValCur is a sub-struct used in the types Account, NativeAndRaw, DetailedPortfolioResponse, +// ValueWithCurrency is a sub-struct used in the types Account, NativeAndRaw, DetailedPortfolioResponse, // FuturesBalanceSummary, ListFuturesSweepsResponse, PerpetualsPortfolioSummary, PerpPositionDetail, // FeeStruct, AmScale, and ConvertResponse -type ValCur struct { +type ValueWithCurrency struct { Value float64 `json:"value,string"` Currency string `json:"currency"` } @@ -37,18 +37,18 @@ type ValCur struct { // Account holds details for a trading account, returned by GetAccountByID and used as // a sub-struct in the type AllAccountsResponse type Account struct { - UUID string `json:"uuid"` - Name string `json:"name"` - Currency string `json:"currency"` - AvailableBalance ValCur `json:"available_balance"` - Default bool `json:"default"` - Active bool `json:"active"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - DeletedAt time.Time `json:"deleted_at"` - Type string `json:"type"` - Ready bool `json:"ready"` - Hold ValCur `json:"hold"` + UUID string `json:"uuid"` + Name string `json:"name"` + Currency string `json:"currency"` + AvailableBalance ValueWithCurrency `json:"available_balance"` + Default bool `json:"default"` + Active bool `json:"active"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + DeletedAt time.Time `json:"deleted_at"` + Type string `json:"type"` + Ready bool `json:"ready"` + Hold ValueWithCurrency `json:"hold"` } // AllAccountsResponse holds many Account structs, as well as pagination information, @@ -71,7 +71,7 @@ type PriceSize struct { Size float64 `json:"size,string"` } -// ProductBook holds bid and ask prices for a particular product, returned by GetProductBookV3 +// ProductBook holds bid and ask prices for a particular product, returned by GetBestBidAsk and used in ProductBookResp type ProductBook struct { ProductID currency.Pair `json:"product_id"` Bids []PriceSize `json:"bids"` @@ -79,6 +79,15 @@ type ProductBook struct { Time time.Time `json:"time"` } +// ProductBookResp holds a ProductBook struct, and associated information, returned by GetProductBookV3 +type ProductBookResp struct { + Pricebook ProductBook `json:"pricebook"` + Last float64 `json:"last,string"` + MidMarket float64 `json:"mid_market,string"` + SpreadBPs float64 `json:"spread_bps,string"` + SpreadAbsolute float64 `json:"spread_absolute,string"` +} + // FCMTradingSessionDetails is a sub-struct used in the type Product type FCMTradingSessionDetails struct { IsSessionOpen bool `json:"is_session_open"` @@ -111,7 +120,7 @@ type FutureProductDetails struct { ContractExpiryType string `json:"contract_expiry_type"` PerpetualDetails PerpetualDetails `json:"perpetual_details"` ContractDisplayName string `json:"contract_display_name"` - TimeToExpiryMS uint64 `json:"time_to_expiry_ms,string"` + TimeToExpiryMS time.Duration `json:"time_to_expiry_ms,string"` NonCrypto bool `json:"non_crypto"` ContractExpiryName string `json:"contract_expiry_name"` } @@ -165,18 +174,14 @@ type AllProducts struct { NumProducts int32 `json:"num_products"` } -// UnixTimestamp is a type used to unmarshal unix timestamps returned from -// the exchange, used in the types History and WebsocketCandle -type UnixTimestamp time.Time - // CandleStruct holds historic trade information, returned by GetHistoricRates type CandleStruct struct { - Start UnixTimestamp `json:"start"` - Low float64 `json:"low,string"` - High float64 `json:"high,string"` - Open float64 `json:"open,string"` - Close float64 `json:"close,string"` - Volume float64 `json:"volume,string"` + Start types.Time `json:"start"` + Low float64 `json:"low,string"` + High float64 `json:"high,string"` + Open float64 `json:"open,string"` + Close float64 `json:"close,string"` + Volume float64 `json:"volume,string"` } // Trades is a sub-struct used in the type Ticker @@ -396,32 +401,32 @@ type FundsData struct { // NativeAndRaw is a sub-struct used in the type DetailedPortfolioResponse type NativeAndRaw struct { - UserNativeCurrency ValCur `json:"userNativeCurrency"` - RawCurrency ValCur `json:"rawCurrency"` + UserNativeCurrency ValueWithCurrency `json:"userNativeCurrency"` + RawCurrency ValueWithCurrency `json:"rawCurrency"` } // PortfolioBalances is a sub-struct used in the type DetailedPortfolioResponse type PortfolioBalances struct { - TotalBalance ValCur `json:"total_balance"` - TotalFuturesBalance ValCur `json:"total_futures_balance"` - TotalCashEquivalentBalance ValCur `json:"total_cash_equivalent_balance"` - TotalCryptoBalance ValCur `json:"total_crypto_balance"` - FuturesUnrealizedPNL ValCur `json:"futures_unrealized_pnl"` - PerpUnrealizedPNL ValCur `json:"perp_unrealized_pnl"` + TotalBalance ValueWithCurrency `json:"total_balance"` + TotalFuturesBalance ValueWithCurrency `json:"total_futures_balance"` + TotalCashEquivalentBalance ValueWithCurrency `json:"total_cash_equivalent_balance"` + TotalCryptoBalance ValueWithCurrency `json:"total_crypto_balance"` + FuturesUnrealizedPNL ValueWithCurrency `json:"futures_unrealized_pnl"` + PerpUnrealizedPNL ValueWithCurrency `json:"perp_unrealized_pnl"` } // SpotPositions is a sub-struct used in the type DetailedPortfolioResponse type SpotPositions struct { - Asset string `json:"asset"` - AccountUUID string `json:"account_uuid"` - TotalBalanceFiat float64 `json:"total_balance_fiat"` - TotalBalanceCrypto float64 `json:"total_balance_crypto"` - AvailableToTreadeFiat float64 `json:"available_to_trade_fiat"` - Allocation float64 `json:"allocation"` - OneDayChange float64 `json:"one_day_change"` - CostBasis ValCur `json:"cost_basis"` - AssetImgURL string `json:"asset_img_url"` - IsCash bool `json:"is_cash"` + Asset string `json:"asset"` + AccountUUID string `json:"account_uuid"` + TotalBalanceFiat float64 `json:"total_balance_fiat"` + TotalBalanceCrypto float64 `json:"total_balance_crypto"` + AvailableToTreadeFiat float64 `json:"available_to_trade_fiat"` + Allocation float64 `json:"allocation"` + OneDayChange float64 `json:"one_day_change"` + CostBasis ValueWithCurrency `json:"cost_basis"` + AssetImgURL string `json:"asset_img_url"` + IsCash bool `json:"is_cash"` } // PerpPositions is a sub-struct used in the type DetailedPortfolioResponse @@ -478,18 +483,18 @@ type DetailedPortfolioResponse struct { // FuturesBalanceSummary contains information on futures balances, returned by // GetFuturesBalanceSummary type FuturesBalanceSummary struct { - FuturesBuyingPower ValCur `json:"futures_buying_power"` - TotalUSDBalance ValCur `json:"total_usd_balance"` - CBIUSDBalance ValCur `json:"cbi_usd_balance"` - CFMUSDBalance ValCur `json:"cfm_usd_balance"` - TotalOpenOrdersHoldAmount ValCur `json:"total_open_orders_hold_amount"` - UnrealizedPNL ValCur `json:"unrealized_pnl"` - DailyRealizedPNL ValCur `json:"daily_realized_pnl"` - InitialMargin ValCur `json:"initial_margin"` - AvailableMargin ValCur `json:"available_margin"` - LiquidationThreshold ValCur `json:"liquidation_threshold"` - LiquidationBufferAmount ValCur `json:"liquidation_buffer_amount"` - LiquidationBufferPercentage float64 `json:"liquidation_buffer_percentage,string"` + FuturesBuyingPower ValueWithCurrency `json:"futures_buying_power"` + TotalUSDBalance ValueWithCurrency `json:"total_usd_balance"` + CBIUSDBalance ValueWithCurrency `json:"cbi_usd_balance"` + CFMUSDBalance ValueWithCurrency `json:"cfm_usd_balance"` + TotalOpenOrdersHoldAmount ValueWithCurrency `json:"total_open_orders_hold_amount"` + UnrealizedPNL ValueWithCurrency `json:"unrealized_pnl"` + DailyRealizedPNL ValueWithCurrency `json:"daily_realized_pnl"` + InitialMargin ValueWithCurrency `json:"initial_margin"` + AvailableMargin ValueWithCurrency `json:"available_margin"` + LiquidationThreshold ValueWithCurrency `json:"liquidation_threshold"` + LiquidationBufferAmount ValueWithCurrency `json:"liquidation_buffer_amount"` + LiquidationBufferPercentage float64 `json:"liquidation_buffer_percentage,string"` } // FuturesPosition contains information on a single futures position, returned by @@ -509,38 +514,38 @@ type FuturesPosition struct { // SweepData contains information on pending and processing sweep requests, returned by ListFuturesSweeps type SweepData struct { - ID string `json:"id"` - RequestedAmount ValCur `json:"requested_amount"` - ShouldSweepAll bool `json:"should_sweep_all"` - Status string `json:"status"` - ScheduledTime time.Time `json:"scheduled_time"` + ID string `json:"id"` + RequestedAmount ValueWithCurrency `json:"requested_amount"` + ShouldSweepAll bool `json:"should_sweep_all"` + Status string `json:"status"` + ScheduledTime time.Time `json:"scheduled_time"` } // PerpetualsPortfolioSummary contains information on perpetuals portfolio balances, used as // a sub-struct in the types PerpPositionDetail, AllPerpPosResponse, and // OnePerpPosResponse type PerpetualsPortfolioSummary struct { - PortfolioUUID string `json:"portfolio_uuid"` - Collateral float64 `json:"collateral,string"` - PositionNotional float64 `json:"position_notional,string"` - OpenPositionNotional float64 `json:"open_position_notional,string"` - PendingFees float64 `json:"pending_fees,string"` - Borrow float64 `json:"borrow,string"` - AccruedInterest float64 `json:"accrued_interest,string"` - RollingDebt float64 `json:"rolling_debt,string"` - PortfolioInitialMargin float64 `json:"portfolio_initial_margin,string"` - PortfolioIMNotional ValCur `json:"portfolio_im_notional"` - PortfolioMaintenanceMargin float64 `json:"portfolio_maintenance_margin,string"` - PortfolioMMNotional ValCur `json:"portfolio_mm_notional"` - LiquidationPercentage float64 `json:"liquidation_percentage,string"` - LiquidationBuffer float64 `json:"liquidation_buffer,string"` - MarginType string `json:"margin_type"` - MarginFlags string `json:"margin_flags"` - LiquidationStatus string `json:"liquidation_status"` - UnrealizedPNL ValCur `json:"unrealized_pnl"` - BuyingPower ValCur `json:"buying_power"` - TotalBalance ValCur `json:"total_balance"` - MaxWithDrawal ValCur `json:"max_withdrawal"` + PortfolioUUID string `json:"portfolio_uuid"` + Collateral float64 `json:"collateral,string"` + PositionNotional float64 `json:"position_notional,string"` + OpenPositionNotional float64 `json:"open_position_notional,string"` + PendingFees float64 `json:"pending_fees,string"` + Borrow float64 `json:"borrow,string"` + AccruedInterest float64 `json:"accrued_interest,string"` + RollingDebt float64 `json:"rolling_debt,string"` + PortfolioInitialMargin float64 `json:"portfolio_initial_margin,string"` + PortfolioIMNotional ValueWithCurrency `json:"portfolio_im_notional"` + PortfolioMaintenanceMargin float64 `json:"portfolio_maintenance_margin,string"` + PortfolioMMNotional ValueWithCurrency `json:"portfolio_mm_notional"` + LiquidationPercentage float64 `json:"liquidation_percentage,string"` + LiquidationBuffer float64 `json:"liquidation_buffer,string"` + MarginType string `json:"margin_type"` + MarginFlags string `json:"margin_flags"` + LiquidationStatus string `json:"liquidation_status"` + UnrealizedPNL ValueWithCurrency `json:"unrealized_pnl"` + BuyingPower ValueWithCurrency `json:"buying_power"` + TotalBalance ValueWithCurrency `json:"total_balance"` + MaxWithDrawal ValueWithCurrency `json:"max_withdrawal"` } // PerpPositionDetail contains information on a single perpetuals position, used as a sub-struct @@ -549,19 +554,19 @@ type PerpPositionDetail struct { ProductID currency.Pair `json:"product_id"` ProductUUID string `json:"product_uuid"` Symbol string `json:"symbol"` - VWAP ValCur `json:"vwap"` + VWAP ValueWithCurrency `json:"vwap"` PositionSide string `json:"position_side"` NetSize float64 `json:"net_size,string"` BuyOrderSize float64 `json:"buy_order_size,string"` SellOrderSize float64 `json:"sell_order_size,string"` IMContribution float64 `json:"im_contribution,string"` - UnrealizedPNL ValCur `json:"unrealized_pnl"` - MarkPrice ValCur `json:"mark_price"` - LiquidationPrice ValCur `json:"liquidation_price"` + UnrealizedPNL ValueWithCurrency `json:"unrealized_pnl"` + MarkPrice ValueWithCurrency `json:"mark_price"` + LiquidationPrice ValueWithCurrency `json:"liquidation_price"` Leverage float64 `json:"leverage,string"` - IMNotional ValCur `json:"im_notional"` - MMNotional ValCur `json:"mm_notional"` - PositionNotional ValCur `json:"position_notional"` + IMNotional ValueWithCurrency `json:"im_notional"` + MMNotional ValueWithCurrency `json:"mm_notional"` + PositionNotional ValueWithCurrency `json:"position_notional"` MarginType string `json:"margin_type"` LiquidationBuffer float64 `json:"liquidation_buffer,string"` LiquidationPercentage float64 `json:"liquidation_percentage,string"` @@ -644,11 +649,11 @@ type Disclosure struct { // FeeStruct is a sub-struct storing information on fees, used in ConvertResponse type FeeStruct struct { - Title string `json:"title"` - Description string `json:"description"` - Amount ValCur `json:"amount"` - Label string `json:"label"` - Disclosure Disclosure `json:"disclosure"` + Title string `json:"title"` + Description string `json:"description"` + Amount ValueWithCurrency `json:"amount"` + Label string `json:"label"` + Disclosure Disclosure `json:"disclosure"` } // Owner is a sub-struct, used in LedgerAccount @@ -675,8 +680,8 @@ type AccountStruct struct { // AmScale is a sub-struct storing information on amounts and scales, used in ConvertResponse type AmScale struct { - Amount ValCur `json:"amount"` - Scale int32 `json:"scale"` + Amount ValueWithCurrency `json:"amount"` + Scale int32 `json:"scale"` } // UnitPrice is a sub-struct used in ConvertResponse @@ -712,18 +717,18 @@ type CancellationReason struct { // TaxDetails is a sub-struct used in ConvertResponse type TaxDetails struct { - Name string `json:"name"` - Amount ValCur `json:"amount"` + Name string `json:"name"` + Amount ValueWithCurrency `json:"amount"` } // TradeIncentiveInfo is a sub-struct used in ConvertResponse type TradeIncentiveInfo struct { - AppliedIncentive bool `json:"applied_incentive"` - UserIncentiveID string `json:"user_incentive_id"` - CodeVal string `json:"code_val"` - EndsAt time.Time `json:"ends_at"` - FeeWithoutIncentive ValCur `json:"fee_without_incentive"` - Redeemed bool `json:"redeemed"` + AppliedIncentive bool `json:"applied_incentive"` + UserIncentiveID string `json:"user_incentive_id"` + CodeVal string `json:"code_val"` + EndsAt time.Time `json:"ends_at"` + FeeWithoutIncentive ValueWithCurrency `json:"fee_without_incentive"` + Redeemed bool `json:"redeemed"` } // ConvertResponse contains information on a convert trade, returned by CreateConvertQuote, @@ -731,10 +736,10 @@ type TradeIncentiveInfo struct { type ConvertResponse struct { ID string `json:"id"` Status string `json:"status"` - UserEnteredAmount ValCur `json:"user_entered_amount"` - Amount ValCur `json:"amount"` - Subtotal ValCur `json:"subtotal"` - Total ValCur `json:"total"` + UserEnteredAmount ValueWithCurrency `json:"user_entered_amount"` + Amount ValueWithCurrency `json:"amount"` + Subtotal ValueWithCurrency `json:"subtotal"` + Total ValueWithCurrency `json:"total"` Fees []FeeStruct `json:"fees"` TotalFee FeeStruct `json:"total_fee"` Source AccountStruct `json:"source"` @@ -747,22 +752,18 @@ type ConvertResponse struct { CancellationReason CancellationReason `json:"cancellation_reason"` SourceID string `json:"source_id"` TargetID string `json:"target_id"` - ExchangeRate ValCur `json:"exchange_rate"` + ExchangeRate ValueWithCurrency `json:"exchange_rate"` TaxDetails []TaxDetails `json:"tax_details"` TradeIncentiveInfo TradeIncentiveInfo `json:"trade_incentive_info"` TotalFeeWithoutTax FeeStruct `json:"total_fee_without_tax"` - FiatDenotedTotal ValCur `json:"fiat_denoted_total"` + FiatDenotedTotal ValueWithCurrency `json:"fiat_denoted_total"` } -// UnixTimestampMilli is a type used to unmarshal unix millisecond timestamps returned from -// the exchange, used in the type ServerTimeV3 -type UnixTimestampMilli time.Time - // ServerTimeV3 holds information on the server's time, returned by GetV3Time type ServerTimeV3 struct { - Iso time.Time `json:"iso"` - EpochSeconds UnixTimestamp `json:"epochSeconds"` - EpochMilliseconds UnixTimestampMilli `json:"epochMillis"` + Iso time.Time `json:"iso"` + EpochSeconds types.Time `json:"epochSeconds"` + EpochMilliseconds types.Time `json:"epochMillis"` } // PaymentMethodData is a sub-type that holds information on a payment method @@ -803,9 +804,7 @@ type PaginationResp struct { NextURI string `json:"next_uri"` } -// PaginationInp holds information needed to engage in pagination with Sign in With -// Coinbase. Used in ListNotifications, GetAllWallets, GetAllAddresses, GetAddressTransactions, -// GetAllTransactions, GetAllFiatTransfers, GetAllPaymentMethods, and preparePagination +// PaginationInp holds information needed to engage in pagination with Sign in With Coinbase. Used in ListNotifications, GetAllWallets, GetAllAddresses, GetAddressTransactions, GetAllTransactions, GetAllFiatTransfers, GetAllPaymentMethods, and preparePagination type PaginationInp struct { Limit uint8 OrderAscend bool @@ -813,45 +812,44 @@ type PaginationInp struct { EndingBefore string } -// AmCur is a sub-struct used in ListNotificationsSubData, WalletData, TransactionData, -// DeposWithdrData, and PaymentMethodData -type AmCur struct { +// AmountWithCurrency is a sub-struct used in ListNotificationsSubData, WalletData, TransactionData, DeposWithdrData, and PaymentMethodData +type AmountWithCurrency struct { Amount float64 `json:"amount,string"` Currency string `json:"currency"` } // Fees is a sub-struct used in ListNotificationsSubData type Fees []struct { - Type string `json:"type"` - Amount AmCur `json:"amount"` + Type string `json:"type"` + Amount AmountWithCurrency `json:"amount"` } // ListNotificationsSubData is a sub-struct used in ListNotificationsData type ListNotificationsSubData struct { - ID string `json:"id"` - Address string `json:"address"` - Name string `json:"name"` - Status string `json:"status"` - PaymentMethod IDResource `json:"payment_method"` - Transaction IDResource `json:"transaction"` - Amount AmCur `json:"amount"` - Total AmCur `json:"total"` - Subtotal AmCur `json:"subtotal"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - Resource string `json:"resource"` - ResourcePath string `json:"resource_path"` - Committed bool `json:"committed"` - Instant bool `json:"instant"` - Fee AmCur `json:"fee"` - Fees []Fees `json:"fees"` - PayoutAt time.Time `json:"payout_at"` + ID string `json:"id"` + Address string `json:"address"` + Name string `json:"name"` + Status string `json:"status"` + PaymentMethod IDResource `json:"payment_method"` + Transaction IDResource `json:"transaction"` + Amount AmountWithCurrency `json:"amount"` + Total AmountWithCurrency `json:"total"` + Subtotal AmountWithCurrency `json:"subtotal"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Resource string `json:"resource"` + ResourcePath string `json:"resource_path"` + Committed bool `json:"committed"` + Instant bool `json:"instant"` + Fee AmountWithCurrency `json:"fee"` + Fees []Fees `json:"fees"` + PayoutAt time.Time `json:"payout_at"` } // AdditionalData is a sub-struct used in ListNotificationsData type AdditionalData struct { - Hash string `json:"hash"` - Amount AmCur `json:"amount"` + Hash string `json:"hash"` + Amount AmountWithCurrency `json:"amount"` } // ListNotificationsData is a sub-struct used in ListNotificationsResponse @@ -958,18 +956,18 @@ type Currency struct { // WalletData is a sub-struct holding wallet information, used in GetAllWalletsResponse type WalletData struct { - ID string `json:"id"` - Name string `json:"name"` - Primary bool `json:"primary"` - Type string `json:"type"` - Currency Currency `json:"currency"` - Balance AmCur `json:"balance"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - Resource string `json:"resource"` - ResourcePath string `json:"resource_path"` - AllowDeposits bool `json:"allow_deposits"` - AllowWithdrawals bool `json:"allow_withdrawals"` + ID string `json:"id"` + Name string `json:"name"` + Primary bool `json:"primary"` + Type string `json:"type"` + Currency Currency `json:"currency"` + Balance AmountWithCurrency `json:"balance"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Resource string `json:"resource"` + ResourcePath string `json:"resource_path"` + AllowDeposits bool `json:"allow_deposits"` + AllowWithdrawals bool `json:"allow_withdrawals"` } // GetAllWalletsResponse holds information on many wallets, returned by GetAllWallets @@ -1067,20 +1065,20 @@ type Network struct { // TransactionData is a sub-type that holds information on a transaction. Used in // ManyTransactionsResp type TransactionData struct { - ID string `json:"id"` - Type string `json:"type"` - Status string `json:"status"` - Amount AmCur `json:"amount"` - NativeAmount AmCur `json:"native_amount"` - Description string `json:"description"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - Resource string `json:"resource"` - ResourcePath string `json:"resource_path"` - Details TitleSubtitle `json:"details"` - Network Network `json:"network"` - To IDResource `json:"to"` - From IDResource `json:"from"` + ID string `json:"id"` + Type string `json:"type"` + Status string `json:"status"` + Amount AmountWithCurrency `json:"amount"` + NativeAmount AmountWithCurrency `json:"native_amount"` + Description string `json:"description"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Resource string `json:"resource"` + ResourcePath string `json:"resource_path"` + Details TitleSubtitle `json:"details"` + Network Network `json:"network"` + To IDResource `json:"to"` + From IDResource `json:"from"` } // ManyTransactionsResp holds information on many transactions. Returned by @@ -1093,20 +1091,20 @@ type ManyTransactionsResp struct { // DeposWithdrData is a sub-type that holds information on a deposit/withdrawal. Used in // ManyDeposWithdrResp type DeposWithdrData struct { - ID string `json:"id"` - Status string `json:"status"` - PaymentMethod IDResource `json:"payment_method"` - Transaction IDResource `json:"transaction"` - Amount AmCur `json:"amount"` - Subtotal AmCur `json:"subtotal"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - Resource string `json:"resource"` - ResourcePath string `json:"resource_path"` - Committed bool `json:"committed"` - Fee AmCur `json:"fee"` - PayoutAt time.Time `json:"payout_at"` - TransferType FiatTransferType `json:"transfer_type"` + ID string `json:"id"` + Status string `json:"status"` + PaymentMethod IDResource `json:"payment_method"` + Transaction IDResource `json:"transaction"` + Amount AmountWithCurrency `json:"amount"` + Subtotal AmountWithCurrency `json:"subtotal"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Resource string `json:"resource"` + ResourcePath string `json:"resource_path"` + Committed bool `json:"committed"` + Fee AmountWithCurrency `json:"fee"` + PayoutAt time.Time `json:"payout_at"` + TransferType FiatTransferType `json:"transfer_type"` } // ManyDeposWithdrResp holds information on many deposits. Returned by GetAllFiatTransfers @@ -1185,7 +1183,7 @@ type WebsocketTickerHolder struct { // WebsocketCandle defines a candle websocket response, used in WebsocketCandleHolder type WebsocketCandle struct { - Start UnixTimestamp `json:"start"` + Start types.Time `json:"start"` Low float64 `json:"low,string"` High float64 `json:"high,string"` Open float64 `json:"open,string"` @@ -1429,12 +1427,12 @@ type Auction struct { // OrderBookResp holds information on bids and asks for a particular currency pair, used for unmarshalling in // GetProductBookV1 type OrderBookResp struct { - Bids [][3]interface{} `json:"bids"` - Asks [][3]interface{} `json:"asks"` - Sequence float64 `json:"sequence"` - AuctionMode bool `json:"auction_mode"` - Auction Auction `json:"auction"` - Time time.Time `json:"time"` + Bids [][3]any `json:"bids"` + Asks [][3]any `json:"asks"` + Sequence float64 `json:"sequence"` + AuctionMode bool `json:"auction_mode"` + Auction Auction `json:"auction"` + Time time.Time `json:"time"` } // Orders holds information on orders, used as a sub-struct in OrderBook @@ -1456,7 +1454,7 @@ type OrderBook struct { } // RawCandles holds raw candle data, used in unmarshalling for GetProductCandles -type RawCandles [6]interface{} +type RawCandles [6]any // Candle holds properly formatted candle data, returned by GetProductCandles type Candle struct { diff --git a/exchanges/coinbasepro/coinbasepro_websocket.go b/exchanges/coinbasepro/coinbasepro_websocket.go index cc57655bed3..723b80fd284 100644 --- a/exchanges/coinbasepro/coinbasepro_websocket.go +++ b/exchanges/coinbasepro/coinbasepro_websocket.go @@ -535,11 +535,7 @@ func getTimestamp(rawData []byte) (time.Time, error) { if err != nil { return time.Time{}, err } - timestamp, err := time.Parse(time.RFC3339, string(data)) - if err != nil { - return time.Time{}, err - } - return timestamp, nil + return time.Parse(time.RFC3339, string(data)) } // processBidAskArray is a helper function that turns WebsocketOrderbookDataHolder into arrays of bids and asks @@ -609,7 +605,7 @@ func stringToStandardAsset(str string) (asset.Item, error) { } // strategyDecoder is a helper function that converts a Coinbase Pro time in force string to a few standardised bools -func strategyDecoder(str string) (bool, bool, error) { +func strategyDecoder(str string) (ioc, fok bool, err error) { switch str { case "IMMEDIATE_OR_CANCEL": return true, false, nil diff --git a/exchanges/coinbasepro/coinbasepro_wrapper.go b/exchanges/coinbasepro/coinbasepro_wrapper.go index c3ce3a819f1..a675e7ba905 100644 --- a/exchanges/coinbasepro/coinbasepro_wrapper.go +++ b/exchanges/coinbasepro/coinbasepro_wrapper.go @@ -346,32 +346,32 @@ func (c *CoinbasePro) UpdateOrderbook(ctx context.Context, p currency.Pair, asse Asset: assetType, VerifyOrderbook: c.CanVerifyOrderbook, } - var orderbookNew *ProductBook + var orderbookNew *ProductBookResp if verified { - orderbookNew, err = c.GetProductBookV3(ctx, p.String(), 1000, true) + orderbookNew, err = c.GetProductBookV3(ctx, p, 1000, 0, true) if err != nil { log.Warnf(log.ExchangeSys, warnAuth, err) verified = false } } if !verified { - orderbookNew, err = c.GetProductBookV3(ctx, p.String(), 1000, false) + orderbookNew, err = c.GetProductBookV3(ctx, p, 1000, 0, false) if err != nil { return book, err } } - book.Bids = make(orderbook.Tranches, len(orderbookNew.Bids)) - for x := range orderbookNew.Bids { + book.Bids = make(orderbook.Tranches, len(orderbookNew.Pricebook.Bids)) + for x := range orderbookNew.Pricebook.Bids { book.Bids[x] = orderbook.Tranche{ - Amount: orderbookNew.Bids[x].Size, - Price: orderbookNew.Bids[x].Price, + Amount: orderbookNew.Pricebook.Bids[x].Size, + Price: orderbookNew.Pricebook.Bids[x].Price, } } - book.Asks = make(orderbook.Tranches, len(orderbookNew.Asks)) - for x := range orderbookNew.Asks { + book.Asks = make(orderbook.Tranches, len(orderbookNew.Pricebook.Asks)) + for x := range orderbookNew.Pricebook.Asks { book.Asks[x] = orderbook.Tranche{ - Amount: orderbookNew.Asks[x].Size, - Price: orderbookNew.Asks[x].Price, + Amount: orderbookNew.Pricebook.Asks[x].Size, + Price: orderbookNew.Pricebook.Asks[x].Price, } } err = book.Process() @@ -1082,26 +1082,26 @@ func (c *CoinbasePro) iterativeGetAllOrders(ctx context.Context, productID, orde } // FormatExchangeKlineIntervalV3 is a helper function used in GetHistoricCandles and GetHistoricCandlesExtended to convert kline.Interval to the string format used by V3 of Coinbase's API -func FormatExchangeKlineIntervalV3(interval kline.Interval) string { +func FormatExchangeKlineIntervalV3(interval kline.Interval) (string, error) { switch interval { case kline.OneMin: - return granOneMin + return granOneMin, nil case kline.FiveMin: - return granFiveMin + return granFiveMin, nil case kline.FifteenMin: - return granFifteenMin + return granFifteenMin, nil case kline.ThirtyMin: - return granThirtyMin + return granThirtyMin, nil case kline.OneHour: - return granOneHour + return granOneHour, nil case kline.TwoHour: - return granTwoHour + return granTwoHour, nil case kline.SixHour: - return granSixHour + return granSixHour, nil case kline.OneDay: - return granOneDay + return granOneDay, nil } - return errIntervalNotSupported + return "", errIntervalNotSupported } // getOrderRespToOrderDetail is a helper function used in GetOrderInfo, GetActiveOrders, and GetOrderHistory to convert data returned by the Coinbase API into a format suitable for the exchange package @@ -1245,7 +1245,11 @@ func (c *CoinbasePro) tickerHelper(ctx context.Context, name string, assetType a // CandleHelper handles calling the candle function, and doing preliminary work on the data func (c *CoinbasePro) candleHelper(ctx context.Context, pair string, granularity kline.Interval, start, end time.Time, verified bool) ([]kline.Candle, error) { - history, err := c.GetHistoricRates(ctx, pair, FormatExchangeKlineIntervalV3(granularity), start, end, verified) + granString, err := FormatExchangeKlineIntervalV3(granularity) + if err != nil { + return nil, err + } + history, err := c.GetHistoricRates(ctx, pair, granString, start, end, verified) if err != nil { if verified { return c.candleHelper(ctx, pair, granularity, start, end, false)