ccxt
Version:
1,224 lines • 140 kB
JavaScript
// ---------------------------------------------------------------------------
import Exchange from './abstract/pacifica.js';
import { ExchangeError, ArgumentsRequired, InvalidOrder, OrderNotFound, BadRequest, InsufficientFunds, PermissionDenied, RateLimitExceeded, ExchangeNotAvailable, RequestTimeout, NotSupported } from './base/errors.js';
import { Precise } from './base/Precise.js';
import { TICK_SIZE } from './base/functions/number.js';
import { eddsa } from './base/functions/crypto.js';
import { ed25519 } from './static_dependencies/noble-curves/ed25519.js';
// ---------------------------------------------------------------------------
/**
* @class pacifica
* @augments Exchange
*/
export default class pacifica extends Exchange {
describe() {
return this.deepExtend(super.describe(), {
'id': 'pacifica',
'name': 'Pacifica',
'countries': [],
'version': 'v1',
'isSandboxModeEnabled': false,
'rateLimit': 50,
'certified': false,
'pro': true,
'dex': true,
'has': {
'CORS': undefined,
'spot': false,
'margin': false,
'swap': true,
'future': true,
'option': false,
'addMargin': false,
'borrowCrossMargin': false,
'borrowIsolatedMargin': false,
'cancelAllOrders': true,
'cancelAllOrdersAfter': false,
'cancelOrder': true,
'cancelOrders': true,
'cancelOrdersForSymbols': undefined,
'closeAllPositions': false,
'closePosition': false,
'createMarketBuyOrderWithCost': false,
'createMarketOrderWithCost': false,
'createMarketSellOrderWithCost': false,
'createOrder': true,
'createOrders': true,
'createOrderWithTakeProfitAndStopLoss': true,
'createReduceOnlyOrder': true,
'createStopOrder': true,
'editOrder': true,
'editOrders': false,
'fetchAccounts': true,
'fetchBalance': true,
'fetchBorrowInterest': false,
'fetchBorrowRateHistories': false,
'fetchBorrowRateHistory': false,
'fetchCanceledAndClosedOrders': true,
'fetchCanceledOrders': true,
'fetchClosedOrders': true,
'fetchCrossBorrowRate': false,
'fetchCrossBorrowRates': false,
'fetchCurrencies': false,
'fetchDepositAddress': false,
'fetchDepositAddresses': false,
'fetchDeposits': false,
'fetchDepositWithdrawFee': 'emulated',
'fetchDepositWithdrawFees': false,
'fetchFundingHistory': true,
'fetchFundingRate': false,
'fetchFundingRateHistory': true,
'fetchFundingRates': true,
'fetchIndexOHLCV': false,
'fetchIsolatedBorrowRate': false,
'fetchIsolatedBorrowRates': false,
'fetchLedger': true,
'fetchLeverage': true,
'fetchLeverageTiers': false,
'fetchLiquidations': false,
'fetchMarginMode': true,
'fetchMarketLeverageTiers': false,
'fetchMarkets': true,
'fetchMarkOHLCV': false,
'fetchMyLiquidations': false,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenInterest': true,
'fetchOpenInterestHistory': false,
'fetchOpenInterests': true,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchOrders': true,
'fetchOrderTrades': false,
'fetchPosition': true,
'fetchPositionMode': false,
'fetchPositions': true,
'fetchPositionsRisk': false,
'fetchPremiumIndexOHLCV': false,
'fetchStatus': undefined,
'fetchTicker': 'emulated',
'fetchTickers': true,
'fetchTime': undefined,
'fetchTrades': true,
'fetchTradingFee': true,
'fetchTradingFees': false,
'fetchTransfer': false,
'fetchTransfers': false,
'fetchWithdrawal': false,
'fetchWithdrawals': false,
'reduceMargin': false,
'repayCrossMargin': false,
'repayIsolatedMargin': false,
'sandbox': true,
'setLeverage': true,
'setMarginMode': true,
'setPositionMode': false,
'transfer': true,
'withdraw': true,
},
'timeframes': {
'1m': '1m',
'3m': '3m',
'5m': '5m',
'15m': '15m',
'30m': '30m',
'1h': '1h',
'2h': '2h',
'4h': '4h',
'8h': '8h',
'12h': '12h',
'1d': '1d',
},
'hostname': 'pacifica.fi',
'urls': {
'logo': 'https://github.com/user-attachments/assets/f795515a-828e-4a04-8fca-bf19fcf17ea4',
'api': {
'public': 'https://api.{hostname}',
'private': 'https://api.{hostname}',
},
'test': {
'public': 'https://test-api.{hostname}',
'private': 'https://test-api.{hostname}',
},
'www': 'https://www.pacifica.fi',
'doc': 'https://docs.pacifica.fi/api-documentation/api/rest-api',
'fees': 'https://docs.pacifica.fi/trading-on-pacifica/trading-fees',
'referral': 'https://app.pacifica.fi?referral=ccxt',
},
'api': {
'public': {
'get': {
// ~12 weight depends on the limit 3 max for api-key, but min without api-key
'info': 1,
'info/prices': 1,
'kline': 12,
'kline/mark': 12,
'book': 1,
'trades': 1,
'funding_rate/history': 1,
'account': 1,
'account/settings': 1,
'positions': 1,
'trades/history': 12,
'funding/history': 1,
'portfolio': 1,
'account/balance/history': 12,
'orders': 1,
'orders/history': 12,
'orders/history_by_id': 1,
'account/builder_codes/approvals': 1,
},
},
'private': {
'post': {
'account/leverage': 1,
'account/margin': 1,
'account/withdraw': 1,
'account/subaccount/create': 1,
'account/subaccount/list': 1,
'account/subaccount/transfer': 1,
'orders/create': 1,
'orders/create_market': 1,
'orders/stop/create': 1,
'positions/tpsl': 1,
'orders/cancel': 0.5,
'orders/cancel_all': 0.5,
'orders/stop/cancel': 0.5,
'orders/edit': 1,
'orders/batch': 1,
'account/builder_codes/approve': 1,
'account/builder_codes/revoke': 1,
'agent/bind': 1,
'account/api_keys/create': 1,
'account/api_keys/revoke': 1,
'account/api_keys': 1,
},
},
},
'fees': {
'swap': {
'taker': this.parseNumber('0.0004'),
'maker': this.parseNumber('0.00015'),
},
},
//
// Reminder:
// If you're using an agent wallet, its private key must also be in the privateKey field in requiredCredentials.
// However, walletAddress must ALWAYS be equal to the main address. For the agent wallet address, there's a field in options: agentAddress
//
'requiredCredentials': {
'apiKey': false,
'secret': false,
'walletAddress': false,
'privateKey': true, // base58 solana private key
},
'exceptions': {
'exact': {
'400': BadRequest,
'403': PermissionDenied,
'404': BadRequest,
'409': ExchangeError,
'422': ExchangeError,
'429': RateLimitExceeded,
'500': ExchangeError,
'503': ExchangeNotAvailable,
'504': RequestTimeout,
},
'broad': {
'UNKNOWN': ExchangeError,
'ACCOUNT_NOT_FOUND': ExchangeError,
'BOOK_NOT_FOUND': ExchangeError,
'INVALID_TICK_LEVEL': InvalidOrder,
'INSUFFICIENT_BALANCE': InsufficientFunds,
'ORDER_NOT_FOUND': OrderNotFound,
'OVER_WITHDRAWAL': InsufficientFunds,
'INVALID_LEVERAGE': ExchangeError,
'CANNOT_UPDATE_MARGIN': ExchangeError,
'POSITION_NOT_FOUND': ExchangeError,
'POSITION_TPSL_LIMIT_EXCEEDED': InvalidOrder,
},
},
'precisionMode': TICK_SIZE,
'commonCurrencies': {},
'options': {
'agentAddress': undefined,
'apiKey': undefined,
'builderCode': 'CCXT',
'feeRate': '0.01',
'builderFee': true,
'batchOrdersMax': 10,
'defaultType': 'swap',
'defaultSlippage': '0.5',
'expiryWindow': 5000,
'maxCostHugeWithApiKey': 3,
'marketHelperProps': [],
'defaultMarginMode': 'cross',
'builderSupportOperations': {
'create_market_order': true,
'create_limit_order': true,
'create_stop_order': true,
'set_position_tpsl': true,
},
},
'features': {
'default': {
'sandbox': true,
'createOrder': {
'marginMode': false,
'triggerPrice': false,
'triggerPriceType': undefined,
'triggerDirection': false,
'stopLossPrice': false,
'takeProfitPrice': false,
'attachedStopLossTakeProfit': {
'triggerPriceType': {
'last': false,
'mark': false,
'index': false,
},
'triggerPrice': true,
'type': true,
'price': true,
},
'timeInForce': {
'IOC': true,
'FOK': false,
'PO': true,
'GTD': false,
},
'hedged': false,
'trailing': false,
'leverage': false,
'marketBuyByCost': false,
'marketBuyRequiresPrice': false,
'selfTradePrevention': false,
'iceberg': false,
},
'createOrders': {
'max': 10,
},
'editOrder': {
'side': false,
'type': false,
},
'fetchMyTrades': {
'marginMode': false,
'limit': 100,
'daysBack': undefined,
'untilDays': undefined,
'symbolRequired': false,
},
'fetchOrder': {
'marginMode': false,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOpenOrders': {
'marginMode': false,
'limit': 100,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOrders': {
'marginMode': false,
'limit': 100,
'daysBack': undefined,
'untilDays': undefined,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchClosedOrders': {
'marginMode': false,
'limit': 100,
'daysBack': undefined,
'daysBackCanceled': undefined,
'untilDays': undefined,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOHLCV': {
'limit': 3950,
},
'fetchLedger': {
'code': false,
},
},
'forPerps': {
'extends': 'default',
'createOrder': {
'stopLossPrice': true,
'takeProfitPrice': true,
'attachedStopLossTakeProfit': undefined,
},
},
'spot': undefined,
'swap': {
'linear': {
'extends': 'forPerps',
},
'inverse': {
'extends': 'forPerps',
},
},
'future': {
'linear': {
'extends': 'forPerps',
},
'inverse': {
'extends': 'forPerps',
},
},
},
});
}
async initializeClient() {
try {
await this.handleBuilderFeeApproval();
}
catch (e) {
return false;
}
return true;
}
async handleBuilderFeeApproval() {
if (this.isSandboxModeEnabled) { // At this stage, building codes are mostly only on the mainnet.
return false;
}
const buildFee = this.safeBool(this.options, 'builderFee', true);
if (!buildFee) {
return false; // skip if builder fee is not enabled
}
const approvedBuilderFee = this.safeBool(this.options, 'approvedBuilderFee', false);
if (approvedBuilderFee) {
return true; // skip if builder fee is already approved
}
try {
const builder = this.safeString(this.options, 'builderCode', 'CCXT'); // case sensitive
const maxFeeRate = this.safeString(this.options, 'feeRate', '0.01');
await this.approveBuilderCode(builder, maxFeeRate);
this.options['approvedBuilderFee'] = true;
}
catch (e) {
this.options['builderFee'] = false; // disable builder fee if an error occurs
}
return true;
}
/**
* @method
* @name pacifica#fetchMarkets
* @description retrieves data on all markets for pacifica
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} an array of objects representing market data
*/
async fetchMarkets(params = {}) {
if (this.checkRequiredCredentials(false)) {
await this.initializeClient();
await this.loadAccountSettings();
}
const swapMarkets = await this.fetchSwapMarkets(params);
return swapMarkets;
}
/**
* @method
* @name pacifica#fetchSwapMarkets
* @description retrieves data on all swap markets for pacifica
* @see https://docs.pacifica.fi/api-documentation/api/rest-api/markets/get-market-info
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} an array of objects representing market data
*/
async fetchSwapMarkets(params = {}) {
const response = await this.publicGetInfo(params); // meta
// {
// "success": true,
// "data": [
// {
// "symbol": "ETH",
// "tick_size": "0.1",
// "min_tick": "0",
// "max_tick": "1000000",
// "lot_size": "0.0001",
// "max_leverage": 50,
// "isolated_only": false,
// "min_order_size": "10",
// "max_order_size": "5000000",
// "funding_rate": "0.0000125",
// "next_funding_rate": "0.0000125",
// "created_at": 1748881333944
// },
// {
// "symbol": "BTC",
// "tick_size": "1",
// "min_tick": "0",
// "max_tick": "1000000",
// "lot_size": "0.00001",
// "max_leverage": 50,
// "isolated_only": false,
// "min_order_size": "10",
// "max_order_size": "5000000",
// "funding_rate": "0.0000125",
// "next_funding_rate": "0.0000125",
// "created_at": 1748881333944
// },
// ....
// ],
// "error": null,
// "code": null
// }
const meta = this.safeList(response, 'data', []);
const results = [];
for (let i = 0; i < meta.length; i++) {
results.push(meta[i]);
}
return this.parseMarkets(results);
}
parseMarket(market) {
// {
// "symbol": "ETH",
// "tick_size": "0.1",
// "min_tick": "0",
// "max_tick": "1000000",
// "lot_size": "0.0001",
// "max_leverage": 50,
// "isolated_only": false,
// "min_order_size": "10",
// "max_order_size": "5000000",
// "funding_rate": "0.0000125",
// "next_funding_rate": "0.0000125",
// "created_at": 1748881333944
// },
// {
// "symbol": "BTC",
// "tick_size": "1",
// "min_tick": "0",
// "max_tick": "1000000",
// "lot_size": "0.00001",
// "max_leverage": 50,
// "isolated_only": false,
// "min_order_size": "10",
// "max_order_size": "5000000",
// "funding_rate": "0.0000125",
// "next_funding_rate": "0.0000125",
// "created_at": 1748881333944
// },
const quoteId = 'usdc';
const settleId = 'usdc';
const id = this.safeString(market, 'symbol');
const baseId = id.toLowerCase();
const baseName = id.toUpperCase();
const base = this.safeCurrencyCode(baseName);
const quote = this.safeCurrencyCode(quoteId);
const settle = this.safeCurrencyCode(settleId);
let symbol = base + '/' + quote;
const contract = true;
const swap = true;
if (contract) {
if (swap) {
symbol = symbol + ':' + settle;
}
}
const fees = this.safeDict(this.fees, 'swap', {});
const taker = this.safeNumber(fees, 'taker');
const maker = this.safeNumber(fees, 'maker');
const amountPrecisionStr = this.safeString(market, 'lot_size');
const pricePrecisionStr = this.safeString(market, 'tick_size');
const active = true; // there is no non-active markets comes from endpoint market info
return this.safeMarketStructure({
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'settle': settle,
'baseId': baseId,
'baseName': baseName,
'quoteId': quoteId,
'settleId': settleId,
'type': 'swap',
'spot': false,
'margin': undefined,
'swap': swap,
'future': false,
'option': false,
'active': active,
'contract': contract,
'linear': true,
'inverse': false,
'taker': taker,
'maker': maker,
'contractSize': this.parseNumber('1'),
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': this.parseNumber(amountPrecisionStr),
'price': this.parseNumber(pricePrecisionStr),
},
'limits': {
'leverage': {
'min': 1,
'max': this.safeInteger(market, 'max_leverage'),
},
'amount': {
'min': undefined,
'max': undefined,
},
'price': {
'min': this.safeString(market, 'min_tick'),
'max': this.safeString(market, 'max_tick'),
},
'cost': {
'min': undefined,
'max': undefined,
},
},
'created': undefined,
'marginModes': { 'cross': true, 'isolated': true },
'info': market,
});
}
/**
* @method
* @name pacifica#fetchBalance
* @description query for balance and get the amount of funds available for trading or funds locked in orders
* @see https://docs.pacifica.fi/api-documentation/api/rest-api/account/get-account-info
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.account] will default to walletAddress if not provided
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/?id=balance-structure}
*/
async fetchBalance(params = {}) {
let userAccount = undefined;
[userAccount, params] = this.handleOriginAndSingleAddress('fetchBalance', params);
const request = {
'account': userAccount,
};
const response = await this.publicGetAccount(this.extend(request, params));
// {
// "success": true,
// "data": {
// "balance": "2000.000000",
// "fee_level": 0,
// "maker_fee": "0.00015",
// "taker_fee": "0.0004",
// "account_equity": "2150.250000",
// "available_to_spend": "1800.750000",
// "available_to_withdraw": "1500.850000",
// "pending_balance": "0.000000",
// "total_margin_used": "349.500000",
// "cross_mmr": "420.690000",
// "positions_count": 2,
// "orders_count": 3,
// "stop_orders_count": 1,
// "updated_at": 1716200000000,
// "use_ltp_for_stop_orders": false
// },
// "error": null,
// "code": null
// }
const data = this.safeDict(response, 'data', {});
const result = {
'info': data,
};
result['free'] = {};
result['used'] = {};
result['total'] = {};
const totalBalance = this.safeNumber(data, 'account_equity');
const usedMargin = this.safeNumber(data, 'total_margin_used');
const freeBalance = this.safeNumber(data, 'available_to_spend');
result['total']['USDC'] = totalBalance;
result['used']['USDC'] = usedMargin;
result['free']['USDC'] = freeBalance;
const timestamp = this.safeInteger(data, 'updated_at');
result['timestamp'] = timestamp;
result['datetime'] = this.iso8601(timestamp);
return this.safeBalance(result);
}
/**
* @method
* @name pacifica#fetchLeverage
* @description fetch the set leverage for a market
* @param {string} symbol unified symbol of the market
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.account] will default to walletAddress if not provided
* @returns {object} a [leverage structure]{@link https://docs.ccxt.com/?id=leverage-structure}
*/
async fetchLeverage(symbol, params = {}) {
await this.loadAccountSettings();
await this.loadMarkets();
const market = this.market(symbol);
let userAccount = undefined;
[userAccount, params] = this.handleOriginAndSingleAddress('fetchLeverage', params);
const cacheAddress = this.walletAddress;
let settings = undefined;
if (userAccount === cacheAddress) {
settings = this.handleOption('fetchLeverage', 'settings', undefined);
}
else {
const request = {
'account': userAccount,
};
settings = await this.fetchAccountSettings(this.extend(request, params));
}
const setting = this.safeDict(settings, symbol, undefined);
if (setting === undefined) {
// NOTE: Upon account creation, all markets have margin settings default to cross margin and leverage default to max.
// When querying this endpoint, all markets with default margin and leverage settings on this account will return blank.
return this.parseLeverageFromMarket(market);
}
else {
return this.parseLeverageFromSetting(symbol, setting);
}
}
parseLeverageFromSetting(symbol, setting) {
// {
// "WLFI/USDC:USDC": {
// "symbol": "WLFI",
// "isolated": false,
// "leverage": 5,
// "created_at": 1758085929703,
// "updated_at": 1758086074002
// },
// }
const isIsolated = this.safeBool(setting, 'isolated', false);
const leverage = this.safeInteger(setting, 'leverage');
const marginMode = isIsolated ? 'isolated' : 'cross';
return {
'info': setting,
'symbol': symbol,
'marginMode': marginMode,
'longLeverage': leverage,
'shortLeverage': leverage,
};
}
parseLeverageFromMarket(market) {
const marketLimits = this.safeDict(market, 'limits', {});
const leverageLimits = this.safeDict(marketLimits, 'leverage', {});
return {
'info': market,
'symbol': this.safeString(market, 'symbol'),
'marginMode': this.handleOption('fetchLeverage', 'defaultMarginMode', 'cross'),
'longLeverage': this.safeInteger(leverageLimits, 'max'),
'shortLeverage': this.safeInteger(leverageLimits, 'max'),
};
}
/**
* @method
* @name pacifica#fetchAccountSettings
* @description fetch account's market settings. Settings are cached for walletAddress. To refresh the cache, call loadAccountSettings with refresh=true
* @see https://docs.pacifica.fi/api-documentation/api/rest-api/account/get-account-settings
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.account] will default to walletAddress if not provided
* @returns {object} Dict repacked from list by symbol key
*/
async fetchAccountSettings(params = {}) {
let userAccount = undefined;
[userAccount, params] = this.handleOriginAndSingleAddress('fetchAccountSettings', params);
const request = {
'account': userAccount,
};
const response = await this.publicGetAccountSettings(this.extend(request, params));
// {
// "success": true,
// "data": [
// {
// "symbol": "WLFI",
// "isolated": false,
// "leverage": 5,
// "created_at": 1758085929703,
// "updated_at": 1758086074002
// }
// ],
// "error": null,
// "code": null
// }
return this.parseAccountSettings(this.safeList(response, 'data', []));
}
async loadAccountSettings(refresh = false, params = {}) {
let settings = this.handleOption('loadAccountSettings', 'settings', undefined);
if ((settings === undefined) || (refresh === true)) {
this.options['settings'] = this.createSafeDictionary();
settings = await this.fetchAccountSettings(params);
this.options['settings'] = settings;
}
}
parseAccountSettings(settings) {
const settingsLen = settings.length;
if (settingsLen === 0) {
return {};
}
const settingsBySymbol = {};
for (let i = 0; i < settings.length; i++) {
const marketId = settings[i]['symbol'];
const market = this.safeMarket(marketId);
const symbol = market['symbol'];
settingsBySymbol[symbol] = settings[i];
}
return settingsBySymbol;
}
/**
* @method
* @name pacifica#fetchMarginMode
* @description fetches the margin mode of the trading pair
* @param {string} symbol unified symbol of the market to fetch the margin mode for
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.account] will default to walletAddress if not provided
* @returns {object} a [margin mode structure]{@link https://docs.ccxt.com/?id=margin-mode-structure}
*/
async fetchMarginMode(symbol, params = {}) {
await this.loadAccountSettings();
let userAccount = undefined;
[userAccount, params] = this.handleOriginAndSingleAddress('fetchMarginMode', params);
const cacheAddress = this.walletAddress;
let settings = undefined;
if (userAccount === cacheAddress) {
settings = this.handleOption('fetchMarginMode', 'settings', undefined);
}
else {
const request = {
'account': userAccount,
};
settings = await this.fetchAccountSettings(this.extend(request, params));
}
// {
// "WLFI/USDC:USDC": {
// "symbol": "WLFI",
// "isolated": false,
// "leverage": 5,
// "created_at": 1758085929703,
// "updated_at": 1758086074002
// },
// }
const setting = this.safeDict(settings, symbol, undefined);
if (setting === undefined) {
// NOTE: Upon account creation, all markets have margin settings default to cross margin and leverage default to max.
// When querying this endpoint, all markets with default margin and leverage settings on this account will return blank.
return {
'symbol': symbol,
'marginMode': this.handleOption('fetchMarginMode', 'defaultMarginMode', 'cross'),
};
}
else {
return this.parseMarginModeFromSetting(symbol, setting);
}
}
parseMarginModeFromSetting(symbol, setting) {
// {
// "symbol": "WLFI",
// "isolated": false,
// "leverage": 5,
// "created_at": 1758085929703,
// "updated_at": 1758086074002
//
// }
const isIsolated = this.safeBool(setting, 'isolated', false);
const marginMode = isIsolated ? 'isolated' : 'cross';
return {
'symbol': symbol,
'marginMode': marginMode,
'info': setting,
};
}
/**
* @method
* @name pacifica#fetchOrderBook
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://docs.pacifica.fi/api-documentation/api/rest-api/markets/get-orderbook
* @param {string} symbol unified symbol of the market to fetch the order book for
* @param {int} [limit] the maximum amount of order book entries to return
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.aggLevel] aggregation level for price grouping. Defaults to 1. Can be 1, 10, 100, 1000, 10000
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/?id=order-book-structure} indexed by market symbols
*/
async fetchOrderBook(symbol, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
let aggLevel = undefined;
[aggLevel, params] = this.handleOptionAndParams(params, 'fetchOrderBook', 'aggLevel', 1);
const request = {
'symbol': market['id'],
'agg_level': aggLevel,
};
const response = await this.publicGetBook(this.extend(request, params));
// {
// "success": true,
// "data": {
// "s": "BTC",
// "l": [
// [
// {
// "p": "106504",
// "a": "0.26203",
// "n": 1
// },
// {
// "p": "106498",
// "a": "0.29281",
// "n": 1
// }
// ],
// [
// {
// "p": "106559",
// "a": "0.26802",
// "n": 1
// },
// {
// "p": "106564",
// "a": "0.3002",
// "n": 1
// },
// ]
// ],
// "t": 1751370536325
// },
// "error": null,
// "code": null
// }
const data = this.safeDict(response, 'data', {});
const levels = this.safeList(data, 'l', []);
const result = {
'bids': this.safeList(levels, 0, []),
'asks': this.safeList(levels, 1, []),
};
const timestamp = this.safeInteger(data, 't');
return this.parseOrderBook(result, this.safeSymbol(undefined, market), timestamp, 'bids', 'asks', 'p', 'a');
}
/**
* @method
* @name pacifica#fetchFundingRates
* @description retrieves data on all swap markets for pacifica
* @param {string[]} [symbols] list of unified market symbols
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} an array of objects representing market data
*/
async fetchFundingRates(symbols = undefined, params = {}) {
const response = await this.publicGetInfoPrices(params);
//
// {
// "success": true,
// "data": [
// {
// "funding": "0.00010529",
// "mark": "1.084819",
// "mid": "1.08615",
// "next_funding": "0.00011096",
// "open_interest": "3634796",
// "oracle": "1.084524",
// "symbol": "XPL",
// "timestamp": 1759222967974,
// "volume_24h": "20896698.0672",
// "yesterday_price": "1.3412"
// }
// ],
// "error": null,
// "code": null
// }
//
const result = this.safeList(response, 'data', []);
return this.parseFundingRates(result, symbols);
}
parseFundingRate(info, market = undefined) {
//
// {
// "funding": "0.00010529",
// "mark": "1.084819",
// "mid": "1.08615",
// "next_funding": "0.00011096",
// "open_interest": "3634796",
// "oracle": "1.084524",
// "symbol": "XPL",
// "timestamp": 1759222967974,
// "volume_24h": "20896698.0672",
// "yesterday_price": "1.3412"
// }
//
const marketId = this.safeString(info, 'symbol');
market = this.safeMarket(marketId, market);
const symbol = market['symbol'];
const funding = this.safeNumber(info, 'funding');
const markPx = this.safeNumber(info, 'mark');
const oraclePx = this.safeNumber(info, 'oracle');
const nextFundingRate = this.safeNumber(info, 'next_funding');
const timestamp = this.safeInteger(info, 'timestamp');
const fundingTimestamp = (Math.floor(this.milliseconds() / 60 / 60 / 1000) + 1) * 60 * 60 * 1000;
return {
'info': info,
'symbol': symbol,
'markPrice': markPx,
'indexPrice': oraclePx,
'interestRate': undefined,
'estimatedSettlePrice': undefined,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'fundingRate': funding,
'fundingTimestamp': fundingTimestamp,
'fundingDatetime': this.iso8601(fundingTimestamp),
'nextFundingRate': nextFundingRate,
'nextFundingTimestamp': undefined,
'nextFundingDatetime': undefined,
'previousFundingRate': undefined,
'previousFundingTimestamp': undefined,
'previousFundingDatetime': undefined,
'interval': '1h',
};
}
/**
* @method
* @name pacifica#fetchOHLCV
* @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @see https://docs.pacifica.fi/api-documentation/api/rest-api/markets/get-candle-data
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
* @param {string} timeframe the length of time each candle represents, support '1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h', '8h', '12h', '1d'
* @param {int} [since] timestamp in ms of the earliest candle to fetch
* @param {int} [limit] the maximum amount of candles to fetch
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] timestamp in ms of the latest candle to fetch. 'limit' is priority
* @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
if (since === undefined) {
throw new ArgumentsRequired(this.id + ' fetchOHLCV() requires a "since" argument');
}
if (symbol === undefined) {
throw new ArgumentsRequired(this.id + ' fetchOHLCV() requires a "symbol" argument');
}
const defaultMaxLimit = 3950; // 4000 by docs, but in fact >~3960 returns error
await this.loadMarkets();
const market = this.market(symbol);
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchOHLCV', 'paginate', false);
if (paginate) {
return await this.fetchPaginatedCallDeterministic('fetchOHLCV', symbol, since, limit, timeframe, params, defaultMaxLimit);
}
const tf = this.safeString(this.timeframes, timeframe, timeframe);
let request = {
'symbol': market['id'],
'interval': tf,
'start_time': since,
};
[request, params] = this.handleUntilOption('end_time', request, params);
const nowMillis = this.milliseconds();
let until = this.safeInteger(request, 'end_time');
if (until === undefined) {
if (limit !== undefined) {
until = since + (limit * (this.parseTimeframe(tf) * 1000)) - 1;
}
if (until === undefined) {
until = since + (defaultMaxLimit * (this.parseTimeframe(tf) * 1000)) - 1;
}
if (until > nowMillis) {
until = nowMillis;
}
request['end_time'] = until;
}
const response = await this.publicGetKline(this.extend(request, params));
//
// {
// "success": true,
// "data": [
// {
// "t": 1748954160000,
// "T": 1748954220000,
// "s": "BTC",
// "i": "1m",
// "o": "105376",
// "c": "105376",
// "h": "105376",
// "l": "105376",
// "v": "0.00022",
// "n": 2
// }
// ],
// "error": null,
// "code": null
// }
//
const candles = this.safeList(response, 'data', []);
return this.parseOHLCVs(candles, market, timeframe, since, limit);
}
parseOHLCV(ohlcv, market = undefined) {
//
// {
// "t": 1748954160000,
// "T": 1748954220000,
// "s": "BTC",
// "i": "1m",
// "o": "105376",
// "c": "105376",
// "h": "105376",
// "l": "105376",
// "v": "0.00022",
// "n": 2
// }
//
return [
this.safeInteger(ohlcv, 't'),
this.safeNumber(ohlcv, 'o'),
this.safeNumber(ohlcv, 'h'),
this.safeNumber(ohlcv, 'l'),
this.safeNumber(ohlcv, 'c'),
this.safeNumber(ohlcv, 'v'),
];
}
/**
* @method
* @name pacifica#fetchTrades
* @description get the list of most recent trades for a particular symbol
* @see https://docs.pacifica.fi/api-documentation/api/rest-api/markets/get-recent-trades
* @param {string} symbol unified market symbol
* @param {int} [since] the earliest time in ms to fetch trades for
* @param {int} [limit] the maximum number of trades structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/?id=trade-structure}
*/
async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
const request = {
'symbol': market['id'],
};
const response = await this.publicGetTrades(this.extend(request, params));
//
// {
// "success": true,
// "data": [
// {
// "event_type": "fulfill_taker",
// "price": "104721",
// "amount": "0.0001",
// "side": "close_long",
// "cause": "normal",
// "created_at": 1765006315306
// }
// ],
// "error": null,
// "code": null,
// "last_order_id": 1557404170
// }
//
const recentTrades = this.safeList(response, 'data', []);
return this.parseTrades(recentTrades, market, since, limit);
}
/**
* @method
* @name pacifica#fetchMyTrades
* @description fetch all trades made by the user
* @see https://docs.pacifica.fi/api-documentation/api/rest-api/account/get-trade-history
* @param {string} [symbol] unified market symbol
* @param {int} [since] the earliest time in ms to fetch trades for
* @param {int} [limit] the maximum number of trades structures to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] timestamp in ms of the latest trade
* @param {string} [params.account] will default to walletAddress if not provided
* @param {string} [params.cursor] pagination cursor from prev request (manual use)
* @param {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params)
* @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/?id=trade-structure}
*/
async fetchMyTrades(symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
let market = undefined;
if (symbol !== undefined) {
market = this.market(symbol);
}
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchMyTrades', 'paginate', false);
let userAddress = undefined;
[userAddress, params] = this.handleOriginAndSingleAddress('fetchMyTrades', params);
const defaultLimit = 100; // Default max limit
if (paginate) {
return await this.fetchPaginatedCallCursor('fetchMyTrades', symbol, since, limit, params, 'next_cursor', 'cursor', undefined, defaultLimit);
}
let request = {};
[request, params] = this.handleUntilOption('end_time', request, params);
request['account'] = userAddress;
if (symbol !== undefined) {
request['symbol'] = market['id'];
}
if (limit !== undefined) {
request['limit'] = limit;
}
if (since !== undefined) {
request['start_time'] = since;
}
const response = await this.publicGetTradesHistory(this.extend(request, params));
//
// {
// "success": true,
// "data": [
// {
// "history_id": 19329801,
// "order_id": 315293920,
// "client_order_id": "acf...",
// "symbol": "LDO",
// "amount": "0.1",
// "price": "1.1904",
// "entry_price": "1.176247",
// "fee": "0",
// "pnl": "-0.001415",
// "event_type": "fulfill_maker",
// "side": "close_short",
// "created_at": 1759215599188,
// "cause": "normal"
// },
// ...
// ],
// "next_cursor": "11111Z5RK", // not included to info!
// "has_more": true // not included to info!
// }
//
const data = this.addPaginationCursorToResult(response);
return this.parseTrades(data, market, since, limit);
}
parseTrade(trade, market = undefined) {
//
// user trades:
// {
// "history_id": 19329801,
// "order_id": 315293920,
// "client_order_id": "acf...",
// "symbol": "LDO",
// "amount": "0.1",
// "price": "1.1904",
// "entry_price": "1.176247",
// "fee": "0",
// "pnl": "-0.001415",
// "event_type": "fulfill_maker",
// "side": "close_short",
// "created_at": 1759215599188,
// "cause": "normal"
// },
// recent public trades:
// {
// "event_type": "fulfill_taker",
// "price": "104721",
// "amount": "0.0001",
// "side": "close_long",
// "cause": "normal",
// "created_at": 1765006315306
// }
//
const eventType = this.safeString(trade, 'event_type');
const timestamp = this.safeInteger(trade, 'created_at');
const price = this.safeString(trade, 'price');
const amount = this.safeString(trade, 'amount');
const symbol = this.safeSymbol(undefined, market);
const id = this.safeString(trade, 'history_id');
let side = this.safeString(trade, 'side');
if (side === 'open_long') {
side = 'buy';
}
else if (side === 'close_long') {
side = 'sell';
}
else if (side === 'open_short') {
side = 'sell';
}
else if (side === 'close_short') {
side = 'buy';
}
cons