ccxt
Version:
1,129 lines (1,127 loc) • 135 kB
JavaScript
// ----------------------------------------------------------------------------
// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
// EDIT THE CORRESPONDENT .ts FILE INSTEAD
// ---------------------------------------------------------------------------
import Exchange from './abstract/bullish.js';
import { AuthenticationError, ArgumentsRequired, BadRequest, BadSymbol, DuplicateOrderId, ExchangeError, InvalidAddress, InvalidNonce, InvalidOrder, InsufficientFunds, MarketClosed, NotSupported, OperationRejected, OrderNotFillable, OrderNotFound, PermissionDenied, RateLimitExceeded } from './base/errors.js';
import { TICK_SIZE } from './base/functions/number.js';
import { sha256 } from './static_dependencies/noble-hashes/sha256.js';
// ---------------------------------------------------------------------------
/**
* @class bullish
* @augments Exchange
*/
export default class bullish extends Exchange {
describe() {
return this.deepExtend(super.describe(), {
'id': 'bullish',
'name': 'Bullish',
'countries': ['DE'],
'version': 'v3',
'rateLimit': 20,
'pro': true,
'has': {
'CORS': undefined,
'spot': true,
'margin': false,
'swap': false,
'future': false,
'option': false,
'addMargin': false,
'borrowMargin': false,
'cancelAllOrders': true,
'cancelOrder': true,
'cancelOrders': false,
'createDepositAddress': false,
'createLimitBuyOrder': true,
'createLimitOrder': true,
'createLimitSellOrder': true,
'createMarketBuyOrder': true,
'createMarketOrder': true,
'createMarketSellOrder': true,
'createOrder': true,
'createPostOnlyOrder': true,
'createTriggerOrder': true,
'deposit': false,
'editOrder': true,
'fetchAccounts': true,
'fetchBalance': true,
'fetchBidsAsks': false,
'fetchBorrowInterest': false,
'fetchBorrowRateHistories': false,
'fetchBorrowRateHistory': true,
'fetchCanceledAndClosedOrders': true,
'fetchCanceledOrders': true,
'fetchClosedOrder': false,
'fetchClosedOrders': true,
'fetchCrossBorrowRate': false,
'fetchCrossBorrowRates': false,
'fetchCurrencies': true,
'fetchDeposit': false,
'fetchDepositAddress': true,
'fetchDepositAddresses': false,
'fetchDepositAddressesByNetwork': false,
'fetchDeposits': false,
'fetchDepositsWithdrawals': true,
'fetchDepositWithdrawFee': false,
'fetchDepositWithdrawFees': false,
'fetchFundingHistory': false,
'fetchFundingRate': false,
'fetchFundingRateHistory': true,
'fetchFundingRates': false,
'fetchIndexOHLCV': false,
'fetchIsolatedBorrowRate': false,
'fetchIsolatedBorrowRates': false,
'fetchL3OrderBook': false,
'fetchLedger': false,
'fetchLeverage': false,
'fetchLeverageTiers': false,
'fetchMarketLeverageTiers': false,
'fetchMarkets': true,
'fetchMarkOHLCV': false,
'fetchMyTrades': true,
'fetchOHLCV': true,
'fetchOpenInterestHistory': false,
'fetchOpenOrder': false,
'fetchOpenOrders': true,
'fetchOrder': true,
'fetchOrderBook': true,
'fetchOrderBooks': false,
'fetchOrders': true,
'fetchOrderTrades': true,
'fetchPosition': false,
'fetchPositionHistory': false,
'fetchPositionMode': false,
'fetchPositions': true,
'fetchPositionsForSymbol': false,
'fetchPositionsHistory': false,
'fetchPositionsRisk': false,
'fetchPremiumIndexOHLCV': false,
'fetchStatus': false,
'fetchTicker': true,
'fetchTickers': false,
'fetchTime': true,
'fetchTrades': true,
'fetchTradingFee': false,
'fetchTradingFees': false,
'fetchTradingLimits': false,
'fetchTransactionFee': false,
'fetchTransactionFees': false,
'fetchTransactions': false,
'fetchTransfers': true,
'fetchWithdrawal': false,
'fetchWithdrawals': false,
'fetchWithdrawalWhitelist': false,
'reduceMargin': false,
'repayMargin': false,
'setLeverage': false,
'setMargin': false,
'setMarginMode': false,
'setPositionMode': false,
'signIn': true,
'transfer': true,
'withdraw': true,
'ws': true,
},
'timeframes': {
'1m': '1m',
'5m': '5m',
'30m': '30m',
'1h': '1h',
'6h': '6h',
'12h': '12h',
'1d': '1d',
},
'urls': {
'logo': 'https://github.com/user-attachments/assets/68f0686b-84f0-4da9-a751-f7089af3a9ed',
'api': {
'public': 'https://api.exchange.bullish.com/trading-api',
'private': 'https://api.exchange.bullish.com/trading-api',
},
'test': {
'public': 'https://api.simnext.bullish-test.com/trading-api',
'private': 'https://api.simnext.bullish-test.com/trading-api',
},
'www': 'https://bullish.com/',
'referral': '',
'doc': [
'https://api.exchange.bullish.com/docs/api/rest/',
],
},
'api': {
'public': {
'get': {
'v1/nonce': 1,
'v1/time': 1,
'v1/assets': 1,
'v1/assets/{symbol}': 1,
'v1/markets': 1,
'v1/markets/{symbol}': 1,
'v1/history/markets/{symbol}': 1,
'v1/markets/{symbol}/orderbook/hybrid': 1,
'v1/markets/{symbol}/trades': 1,
'v1/markets/{symbol}/tick': 1,
'v1/markets/{symbol}/candle': 1,
'v1/history/markets/{symbol}/trades': 1,
'v1/history/markets/{symbol}/funding-rate': 1,
'v1/index-prices': 1,
'v1/index-prices/{assetSymbol}': 1,
'v1/expiry-prices/{symbol}': 1,
'v1/option-ladder': 1,
'v1/option-ladder/{symbol}': 1,
},
},
'private': {
'get': {
'v2/orders': 1,
'v2/history/orders': 1,
'v2/orders/{orderId}': 1,
'v2/amm-instructions': 1,
'v2/amm-instructions/{instructionId}': 1,
'v1/wallets/transactions': 1,
'v1/wallets/limits/{symbol}': 1,
'v1/wallets/deposit-instructions/crypto/{symbol}': 1,
'v1/wallets/withdrawal-instructions/crypto/{symbol}': 1,
'v1/wallets/deposit-instructions/fiat/{symbol}': 1,
'v1/wallets/withdrawal-instructions/fiat/{symbol}': 1,
'v1/wallets/self-hosted/verification-attempts': 1,
'v1/trades': 5,
'v1/history/trades': 5,
'v1/trades/{tradeId}': 5,
'v1/trades/client-order-id/{clientOrderId}': 1,
'v1/accounts/asset': 1,
'v1/accounts/asset/{symbol}': 1,
'v1/users/logout': 1,
'v1/users/hmac/login': 1,
'v1/accounts/trading-accounts': 1,
'v1/accounts/trading-accounts/{tradingAccountId}': 1,
'v1/derivatives-positions': 1,
'v1/history/derivatives-settlement': 1,
'v1/history/transfer': 1,
'v1/history/borrow-interest': 1,
'v2/mmp-configuration': 1,
'v2/otc-trades': 1,
'v2/otc-trades/{otcTradeId}': 1,
'v2/otc-trades/unconfirmed-trade': 1,
},
'post': {
'v2/orders': 5,
'v2/command': 5,
'v2/amm-instructions': 1,
'v1/wallets/withdrawal': 1,
'v2/users/login': 1,
'v1/simulate-portfolio-margin': 1,
'v1/wallets/self-hosted/initiate': 1,
'v2/mmp-configuration': 1,
'v2/otc-trades': 1,
'v2/otc-command': 1,
},
},
},
'fees': {
'trading': {
'tierBased': false,
'percentage': true,
// todo check fees
'taker': this.parseNumber('0.001'),
'maker': this.parseNumber('0.001'),
},
},
'precisionMode': TICK_SIZE,
// exchange-specific options
'options': {
'timeDifference': 0,
'adjustForTimeDifference': false,
'networks': {
'BTC': 'BTC',
'EOS': 'EOS',
'ERC20': 'ETH',
},
'defaultNetwork': 'ERC20',
'defaultNetworks': {
'USDC': 'ERC20',
},
'tradingAccountId': undefined,
},
'features': {
'default': {
'sandbox': true,
'createOrder': {
'marginMode': false,
'triggerPrice': true,
'triggerPriceType': undefined,
'triggerDirection': false,
'stopLossPrice': false,
'takeProfitPrice': false,
'attachedStopLossTakeProfit': undefined,
'timeInForce': {
'IOC': true,
'FOK': true,
'PO': true,
'GTD': false,
},
'hedged': false,
'trailing': false,
'leverage': false,
'marketBuyByCost': false,
'marketBuyRequiresPrice': false,
'selfTradePrevention': false,
'iceberg': false,
},
'createOrders': undefined,
'fetchMyTrades': {
'marginMode': false,
'limit': 100,
'daysBack': 90,
'symbolRequired': false,
'untilDays': 90,
},
'fetchOrder': {
'marginMode': false,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOrders': {
'marginMode': false,
'limit': 100,
'daysBack': 90,
'untilDays': 90,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOpenOrders': {
'marginMode': false,
'limit': 100,
'daysBack': 90,
'untilDays': 90,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchCanceledAndClosedOrders': {
'marginMode': false,
'limit': 100,
'daysBack': 90,
'untilDays': 90,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchClosedOrders': {
'marginMode': false,
'limit': 100,
'daysBack': 1,
'daysBackCanceled': 1,
'untilDays': 1,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchCanceledOrders': {
'marginMode': false,
'limit': 100,
'daysBack': 1,
'untilDays': 1,
'trigger': false,
'trailing': false,
'symbolRequired': false,
},
'fetchOHLCV': {
'limit': 1000,
},
},
'spot': {
'extends': 'default',
},
'swap': {
'linear': {
'extends': 'default',
},
'inverse': undefined,
},
'future': {
'linear': {
'extends': 'default',
},
'inverse': undefined,
},
},
'exceptions': {
'exact': {
'1': BadRequest,
'5': InvalidOrder,
'6': DuplicateOrderId,
'13': BadRequest,
'15': BadRequest,
'18': BadRequest,
'1002': BadRequest,
'2001': BadRequest,
'2002': BadRequest,
'2003': BadRequest,
'2004': BadRequest,
'2005': ExchangeError,
'2006': BadRequest,
'2007': BadRequest,
'2008': BadRequest,
'2009': BadSymbol,
'2010': AuthenticationError,
'2011': AuthenticationError,
'2012': BadRequest,
'2013': InvalidOrder,
'2015': OperationRejected,
'2016': BadRequest,
'2017': BadRequest,
'2018': BadRequest,
'2020': PermissionDenied,
'2021': OperationRejected,
'2029': InvalidNonce,
'2035': InvalidNonce,
'3001': InsufficientFunds,
'3002': OrderNotFound,
'3003': PermissionDenied,
'3004': InsufficientFunds,
'3005': InsufficientFunds,
'3006': InsufficientFunds,
'3007': DuplicateOrderId,
'3031': BadRequest,
'3032': BadRequest,
'3033': PermissionDenied,
'3034': RateLimitExceeded,
'3035': RateLimitExceeded,
'3047': OperationRejected,
'3048': OperationRejected,
'3049': OperationRejected,
'3051': InsufficientFunds,
'3052': InsufficientFunds,
'3063': BadRequest,
'3064': OrderNotFillable,
'3065': MarketClosed,
'3066': ExchangeError,
'3067': MarketClosed,
'6007': InvalidOrder,
'6011': InvalidOrder,
'6012': InvalidOrder,
'6013': InvalidOrder,
'8301': ExchangeError,
'8305': ExchangeError,
'8306': ExchangeError,
'8307': ExchangeError,
'8310': InvalidAddress,
'8311': BadRequest,
'8313': BadRequest,
'8315': OperationRejected,
'8316': OperationRejected,
'8317': OperationRejected,
'8318': NotSupported,
'8319': NotSupported,
'8320': InvalidAddress,
'8322': BadRequest,
'8327': AuthenticationError,
'8329': ExchangeError,
'8331': InvalidAddress,
'8332': BadRequest,
'8333': BadRequest,
'8334': BadRequest,
'8335': InvalidAddress,
'8336': InvalidAddress,
'8399': ExchangeError, // Unknown error
},
'broad': {
'HttpInvalidParameterException': BadRequest,
'UNAUTHORIZED_COMMAND': AuthenticationError,
'QUERY_FILTER_ERROR': BadRequest,
'INVALID_SYMBOL': BadSymbol, // {"message":"Invalid symbol provided","errorCode":28004,"errorCodeName":"INVALID_SYMBOL"}
},
},
});
}
/**
* @method
* @name bullish#fetchTime
* @description fetches the current integer timestamp in milliseconds from the exchange server
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#tag--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 = {}) {
const response = await this.publicGetV1Time(params);
//
// {
// "datetime": "2025-05-05T20:05:50.999Z",
// "timestamp": 1746475550999
// }
//
return this.safeInteger(response, 'timestamp');
}
/**
* @method
* @name bullish#fetchCurrencies
* @description fetches all available currencies on an exchange
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/assets
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @returns {object} an associative dictionary of currencies
*/
async fetchCurrencies(params = {}) {
const response = await this.publicGetV1Assets(params);
//
// [
// {
// "assetId": "72",
// "symbol": "BTT1M",
// "name": "BitTorrent (millions)",
// "precision": "5",
// "minBalanceInterest": "0.00000",
// "apr": "10.00",
// "minFee": "0.00000",
// "maxBorrow": "0.00000",
// "totalOfferedLoanQuantity": "0.00000",
// "loanBorrowedQuantity": "0.00000",
// "collateralBands":
// [
// {
// "collateralPercentage": "90.00",
// "bandLimitUSD": "100000.0000"
// },
// {
// "collateralPercentage": "68.00",
// "bandLimitUSD": "300000.0000"
// },
// {
// "collateralPercentage": "25.00",
// "bandLimitUSD": "600000.0000"
// }
// ],
// "underlyingAsset":
// {
// "symbol": "BTT1M",
// "assetId": "72",
// "bpmMinReturnStart": "0.9200",
// "bpmMinReturnEnd": "0.9300",
// "bpmMaxReturnStart": "1.0800",
// "bpmMaxReturnEnd": "1.0800",
// "marketRiskFloorPctStart": "2.60",
// "marketRiskFloorPctEnd": "2.50",
// "bpmTransitionDateTimeStart": "2025-05-05T08:00:00.000Z",
// "bpmTransitionDateTimeEnd": "2025-05-08T08:00:00.000Z"
// }
// }, ...
// ]
//
const result = {};
for (let i = 0; i < response.length; i++) {
const currency = response[i];
const id = this.safeString(currency, 'symbol');
const code = this.safeCurrencyCode(id);
const name = this.safeString(currency, 'name');
const precision = this.safeString(currency, 'precision');
result[code] = {
'id': id,
'code': code,
'name': name,
'active': undefined,
'deposit': undefined,
'withdraw': undefined,
'fee': this.safeNumber(currency, 'minFee'),
'precision': this.parseNumber(this.parsePrecision(precision)),
'limits': {
'amount': { 'min': undefined, 'max': undefined },
'withdraw': { 'min': undefined, 'max': undefined },
},
'networks': {},
'type': 'crypto',
'info': currency,
};
}
return result;
}
/**
* @method
* @name bullish#fetchMarkets
* @description retrieves data on all markets for ace
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/markets
* @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.options['adjustForTimeDifference']) {
await this.loadTimeDifference();
}
const response = await this.publicGetV1Markets(params);
return this.parseMarkets(response);
}
parseMarket(market) {
//
// {
// "marketId": "20069",
// "symbol": "BTC-USDC-20250516",
// "quoteAssetId": "5",
// "baseAssetId": "1",
// "quoteSymbol": "USDC",
// "baseSymbol": "BTC",
// "quotePrecision": "4",
// "basePrecision": "8",
// "pricePrecision": "4",
// "quantityPrecision": "8",
// "costPrecision": "4",
// "minQuantityLimit": "0.00050000",
// "maxQuantityLimit": "200.00000000",
// "maxPriceLimit": null,
// "minPriceLimit": null,
// "maxCostLimit": null,
// "minCostLimit": null,
// "timeZone": "Etc/UTC",
// "tickSize": "0.1000",
// "liquidityTickSize": "100.0000",
// "liquidityPrecision": "4",
// "makerFee": "0",
// "takerFee": "2",
// "roundingCorrectionFactor": "0.00000100",
// "makerMinLiquidityAddition": "1000000",
// "orderTypes":
// [
// "LMT",
// "MKT",
// "STOP_LIMIT",
// "POST_ONLY"
// ],
// "spotTradingEnabled": true,
// "marginTradingEnabled": true,
// "marketEnabled": true,
// "createOrderEnabled": true,
// "cancelOrderEnabled": true,
// "liquidityInvestEnabled": true,
// "liquidityWithdrawEnabled": true,
// "feeTiers":
// [
// {
// "feeTierId": "1",
// "staticSpreadFee": "0.00000000",
// "isDislocationEnabled": false
// },
// {
// "feeTierId": "10",
// "staticSpreadFee": "0.00100000",
// "isDislocationEnabled": true
// },
// {
// "feeTierId": "11",
// "staticSpreadFee": "0.00150000",
// "isDislocationEnabled": false
// },
// {
// "feeTierId": "12",
// "staticSpreadFee": "0.00150000",
// "isDislocationEnabled": true
// },
// {
// "feeTierId": "13",
// "staticSpreadFee": "0.00300000",
// "isDislocationEnabled": false
// },
// {
// "feeTierId": "14",
// "staticSpreadFee": "0.00300000",
// "isDislocationEnabled": true
// },
// {
// "feeTierId": "15",
// "staticSpreadFee": "0.00500000",
// "isDislocationEnabled": false
// },
// {
// "feeTierId": "16",
// "staticSpreadFee": "0.00500000",
// "isDislocationEnabled": true
// },
// {
// "feeTierId": "17",
// "staticSpreadFee": "0.01000000",
// "isDislocationEnabled": false
// },
// {
// "feeTierId": "18",
// "staticSpreadFee": "0.01000000",
// "isDislocationEnabled": true
// },
// {
// "feeTierId": "19",
// "staticSpreadFee": "0.01500000",
// "isDislocationEnabled": false
// },
// {
// "feeTierId": "2",
// "staticSpreadFee": "0.00000000",
// "isDislocationEnabled": true
// },
// {
// "feeTierId": "20",
// "staticSpreadFee": "0.01500000",
// "isDislocationEnabled": true
// },
// {
// "feeTierId": "21",
// "staticSpreadFee": "0.02000000",
// "isDislocationEnabled": false
// },
// {
// "feeTierId": "22",
// "staticSpreadFee": "0.02000000",
// "isDislocationEnabled": true
// },
// {
// "feeTierId": "3",
// "staticSpreadFee": "0.00010000",
// "isDislocationEnabled": false
// },
// {
// "feeTierId": "4",
// "staticSpreadFee": "0.00010000",
// "isDislocationEnabled": true
// },
// {
// "feeTierId": "5",
// "staticSpreadFee": "0.00020000",
// "isDislocationEnabled": false
// },
// {
// "feeTierId": "6",
// "staticSpreadFee": "0.00020000",
// "isDislocationEnabled": true
// },
// {
// "feeTierId": "7",
// "staticSpreadFee": "0.00060000",
// "isDislocationEnabled": false
// },
// {
// "feeTierId": "8",
// "staticSpreadFee": "0.00060000",
// "isDislocationEnabled": true
// },
// {
// "feeTierId": "9",
// "staticSpreadFee": "0.00100000",
// "isDislocationEnabled": false
// }
// ],
// "marketType": "DATED_FUTURE",
// "contractMultiplier": "1",
// "settlementAssetSymbol": "USDC",
// "underlyingQuoteSymbol": "USDC",
// "underlyingBaseSymbol": "BTC",
// "openInterestLimitUSD": "100000000.0000",
// "concentrationRiskPercentage": "100.00",
// "concentrationRiskThresholdUSD": "30000000.0000",
// "expiryDatetime": "2025-05-16T08:00:00.000Z",
// "priceBuffer": "0.1",
// "feeGroupId": "4"
// }
//
// option
// {
// "marketId": "20997",
// "symbol": "BTC-USDC-20260130-160000-P",
// "quoteAssetId": "5",
// "baseAssetId": "1",
// "quoteSymbol": "USDC",
// "baseSymbol": "BTC",
// "quotePrecision": "4",
// "basePrecision": "8",
// "pricePrecision": "4",
// "quantityPrecision": "8",
// "costPrecision": "4",
// "minQuantityLimit": "0.00050000",
// "maxQuantityLimit": "200.00000000",
// "maxPriceLimit": null,
// "minPriceLimit": null,
// "maxCostLimit": null,
// "minCostLimit": null,
// "timeZone": "Etc/UTC",
// "tickSize": "10.0000",
// "makerFee": "0",
// "takerFee": "2",
// "roundingCorrectionFactor": "0.00000100",
// "makerMinLiquidityAddition": "-1",
// "orderTypes": [ "LMT", "MKT", "STOP_LIMIT", "POST_ONLY" ],
// "spotTradingEnabled": true,
// "marginTradingEnabled": true,
// "marketEnabled": true,
// "createOrderEnabled": true,
// "cancelOrderEnabled": true,
// "amendOrderEnabled": true,
// "marketType": "OPTION",
// "contractMultiplier": "1",
// "settlementAssetSymbol": "USDC",
// "underlyingQuoteSymbol": "USDC",
// "underlyingBaseSymbol": "BTC",
// "openInterestLimitUSD": "100000000.0000",
// "concentrationRiskPercentage": "100.00",
// "concentrationRiskThresholdUSD": "30000000.0000",
// "expiryDatetime": "2026-01-30T08:00:00.000Z",
// "priceBuffer": "0",
// "feeGroupId": "10",
// "optionStrikePrice": "160000.0000",
// "optionType": "PUT",
// "premiumCapRatio": "0.1000"
// }
//
const id = this.safeString(market, 'symbol');
const baseId = this.safeString(market, 'baseSymbol');
const quoteId = this.safeString(market, 'quoteSymbol');
const base = this.safeCurrencyCode(baseId);
const quote = this.safeCurrencyCode(quoteId);
let symbol = base + '/' + quote;
const basePrecision = this.safeString(market, 'basePrecision');
const quotePrecision = this.safeString(market, 'quotePrecision');
const amountPrecision = this.safeString(market, 'quantityPrecision');
const pricePrecision = this.safeString(market, 'pricePrecision');
const costPrecision = this.safeString(market, 'costPrecision');
const minQuantityLimit = this.safeString(market, 'minQuantityLimit');
const maxQuantityLimit = this.safeString(market, 'maxQuantityLimit');
const minPriceLimit = this.safeString(market, 'minPriceLimit');
const maxPriceLimit = this.safeString(market, 'maxPriceLimit');
const minCostLimit = this.safeString(market, 'minCostLimit');
const maxCostLimit = this.safeString(market, 'maxCostLimit');
const settleId = this.safeString(market, 'settlementAssetSymbol');
const settle = this.safeCurrencyCode(settleId);
const type = this.parseMarketType(this.safeString(market, 'marketType'), 'spot');
let spot = false;
let swap = false;
let future = false;
let option = false;
let contract = true;
let linear = undefined;
let inverse = undefined;
let expiryDatetime = undefined;
let contractSize = undefined;
let optionType = undefined;
let strike = undefined;
let margin = false;
if (type === 'spot') {
spot = true;
contract = false;
margin = this.safeBool(market, 'marginTradingEnabled');
}
else {
contractSize = this.safeNumber(market, 'contractMultiplier');
symbol += ':' + settle;
linear = settle === quote;
inverse = !linear;
if (type === 'swap') {
swap = true;
}
else {
expiryDatetime = this.safeString(market, 'expiryDatetime');
const idParts = id.split('-');
const datePart = this.safeString(idParts, 2);
symbol += '-' + datePart;
if (type === 'future') {
future = true;
}
else if (type === 'option') {
option = true;
optionType = this.safeStringLower(market, 'optionType');
strike = this.parseToNumeric(this.safeString(market, 'optionStrikePrice'));
symbol += '-' + this.numberToString(strike) + '-' + this.safeString(idParts, 4);
}
}
}
return this.safeMarketStructure({
'id': id,
'symbol': symbol,
'base': base,
'baseId': baseId,
'quote': quote,
'quoteId': quoteId,
'settle': settle,
'settleId': settleId,
'type': type,
'spot': spot,
'margin': margin,
'swap': swap,
'future': future,
'option': option,
'contract': contract,
'linear': linear,
'inverse': inverse,
'taker': this.fees['trading']['taker'],
'maker': this.fees['trading']['maker'],
'contractSize': contractSize,
'expiry': this.parse8601(expiryDatetime),
'expiryDatetime': expiryDatetime,
'strike': strike,
'optionType': optionType,
'limits': {
'amount': {
'min': this.parseNumber(minQuantityLimit),
'max': this.parseNumber(maxQuantityLimit),
},
'price': {
'min': this.parseNumber(minPriceLimit),
'max': this.parseNumber(maxPriceLimit),
},
'cost': {
'min': this.parseNumber(minCostLimit),
'max': this.parseNumber(maxCostLimit),
},
'leverage': {
'min': undefined,
'max': undefined,
},
},
'precision': {
'amount': this.parseNumber(this.parsePrecision(amountPrecision)),
'price': this.parseNumber(this.parsePrecision(pricePrecision)),
'cost': this.parseNumber(this.parsePrecision(costPrecision)),
'base': this.parseNumber(this.parsePrecision(basePrecision)),
'quote': this.parseNumber(this.parsePrecision(quotePrecision)),
},
'active': this.safeBool(market, 'marketEnabled'),
'created': undefined,
'info': market,
});
}
parseMarketType(type, defaultType = undefined) {
const types = {
'SPOT': 'spot',
'PERPETUAL': 'swap',
'DATED_FUTURE': 'future',
'OPTION': 'option',
};
return this.safeString(types, type, defaultType);
}
/**
* @method
* @name bullish#fetchOrderBook
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/markets/-symbol-/orderbook/hybrid
* @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 (not used by bullish)
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @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);
const request = {
'symbol': market['id'],
};
const response = await this.publicGetV1MarketsSymbolOrderbookHybrid(this.extend(request, params));
//
// {
// "bids": [
// {
// "price": "1.00000000",
// "priceLevelQuantity": "1.00000000"
// }
// ],
// "asks": [
// {
// "price": "1.00000000",
// "priceLevelQuantity": "1.00000000"
// }
// ],
// "datetime": "2021-05-20T01:01:01.000Z",
// "timestamp": "1621490985000",
// "sequenceNumber": 999
// }
//
const timestamp = this.safeInteger(response, 'timestamp');
return this.parseOrderBook(response, symbol, timestamp, 'bids', 'asks', 'price', 'priceLevelQuantity');
}
/**
* @method
* @name bullish#fetchTrades
* @description get the list of most recent trades for a particular symbol
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/markets/-symbol-/trades
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/history/markets/-symbol-/trades
* @param {string} symbol unified symbol of the market to fetch trades for
* @param {int} [since] timestamp in ms of the earliest trade to fetch
* @param {int} [limit] the maximum amount of trades to fetch (max 100)
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {int} [params.until] timestamp in ms of the latest trade to fetch
* @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=public-trades}
*/
async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const maxLimit = 100;
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchFundingRateHistory', 'paginate');
if (paginate) {
params = this.handlePaginationParams('fetchTrades', since, params);
return await this.fetchPaginatedCallDynamic('fetchTrades', symbol, since, limit, params, maxLimit);
}
const market = this.market(symbol);
const request = {
'symbol': market['id'],
};
params = this.handleSinceAndUntil(since, params);
if (limit !== undefined) {
request['_pageSize'] = this.getClosestLimit(limit);
}
const response = await this.publicGetV1HistoryMarketsSymbolTrades(this.extend(request, params));
//
// [
// {
// "tradeId": "100178000000367159",
// "symbol": "BTCUSDC",
// "price": "103891.8977",
// "quantity": "0.00029411",
// "quoteAmount": "30.5556",
// "side": "BUY",
// "isTaker": true,
// "createdAtTimestamp": "1747768055826",
// "createdAtDatetime": "2025-05-20T19:07:35.826Z"
// }, ...
// ]
//
return this.parseTrades(response, market, since, limit);
}
/**
* @method
* @name bullish#fetchMyTrades
* @description fetch all trades made by the user
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/history/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
* @param {int} [params.until] the latest time in ms to fetch trades for
* @param {string} [params.orderId] the order id to fetch trades for
* @param {string} [params.clientOrderId] the client order id to fetch trades for
* @param {string} [params.tradingAccountId] the trading account id to fetch trades for
* @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 Promise.all([this.loadMarkets(), this.handleToken()]);
const tradingAccountId = await this.loadAccount(params);
const request = {
'tradingAccountId': tradingAccountId,
};
let market = undefined;
if (symbol !== undefined) {
market = this.market(symbol);
request['symbol'] = market['id'];
}
const clientOrderId = this.safeString(params, 'clientOrderId');
let response = undefined;
if (clientOrderId !== undefined) {
response = await this.privateGetV1TradesClientOrderIdClientOrderId(this.extend(request, params));
}
else {
let paginate = false;
[paginate, params] = this.handleOptionAndParams(params, 'fetchMyTrades', 'paginate');
if (paginate) {
params = this.handlePaginationParams('fetchMyTrades', since, params);
return await this.fetchPaginatedCallDynamic('fetchMyTrades', symbol, since, limit, params, 100);
}
params = this.handleSinceAndUntil(since, params);
if (limit !== undefined) {
request['_pageSize'] = this.getClosestLimit(limit);
}
//
// [
// {
// "baseFee": "0.00000000",
// "createdAtDatetime": "2025-05-18T15:57:28.132Z",
// "createdAtTimestamp": "1747583848132",
// "handle": null,
// "isTaker": true,
// "orderId": "844242293909618689",
// "price": "103942.7048",
// "publishedAtTimestamp": "1747769786131",
// "quantity": "1.00000000",
// "quoteAmount": "103942.7048",
// "quoteFee": "0.0000",
// "side": "BUY",
// "symbol": "BTCUSDC",
// "tradeId": "100178000000288892"
// }, ...
// ]
//
response = await this.privateGetV1HistoryTrades(this.extend(request, params));
}
return this.parseTrades(response, market, since, limit);
}
/**
* @method
* @name bullish#fetchOrderTrades
* @description fetch all the trades made from a single order
* @see https://api.exchange.bullish.com/docs/api/rest/trading-api/v2/#get-/v1/history/trades
* @param {string} id order id
* @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 to retrieve
* @param {object} [params] extra parameters specific to the exchange API endpoint
* @param {string} [params.clientOrderId] the client order id to fetch trades for
* @returns {object[]} a list of [trade structures]{@link https://docs.ccxt.com/?id=trade-structure}
*/
async fetchOrderTrades(id, symbol = undefined, since = undefined, limit = undefined, params = {}) {
await this.loadMarkets();
const clientOrderId = this.safeString(params, 'clientOrderId');
if (clientOrderId === undefined) {
params = this.extend({ 'orderId': id }, params);
}
return await this.fetchMyTrades(symbol, since, limit, params);
}
parseTrade(trade, market = undefined) {
//
// fetchTrades
// [
// {
// "tradeId": "100178000000367159",
// "symbol": "BTCUSDC",
// "price": "103891.8977",
// "quantity": "0.00029411",
// "quoteAmount": "30.5556",
// "side": "BUY",
// "isTaker": true,
// "createdAtTimestamp": "1747768055826",
// "createdAtDatetime": "2025-05-20T19:07:35.826Z"
// }, ...
// ]
//
// [
// {
// "tradeId": "100020000000000060",
// "symbol": "BTCUSDC",
// "price": "1.00000000",
// "quantity": "1.00000000",
// "side": "BUY",
// "isTaker": true,
// "createdAtDatetime": "2021-05-20T01:01:01.000Z",
// "createdAtTimestamp": "1621490985000"
// }
// ]
//
// fetchMyTrades
// [
// {
// "baseFee": "0.00000000",
// "createdAtDatetime": "2025-05-18T15:57:28.132Z",
// "createdAtTimestamp": "1747583848132",
// "handle": null,
// "isTaker": true,
// "orderId": "844242293909618689",
// "price": "103942.7048",
// "publishedAtTimestamp": "1747769786131",
// "quantity": "1.00000000",
// "quoteAmount": "103942.7048",
// "quoteFee": "0.0000",
// "side": "BUY",
// "symbol": "BTCUSDC",
// "tradeId": "100178000000288892"
// }, ...
// ]
//
const marketId = this.safeString(trade, 'symbol');
market = this.safeMarket(marketId, market);
const symbol = market['symbol'];
const timestamp = this.safeInteger(trade, 'createdAtTimestamp');
const price = this.safeString(trade, 'price');
const amount = this.safeString(trade, 'quantity');
cons