ccxt
Version:
1,160 lines • 197 kB
JavaScript
// ---------------------------------------------------------------------------
import Exchange from './abstract/aster.js';
import { AccountNotEnabled, AccountSuspended, ArgumentsRequired, AuthenticationError, BadRequest, BadResponse, BadSymbol, DuplicateOrderId, ExchangeClosedByUser, ExchangeError, InsufficientFunds, InvalidNonce, InvalidOrder, MarketClosed, NetworkError, NoChange, NotSupported, OperationFailed, OperationRejected, OrderImmediatelyFillable, OrderNotFillable, OrderNotFound, PermissionDenied, RateLimitExceeded, RequestTimeout } from './base/errors.js';
import { TRUNCATE, TICK_SIZE } from './base/functions/number.js';
import Precise from './base/Precise.js';
import { ecdsa } from './base/functions/crypto.js';
import { keccak_256 as keccak } from './static_dependencies/noble-hashes/sha3.js';
import { secp256k1 } from './static_dependencies/noble-curves/secp256k1.js';
// ---------------------------------------------------------------------------xs
/**
* @class aster
* @augments Exchange
*/
export default class aster extends Exchange {
describe() {
return this.deepExtend(super.describe(), {
'id': 'aster',
'name': 'Aster',
'countries': ['US'],
// 3 req/s for free
// 150 req/s for subscribers: https://aster.markets/data
// for brokers: https://aster.markets/docs/api-references/broker-api/#authentication-and-rate-limit
'rateLimit': 333,
'hostname': 'aster.markets',
'certified': false,
'pro': true,
'dex': true,
'urls': {
'logo': 'https://github.com/user-attachments/assets/4982201b-73cd-4d7a-8907-e69e239e9609',
'www': 'https://www.asterdex.com/en',
'api': {
'fapiPublic': 'https://fapi.asterdex.com/fapi',
'fapiPrivate': 'https://fapi.asterdex.com/fapi',
'sapiPublic': 'https://sapi.asterdex.com/api',
'sapiPrivate': 'https://sapi.asterdex.com/api',
},
'doc': 'https://github.com/asterdex/api-docs',
'fees': 'https://docs.asterdex.com/product/asterex-simple/fees-and-slippage',
'referral': {
'url': 'https://www.asterdex.com/en/referral/aA1c2B',
'discount': 0.1,
},
},
'has': {
'CORS': undefined,
'spot': false,
'margin': false,
'swap': false,
'future': false,
'option': false,
'addMargin': true,
'borrowCrossMargin': false,
'borrowIsolatedMargin': false,
'cancelAllOrders': true,
'cancelOrder': true,
'cancelOrders': true,
'closeAllPositions': false,
'closePosition': false,
'createConvertTrade': false,
'createDepositAddress': false,
'createLimitBuyOrder': false,
'createLimitSellOrder': false,
'createMarketBuyOrder': false,
'createMarketBuyOrderWithCost': false,
'createMarketOrderWithCost': false,
'createMarketSellOrder': false,
'createMarketSellOrderWithCost': false,
'createOrder': true,
'createOrders': false,
'createOrderWithTakeProfitAndStopLoss': false,
'createPostOnlyOrder': false,
'createReduceOnlyOrder': false,
'createStopLimitOrder': false,
'createStopLossOrder': false,
'createStopMarketOrder': false,
'createStopOrder': false,
'createTakeProfitOrder': false,
'createTrailingPercentOrder': false,
'createTriggerOrder': false,
'editOrder': false,
'editOrders': false,
'fetchAccounts': undefined,
'fetchBalance': true,
'fetchBidsAsks': false,
'fetchBorrowInterest': false,
'fetchBorrowRateHistories': false,
'fetchBorrowRateHistory': false,
'fetchCanceledAndClosedOrders': 'emulated',
'fetchCanceledOrders': 'emulated',
'fetchClosedOrder': false,
'fetchClosedOrders': 'emulated',
'fetchConvertCurrencies': false,
'fetchConvertQuote': false,
'fetchConvertTrade': false,
'fetchConvertTradeHistory': false,
'fetchCrossBorrowRate': false,
'fetchCrossBorrowRates': false,
'fetchCurrencies': true,
'fetchDeposit': false,
'fetchDepositAddress': false,
'fetchDepositAddresses': false,
'fetchDepositAddressesByNetwork': false,
'fetchDeposits': false,
'fetchDepositsWithdrawals': false,
'fetchDepositWithdrawFee': 'emulated',
'fetchDepositWithdrawFees': false,
'fetchFundingHistory': true,
'fetchFundingInterval': 'emulated',
'fetchFundingIntervals': true,
'fetchFundingRate': true,
'fetchFundingRateHistory': true,
'fetchFundingRates': true,
'fetchGreeks': false,
'fetchIndexOHLCV': false,
'fetchIsolatedBorrowRate': 'emulated',
'fetchIsolatedBorrowRates': false,
'fetchL3OrderBook': false,
'fetchLastPrices': false,
'fetchLedger': true,
'fetchLedgerEntry': false,
'fetchLeverage': 'emulated',
'fetchLeverages': true,
'fetchLeverageTiers': false,
'fetchLiquidations': false,
'fetchLongShortRatio': false,
'fetchLongShortRatioHistory': false,
'fetchMarginAdjustmentHistory': true,
'fetchMarginMode': 'emulated',
'fetchMarginModes': true,
'fetchMarketLeverageTiers': 'emulated',
'fetchMarkets': true,
'fetchMarkOHLCV': false,
'fetchMarkPrice': false,
'fetchMarkPrices': false,
'fetchMyLiquidations': false,
'fetchMySettlementHistory': false,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenInterest': false,
'fetchOpenInterestHistory': false,
'fetchOpenOrder': true,
'fetchOpenOrders': true,
'fetchOption': false,
'fetchOptionChain': false,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchOrderBooks': false,
'fetchOrders': true,
'fetchOrderTrades': false,
'fetchPosition': false,
'fetchPositionHistory': false,
'fetchPositionMode': true,
'fetchPositions': true,
'fetchPositionsHistory': false,
'fetchPositionsRisk': true,
'fetchPremiumIndexOHLCV': false,
'fetchSettlementHistory': false,
'fetchStatus': false,
'fetchTicker': true,
'fetchTickers': true,
'fetchTime': true,
'fetchTrades': true,
'fetchTradingFee': true,
'fetchTradingFees': false,
'fetchTradingLimits': 'emulated',
'fetchTransactionFee': 'emulated',
'fetchTransactionFees': false,
'fetchTransactions': false,
'fetchTransfer': false,
'fetchTransfers': false,
'fetchUnderlyingAssets': false,
'fetchVolatilityHistory': false,
'fetchWithdrawAddresses': false,
'fetchWithdrawal': false,
'fetchWithdrawals': false,
'fetchWithdrawalWhitelist': false,
'reduceMargin': true,
'repayCrossMargin': false,
'repayIsolatedMargin': false,
'sandbox': false,
'setLeverage': true,
'setMargin': false,
'setMarginMode': true,
'setPositionMode': true,
'signIn': true,
'transfer': true,
'withdraw': true,
},
'api': {
'fapiPublic': {
'get': {
'v1/ping': 1,
'v3/ping': 1,
'v1/time': 1,
'v3/time': 1,
'v1/exchangeInfo': 1,
'v3/exchangeInfo': 1,
'v1/depth': 1,
'v3/depth': 2,
'v1/trades': 1,
'v3/trades': 1,
'v1/historicalTrades': 1,
'v3/historicalTrades': 20,
'v1/aggTrades': 1,
'v3/aggTrades': 20,
'v1/klines': 1,
'v3/klines': 1,
'v1/indexPriceKlines': 1,
'v3/indexPriceKlines': 1,
'v1/markPriceKlines': 1,
'v3/markPriceKlines': 1,
'v1/premiumIndex': 1,
'v3/premiumIndex': 1,
'v1/fundingRate': 1,
'v3/fundingRate': 1,
'v1/fundingInfo': 1,
'v3/fundingInfo': 1,
'v1/ticker/24hr': 1,
'v3/ticker/24hr': 1,
'v1/ticker/price': 1,
'v3/ticker/price': 1,
'v1/ticker/bookTicker': 1,
'v3/ticker/bookTicker': 1,
// different endpoints
'v1/adlQuantile': 1,
'v1/forceOrders': 1,
'v3/indexreferences': 1,
},
},
'fapiPrivate': {
'get': {
'v1/positionSide/dual': 1,
'v3/positionSide/dual': 30,
'v1/multiAssetsMargin': 1,
'v3/multiAssetsMargin': 1,
'v1/order': 1,
'v3/order': 1,
'v1/openOrder': 1,
'v3/openOrder': 1,
'v1/openOrders': 1,
'v3/openOrders': 1,
'v1/allOrders': 1,
'v3/allOrders': 1,
'v2/balance': 1,
'v3/balance': 1,
'v3/account': 1,
'v1/positionMargin/history': 1,
'v3/positionMargin/history': 1,
'v2/positionRisk': 1,
'v3/positionRisk': 1,
'v1/userTrades': 1,
'v3/userTrades': 5,
'v1/income': 1,
'v3/income': 1,
'v1/leverageBracket': 1,
'v3/leverageBracket': 1,
'v1/commissionRate': 1,
'v3/commissionRate': 1,
// others
'v3/adlQuantile': 1,
'v3/forceOrders': 1,
'v3/mmp': 1,
'v3/accountWithJoinMargin': 1,
'v4/account': 1,
// builder
'v3/agent': 1,
'v3/builder': 1,
},
'post': {
'v1/positionSide/dual': 1,
'v3/positionSide/dual': 1,
'v1/multiAssetsMargin': 1,
'v3/multiAssetsMargin': 1,
'v1/order': 1,
'v3/order': 1,
'v1/order/test': 1,
'v3/order/test': 1,
'v1/batchOrders': 1,
'v3/batchOrders': 1,
'v1/asset/wallet/transfer': 1,
'v3/asset/wallet/transfer': 1,
'v1/countdownCancelAll': 1,
'v3/countdownCancelAll': 1,
'v1/leverage': 1,
'v3/leverage': 1,
'v1/marginType': 1,
'v3/marginType': 1,
'v1/positionMargin': 1,
'v3/positionMargin': 1,
'v1/listenKey': 1,
'v3/listenKey': 1,
// others
'v3/mmp': 1,
'v3/mmpReset': 1,
'v3/noop': 1,
// builder
'v3/approveAgent': 1,
'v3/updateAgent': 1,
'v3/approveBuilder': 1,
'v3/updateBuilder': 1,
},
'put': {
'v1/listenKey': 1,
'v3/listenKey': 1,
},
'delete': {
'v1/order': 1,
'v3/order': 1,
'v1/allOpenOrders': 1,
'v3/allOpenOrders': 1,
'v1/batchOrders': 1,
'v3/batchOrders': 1,
'v3/mmp': 1,
'v1/listenKey': 1,
'v3/listenKey': 1,
// builder
'v3/agent': 1,
'v3/builder': 1,
},
},
'sapiPublic': {
'get': {
// v1
'v1/ping': 1,
'v1/time': 1,
'v1/exchangeInfo': 1,
'v1/depth': 1,
'v1/trades': 1,
'v1/historicalTrades': 1,
'v1/aggTrades': 1,
'v1/klines': 1,
'v1/ticker/24hr': 1,
'v1/ticker/price': 1,
'v1/ticker/bookTicker': 1,
'v1/aster/withdraw/estimateFee': 1,
// v3
'v3/ping': 1,
'v3/time': 1,
'v3/exchangeInfo': 1,
'v3/depth': { 'cost': 2, 'byLimit': [[50, 2], [100, 5], [500, 10], [1000, 20]] },
'v3/trades': 1,
'v3/historicalTrades': 20,
'v3/aggTrades': 20,
'v3/klines': { 'cost': 1, 'byLimit': [[99, 1], [499, 2], [1000, 5], [10000, 10]] },
'v3/ticker/24hr': { 'cost': 1, 'noSymbol': 40 },
'v3/ticker/price': { 'cost': 1, 'noSymbol': 2 },
'v3/ticker/bookTicker': { 'cost': 1, 'noSymbol': 2 },
'v3/aster/withdraw/estimateFee': 1,
},
},
'sapiPrivate': {
'get': {
// v1
'v1/commissionRate': 1,
'v1/order': 1,
'v1/openOrders': 1,
'v1/allOrders': 1,
'v1/transactionHistory': 1,
'v1/account': 1,
'v1/userTrades': 1,
// v3
'v3/commissionRate': { 'cost': 1, 'noSymbol': 2 },
'v3/order': 1,
'v3/openOrders': 1,
'v3/allOrders': 5,
'v3/account': 5,
'v3/userTrades': 5,
'v3/openOrder': 1,
},
'post': {
// v1
'v1/order': 1,
'v1/asset/wallet/transfer': 5,
'v1/asset/sendToAddress': 1,
'v1/listenKey': 1,
// v3
'v3/order': 1,
'v3/asset/wallet/transfer': 5,
'v3/aster/user-withdraw': 1,
'v3/listenKey': 1,
},
'put': [
'v1/listenKey',
'v3/listenKey',
],
'delete': {
// v1
'v1/order': 1,
'v1/allOpenOrders': 1,
'v1/listenKey': 1,
// v3
'v3/allOpenOrders': 1,
'v3/order': 1,
'v3/listenKey': 1,
},
},
},
'timeframes': {
'1m': '1m',
'3m': '3m',
'5m': '5m',
'15m': '15m',
'30m': '30m',
'1h': '1h',
'2h': '2h',
'4h': '4h',
'6h': '6h',
'8h': '8h',
'12h': '12h',
'1d': '1d',
'3d': '3d',
'1w': '1w',
'1M': '1M',
},
'precisionMode': TICK_SIZE,
'requiredCredentials': {
'apiKey': false,
'secret': false,
'privateKey': true,
},
'fees': {
'trading': {
'tierBased': true,
'percentage': true,
'maker': this.parseNumber('0.0001'),
'taker': this.parseNumber('0.00035'),
},
},
'options': {
'defaultType': 'spot',
'recvWindow': 10 * 1000,
'defaultTimeInForce': 'GTC',
'zeroAddress': '0x0000000000000000000000000000000000000000',
'v3ChainId': 1666,
'quoteOrderQty': true,
'accountsByType': {
'spot': 'SPOT',
'swap': 'FUTURE',
'future': 'FUTURE',
'linear': 'FUTURE',
},
'networks': {
'ERC20': 'ETH',
'BEP20': 'BSC',
'ARBONE': 'Arbitrum',
},
'networksToChainId': {
'ETH': 1,
'BSC': 56,
'Arbitrum': 42161,
},
'fetchOpenOrders': {
'warnIfNoSymbol': true, // set to false to suppress warning when calling fetchOpenOrders without symbol
},
'builderFee': true,
'builder': '0x1F5877C19e3777Cfd15F9d57253eA4aA5254Ec39',
'builderRate': '0.001',
},
'exceptions': {
'exact': {
// 10xx - General Server or Network issues
'-1000': OperationRejected,
'-1001': NetworkError,
'-1002': AuthenticationError,
'-1003': RateLimitExceeded,
'-1004': DuplicateOrderId,
'-1005': BadRequest,
'-1006': BadResponse,
'-1007': RequestTimeout,
'-1010': OperationRejected,
'-1011': PermissionDenied,
'-1013': BadRequest,
'-1014': OrderNotFillable,
'-1015': RateLimitExceeded,
'-1016': ExchangeClosedByUser,
'-1020': NotSupported,
'-1021': InvalidNonce,
'-1022': AuthenticationError,
'-1023': BadRequest,
// 11xx - Request issues
'-1100': BadRequest,
'-1101': BadRequest,
'-1102': ArgumentsRequired,
'-1103': BadRequest,
'-1104': BadRequest,
'-1105': ArgumentsRequired,
'-1106': BadRequest,
'-1108': BadRequest,
'-1109': BadRequest,
'-1110': BadSymbol,
'-1111': BadRequest,
'-1112': BadRequest,
'-1113': BadRequest,
'-1114': BadRequest,
'-1115': InvalidOrder,
'-1116': InvalidOrder,
'-1117': InvalidOrder,
'-1118': InvalidOrder,
'-1119': InvalidOrder,
'-1120': BadRequest,
'-1121': BadSymbol,
'-1125': AuthenticationError,
'-1127': BadRequest,
'-1128': BadRequest,
'-1130': BadRequest,
'-1136': InvalidOrder,
// 20xx - Processing Issues
'-2010': InvalidOrder,
'-2011': OrderNotFound,
'-2013': OrderNotFound,
'-2014': AuthenticationError,
'-2015': AuthenticationError,
'-2016': MarketClosed,
'-2018': InsufficientFunds,
'-2019': InsufficientFunds,
'-2020': OrderNotFillable,
'-2021': OrderImmediatelyFillable,
'-2022': OperationRejected,
'-2023': AccountSuspended,
'-2024': InsufficientFunds,
'-2025': RateLimitExceeded,
'-2026': NotSupported,
'-2027': BadRequest,
'-2028': BadRequest,
// 40xx - Filters and other Issues
'-4000': InvalidOrder,
'-4001': InvalidOrder,
'-4002': InvalidOrder,
'-4003': InvalidOrder,
'-4004': InvalidOrder,
'-4005': InvalidOrder,
'-4006': InvalidOrder,
'-4007': InvalidOrder,
'-4008': InvalidOrder,
'-4009': InvalidOrder,
'-4010': InvalidOrder,
'-4011': InvalidOrder,
'-4012': RateLimitExceeded,
'-4013': InvalidOrder,
'-4014': InvalidOrder,
'-4015': InvalidOrder,
'-4016': InvalidOrder,
'-4017': InvalidOrder,
'-4018': InvalidOrder,
'-4019': BadRequest,
'-4020': BadRequest,
'-4021': BadRequest,
'-4022': MarketClosed,
'-4023': InvalidOrder,
'-4024': InvalidOrder,
'-4025': BadRequest,
'-4026': BadRequest,
'-4027': BadRequest,
'-4028': BadRequest,
'-4029': BadRequest,
'-4030': BadRequest,
'-4031': BadRequest,
'-4032': RateLimitExceeded,
'-4033': AccountNotEnabled,
'-4044': BadRequest,
'-4045': RateLimitExceeded,
'-4046': NoChange,
'-4047': OperationRejected,
'-4048': OperationRejected,
'-4049': OperationRejected,
'-4050': InsufficientFunds,
'-4051': InsufficientFunds,
'-4052': NoChange,
'-4053': OperationRejected,
'-4054': OperationRejected,
'-4055': ArgumentsRequired,
'-4056': AuthenticationError,
'-4057': AuthenticationError,
'-4058': InvalidOrder,
'-4059': NoChange,
'-4060': InvalidOrder,
'-4061': InvalidOrder,
'-4062': OperationRejected,
'-4063': BadRequest,
'-4064': BadRequest,
'-4065': BadRequest,
'-4066': BadRequest,
'-4067': OperationRejected,
'-4068': OperationRejected,
'-4069': BadRequest,
'-4070': InvalidOrder,
'-4071': InvalidOrder,
'-4072': NoChange,
'-4073': BadRequest,
'-4074': InvalidOrder,
'-4075': OperationRejected,
'-4076': OperationRejected,
'-4077': RateLimitExceeded,
'-4078': BadRequest,
'-4079': BadRequest,
'-4080': BadRequest,
'-4081': BadRequest,
'-4082': RateLimitExceeded,
'-4083': OperationFailed,
'-4084': NotSupported,
'-4085': BadRequest,
'-4086': BadRequest,
'-4087': PermissionDenied,
'-4088': PermissionDenied,
'-4104': BadSymbol,
'-4114': InvalidOrder,
'-4115': DuplicateOrderId,
'-4118': InsufficientFunds,
'-4131': InvalidOrder,
'-4135': InvalidOrder,
'-4137': InvalidOrder,
'-4138': OperationRejected,
'-4139': InvalidOrder,
'-4140': OperationRejected,
'-4141': MarketClosed,
'-4142': InvalidOrder,
'-4144': BadSymbol,
'-4161': OperationRejected,
'-4164': InvalidOrder,
'-4165': BadRequest,
'-4183': InvalidOrder,
'-4184': InvalidOrder,
'-5060': OperationRejected,
'-5076': OperationRejected,
// occured errors:
'-4168': OperationRejected, // Unable to adjust to isolated-margin mode under the Multi-Assets mode.
},
'broad': {},
},
});
}
isInverse(type, subType = undefined) {
if (subType === undefined) {
return (type === 'delivery');
}
else {
return subType === 'inverse';
}
}
isLinear(type, subType = undefined) {
if (subType === undefined) {
return (type === 'future') || (type === 'swap');
}
else {
return subType === 'linear';
}
}
/**
* @method
* @name aster#fetchCurrencies
* @description fetches all available currencies on an exchange
* @see https://asterdex.github.io/aster-api-website/spot-v3/market-data/#trading-specification-information
* @see https://asterdex.github.io/aster-api-website/futures-v3/market-data/#exchange-information
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an associative dictionary of currencies
*/
async fetchCurrencies(params = {}) {
const sapiResult = await this.sapiPublicGetV3ExchangeInfo(params);
const sapiRows = this.safeList(sapiResult, 'assets', []);
//
// [
// {
// "asset": "USDT",
// "marginAvailable": true, // only in PERP
// "autoAssetExchange": "-10000" // only in PERP
// }
// ]
//
return this.parseCurrencies(sapiRows);
}
parseCurrency(rawCurrency) {
const currencyId = this.safeString(rawCurrency, 'asset');
const code = this.safeCurrencyCode(currencyId);
return this.safeCurrencyStructure({
'info': rawCurrency,
'code': code,
'id': currencyId,
'name': code,
'active': undefined,
'deposit': undefined,
'withdraw': undefined,
'fee': undefined,
'precision': undefined,
'margin': this.safeBool(rawCurrency, 'marginAvailable'),
'limits': {
'amount': {
'min': undefined,
'max': undefined,
},
'withdraw': {
'min': undefined,
'max': undefined,
},
'deposit': {
'min': undefined,
'max': undefined,
},
},
'networks': undefined,
'type': 'crypto', // atm exchange api provides only cryptos
});
}
/**
* @method
* @name aster#fetchMarkets
* @description retrieves data on all markets for bigone
* @see https://asterdex.github.io/aster-api-website/spot-v3/market-data/#trading-specification-information
* @see https://asterdex.github.io/aster-api-website/futures-v3/market-data/#exchange-information
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object[]} an array of objects representing market data
*/
async fetchMarkets(params = {}) {
const promises = [
this.sapiPublicGetV3ExchangeInfo(params),
this.fapiPublicGetV3ExchangeInfo(params),
];
promises.push(this.signIn());
const results = await Promise.all(promises);
const sapiResult = this.safeDict(results, 0, {});
const sapiRows = this.safeList(sapiResult, 'symbols', []);
const fapiResult = this.safeDict(results, 1, {});
const fapiRows = this.safeList(fapiResult, 'symbols', []);
//
// example:
//
// [
// {
// symbol: "TESTUSDT",
// status: "TRADING",
// baseAsset: "TEST",
// quoteAsset: "USDT",
// pricePrecision: "2",
// quantityPrecision: "5",
// baseAssetPrecision: "8",
// quotePrecision: "8",
// listingTime: "1756289680210", // only in SPOT
// baseAssetAddress: null, // only in SPOT
// ocoAllowed: false, // only in SPOT
// pair: "ASTERUSDT", // only in PERP
// contractType: "PERPETUAL", // only in PERP
// deliveryDate: "4133404800000", // only in PERP
// onboardDate: "1758178800000", // only in PERP
// maintMarginPercent: "12.5000", // only in PERP
// requiredMarginPercent: "25.0000", // only in PERP
// marginAsset: "USDT", // only in PERP
// underlyingType: "COIN", // only in PERP
// underlyingSubType: [ "Top", ], // only in PERP
// symbolType: "0", // only in PERP
// tradingMode: "0", // only in PERP
// name: "", // only in PERP
// channel: "{}", // only in PERP
// sequenceNo: "100", // only in PERP
// twapMinNotional: "1000", // only in PERP
// imn: "4000.00", // only in PERP
// tags: [], // only in PERP
// settlePlan: "0", // only in PERP
// triggerProtect: "0.1500", // only in PERP
// liquidationFee: "0.025000", // only in PERP
// marketTakeBound: "0.05", // only in PERP
// createTime: "1758215451058", // only in PERP
// filters: [
// {
// minPrice: "0.01",
// maxPrice: "1000000",
// filterType: "PRICE_FILTER",
// tickSize: "0.01",
// },
// {
// stepSize: "0.00001",
// filterType: "LOT_SIZE",
// maxQty: "9000",
// minQty: "0.00001",
// },
// {
// stepSize: "0.00001",
// filterType: "MARKET_LOT_SIZE",
// maxQty: "9000",
// minQty: "0.00001",
// },
// {
// limit: "200",
// filterType: "MAX_NUM_ORDERS",
// },
// {
// minNotional: "5",
// filterType: "MIN_NOTIONAL",
// },
// {
// minNotional: "5",
// avgPriceMins: "5",
// applyMinToMarket: true,
// filterType: "NOTIONAL", // only in SPOT
// applyMaxToMarket: true,
// },
// {
// multiplierDown: "0.2",
// multiplierUp: "5",
// multiplierDecimal: "1",
// filterType: "PERCENT_PRICE",
// },
// {
// bidMultiplierUp: "5",
// askMultiplierUp: "5",
// bidMultiplierDown: "0.2",
// avgPriceMins: "5",
// multiplierDecimal: "1",
// filterType: "PERCENT_PRICE_BY_SIDE", // only in SPOT
// askMultiplierDown: "0.2",
// },
// ],
// orderTypes: [ "LIMIT", "MARKET", "STOP", "STOP_MARKET", "TAKE_PROFIT", "TAKE_PROFIT_MARKET", "TRAILING_STOP_MARKET", ],
// timeInForce: [ "GTC", "IOC", "FOK", "GTX", "HIDDEN", ],
// }
// ]
//
//
const fapiRowsFiltered = [];
for (let i = 0; i < fapiRows.length; i++) {
const market = fapiRows[i];
// tmp skip some markets with base = undefined
if (this.safeString(market, 'baseAsset')) {
fapiRowsFiltered.push(market);
}
}
const rows = this.arrayConcat(sapiRows, fapiRowsFiltered);
return this.parseMarkets(rows);
}
parseMarket(market) {
const id = this.safeString(market, 'symbol');
const baseId = this.safeString(market, 'baseAsset');
const quoteId = this.safeString(market, 'quoteAsset');
const base = this.safeCurrencyCode(baseId);
const quote = this.safeCurrencyCode(quoteId);
const active = this.safeString(market, 'status') === 'TRADING';
let spot = undefined;
let symbol = undefined;
let settle = undefined;
let settleId = undefined;
let swap = undefined;
let linear = undefined;
let inverse = undefined;
let contractSize = undefined;
const contractType = this.safeString(market, 'contractType');
const isContract = contractType !== undefined;
if (isContract) {
// currently, there is only perpetuals, not futures
spot = false;
swap = true;
settleId = this.safeString(market, 'marginAsset');
settle = this.safeCurrencyCode(settleId);
symbol = base + '/' + quote + ':' + settle;
linear = settle === quote;
inverse = settle === base;
contractSize = this.safeNumber2(market, 'contractSize', 'unit', this.parseNumber('1'));
}
else {
spot = true;
swap = false;
symbol = base + '/' + quote;
}
// filters
const filters = this.safeList(market, 'filters', []);
const filtersByType = this.indexBy(filters, 'filterType');
const filterNotional = this.safeDict2(filtersByType, 'MIN_NOTIONAL', 'NOTIONAL');
const filterPrice = this.safeDict(filtersByType, 'PRICE_FILTER');
const filterLotSize = this.safeDict(filtersByType, 'LOT_SIZE');
const filterMarketLotSize = this.safeDict(filtersByType, 'MARKET_LOT_SIZE', {});
let pricePrecision = this.safeNumber(filterPrice, 'tickSize');
if (pricePrecision === undefined) {
pricePrecision = this.parseNumber(this.parsePrecision(this.safeString(market, 'pricePrecision')));
}
const amountPrecision = (filterLotSize !== undefined) ? this.safeNumber(filterLotSize, 'stepSize') : this.parseNumber(this.parsePrecision(this.safeString(market, 'quantityPrecision')));
return this.safeMarketStructure({
'id': id,
'symbol': symbol,
'base': base,
'quote': quote,
'settle': settle,
'baseId': baseId,
'quoteId': quoteId,
'settleId': settleId,
'type': isContract ? 'swap' : 'spot',
'spot': spot,
'margin': false,
'swap': swap,
'future': false,
'option': false,
'active': active,
'contract': isContract,
'linear': linear,
'inverse': inverse,
'taker': this.fees['trading']['taker'],
'maker': this.fees['trading']['maker'],
'contractSize': contractSize,
'expiry': undefined,
'expiryDatetime': undefined,
'strike': undefined,
'optionType': undefined,
'precision': {
'amount': amountPrecision,
'price': pricePrecision,
'base': this.parseNumber(this.parsePrecision(this.safeString(market, 'baseAssetPrecision'))),
'quote': this.parseNumber(this.parsePrecision(this.safeString(market, 'quotePrecision'))),
},
'limits': {
'leverage': {
'min': undefined,
'max': undefined,
},
'amount': {
'min': this.safeNumber(filterLotSize, 'minQty'),
'max': this.safeNumber(filterLotSize, 'maxQty'),
},
'price': {
'min': this.safeNumber(filterPrice, 'minPrice'),
'max': this.safeNumber(filterPrice, 'maxPrice'),
},
'cost': {
'min': this.safeNumber2(filterNotional, 'notional', 'minNotional'),
'max': undefined,
},
'market': {
'min': this.safeNumber(filterMarketLotSize, 'minQty'),
'max': this.safeNumber(filterMarketLotSize, 'maxQty'),
},
},
'created': this.safeInteger2(market, 'listingTime', 'createTime'),
'info': market,
});
}
/**
* @method
* @name aster#fetchTime
* @description fetches the current integer timestamp in milliseconds from the exchange server
* @see https://asterdex.github.io/aster-api-website/spot-v3/market-data/#get-server-time
* @see https://asterdex.github.io/aster-api-website/futures-v3/market-data/#check-server-time
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {int} the current integer timestamp in milliseconds from the exchange server
*/
async fetchTime(params = {}) {
let marketType = undefined;
[marketType, params] = this.handleMarketTypeAndParams('fetchTime', undefined, params);
let response = undefined;
if (marketType === 'swap') {
response = await this.fapiPublicGetV3Time(params);
}
else {
response = await this.sapiPublicGetV3Time(params);
}
//
// both SPOT & PERP has same format
//
// {
// "serverTime": 1499827319559
// }
//
return this.safeInteger(response, 'serverTime');
}
parseOHLCV(ohlcv, market = undefined) {
//
// spot:
//
// [
// 1499040000000, // Open time
// "0.01634790", // Open
// "0.80000000", // High
// "0.01575800", // Low
// "0.01577100", // Close
// "148976.11427815", // Volume
// 1499644799999, // Close time
// "2434.19055334", // Quote asset volume
// 308, // Number of trades
// "1756.87402397", // Taker buy base asset volume
// "28.46694368", // Taker buy quote asset volume
// "0" // ??
// ]
//
return [
this.safeInteger(ohlcv, 0),
this.safeNumber(ohlcv, 1),
this.safeNumber(ohlcv, 2),
this.safeNumber(ohlcv, 3),
this.safeNumber(ohlcv, 4),
this.safeNumber(ohlcv, 5),
];
}
/**
* @method
* @name aster#fetchOHLCV
* @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
* @see https://asterdex.github.io/aster-api-website/spot-v3/market-data/#k-line-data
* @see https://asterdex.github.io/aster-api-website/futures-v3/market-data/#klinecandlestick-data
* @see https://asterdex.github.io/aster-api-website/futures-v3/market-data/#index-price-klinecandlestick-data
* @see https://asterdex.github.io/aster-api-website/futures-v3/market-data/#mark-price-klinecandlestick-data
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
* @param {string} timeframe the length of time each candle represents
* @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 {string} [params.price] "mark" or "index" for mark price and index price candles
* @param {int} [params.until] the latest time in ms to fetch orders for
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
*/
async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const market = this.market(symbol);
let request = {};
if (since !== undefined) {
request['startTime'] = since;
}
if (limit !== undefined) {
request['limit'] = Math.min(limit, 1500);
}
[request, params] = this.handleUntilOption('endTime', request, params);
request['interval'] = this.safeString(this.timeframes, timeframe, timeframe);
const price = this.safeString(params, 'price');
const isMark = (price === 'mark');
const isIndex = (price === 'index');
params = this.omit(params, 'price');
let response = undefined;
if (isMark) {
request['symbol'] = market['id'];
response = await this.fapiPublicGetV3MarkPriceKlines(this.extend(request, params));
}
else if (isIndex) {
request['pair'] = market['id'];
response = await this.fapiPublicGetV3IndexPriceKlines(this.extend(request, params));
}
else {
request['symbol'] = market['id'];
if (market['linear']) {
response = await this.fapiPublicGetV3Klines(this.extend(request, params));
}
else {
response = await this.sapiPublicGetV3Klines(this.extend(request, params));
}
//
// both SPOT & PERP has same format
//
// [
// [
// 1499040000000, // Open time
// "0.01634790", // Open
// "0.80000000", // High
// "0.01575800", // Low
// "0.01577100", // Close
// "148976.11427815", // Volume
// 1499644799999, // Close time
// "2434.19055334", // Quote asset volume
// 308, // Number of trades
// "1756.87402397", // Taker buy base asset volume
// "28.46694368", // Taker buy quote asset volume,
// "0"
// ]
// ]
//
}
return this.parseOHLCVs(response, market, timeframe, since, limit);
}
parseTrade(trade, market = undefined) {
//
// fetchTrades
//
// recent trades:
//
// {
// "id": 3913206,
// "price": "644.100",
// "qty": "0.08",
// "quoteQty": "51.528", // present in PERP
// "baseQty": "4.95049505", // present in SPOT
// "time": 1749784506633,
// "isBuyerMaker": true
// }
//
// aggrTrades
//
// {
// "a": 26129, // Aggregate tradeId
// "p": "0.01633102", // Price
// "q": "4.70443515", // Quantity
// "f": 27781, // First tradeId
// "l": 27781, // Last tradeId
// "T": 1498793709153, // Timestamp
// "m": true, // Was the buyer the maker?
// }
//
// fetchMyTrades (SPOT & PERP have similar format)
//
// {
// "symbol": "ETHUSDT",
// "id": 2583152,
// "orderId": 418588675,
// "side": "SELL",
// "price": "2330.04",
// "qty": "0.0030",
// "quoteQty": "6.99000000",
// "commission": "0.00279605",
// "commissionAsset": "USDT",
// "time": 1776409179230,
// "counterpartyId": 5143150, // only in SPOT
// "createUpdateId": null, // only in SPOT
// "maker": false, // only in SPOT
// "buyer": false, // only in SPOT
// "realizedPnl": "0.00029999", // only in PERP
// "marginAsset": "USDT", // only in PERP
// "positionSide": "BOTH", // only in PERP
// }
//
const id = this.safeString2(trade, 'id', 'a');
const marketId = this.safeString(trade, 'symbol');
const marketType = ('positionSide' in trade) ? 'swap' : 'spot';
market = this.safeMarket(marketId, market, undefined, marketType);
const currencyId = this.safeString2(trade, 'commissionAsset', 'marginAsset');
const currencyCode = this.safeCurrencyCode(currencyId);
const amountString = this.safeString2(trade, 'qty', 'q');
const priceString = this.safeString2(trade, 'price', 'p');
const costString = this.safeString2(trade, 'quoteQty', 'baseQty');
const timestamp = this.safeInteger2(trade, 'time', 'T');
let side = this.safeStringLower(trade, 'side');
const isMaker = this.safeBool(trade, 'maker');
let takerOrMaker = undefined;
if (isMaker !== undefined) {
takerOrMaker = isMaker ? 'maker' : 'taker';
if (side === undefined) {
const isBuyer = this.safeBool(trade, 'buyer');
if (isBuyer !== undefined) {
side = isBuyer ? 'buy' : 'sell';
}
}
}
const isBuyerMaker = this.safeBool2(trade, 'isBuyerMaker', 'm');
if (isBuyerMaker !== undefined) {
side = isBuyerMaker ? 'sell' : 'buy';
}
return this.safeTrade({
'id': id,
'info': trade,
'timestamp': timestamp,
'datetime': this.iso8601(timestamp),
'symbol': market['symbol'],
'order': this.safeString(trade, 'orderId'),
'type': undefined,
'side': side,
'takerOrMaker': takerOrMaker,
'price': priceString,
'amount': amountString,
'cost': costString,
'fee': {
'cost': this.parseNumber(Precise.stringAbs(this.safeString(trade, 'commission'))),
'currency': currencyCode,
},
}, market);
}
/**
* @method
* @name aster#fetchTrades
* @description get the list of most recent trades for a particular symbol
* @see https://asterdex.github.io/aster-api-website/spot-v3/market-data/#recent-trades-list
* @see https://asterdex.github.io/aster-api-website/spot-v3/market-data/#recent-tr