ccxt 4.4.67 → 4.4.68

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/ccxt.js CHANGED
@@ -200,7 +200,7 @@ var xt$1 = require('./src/pro/xt.js');
200
200
 
201
201
  //-----------------------------------------------------------------------------
202
202
  // this is updated by vss.js when building
203
- const version = '4.4.67';
203
+ const version = '4.4.68';
204
204
  Exchange["default"].ccxtVersion = version;
205
205
  const exchanges = {
206
206
  'ace': ace,
@@ -4231,7 +4231,7 @@ class Exchange {
4231
4231
  return await this.fetch(request['url'], request['method'], request['headers'], request['body']);
4232
4232
  }
4233
4233
  catch (e) {
4234
- if (e instanceof errors.NetworkError) {
4234
+ if (e instanceof errors.OperationFailed) {
4235
4235
  if (i < retries) {
4236
4236
  if (this.verbose) {
4237
4237
  this.log('Request failed with the error: ' + e.toString() + ', retrying ' + (i + 1).toString() + ' of ' + retries.toString() + '...');
@@ -4239,10 +4239,12 @@ class Exchange {
4239
4239
  if ((retryDelay !== undefined) && (retryDelay !== 0)) {
4240
4240
  await this.sleep(retryDelay);
4241
4241
  }
4242
- // continue; //check this
4242
+ }
4243
+ else {
4244
+ throw e;
4243
4245
  }
4244
4246
  }
4245
- if (i >= retries) {
4247
+ else {
4246
4248
  throw e;
4247
4249
  }
4248
4250
  }
@@ -1675,7 +1675,7 @@ class bitrue extends bitrue$1 {
1675
1675
  const tickers = {};
1676
1676
  for (let i = 0; i < data.length; i++) {
1677
1677
  const ticker = this.safeDict(data, i, {});
1678
- const market = this.market(this.safeValue(ticker, 'symbol'));
1678
+ const market = this.safeMarket(this.safeString(ticker, 'symbol'));
1679
1679
  tickers[market['id']] = ticker;
1680
1680
  }
1681
1681
  return this.parseTickers(tickers, symbols);
@@ -9304,7 +9304,7 @@ class bybit extends bybit$1 {
9304
9304
  else {
9305
9305
  feedback = this.id + ' ' + body;
9306
9306
  }
9307
- if (body.indexOf('Withdraw address chain or destination tag are not equal')) {
9307
+ if (body.indexOf('Withdraw address chain or destination tag are not equal') > -1) {
9308
9308
  feedback = feedback + '; You might also need to ensure the address is whitelisted';
9309
9309
  }
9310
9310
  this.throwBroadlyMatchedException(this.exceptions['broad'], body, feedback);
@@ -4305,7 +4305,8 @@ class coinbase extends coinbase$1 {
4305
4305
  // }
4306
4306
  // }
4307
4307
  //
4308
- const data = this.safeDict(response, 'data', {});
4308
+ // https://github.com/ccxt/ccxt/issues/25484
4309
+ const data = this.safeDict2(response, 'data', 'transfer', {});
4309
4310
  return this.parseTransaction(data);
4310
4311
  }
4311
4312
  /**
@@ -4373,7 +4374,8 @@ class coinbase extends coinbase$1 {
4373
4374
  // }
4374
4375
  // }
4375
4376
  //
4376
- const data = this.safeDict(response, 'data', {});
4377
+ // https://github.com/ccxt/ccxt/issues/25484
4378
+ const data = this.safeDict2(response, 'data', 'transfer', {});
4377
4379
  return this.parseTransaction(data);
4378
4380
  }
4379
4381
  /**
@@ -4867,6 +4869,76 @@ class coinbase extends coinbase$1 {
4867
4869
  }
4868
4870
  return result;
4869
4871
  }
4872
+ /**
4873
+ * @method
4874
+ * @name coinbase#fetchPortfolioDetails
4875
+ * @description Fetch details for a specific portfolio by UUID
4876
+ * @see https://docs.cloud.coinbase.com/advanced-trade/reference/retailbrokerageapi_getportfolios
4877
+ * @param {string} portfolioUuid The unique identifier of the portfolio to fetch
4878
+ * @param {Dict} [params] Extra parameters specific to the exchange API endpoint
4879
+ * @returns {any[]} An account structure <https://docs.ccxt.com/#/?id=account-structure>
4880
+ */
4881
+ async fetchPortfolioDetails(portfolioUuid, params = {}) {
4882
+ await this.loadMarkets();
4883
+ const request = {
4884
+ 'portfolio_uuid': portfolioUuid,
4885
+ };
4886
+ const response = await this.v3PrivateGetBrokeragePortfoliosPortfolioUuid(this.extend(request, params));
4887
+ const result = this.parsePortfolioDetails(response);
4888
+ return result;
4889
+ }
4890
+ /**
4891
+ * Parse a Coinbase portfolio JSON object and extract relevant trading information.
4892
+ * @param {Dict} portfolioData The JSON response containing portfolio details
4893
+ * @returns {any[]} List of dictionaries with parsed portfolio position data
4894
+ */
4895
+ parsePortfolioDetails(portfolioData) {
4896
+ const breakdown = portfolioData['breakdown'];
4897
+ const portfolioInfo = this.safeDict(breakdown, 'portfolio', {});
4898
+ const portfolioName = this.safeString(portfolioInfo, 'name', 'Unknown');
4899
+ const portfolioUuid = this.safeString(portfolioInfo, 'uuid', '');
4900
+ const spotPositions = this.safeList(breakdown, 'spot_positions', []);
4901
+ const parsedPositions = [];
4902
+ for (let i = 0; i < spotPositions.length; i++) {
4903
+ const position = spotPositions[i];
4904
+ const currencyCode = this.safeString(position, 'asset', 'Unknown');
4905
+ const availableBalanceStr = this.safeString(position, 'available_to_trade_fiat', '0');
4906
+ const availableBalance = this.parseNumber(availableBalanceStr);
4907
+ const totalBalanceFiatStr = this.safeString(position, 'total_balance_fiat', '0');
4908
+ const totalBalanceFiat = this.parseNumber(totalBalanceFiatStr);
4909
+ const holdAmount = totalBalanceFiat - availableBalance;
4910
+ const costBasisDict = this.safeDict(position, 'cost_basis', {});
4911
+ const costBasisStr = this.safeString(costBasisDict, 'value', '0');
4912
+ const averageEntryPriceDict = this.safeDict(position, 'average_entry_price', {});
4913
+ const averageEntryPriceStr = this.safeString(averageEntryPriceDict, 'value', '0');
4914
+ const positionData = {
4915
+ 'currency': currencyCode,
4916
+ 'available_balance': availableBalance,
4917
+ 'hold_amount': holdAmount > 0 ? holdAmount : 0,
4918
+ 'wallet_name': portfolioName,
4919
+ 'account_id': portfolioUuid,
4920
+ 'account_uuid': this.safeString(position, 'account_uuid', ''),
4921
+ 'total_balance_fiat': totalBalanceFiat,
4922
+ 'total_balance_crypto': this.parseNumber(this.safeString(position, 'total_balance_crypto', '0')),
4923
+ 'available_to_trade_fiat': this.parseNumber(this.safeString(position, 'available_to_trade_fiat', '0')),
4924
+ 'available_to_trade_crypto': this.parseNumber(this.safeString(position, 'available_to_trade_crypto', '0')),
4925
+ 'available_to_transfer_fiat': this.parseNumber(this.safeString(position, 'available_to_transfer_fiat', '0')),
4926
+ 'available_to_transfer_crypto': this.parseNumber(this.safeString(position, 'available_to_trade_crypto', '0')),
4927
+ 'allocation': this.parseNumber(this.safeString(position, 'allocation', '0')),
4928
+ 'cost_basis': this.parseNumber(costBasisStr),
4929
+ 'cost_basis_currency': this.safeString(costBasisDict, 'currency', 'USD'),
4930
+ 'is_cash': this.safeBool(position, 'is_cash', false),
4931
+ 'average_entry_price': this.parseNumber(averageEntryPriceStr),
4932
+ 'average_entry_price_currency': this.safeString(averageEntryPriceDict, 'currency', 'USD'),
4933
+ 'asset_uuid': this.safeString(position, 'asset_uuid', ''),
4934
+ 'unrealized_pnl': this.parseNumber(this.safeString(position, 'unrealized_pnl', '0')),
4935
+ 'asset_color': this.safeString(position, 'asset_color', ''),
4936
+ 'account_type': this.safeString(position, 'account_type', ''),
4937
+ };
4938
+ parsedPositions.push(positionData);
4939
+ }
4940
+ return parsedPositions;
4941
+ }
4870
4942
  createAuthToken(seconds, method = undefined, url = undefined) {
4871
4943
  // it may not work for v2
4872
4944
  let uri = undefined;
@@ -32,6 +32,7 @@ class luno extends luno$1 {
32
32
  'cancelOrder': true,
33
33
  'closeAllPositions': false,
34
34
  'closePosition': false,
35
+ 'createDepositAddress': true,
35
36
  'createOrder': true,
36
37
  'createReduceOnlyOrder': false,
37
38
  'fetchAccounts': true,
@@ -40,6 +41,7 @@ class luno extends luno$1 {
40
41
  'fetchClosedOrders': true,
41
42
  'fetchCrossBorrowRate': false,
42
43
  'fetchCrossBorrowRates': false,
44
+ 'fetchDepositAddress': true,
43
45
  'fetchFundingHistory': false,
44
46
  'fetchFundingRate': false,
45
47
  'fetchFundingRateHistory': false,
@@ -1208,6 +1210,116 @@ class luno extends luno$1 {
1208
1210
  'fee': undefined,
1209
1211
  }, currency);
1210
1212
  }
1213
+ /**
1214
+ * @method
1215
+ * @name luno#createDepositAddress
1216
+ * @description create a currency deposit address
1217
+ * @see https://www.luno.com/en/developers/api#tag/Receive/operation/createFundingAddress
1218
+ * @param {string} code unified currency code of the currency for the deposit address
1219
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1220
+ * @param {string} [params.name] an optional name for the new address
1221
+ * @param {int} [params.account_id] an optional account id for the new address
1222
+ * @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure}
1223
+ */
1224
+ async createDepositAddress(code, params = {}) {
1225
+ await this.loadMarkets();
1226
+ const currency = this.currency(code);
1227
+ const request = {
1228
+ 'asset': currency['id'],
1229
+ };
1230
+ const response = await this.privatePostFundingAddress(this.extend(request, params));
1231
+ //
1232
+ // {
1233
+ // "account_id": "string",
1234
+ // "address": "string",
1235
+ // "address_meta": [
1236
+ // {
1237
+ // "label": "string",
1238
+ // "value": "string"
1239
+ // }
1240
+ // ],
1241
+ // "asset": "string",
1242
+ // "assigned_at": 0,
1243
+ // "name": "string",
1244
+ // "network": 0,
1245
+ // "qr_code_uri": "string",
1246
+ // "receive_fee": "string",
1247
+ // "total_received": "string",
1248
+ // "total_unconfirmed": "string"
1249
+ // }
1250
+ //
1251
+ return this.parseDepositAddress(response, currency);
1252
+ }
1253
+ /**
1254
+ * @method
1255
+ * @name luno#fetchDepositAddress
1256
+ * @description fetch the deposit address for a currency associated with this account
1257
+ * @see https://www.luno.com/en/developers/api#tag/Receive/operation/getFundingAddress
1258
+ * @param {string} code unified currency code
1259
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
1260
+ * @param {string} [params.address] a specific cryptocurrency address to retrieve
1261
+ * @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure}
1262
+ */
1263
+ async fetchDepositAddress(code, params = {}) {
1264
+ await this.loadMarkets();
1265
+ const currency = this.currency(code);
1266
+ const request = {
1267
+ 'asset': currency['id'],
1268
+ };
1269
+ const response = await this.privateGetFundingAddress(this.extend(request, params));
1270
+ //
1271
+ // {
1272
+ // "account_id": "string",
1273
+ // "address": "string",
1274
+ // "address_meta": [
1275
+ // {
1276
+ // "label": "string",
1277
+ // "value": "string"
1278
+ // }
1279
+ // ],
1280
+ // "asset": "string",
1281
+ // "assigned_at": 0,
1282
+ // "name": "string",
1283
+ // "network": 0,
1284
+ // "qr_code_uri": "string",
1285
+ // "receive_fee": "string",
1286
+ // "total_received": "string",
1287
+ // "total_unconfirmed": "string"
1288
+ // }
1289
+ //
1290
+ return this.parseDepositAddress(response, currency);
1291
+ }
1292
+ parseDepositAddress(depositAddress, currency = undefined) {
1293
+ //
1294
+ // {
1295
+ // "account_id": "string",
1296
+ // "address": "string",
1297
+ // "address_meta": [
1298
+ // {
1299
+ // "label": "string",
1300
+ // "value": "string"
1301
+ // }
1302
+ // ],
1303
+ // "asset": "string",
1304
+ // "assigned_at": 0,
1305
+ // "name": "string",
1306
+ // "network": 0,
1307
+ // "qr_code_uri": "string",
1308
+ // "receive_fee": "string",
1309
+ // "total_received": "string",
1310
+ // "total_unconfirmed": "string"
1311
+ // }
1312
+ //
1313
+ const currencyId = this.safeStringUpper(depositAddress, 'currency');
1314
+ const code = this.safeCurrencyCode(currencyId, currency);
1315
+ return {
1316
+ 'info': depositAddress,
1317
+ 'currency': code,
1318
+ 'network': undefined,
1319
+ 'address': this.safeString(depositAddress, 'address'),
1320
+ 'tag': this.safeString(depositAddress, 'name'),
1321
+ };
1322
+ }
1211
1323
  sign(path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
1212
1324
  let url = this.urls['api'][api] + '/' + this.version + '/' + this.implodeParams(path, params);
1213
1325
  const query = this.omit(params, this.extractParams(path));
@@ -128,7 +128,7 @@ class tradeogre extends tradeogre$1 {
128
128
  'orders/{market}': 1,
129
129
  'ticker/{market}': 1,
130
130
  'history/{market}': 1,
131
- 'chart/{interval}/{market}/{timestamp}': 1,
131
+ 'chart/{interval}/{market}': 1,
132
132
  },
133
133
  },
134
134
  'private': {
@@ -441,6 +441,7 @@ class tradeogre extends tradeogre$1 {
441
441
  * @param {int} [since] timestamp in ms of the earliest candle to fetch
442
442
  * @param {int} [limit] the maximum amount of candles to fetch
443
443
  * @param {object} [params] extra parameters specific to the exchange API endpoint
444
+ * @param {int} [params.until] timestamp of the latest candle in ms
444
445
  * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
445
446
  */
446
447
  async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
@@ -450,13 +451,12 @@ class tradeogre extends tradeogre$1 {
450
451
  'market': market['id'],
451
452
  'interval': this.safeString(this.timeframes, timeframe, timeframe),
452
453
  };
453
- if (since === undefined) {
454
- throw new errors.BadRequest(this.id + ' fetchOHLCV requires a since argument');
454
+ const until = this.safeInteger(params, 'until');
455
+ if (until !== undefined) {
456
+ params = this.omit(params, 'until');
457
+ request['timestamp'] = until;
455
458
  }
456
- else {
457
- request['timestamp'] = since;
458
- }
459
- const response = await this.publicGetChartIntervalMarketTimestamp(this.extend(request, params));
459
+ const response = await this.publicGetChartIntervalMarket(this.extend(request, params));
460
460
  //
461
461
  // [
462
462
  // [
package/js/ccxt.d.ts CHANGED
@@ -4,7 +4,7 @@ import * as functions from './src/base/functions.js';
4
4
  import * as errors from './src/base/errors.js';
5
5
  import type { Int, int, Str, Strings, Num, Bool, IndexType, OrderSide, OrderType, MarketType, SubType, Dict, NullableDict, List, NullableList, Fee, OHLCV, OHLCVC, implicitReturnType, Market, Currency, Dictionary, MinMax, FeeInterface, TradingFeeInterface, MarketInterface, Trade, Order, OrderBook, Ticker, Transaction, Tickers, CurrencyInterface, Balance, BalanceAccount, Account, PartialBalances, Balances, DepositAddress, WithdrawalResponse, DepositAddressResponse, FundingRate, FundingRates, Position, BorrowInterest, LeverageTier, LedgerEntry, DepositWithdrawFeeNetwork, DepositWithdrawFee, TransferEntry, CrossBorrowRate, IsolatedBorrowRate, FundingRateHistory, OpenInterest, Liquidation, OrderRequest, CancellationRequest, FundingHistory, MarketMarginModes, MarginMode, Greeks, Conversion, Option, LastPrice, Leverage, MarginModification, Leverages, LastPrices, Currencies, TradingFees, MarginModes, OptionChain, IsolatedBorrowRates, CrossBorrowRates, LeverageTiers, LongShortRatio, OrderBooks, OpenInterests } from './src/base/types.js';
6
6
  import { BaseError, ExchangeError, AuthenticationError, PermissionDenied, AccountNotEnabled, AccountSuspended, ArgumentsRequired, BadRequest, BadSymbol, OperationRejected, NoChange, MarginModeAlreadySet, MarketClosed, ManualInteractionNeeded, InsufficientFunds, InvalidAddress, AddressPending, InvalidOrder, OrderNotFound, OrderNotCached, OrderImmediatelyFillable, OrderNotFillable, DuplicateOrderId, ContractUnavailable, NotSupported, InvalidProxySettings, ExchangeClosedByUser, OperationFailed, NetworkError, DDoSProtection, RateLimitExceeded, ExchangeNotAvailable, OnMaintenance, InvalidNonce, ChecksumError, RequestTimeout, BadResponse, NullResponse, CancelPending, UnsubscribeError } from './src/base/errors.js';
7
- declare const version = "4.4.66";
7
+ declare const version = "4.4.67";
8
8
  import ace from './src/ace.js';
9
9
  import alpaca from './src/alpaca.js';
10
10
  import ascendex from './src/ascendex.js';
package/js/ccxt.js CHANGED
@@ -32,7 +32,7 @@ import * as errors from './src/base/errors.js';
32
32
  import { BaseError, ExchangeError, AuthenticationError, PermissionDenied, AccountNotEnabled, AccountSuspended, ArgumentsRequired, BadRequest, BadSymbol, OperationRejected, NoChange, MarginModeAlreadySet, MarketClosed, ManualInteractionNeeded, InsufficientFunds, InvalidAddress, AddressPending, InvalidOrder, OrderNotFound, OrderNotCached, OrderImmediatelyFillable, OrderNotFillable, DuplicateOrderId, ContractUnavailable, NotSupported, InvalidProxySettings, ExchangeClosedByUser, OperationFailed, NetworkError, DDoSProtection, RateLimitExceeded, ExchangeNotAvailable, OnMaintenance, InvalidNonce, ChecksumError, RequestTimeout, BadResponse, NullResponse, CancelPending, UnsubscribeError } from './src/base/errors.js';
33
33
  //-----------------------------------------------------------------------------
34
34
  // this is updated by vss.js when building
35
- const version = '4.4.67';
35
+ const version = '4.4.68';
36
36
  Exchange.ccxtVersion = version;
37
37
  //-----------------------------------------------------------------------------
38
38
  import ace from './src/ace.js';
@@ -5,7 +5,7 @@ interface Exchange {
5
5
  publicGetOrdersMarket(params?: {}): Promise<implicitReturnType>;
6
6
  publicGetTickerMarket(params?: {}): Promise<implicitReturnType>;
7
7
  publicGetHistoryMarket(params?: {}): Promise<implicitReturnType>;
8
- publicGetChartIntervalMarketTimestamp(params?: {}): Promise<implicitReturnType>;
8
+ publicGetChartIntervalMarket(params?: {}): Promise<implicitReturnType>;
9
9
  privateGetAccountBalance(params?: {}): Promise<implicitReturnType>;
10
10
  privateGetAccountBalances(params?: {}): Promise<implicitReturnType>;
11
11
  privateGetAccountOrderUuid(params?: {}): Promise<implicitReturnType>;
@@ -5,7 +5,7 @@ const { isNode, selfIsDefined, deepExtend, extend, clone, flatten, unique, index
5
5
  import { keys as keysFunc, values as valuesFunc, vwap as vwapFunc } from './functions.js';
6
6
  // import exceptions from "./errors.js"
7
7
  import { // eslint-disable-line object-curly-newline
8
- ExchangeError, BadSymbol, NullResponse, InvalidAddress, InvalidOrder, NotSupported, BadResponse, AuthenticationError, DDoSProtection, RequestTimeout, NetworkError, InvalidProxySettings, ExchangeNotAvailable, ArgumentsRequired, RateLimitExceeded, BadRequest, ExchangeClosedByUser, UnsubscribeError } from "./errors.js";
8
+ ExchangeError, BadSymbol, NullResponse, InvalidAddress, InvalidOrder, NotSupported, OperationFailed, BadResponse, AuthenticationError, DDoSProtection, RequestTimeout, NetworkError, InvalidProxySettings, ExchangeNotAvailable, ArgumentsRequired, RateLimitExceeded, BadRequest, ExchangeClosedByUser, UnsubscribeError } from "./errors.js";
9
9
  import { Precise } from './Precise.js';
10
10
  //-----------------------------------------------------------------------------
11
11
  import WsClient from './ws/WsClient.js';
@@ -4208,7 +4208,7 @@ export default class Exchange {
4208
4208
  return await this.fetch(request['url'], request['method'], request['headers'], request['body']);
4209
4209
  }
4210
4210
  catch (e) {
4211
- if (e instanceof NetworkError) {
4211
+ if (e instanceof OperationFailed) {
4212
4212
  if (i < retries) {
4213
4213
  if (this.verbose) {
4214
4214
  this.log('Request failed with the error: ' + e.toString() + ', retrying ' + (i + 1).toString() + ' of ' + retries.toString() + '...');
@@ -4216,10 +4216,12 @@ export default class Exchange {
4216
4216
  if ((retryDelay !== undefined) && (retryDelay !== 0)) {
4217
4217
  await this.sleep(retryDelay);
4218
4218
  }
4219
- // continue; //check this
4219
+ }
4220
+ else {
4221
+ throw e;
4220
4222
  }
4221
4223
  }
4222
- if (i >= retries) {
4224
+ else {
4223
4225
  throw e;
4224
4226
  }
4225
4227
  }
package/js/src/bitrue.js CHANGED
@@ -1672,7 +1672,7 @@ export default class bitrue extends Exchange {
1672
1672
  const tickers = {};
1673
1673
  for (let i = 0; i < data.length; i++) {
1674
1674
  const ticker = this.safeDict(data, i, {});
1675
- const market = this.market(this.safeValue(ticker, 'symbol'));
1675
+ const market = this.safeMarket(this.safeString(ticker, 'symbol'));
1676
1676
  tickers[market['id']] = ticker;
1677
1677
  }
1678
1678
  return this.parseTickers(tickers, symbols);
package/js/src/bybit.js CHANGED
@@ -9305,7 +9305,7 @@ export default class bybit extends Exchange {
9305
9305
  else {
9306
9306
  feedback = this.id + ' ' + body;
9307
9307
  }
9308
- if (body.indexOf('Withdraw address chain or destination tag are not equal')) {
9308
+ if (body.indexOf('Withdraw address chain or destination tag are not equal') > -1) {
9309
9309
  feedback = feedback + '; You might also need to ensure the address is whitelisted';
9310
9310
  }
9311
9311
  this.throwBroadlyMatchedException(this.exceptions['broad'], body, feedback);
@@ -605,6 +605,22 @@ export default class coinbase extends Exchange {
605
605
  * @returns {object} a dictionary of [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure} indexed by market symbols
606
606
  */
607
607
  fetchTradingFees(params?: {}): Promise<TradingFees>;
608
+ /**
609
+ * @method
610
+ * @name coinbase#fetchPortfolioDetails
611
+ * @description Fetch details for a specific portfolio by UUID
612
+ * @see https://docs.cloud.coinbase.com/advanced-trade/reference/retailbrokerageapi_getportfolios
613
+ * @param {string} portfolioUuid The unique identifier of the portfolio to fetch
614
+ * @param {Dict} [params] Extra parameters specific to the exchange API endpoint
615
+ * @returns {any[]} An account structure <https://docs.ccxt.com/#/?id=account-structure>
616
+ */
617
+ fetchPortfolioDetails(portfolioUuid: string, params?: {}): Promise<any[]>;
618
+ /**
619
+ * Parse a Coinbase portfolio JSON object and extract relevant trading information.
620
+ * @param {Dict} portfolioData The JSON response containing portfolio details
621
+ * @returns {any[]} List of dictionaries with parsed portfolio position data
622
+ */
623
+ parsePortfolioDetails(portfolioData: Dict): any[];
608
624
  createAuthToken(seconds: Int, method?: Str, url?: Str): string;
609
625
  nonce(): number;
610
626
  sign(path: any, api?: any[], method?: string, params?: {}, headers?: any, body?: any): {
@@ -4302,7 +4302,8 @@ export default class coinbase extends Exchange {
4302
4302
  // }
4303
4303
  // }
4304
4304
  //
4305
- const data = this.safeDict(response, 'data', {});
4305
+ // https://github.com/ccxt/ccxt/issues/25484
4306
+ const data = this.safeDict2(response, 'data', 'transfer', {});
4306
4307
  return this.parseTransaction(data);
4307
4308
  }
4308
4309
  /**
@@ -4370,7 +4371,8 @@ export default class coinbase extends Exchange {
4370
4371
  // }
4371
4372
  // }
4372
4373
  //
4373
- const data = this.safeDict(response, 'data', {});
4374
+ // https://github.com/ccxt/ccxt/issues/25484
4375
+ const data = this.safeDict2(response, 'data', 'transfer', {});
4374
4376
  return this.parseTransaction(data);
4375
4377
  }
4376
4378
  /**
@@ -4864,6 +4866,76 @@ export default class coinbase extends Exchange {
4864
4866
  }
4865
4867
  return result;
4866
4868
  }
4869
+ /**
4870
+ * @method
4871
+ * @name coinbase#fetchPortfolioDetails
4872
+ * @description Fetch details for a specific portfolio by UUID
4873
+ * @see https://docs.cloud.coinbase.com/advanced-trade/reference/retailbrokerageapi_getportfolios
4874
+ * @param {string} portfolioUuid The unique identifier of the portfolio to fetch
4875
+ * @param {Dict} [params] Extra parameters specific to the exchange API endpoint
4876
+ * @returns {any[]} An account structure <https://docs.ccxt.com/#/?id=account-structure>
4877
+ */
4878
+ async fetchPortfolioDetails(portfolioUuid, params = {}) {
4879
+ await this.loadMarkets();
4880
+ const request = {
4881
+ 'portfolio_uuid': portfolioUuid,
4882
+ };
4883
+ const response = await this.v3PrivateGetBrokeragePortfoliosPortfolioUuid(this.extend(request, params));
4884
+ const result = this.parsePortfolioDetails(response);
4885
+ return result;
4886
+ }
4887
+ /**
4888
+ * Parse a Coinbase portfolio JSON object and extract relevant trading information.
4889
+ * @param {Dict} portfolioData The JSON response containing portfolio details
4890
+ * @returns {any[]} List of dictionaries with parsed portfolio position data
4891
+ */
4892
+ parsePortfolioDetails(portfolioData) {
4893
+ const breakdown = portfolioData['breakdown'];
4894
+ const portfolioInfo = this.safeDict(breakdown, 'portfolio', {});
4895
+ const portfolioName = this.safeString(portfolioInfo, 'name', 'Unknown');
4896
+ const portfolioUuid = this.safeString(portfolioInfo, 'uuid', '');
4897
+ const spotPositions = this.safeList(breakdown, 'spot_positions', []);
4898
+ const parsedPositions = [];
4899
+ for (let i = 0; i < spotPositions.length; i++) {
4900
+ const position = spotPositions[i];
4901
+ const currencyCode = this.safeString(position, 'asset', 'Unknown');
4902
+ const availableBalanceStr = this.safeString(position, 'available_to_trade_fiat', '0');
4903
+ const availableBalance = this.parseNumber(availableBalanceStr);
4904
+ const totalBalanceFiatStr = this.safeString(position, 'total_balance_fiat', '0');
4905
+ const totalBalanceFiat = this.parseNumber(totalBalanceFiatStr);
4906
+ const holdAmount = totalBalanceFiat - availableBalance;
4907
+ const costBasisDict = this.safeDict(position, 'cost_basis', {});
4908
+ const costBasisStr = this.safeString(costBasisDict, 'value', '0');
4909
+ const averageEntryPriceDict = this.safeDict(position, 'average_entry_price', {});
4910
+ const averageEntryPriceStr = this.safeString(averageEntryPriceDict, 'value', '0');
4911
+ const positionData = {
4912
+ 'currency': currencyCode,
4913
+ 'available_balance': availableBalance,
4914
+ 'hold_amount': holdAmount > 0 ? holdAmount : 0,
4915
+ 'wallet_name': portfolioName,
4916
+ 'account_id': portfolioUuid,
4917
+ 'account_uuid': this.safeString(position, 'account_uuid', ''),
4918
+ 'total_balance_fiat': totalBalanceFiat,
4919
+ 'total_balance_crypto': this.parseNumber(this.safeString(position, 'total_balance_crypto', '0')),
4920
+ 'available_to_trade_fiat': this.parseNumber(this.safeString(position, 'available_to_trade_fiat', '0')),
4921
+ 'available_to_trade_crypto': this.parseNumber(this.safeString(position, 'available_to_trade_crypto', '0')),
4922
+ 'available_to_transfer_fiat': this.parseNumber(this.safeString(position, 'available_to_transfer_fiat', '0')),
4923
+ 'available_to_transfer_crypto': this.parseNumber(this.safeString(position, 'available_to_trade_crypto', '0')),
4924
+ 'allocation': this.parseNumber(this.safeString(position, 'allocation', '0')),
4925
+ 'cost_basis': this.parseNumber(costBasisStr),
4926
+ 'cost_basis_currency': this.safeString(costBasisDict, 'currency', 'USD'),
4927
+ 'is_cash': this.safeBool(position, 'is_cash', false),
4928
+ 'average_entry_price': this.parseNumber(averageEntryPriceStr),
4929
+ 'average_entry_price_currency': this.safeString(averageEntryPriceDict, 'currency', 'USD'),
4930
+ 'asset_uuid': this.safeString(position, 'asset_uuid', ''),
4931
+ 'unrealized_pnl': this.parseNumber(this.safeString(position, 'unrealized_pnl', '0')),
4932
+ 'asset_color': this.safeString(position, 'asset_color', ''),
4933
+ 'account_type': this.safeString(position, 'account_type', ''),
4934
+ };
4935
+ parsedPositions.push(positionData);
4936
+ }
4937
+ return parsedPositions;
4938
+ }
4867
4939
  createAuthToken(seconds, method = undefined, url = undefined) {
4868
4940
  // it may not work for v2
4869
4941
  let uri = undefined;
package/js/src/luno.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import Exchange from './abstract/luno.js';
2
- import type { Balances, Currency, Int, Market, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, OHLCV, Num, Account, TradingFeeInterface, Dict, int, LedgerEntry } from './base/types.js';
2
+ import type { Balances, Currency, Int, Market, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade, OHLCV, Num, Account, TradingFeeInterface, Dict, int, LedgerEntry, DepositAddress } from './base/types.js';
3
3
  /**
4
4
  * @class luno
5
5
  * @augments Exchange
@@ -210,6 +210,30 @@ export default class luno extends Exchange {
210
210
  referenceId: any;
211
211
  };
212
212
  parseLedgerEntry(entry: any, currency?: Currency): LedgerEntry;
213
+ /**
214
+ * @method
215
+ * @name luno#createDepositAddress
216
+ * @description create a currency deposit address
217
+ * @see https://www.luno.com/en/developers/api#tag/Receive/operation/createFundingAddress
218
+ * @param {string} code unified currency code of the currency for the deposit address
219
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
220
+ * @param {string} [params.name] an optional name for the new address
221
+ * @param {int} [params.account_id] an optional account id for the new address
222
+ * @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure}
223
+ */
224
+ createDepositAddress(code: string, params?: {}): Promise<DepositAddress>;
225
+ /**
226
+ * @method
227
+ * @name luno#fetchDepositAddress
228
+ * @description fetch the deposit address for a currency associated with this account
229
+ * @see https://www.luno.com/en/developers/api#tag/Receive/operation/getFundingAddress
230
+ * @param {string} code unified currency code
231
+ * @param {object} [params] extra parameters specific to the exchange API endpoint
232
+ * @param {string} [params.address] a specific cryptocurrency address to retrieve
233
+ * @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure}
234
+ */
235
+ fetchDepositAddress(code: string, params?: {}): Promise<DepositAddress>;
236
+ parseDepositAddress(depositAddress: any, currency?: Currency): DepositAddress;
213
237
  sign(path: any, api?: string, method?: string, params?: {}, headers?: any, body?: any): {
214
238
  url: string;
215
239
  method: string;