@proton/ccxt
Version:
A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 130+ exchanges
1,056 lines (1,053 loc) • 132 kB
JavaScript
// ----------------------------------------------------------------------------
// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
// EDIT THE CORRESPONDENT .ts FILE INSTEAD
// ----------------------------------------------------------------------------
import Exchange from './abstract/coinbase.js';
import { ExchangeError, ArgumentsRequired, AuthenticationError, BadRequest, InvalidOrder, NotSupported, OrderNotFound, RateLimitExceeded, InvalidNonce } from './base/errors.js';
import { Precise } from './base/Precise.js';
import { TICK_SIZE, TRUNCATE, DECIMAL_PLACES } from './base/functions/number.js';
import { sha256 } from './static_dependencies/noble-hashes/sha256.js';
// ----------------------------------------------------------------------------
export default class coinbase extends Exchange {
describe() {
return this.deepExtend(super.describe(), {
'id': 'coinbase',
'name': 'Coinbase',
'countries': ['US'],
'rateLimit': 400,
'version': 'v2',
'userAgent': this.userAgents['chrome'],
'headers': {
'CB-VERSION': '2018-05-30',
},
'has': {
'CORS': true,
'spot': true,
'margin': false,
'swap': false,
'future': false,
'option': false,
'addMargin': false,
'cancelOrder': true,
'cancelOrders': true,
'createDepositAddress': true,
'createLimitBuyOrder': true,
'createLimitSellOrder': true,
'createMarketBuyOrder': true,
'createMarketSellOrder': true,
'createOrder': true,
'createPostOnlyOrder': true,
'createReduceOnlyOrder': false,
'createStopLimitOrder': true,
'createStopMarketOrder': false,
'createStopOrder': true,
'fetchAccounts': true,
'fetchBalance': true,
'fetchBorrowRate': false,
'fetchBorrowRateHistories': false,
'fetchBorrowRateHistory': false,
'fetchBorrowRates': false,
'fetchBorrowRatesPerSymbol': false,
'fetchCanceledOrders': true,
'fetchClosedOrders': true,
'fetchCurrencies': true,
'fetchDeposits': true,
'fetchFundingHistory': false,
'fetchFundingRate': false,
'fetchFundingRateHistory': false,
'fetchFundingRates': false,
'fetchIndexOHLCV': false,
'fetchL2OrderBook': false,
'fetchLedger': true,
'fetchLeverage': false,
'fetchLeverageTiers': false,
'fetchMarginMode': false,
'fetchMarkets': true,
'fetchMarkOHLCV': false,
'fetchMyBuys': true,
'fetchMySells': true,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenInterestHistory': false,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrderBook': false,
'fetchOrders': true,
'fetchPosition': false,
'fetchPositionMode': false,
'fetchPositions': false,
'fetchPositionsRisk': false,
'fetchPremiumIndexOHLCV': false,
'fetchTicker': true,
'fetchTickers': true,
'fetchTime': true,
'fetchTrades': true,
'fetchTradingFee': false,
'fetchTradingFees': false,
'fetchWithdrawals': true,
'reduceMargin': false,
'setLeverage': false,
'setMarginMode': false,
'setPositionMode': false,
'withdraw': undefined,
},
'urls': {
'logo': 'https://user-images.githubusercontent.com/1294454/40811661-b6eceae2-653a-11e8-829e-10bfadb078cf.jpg',
'api': {
'rest': 'https://api.coinbase.com',
},
'www': 'https://www.coinbase.com',
'doc': [
'https://developers.coinbase.com/api/v2',
'https://docs.cloud.coinbase.com/advanced-trade-api/docs/welcome',
],
'fees': [
'https://support.coinbase.com/customer/portal/articles/2109597-buy-sell-bank-transfer-fees',
'https://www.coinbase.com/advanced-fees',
],
'referral': 'https://www.coinbase.com/join/58cbe25a355148797479dbd2',
},
'requiredCredentials': {
'apiKey': true,
'secret': true,
},
'api': {
'v2': {
'public': {
'get': [
'currencies',
'time',
'exchange-rates',
'users/{user_id}',
'prices/{symbol}/buy',
'prices/{symbol}/sell',
'prices/{symbol}/spot',
],
},
'private': {
'get': [
'accounts',
'accounts/{account_id}',
'accounts/{account_id}/addresses',
'accounts/{account_id}/addresses/{address_id}',
'accounts/{account_id}/addresses/{address_id}/transactions',
'accounts/{account_id}/transactions',
'accounts/{account_id}/transactions/{transaction_id}',
'accounts/{account_id}/buys',
'accounts/{account_id}/buys/{buy_id}',
'accounts/{account_id}/sells',
'accounts/{account_id}/sells/{sell_id}',
'accounts/{account_id}/deposits',
'accounts/{account_id}/deposits/{deposit_id}',
'accounts/{account_id}/withdrawals',
'accounts/{account_id}/withdrawals/{withdrawal_id}',
'payment-methods',
'payment-methods/{payment_method_id}',
'user',
'user/auth',
],
'post': [
'accounts',
'accounts/{account_id}/primary',
'accounts/{account_id}/addresses',
'accounts/{account_id}/transactions',
'accounts/{account_id}/transactions/{transaction_id}/complete',
'accounts/{account_id}/transactions/{transaction_id}/resend',
'accounts/{account_id}/buys',
'accounts/{account_id}/buys/{buy_id}/commit',
'accounts/{account_id}/sells',
'accounts/{account_id}/sells/{sell_id}/commit',
'accounts/{account_id}/deposits',
'accounts/{account_id}/deposits/{deposit_id}/commit',
'accounts/{account_id}/withdrawals',
'accounts/{account_id}/withdrawals/{withdrawal_id}/commit',
],
'put': [
'accounts/{account_id}',
'user',
],
'delete': [
'accounts/{id}',
'accounts/{account_id}/transactions/{transaction_id}',
],
},
},
'v3': {
'private': {
'get': [
'brokerage/accounts',
'brokerage/accounts/{account_uuid}',
'brokerage/orders/historical/batch',
'brokerage/orders/historical/fills',
'brokerage/orders/historical/{order_id}',
'brokerage/products',
'brokerage/products/{product_id}',
'brokerage/products/{product_id}/candles',
'brokerage/products/{product_id}/ticker',
'brokerage/transaction_summary',
'brokerage/product_book',
'brokerage/best_bid_ask',
],
'post': [
'brokerage/orders',
'brokerage/orders/batch_cancel',
],
},
},
},
'fees': {
'trading': {
'taker': this.parseNumber('0.006'),
'maker': this.parseNumber('0.004'),
'tierBased': true,
'percentage': true,
'tiers': {
'taker': [
[this.parseNumber('0'), this.parseNumber('0.006')],
[this.parseNumber('10000'), this.parseNumber('0.004')],
[this.parseNumber('50000'), this.parseNumber('0.0025')],
[this.parseNumber('100000'), this.parseNumber('0.002')],
[this.parseNumber('1000000'), this.parseNumber('0.0018')],
[this.parseNumber('15000000'), this.parseNumber('0.0016')],
[this.parseNumber('75000000'), this.parseNumber('0.0012')],
[this.parseNumber('250000000'), this.parseNumber('0.0008')],
[this.parseNumber('400000000'), this.parseNumber('0.0005')],
],
'maker': [
[this.parseNumber('0'), this.parseNumber('0.004')],
[this.parseNumber('10000'), this.parseNumber('0.0025')],
[this.parseNumber('50000'), this.parseNumber('0.0015')],
[this.parseNumber('100000'), this.parseNumber('0.001')],
[this.parseNumber('1000000'), this.parseNumber('0.0008')],
[this.parseNumber('15000000'), this.parseNumber('0.0006')],
[this.parseNumber('75000000'), this.parseNumber('0.0003')],
[this.parseNumber('250000000'), this.parseNumber('0.0')],
[this.parseNumber('400000000'), this.parseNumber('0.0')],
],
},
},
},
'precisionMode': TICK_SIZE,
'exceptions': {
'exact': {
'two_factor_required': AuthenticationError,
'param_required': ExchangeError,
'validation_error': ExchangeError,
'invalid_request': ExchangeError,
'personal_details_required': AuthenticationError,
'identity_verification_required': AuthenticationError,
'jumio_verification_required': AuthenticationError,
'jumio_face_match_verification_required': AuthenticationError,
'unverified_email': AuthenticationError,
'authentication_error': AuthenticationError,
'invalid_authentication_method': AuthenticationError,
'invalid_token': AuthenticationError,
'revoked_token': AuthenticationError,
'expired_token': AuthenticationError,
'invalid_scope': AuthenticationError,
'not_found': ExchangeError,
'rate_limit_exceeded': RateLimitExceeded,
'internal_server_error': ExchangeError, // 500 Internal server error
},
'broad': {
'request timestamp expired': InvalidNonce,
'order with this orderID was not found': OrderNotFound, // {"error":"unknown","error_details":"order with this orderID was not found","message":"order with this orderID was not found"}
},
},
'timeframes': {
'1m': 'ONE_MINUTE',
'5m': 'FIVE_MINUTE',
'15m': 'FIFTEEN_MINUTE',
'30m': 'THIRTY_MINUTE',
'1h': 'ONE_HOUR',
'2h': 'TWO_HOUR',
'6h': 'SIX_HOUR',
'1d': 'ONE_DAY',
},
'commonCurrencies': {
'CGLD': 'CELO',
},
'options': {
'stablePairs': ['BUSD-USD', 'CBETH-ETH', 'DAI-USD', 'GUSD-USD', 'GYEN-USD', 'PAX-USD', 'PAX-USDT', 'USDC-EUR', 'USDC-GBP', 'USDT-EUR', 'USDT-GBP', 'USDT-USD', 'USDT-USDC', 'WBTC-BTC'],
'fetchCurrencies': {
'expires': 5000,
},
'accounts': [
'wallet',
'fiat',
// 'vault',
],
'createMarketBuyOrderRequiresPrice': true,
'advanced': true,
'fetchMarkets': 'fetchMarketsV3',
'fetchTicker': 'fetchTickerV3',
'fetchTickers': 'fetchTickersV3',
'fetchAccounts': 'fetchAccountsV3', // 'fetchAccountsV3' or 'fetchAccountsV2'
},
});
}
async fetchTime(params = {}) {
/**
* @method
* @name coinbase#fetchTime
* @description fetches the current integer timestamp in milliseconds from the exchange server
* @param {object} params extra parameters specific to the coinbase api endpoint
* @returns {int} the current integer timestamp in milliseconds from the exchange server
*/
const response = await this.v2PublicGetTime(params);
//
// {
// "data": {
// "epoch": 1589295679,
// "iso": "2020-05-12T15:01:19Z"
// }
// }
//
const data = this.safeValue(response, 'data', {});
return this.safeTimestamp(data, 'epoch');
}
async fetchAccounts(params = {}) {
/**
* @method
* @name coinbase#fetchAccounts
* @description fetch all the accounts associated with a profile
* @param {object} params extra parameters specific to the coinbase api endpoint
* @returns {object} a dictionary of [account structures]{@link https://docs.ccxt.com/#/?id=account-structure} indexed by the account type
*/
const method = this.safeString(this.options, 'fetchAccounts', 'fetchAccountsV3');
if (method === 'fetchAccountsV3') {
return await this.fetchAccountsV3(params);
}
return await this.fetchAccountsV2(params);
}
async fetchAccountsV2(params = {}) {
await this.loadMarkets();
const request = {
'limit': 100,
};
const response = await this.v2PrivateGetAccounts(this.extend(request, params));
//
// {
// "pagination": {
// "ending_before": null,
// "starting_after": null,
// "previous_ending_before": null,
// "next_starting_after": null,
// "limit": 244,
// "order": "desc",
// "previous_uri": null,
// "next_uri": null
// },
// "data": [
// {
// "id": "XLM",
// "name": "XLM Wallet",
// "primary": false,
// "type": "wallet",
// "currency": {
// "code": "XLM",
// "name": "Stellar Lumens",
// "color": "#000000",
// "sort_index": 127,
// "exponent": 7,
// "type": "crypto",
// "address_regex": "^G[A-Z2-7]{55}$",
// "asset_id": "13b83335-5ede-595b-821e-5bcdfa80560f",
// "destination_tag_name": "XLM Memo ID",
// "destination_tag_regex": "^[ -~]{1,28}$"
// },
// "balance": {
// "amount": "0.0000000",
// "currency": "XLM"
// },
// "created_at": null,
// "updated_at": null,
// "resource": "account",
// "resource_path": "/v2/accounts/XLM",
// "allow_deposits": true,
// "allow_withdrawals": true
// },
// ]
// }
//
const data = this.safeValue(response, 'data', []);
return this.parseAccounts(data, params);
}
async fetchAccountsV3(params = {}) {
await this.loadMarkets();
const request = {
'limit': 100,
};
const response = await this.v3PrivateGetBrokerageAccounts(this.extend(request, params));
//
// {
// "accounts": [
// {
// "uuid": "11111111-1111-1111-1111-111111111111",
// "name": "USDC Wallet",
// "currency": "USDC",
// "available_balance": {
// "value": "0.0000000000000000",
// "currency": "USDC"
// },
// "default": true,
// "active": true,
// "created_at": "2023-01-04T06:20:06.456Z",
// "updated_at": "2023-01-04T06:20:07.181Z",
// "deleted_at": null,
// "type": "ACCOUNT_TYPE_CRYPTO",
// "ready": false,
// "hold": {
// "value": "0.0000000000000000",
// "currency": "USDC"
// }
// },
// ...
// ],
// "has_next": false,
// "cursor": "",
// "size": 9
// }
//
const data = this.safeValue(response, 'accounts', []);
return this.parseAccounts(data, params);
}
parseAccount(account) {
//
// fetchAccountsV2
//
// {
// "id": "XLM",
// "name": "XLM Wallet",
// "primary": false,
// "type": "wallet",
// "currency": {
// "code": "XLM",
// "name": "Stellar Lumens",
// "color": "#000000",
// "sort_index": 127,
// "exponent": 7,
// "type": "crypto",
// "address_regex": "^G[A-Z2-7]{55}$",
// "asset_id": "13b83335-5ede-595b-821e-5bcdfa80560f",
// "destination_tag_name": "XLM Memo ID",
// "destination_tag_regex": "^[ -~]{1,28}$"
// },
// "balance": {
// "amount": "0.0000000",
// "currency": "XLM"
// },
// "created_at": null,
// "updated_at": null,
// "resource": "account",
// "resource_path": "/v2/accounts/XLM",
// "allow_deposits": true,
// "allow_withdrawals": true
// }
//
// fetchAccountsV3
//
// {
// "uuid": "11111111-1111-1111-1111-111111111111",
// "name": "USDC Wallet",
// "currency": "USDC",
// "available_balance": {
// "value": "0.0000000000000000",
// "currency": "USDC"
// },
// "default": true,
// "active": true,
// "created_at": "2023-01-04T06:20:06.456Z",
// "updated_at": "2023-01-04T06:20:07.181Z",
// "deleted_at": null,
// "type": "ACCOUNT_TYPE_CRYPTO",
// "ready": false,
// "hold": {
// "value": "0.0000000000000000",
// "currency": "USDC"
// }
// }
//
const active = this.safeValue(account, 'active');
const currencyIdV3 = this.safeString(account, 'currency');
const currency = this.safeValue(account, 'currency', {});
const currencyId = this.safeString(currency, 'code', currencyIdV3);
const typeV3 = this.safeString(account, 'name');
const typeV2 = this.safeString(account, 'type');
const parts = typeV3.split(' ');
return {
'id': this.safeString2(account, 'id', 'uuid'),
'type': (active !== undefined) ? this.safeStringLower(parts, 1) : typeV2,
'code': this.safeCurrencyCode(currencyId),
'info': account,
};
}
async createDepositAddress(code, params = {}) {
/**
* @method
* @name coinbase#createDepositAddress
* @description create a currency deposit address
* @param {string} code unified currency code of the currency for the deposit address
* @param {object} params extra parameters specific to the coinbase api endpoint
* @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure}
*/
let accountId = this.safeString(params, 'account_id');
params = this.omit(params, 'account_id');
if (accountId === undefined) {
await this.loadAccounts();
for (let i = 0; i < this.accounts.length; i++) {
const account = this.accounts[i];
if (account['code'] === code && account['type'] === 'wallet') {
accountId = account['id'];
break;
}
}
}
if (accountId === undefined) {
throw new ExchangeError(this.id + ' createDepositAddress() could not find the account with matching currency code, specify an `account_id` extra param');
}
const request = {
'account_id': accountId,
};
const response = await this.v2PrivatePostAccountsAccountIdAddresses(this.extend(request, params));
//
// {
// "data": {
// "id": "05b1ebbf-9438-5dd4-b297-2ddedc98d0e4",
// "address": "coinbasebase",
// "address_info": {
// "address": "coinbasebase",
// "destination_tag": "287594668"
// },
// "name": null,
// "created_at": "2019-07-01T14:39:29Z",
// "updated_at": "2019-07-01T14:39:29Z",
// "network": "eosio",
// "uri_scheme": "eosio",
// "resource": "address",
// "resource_path": "/v2/accounts/14cfc769-e852-52f3-b831-711c104d194c/addresses/05b1ebbf-9438-5dd4-b297-2ddedc98d0e4",
// "warnings": [
// {
// "title": "Only send EOS (EOS) to this address",
// "details": "Sending any other cryptocurrency will result in permanent loss.",
// "image_url": "https://dynamic-assets.coinbase.com/deaca3d47b10ed4a91a872e9618706eec34081127762d88f2476ac8e99ada4b48525a9565cf2206d18c04053f278f693434af4d4629ca084a9d01b7a286a7e26/asset_icons/1f8489bb280fb0a0fd643c1161312ba49655040e9aaaced5f9ad3eeaf868eadc.png"
// },
// {
// "title": "Both an address and EOS memo are required to receive EOS",
// "details": "If you send funds without an EOS memo or with an incorrect EOS memo, your funds cannot be credited to your account.",
// "image_url": "https://www.coinbase.com/assets/receive-warning-2f3269d83547a7748fb39d6e0c1c393aee26669bfea6b9f12718094a1abff155.png"
// }
// ],
// "warning_title": "Only send EOS (EOS) to this address",
// "warning_details": "Sending any other cryptocurrency will result in permanent loss.",
// "destination_tag": "287594668",
// "deposit_uri": "eosio:coinbasebase?dt=287594668",
// "callback_url": null
// }
// }
//
const data = this.safeValue(response, 'data', {});
const tag = this.safeString(data, 'destination_tag');
const address = this.safeString(data, 'address');
return {
'currency': code,
'tag': tag,
'address': address,
'info': response,
};
}
async fetchMySells(symbol = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name coinbase#fetchMySells
* @description fetch sells
* @param {string|undefined} symbol not used by coinbase fetchMySells ()
* @param {int|undefined} since timestamp in ms of the earliest sell, default is undefined
* @param {int|undefined} limit max number of sells to return, default is undefined
* @param {object} params extra parameters specific to the coinbase api endpoint
* @returns {object} a [list of order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
// v2 did't have an endpoint for all historical trades
const request = this.prepareAccountRequest(limit, params);
await this.loadMarkets();
const query = this.omit(params, ['account_id', 'accountId']);
const sells = await this.v2PrivateGetAccountsAccountIdSells(this.extend(request, query));
return this.parseTrades(sells['data'], undefined, since, limit);
}
async fetchMyBuys(symbol = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name coinbase#fetchMyBuys
* @description fetch buys
* @param {string|undefined} symbol not used by coinbase fetchMyBuys ()
* @param {int|undefined} since timestamp in ms of the earliest buy, default is undefined
* @param {int|undefined} limit max number of buys to return, default is undefined
* @param {object} params extra parameters specific to the coinbase api endpoint
* @returns {object} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
*/
// v2 did't have an endpoint for all historical trades
const request = this.prepareAccountRequest(limit, params);
await this.loadMarkets();
const query = this.omit(params, ['account_id', 'accountId']);
const buys = await this.v2PrivateGetAccountsAccountIdBuys(this.extend(request, query));
return this.parseTrades(buys['data'], undefined, since, limit);
}
async fetchTransactionsWithMethod(method, code = undefined, since = undefined, limit = undefined, params = {}) {
const request = await this.prepareAccountRequestWithCurrencyCode(code, limit, params);
await this.loadMarkets();
const query = this.omit(params, ['account_id', 'accountId']);
const response = await this[method](this.extend(request, query));
return this.parseTransactions(response['data'], undefined, since, limit);
}
async fetchWithdrawals(code = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name coinbase#fetchWithdrawals
* @description fetch all withdrawals made from an account
* @param {string|undefined} code unified currency code
* @param {int|undefined} since the earliest time in ms to fetch withdrawals for
* @param {int|undefined} limit the maximum number of withdrawals structures to retrieve
* @param {object} params extra parameters specific to the coinbase api endpoint
* @returns {[object]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure}
*/
// fiat only, for crypto transactions use fetchLedger
return await this.fetchTransactionsWithMethod('v2PrivateGetAccountsAccountIdWithdrawals', code, since, limit, params);
}
async fetchDeposits(code = undefined, since = undefined, limit = undefined, params = {}) {
/**
* @method
* @name coinbase#fetchDeposits
* @description fetch all deposits made to an account
* @param {string|undefined} code unified currency code
* @param {int|undefined} since the earliest time in ms to fetch deposits for
* @param {int|undefined} limit the maximum number of deposits structures to retrieve
* @param {object} params extra parameters specific to the coinbase api endpoint
* @returns {[object]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure}
*/
// fiat only, for crypto transactions use fetchLedger
return await this.fetchTransactionsWithMethod('v2PrivateGetAccountsAccountIdDeposits', code, since, limit, params);
}
parseTransactionStatus(status) {
const statuses = {
'created': 'pending',
'completed': 'ok',
'canceled': 'canceled',
};
return this.safeString(statuses, status, status);
}
parseTransaction(transaction, market = undefined) {
//
// fiat deposit
//
// {
// "id": "f34c19f3-b730-5e3d-9f72",
// "status": "completed",
// "payment_method": {
// "id": "a022b31d-f9c7-5043-98f2",
// "resource": "payment_method",
// "resource_path": "/v2/payment-methods/a022b31d-f9c7-5043-98f2"
// },
// "transaction": {
// "id": "04ed4113-3732-5b0c-af86-b1d2146977d0",
// "resource": "transaction",
// "resource_path": "/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/transactions/04ed4113-3732-5b0c-af86"
// },
// "user_reference": "2VTYTH",
// "created_at": "2017-02-09T07:01:18Z",
// "updated_at": "2017-02-09T07:01:26Z",
// "resource": "deposit",
// "resource_path": "/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/deposits/f34c19f3-b730-5e3d-9f72",
// "committed": true,
// "payout_at": "2017-02-12T07:01:17Z",
// "instant": false,
// "fee": { "amount": "0.00", "currency": "EUR" },
// "amount": { "amount": "114.02", "currency": "EUR" },
// "subtotal": { "amount": "114.02", "currency": "EUR" },
// "hold_until": null,
// "hold_days": 0,
// "hold_business_days": 0,
// "next_step": null
// }
//
// fiat_withdrawal
//
// {
// "id": "cfcc3b4a-eeb6-5e8c-8058",
// "status": "completed",
// "payment_method": {
// "id": "8b94cfa4-f7fd-5a12-a76a",
// "resource": "payment_method",
// "resource_path": "/v2/payment-methods/8b94cfa4-f7fd-5a12-a76a"
// },
// "transaction": {
// "id": "fcc2550b-5104-5f83-a444",
// "resource": "transaction",
// "resource_path": "/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/transactions/fcc2550b-5104-5f83-a444"
// },
// "user_reference": "MEUGK",
// "created_at": "2018-07-26T08:55:12Z",
// "updated_at": "2018-07-26T08:58:18Z",
// "resource": "withdrawal",
// "resource_path": "/v2/accounts/91cd2d36-3a91-55b6-a5d4-0124cf105483/withdrawals/cfcc3b4a-eeb6-5e8c-8058",
// "committed": true,
// "payout_at": "2018-07-31T08:55:12Z",
// "instant": false,
// "fee": { "amount": "0.15", "currency": "EUR" },
// "amount": { "amount": "13130.69", "currency": "EUR" },
// "subtotal": { "amount": "13130.84", "currency": "EUR" },
// "idem": "e549dee5-63ed-4e79-8a96",
// "next_step": null
// }
//
const subtotalObject = this.safeValue(transaction, 'subtotal', {});
const feeObject = this.safeValue(transaction, 'fee', {});
const id = this.safeString(transaction, 'id');
const timestamp = this.parse8601(this.safeValue(transaction, 'created_at'));
const updated = this.parse8601(this.safeValue(transaction, 'updated_at'));
const type = this.safeString(transaction, 'resource');
const amount = this.safeNumber(subtotalObject, 'amount');
const currencyId = this.safeString(subtotalObject, 'currency');
const currency = this.safeCurrencyCode(currencyId);
const feeCost = this.safeNumber(feeObject, 'amount');
const feeCurrencyId = this.safeString(feeObject, 'currency');
const feeCurrency = this.safeCurrencyCode(feeCurrencyId);
const fee = {
'cost': feeCost,
'currency': feeCurrency,
};
let status = this.parseTransactionStatus(this.safeString(transaction, 'status'));
if (status === undefined) {
const committed = this.safeValue(transaction, 'committed');
status = committed ? 'ok' : 'pending';
}
return {
'info': transaction,
'id': id,
'txid': id,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'network': undefined,
'address': undefined,
'addressTo': undefined,
'addressFrom': undefined,
'tag': undefined,
'tagTo': undefined,
'tagFrom': undefined,
'type': type,
'amount': amount,
'currency': currency,
'status': status,
'updated': updated,
'fee': fee,
};
}
parseTrade(trade, market = undefined) {
//
// fetchMyBuys, fetchMySells
//
// {
// "id": "67e0eaec-07d7-54c4-a72c-2e92826897df",
// "status": "completed",
// "payment_method": {
// "id": "83562370-3e5c-51db-87da-752af5ab9559",
// "resource": "payment_method",
// "resource_path": "/v2/payment-methods/83562370-3e5c-51db-87da-752af5ab9559"
// },
// "transaction": {
// "id": "441b9494-b3f0-5b98-b9b0-4d82c21c252a",
// "resource": "transaction",
// "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/transactions/441b9494-b3f0-5b98-b9b0-4d82c21c252a"
// },
// "amount": { "amount": "1.00000000", "currency": "BTC" },
// "total": { "amount": "10.25", "currency": "USD" },
// "subtotal": { "amount": "10.10", "currency": "USD" },
// "created_at": "2015-01-31T20:49:02Z",
// "updated_at": "2015-02-11T16:54:02-08:00",
// "resource": "buy",
// "resource_path": "/v2/accounts/2bbf394c-193b-5b2a-9155-3b4732659ede/buys/67e0eaec-07d7-54c4-a72c-2e92826897df",
// "committed": true,
// "instant": false,
// "fee": { "amount": "0.15", "currency": "USD" },
// "payout_at": "2015-02-18T16:54:00-08:00"
// }
//
// fetchTrades
//
// {
// "trade_id": "10092327",
// "product_id": "BTC-USDT",
// "price": "17488.12",
// "size": "0.0000623",
// "time": "2023-01-11T00:52:37.557001Z",
// "side": "BUY",
// "bid": "",
// "ask": ""
// }
//
// fetchMyTrades
//
// {
// "entry_id": "b88b82cc89e326a2778874795102cbafd08dd979a2a7a3c69603fc4c23c2e010",
// "trade_id": "cdc39e45-bbd3-44ec-bf02-61742dfb16a1",
// "order_id": "813a53c5-3e39-47bb-863d-2faf685d22d8",
// "trade_time": "2023-01-18T01:37:38.091377090Z",
// "trade_type": "FILL",
// "price": "21220.64",
// "size": "0.0046830664333996",
// "commission": "0.0000280983986004",
// "product_id": "BTC-USDT",
// "sequence_timestamp": "2023-01-18T01:37:38.092520Z",
// "liquidity_indicator": "UNKNOWN_LIQUIDITY_INDICATOR",
// "size_in_quote": true,
// "user_id": "1111111-1111-1111-1111-111111111111",
// "side": "BUY"
// }
//
let symbol = undefined;
const totalObject = this.safeValue(trade, 'total', {});
const amountObject = this.safeValue(trade, 'amount', {});
const subtotalObject = this.safeValue(trade, 'subtotal', {});
const feeObject = this.safeValue(trade, 'fee', {});
const marketId = this.safeString(trade, 'product_id');
market = this.safeMarket(marketId, market, '-');
if (market !== undefined) {
symbol = market['symbol'];
}
else {
const baseId = this.safeString(amountObject, 'currency');
const quoteId = this.safeString(totalObject, 'currency');
if ((baseId !== undefined) && (quoteId !== undefined)) {
const base = this.safeCurrencyCode(baseId);
const quote = this.safeCurrencyCode(quoteId);
symbol = base + '/' + quote;
}
}
const sizeInQuote = this.safeValue(trade, 'size_in_quote');
const v3Price = this.safeString(trade, 'price');
const v3Amount = (sizeInQuote) ? undefined : this.safeString(trade, 'size');
const v3Cost = (sizeInQuote) ? this.safeString(trade, 'size') : undefined;
const v3FeeCost = this.safeString(trade, 'commission');
const amountString = this.safeString(amountObject, 'amount', v3Amount);
const costString = this.safeString(subtotalObject, 'amount', v3Cost);
let priceString = undefined;
let cost = undefined;
if ((costString !== undefined) && (amountString !== undefined)) {
priceString = Precise.stringDiv(costString, amountString);
}
else {
priceString = v3Price;
}
if ((priceString !== undefined) && (amountString !== undefined)) {
cost = Precise.stringMul(priceString, amountString);
}
else {
cost = costString;
}
const feeCurrencyId = this.safeString(feeObject, 'currency');
const datetime = this.safeStringN(trade, ['created_at', 'trade_time', 'time']);
const side = this.safeStringLower2(trade, 'resource', 'side');
const takerOrMaker = this.safeStringLower(trade, 'liquidity_indicator');
return this.safeTrade({
'info': trade,
'id': this.safeString2(trade, 'id', 'trade_id'),
'order': this.safeString(trade, 'order_id'),
'timestamp': this.parse8601(datetime),
'datetime': datetime,
'symbol': symbol,
'type': undefined,
'side': (side === 'unknown_order_side') ? undefined : side,
'takerOrMaker': (takerOrMaker === 'unknown_liquidity_indicator') ? undefined : takerOrMaker,
'price': priceString,
'amount': amountString,
'cost': cost,
'fee': {
'cost': this.safeNumber(feeObject, 'amount', this.parseNumber(v3FeeCost)),
'currency': this.safeCurrencyCode(feeCurrencyId),
},
});
}
async fetchMarkets(params = {}) {
/**
* @method
* @name coinbase#fetchMarkets
* @description retrieves data on all markets for coinbase
* @param {object} params extra parameters specific to the exchange api endpoint
* @returns {[object]} an array of objects representing market data
*/
const method = this.safeString(this.options, 'fetchMarkets', 'fetchMarketsV3');
return await this[method](params);
}
async fetchMarketsV2(params = {}) {
const response = await this.fetchCurrenciesFromCache(params);
const currencies = this.safeValue(response, 'currencies', {});
const exchangeRates = this.safeValue(response, 'exchangeRates', {});
const data = this.safeValue(currencies, 'data', []);
const dataById = this.indexBy(data, 'id');
const rates = this.safeValue(this.safeValue(exchangeRates, 'data', {}), 'rates', {});
const baseIds = Object.keys(rates);
const result = [];
for (let i = 0; i < baseIds.length; i++) {
const baseId = baseIds[i];
const base = this.safeCurrencyCode(baseId);
const type = (baseId in dataById) ? 'fiat' : 'crypto';
// https://github.com/ccxt/ccxt/issues/6066
if (type === 'crypto') {
for (let j = 0; j < data.length; j++) {
const quoteCurrency = data[j];
const quoteId = this.safeString(quoteCurrency, 'id');
const quote = this.safeCurrencyCode(quoteId);
result.push({
'id': baseId + '-' + quoteId,
'symbol': base + '/' + quote,
'base': base,
'quote': quote,
'settle': undefined,
'baseId': baseId,
'quoteId': quoteId,
'settleId': undefined,
'type': 'spot',
'spot': true,
'margin': false,
'swap': false,
'future': false,
'option': false,
'active': undefined,
'contract': false,
'linear': undefined,
'inverse': undefined,
'contractSize': undefined,
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': undefined,
'price': undefined,
},
'limits': {
'leverage': {
'min': undefined,
'max': undefined,
},
'amount': {
'min': undefined,
'max': undefined,
},
'price': {
'min': undefined,
'max': undefined,
},
'cost': {
'min': this.safeNumber(quoteCurrency, 'min_size'),
'max': undefined,
},
},
'info': quoteCurrency,
});
}
}
}
return result;
}
async fetchMarketsV3(params = {}) {
const response = await this.v3PrivateGetBrokerageProducts(params);
//
// [
// {
// "product_id": "TONE-USD",
// "price": "0.01523",
// "price_percentage_change_24h": "1.94109772423025",
// "volume_24h": "19773129",
// "volume_percentage_change_24h": "437.0170530929949",
// "base_increment": "1",
// "quote_increment": "0.00001",
// "quote_min_size": "1",
// "quote_max_size": "10000000",
// "base_min_size": "26.7187147229469674",
// "base_max_size": "267187147.2294696735908216",
// "base_name": "TE-FOOD",
// "quote_name": "US Dollar",
// "watched": false,
// "is_disabled": false,
// "new": false,
// "status": "online",
// "cancel_only": false,
// "limit_only": false,
// "post_only": false,
// "trading_disabled": false,
// "auction_mode": false,
// "product_type": "SPOT",
// "quote_currency_id": "USD",
// "base_currency_id": "TONE",
// "fcm_trading_session_details": null,
// "mid_market_price": ""
// },
// ...
// ]
//
const fees = await this.v3PrivateGetBrokerageTransactionSummary(params);
//
// {
// "total_volume": 0,
// "total_fees": 0,
// "fee_tier": {
// "pricing_tier": "",
// "usd_from": "0",
// "usd_to": "10000",
// "taker_fee_rate": "0.006",
// "maker_fee_rate": "0.004"
// },
// "margin_rate": null,
// "goods_and_services_tax": null,
// "advanced_trade_only_volume": 0,
// "advanced_trade_only_fees": 0,
// "coinbase_pro_volume": 0,
// "coinbase_pro_fees": 0
// }
//
const feeTier = this.safeValue(fees, 'fee_tier', {});
const data = this.safeValue(response, 'products', []);
const result = [];
for (let i = 0; i < data.length; i++) {
const market = data[i];
const id = this.safeString(market, 'product_id');
const baseId = this.safeString(market, 'base_currency_id');
const quoteId = this.safeString(market, 'quote_currency_id');
const base = this.safeCurrencyCode(baseId);
const quote = this.safeCurrencyCode(quoteId);
const marketType = this.safeStringLower(market, 'product_type');
const tradingDisabled = this.safeValue(market, 'trading_disabled');
const stablePairs = this.safeValue(this.options, 'stablePairs', []);
result.push({
'id': id,
'symbol': base + '/' + quote,
'base': base,
'quote': quote,
'settle': undefined,
'baseId': baseId,
'quoteId': quoteId,
'settleId': undefined,
'type': marketType,
'spot': (marketType === 'spot'),
'margin': undefined,
'swap': false,
'future': false,
'option': false,
'active': !tradingDisabled,
'contract': false,
'linear': undefined,
'inverse': undefined,
'taker': this.inArray(id, stablePairs) ? 0.00001 : this.safeNumber(feeTier, 'taker_fee_rate'),
'maker': this.inArray(id, stablePairs) ? 0.0 : this.safeNumber(feeTier, 'maker_fee_rate'),
'contractSize': undefined,
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': this.safeNumber(market, 'base_increment'),
'price': this.safeNumber(market, 'quote_increment'),
},
'limits': {
'leverage': {