UNPKG

@kraken-crypto/ccxt

Version:

A cryptocurrency trading API with more than 100 exchanges in JavaScript / TypeScript / Python / C# / PHP / Go

1,195 lines (1,191 loc) 90.7 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var hibachi$1 = require('./abstract/hibachi.js'); var number = require('./base/functions/number.js'); var crypto = require('./base/functions/crypto.js'); var sha256 = require('./static_dependencies/noble-hashes/sha256.js'); var secp256k1 = require('./static_dependencies/noble-curves/secp256k1.js'); var Precise = require('./base/Precise.js'); var errors = require('./base/errors.js'); // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- /** * @class hibachi * @augments Exchange */ class hibachi extends hibachi$1["default"] { describe() { return this.deepExtend(super.describe(), { 'id': 'hibachi', 'name': 'Hibachi', 'countries': ['US'], 'rateLimit': 100, 'userAgent': this.userAgents['chrome'], 'certified': false, 'pro': false, 'dex': true, 'has': { 'CORS': undefined, 'spot': false, 'margin': false, 'swap': true, 'future': false, 'option': false, 'addMargin': false, 'cancelAllOrders': true, 'cancelOrder': true, 'cancelOrders': true, 'cancelWithdraw': false, 'closeAllPositions': false, 'closePosition': false, 'createConvertTrade': false, 'createDepositAddress': false, 'createMarketBuyOrderWithCost': false, 'createMarketOrder': false, 'createMarketOrderWithCost': false, 'createMarketSellOrderWithCost': false, 'createOrder': true, 'createOrders': true, 'createOrderWithTakeProfitAndStopLoss': false, 'createReduceOnlyOrder': false, 'createStopLimitOrder': false, 'createStopLossOrder': false, 'createStopMarketOrder': false, 'createStopOrder': false, 'createTakeProfitOrder': false, 'createTrailingAmountOrder': false, 'createTrailingPercentOrder': false, 'createTriggerOrder': false, 'editOrder': true, 'editOrders': true, 'fetchAccounts': false, 'fetchBalance': true, 'fetchCanceledOrders': false, 'fetchClosedOrder': false, 'fetchClosedOrders': false, 'fetchConvertCurrencies': false, 'fetchConvertQuote': false, 'fetchCurrencies': false, 'fetchDepositAddress': true, 'fetchDeposits': true, 'fetchDepositsWithdrawals': false, 'fetchFundingHistory': false, 'fetchFundingInterval': false, 'fetchFundingIntervals': false, 'fetchFundingRate': true, 'fetchFundingRateHistory': true, 'fetchFundingRates': false, 'fetchIndexOHLCV': false, 'fetchLedger': true, 'fetchLeverage': false, 'fetchMarginAdjustmentHistory': false, 'fetchMarginMode': false, 'fetchMarkets': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenInterest': true, 'fetchOpenInterestHistory': false, 'fetchOpenOrder': false, 'fetchOpenOrders': true, 'fetchOrder': true, 'fetchOrderBook': true, 'fetchOrders': false, 'fetchOrderTrades': false, 'fetchPosition': false, 'fetchPositionMode': false, 'fetchPositions': true, 'fetchPremiumIndexOHLCV': false, 'fetchStatus': false, 'fetchTicker': true, 'fetchTickers': false, 'fetchTime': true, 'fetchTrades': true, 'fetchTradingFee': false, 'fetchTradingFees': true, 'fetchTradingLimits': false, 'fetchTransactions': 'emulated', 'fetchTransfers': false, 'fetchWithdrawals': true, 'reduceMargin': false, 'setLeverage': false, 'setMargin': false, 'setPositionMode': false, 'transfer': false, 'withdraw': true, }, 'timeframes': { '1m': '1min', '5m': '5min', '15m': '15min', '1h': '1h', '4h': '4h', '1d': '1d', '1w': '1w', }, 'urls': { 'logo': 'https://github.com/user-attachments/assets/7301bbb1-4f27-4167-8a55-75f74b14e973', 'api': { 'public': 'https://data-api.hibachi.xyz', 'private': 'https://api.hibachi.xyz', }, 'www': 'https://www.hibachi.xyz/', 'referral': { 'url': 'hibachi.xyz/r/ZBL2YFWIHU', }, }, 'api': { 'public': { 'get': { 'market/exchange-info': 1, 'market/data/trades': 1, 'market/data/prices': 1, 'market/data/stats': 1, 'market/data/klines': 1, 'market/data/orderbook': 1, 'market/data/open-interest': 1, 'market/data/funding-rates': 1, 'exchange/utc-timestamp': 1, }, }, 'private': { 'get': { 'capital/deposit-info': 1, 'capital/history': 1, 'trade/account/trading_history': 1, 'trade/account/info': 1, 'trade/order': 1, 'trade/account/trades': 1, 'trade/orders': 1, }, 'put': { 'trade/order': 1, }, 'delete': { 'trade/order': 1, 'trade/orders': 1, }, 'post': { 'trade/order': 1, 'trade/orders': 1, 'capital/withdraw': 1, }, }, }, 'requiredCredentials': { 'apiKey': true, 'secret': false, 'accountId': true, 'privateKey': true, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': this.parseNumber('0.00015'), 'taker': this.parseNumber('0.00045'), }, }, 'currencies': this.hardcodedCurrencies(), 'options': {}, 'features': { 'default': { 'sandbox': false, 'createOrder': { 'marginMode': false, 'triggerPrice': true, 'triggerPriceType': undefined, 'triggerDirection': undefined, 'stopLossPrice': false, 'takeProfitPrice': false, 'attachedStopLossTakeProfit': undefined, 'timeInForce': { 'IOC': true, 'FOK': false, 'PO': true, 'GTD': false, }, 'hedged': false, 'trailing': false, 'leverage': false, 'marketBuyByCost': false, 'marketBuyRequiresPrice': false, 'selfTradePrevention': false, 'iceberg': false, }, 'createOrders': undefined, 'fetchMyTrades': { 'marginMode': false, 'limit': undefined, 'daysBack': undefined, 'untilDays': undefined, 'symbolRequired': false, }, 'fetchOrder': { 'marginMode': false, 'trigger': false, 'trailing': false, 'symbolRequired': false, }, 'fetchOpenOrders': { 'marginMode': false, 'limit': undefined, 'trigger': false, 'trailing': false, 'symbolRequired': false, }, 'fetchOrders': undefined, 'fetchClosedOrders': undefined, 'fetchOHLCV': { 'limit': undefined, }, }, 'swap': { 'linear': { 'extends': 'default', }, 'inverse': undefined, }, 'future': { 'linear': { 'extends': 'default', }, 'inverse': undefined, }, }, 'commonCurrencies': {}, 'exceptions': { 'exact': { '2': errors.BadRequest, '3': errors.OrderNotFound, '4': errors.BadRequest, // {"errorCode":4,"message":"Missing accountId","status":"failed"} }, 'broad': {}, }, 'precisionMode': number.TICK_SIZE, }); } getAccountId() { this.checkRequiredCredentials(); const id = this.parseToInt(this.accountId); return id; } parseMarket(market) { const marketId = this.safeString(market, 'symbol'); const numericId = this.safeNumber(market, 'id'); const marketType = 'swap'; const baseId = this.safeString(market, 'underlyingSymbol'); const quoteId = this.safeString(market, 'settlementSymbol'); const base = this.safeCurrencyCode(baseId); const quote = this.safeCurrencyCode(quoteId); const settleId = this.safeString(market, 'settlementSymbol'); const settle = this.safeCurrencyCode(settleId); const symbol = base + '/' + quote + ':' + settle; const created = this.safeIntegerProduct(market, 'marketCreationTimestamp', 1000); return { 'id': marketId, 'numericId': numericId, 'symbol': symbol, 'base': base, 'quote': quote, 'settle': settle, 'baseId': baseId, 'quoteId': quoteId, 'settleId': settleId, 'type': marketType, 'spot': false, 'margin': false, 'swap': true, 'future': false, 'option': false, 'active': this.safeString(market, 'status') === 'LIVE', 'contract': true, 'linear': true, 'inverse': false, 'contractSize': this.parseNumber('1'), 'expiry': undefined, 'expiryDatetime': undefined, 'strike': undefined, 'optionType': undefined, 'precision': { 'amount': this.parseNumber(this.parsePrecision(this.safeString(market, 'underlyingDecimals'))), 'price': this.parseNumber(this.safeList(market, 'orderbookGranularities')[0]) / 10000.0, }, 'limits': { 'leverage': { 'min': undefined, 'max': undefined, }, 'amount': { 'min': undefined, 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': this.safeNumber(market, 'minNotional'), 'max': undefined, }, }, 'created': created, 'info': market, }; } /** * @method * @name hibachi#fetchMarkets * @description retrieves data on all markets for hibachi * @see https://api-doc.hibachi.xyz/#183981da-8df5-40a0-a155-da15015dd536 * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object[]} an array of objects representing market data */ async fetchMarkets(params = {}) { const response = await this.publicGetMarketExchangeInfo(params); // { // "displayName": "ETH/USDT Perps", // "id": 1, // "maintenanceFactorForPositions": "0.030000", // "marketCloseTimestamp": null, // "marketOpenTimestamp": null, // "minNotional": "1", // "minOrderSize": "0.000000001", // "orderbookGranularities": [ // "0.01", // "0.1", // "1", // "10" // ], // "riskFactorForOrders": "0.066667", // "riskFactorForPositions": "0.030000", // "settlementDecimals": 6, // "settlementSymbol": "USDT", // "status": "LIVE", // "stepSize": "0.000000001", // "symbol": "ETH/USDT-P", // "tickSize": "0.000001", // "underlyingDecimals": 9, // "underlyingSymbol": "ETH" // }, const rows = this.safeList(response, 'futureContracts'); return this.parseMarkets(rows); } hardcodedCurrencies() { // Hibachi only supports USDT on Arbitrum at this time // We don't have an API endpoint to expose this information yet const result = {}; const networks = {}; const networkId = 'ARBITRUM'; networks[networkId] = { 'id': networkId, 'network': networkId, 'limits': { 'withdraw': { 'min': undefined, 'max': undefined, }, 'deposit': { 'min': undefined, 'max': undefined, }, }, 'active': undefined, 'deposit': undefined, 'withdraw': undefined, 'info': {}, }; const code = this.safeCurrencyCode('USDT'); result[code] = this.safeCurrencyStructure({ 'id': 'USDT', 'name': 'USDT', 'type': 'fiat', 'code': code, 'precision': this.parseNumber('0.000001'), 'active': true, 'fee': undefined, 'networks': networks, 'deposit': true, 'withdraw': true, 'limits': { 'deposit': { 'min': undefined, 'max': undefined, }, 'withdraw': { 'min': undefined, 'max': undefined, }, }, 'info': {}, }); return result; } parseBalance(response) { const result = { 'info': response, }; // Hibachi only supports USDT on Arbitrum at this time const code = this.safeCurrencyCode('USDT'); const account = this.account(); account['total'] = this.safeString(response, 'balance'); account['free'] = this.safeString(response, 'maximalWithdraw'); result[code] = account; return this.safeBalance(result); } /** * @method * @name hibachi#fetchBalance * @description query for balance and get the amount of funds available for trading or funds locked in orders * @see https://api-doc.hibachi.xyz/#69aafedb-8274-4e21-bbaf-91dace8b8f31 * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure} */ async fetchBalance(params = {}) { const request = { 'accountId': this.getAccountId(), }; const response = await this.privateGetTradeAccountInfo(this.extend(request, params)); // // { // assets: [ { quantity: '3.000000', symbol: 'USDT' } ], // balance: '3.000000', // maximalWithdraw: '3.000000', // numFreeTransfersRemaining: '100', // positions: [], // totalOrderNotional: '0.000000', // totalPositionNotional: '0.000000', // totalUnrealizedFundingPnl: '0.000000', // totalUnrealizedPnl: '0.000000', // totalUnrealizedTradingPnl: '0.000000', // tradeMakerFeeRate: '0.00000000', // tradeTakerFeeRate: '0.00020000' // } // return this.parseBalance(response); } parseTicker(ticker, market = undefined) { const prices = this.safeDict(ticker, 'prices'); const stats = this.safeDict(ticker, 'stats'); const bid = this.safeNumber(prices, 'bidPrice'); const ask = this.safeNumber(prices, 'askPrice'); const last = this.safeNumber(prices, 'tradePrice'); const high = this.safeNumber(stats, 'high24h'); const low = this.safeNumber(stats, 'low24h'); const volume = this.safeNumber(stats, 'volume24h'); return this.safeTicker({ 'symbol': this.safeSymbol(undefined, market), 'timestamp': undefined, 'datetime': undefined, 'bid': bid, 'ask': ask, 'last': last, 'high': high, 'low': low, 'bidVolume': undefined, 'askVolume': undefined, 'vwap': undefined, 'open': undefined, 'close': last, 'previousClose': undefined, 'change': undefined, 'percentage': undefined, 'average': undefined, 'baseVolume': undefined, 'quoteVolume': volume, 'info': ticker, }, market); } parseTrade(trade, market = undefined) { // public fetchTrades: // { // "price": "3512.431902", // "quantity": "1.414780098", // "takerSide": "Buy", // "timestamp": 1712692147 // } // // private fetchMyTrades: // { // "askAccountId": 221, // "askOrderId": 589168494921909200, // "bidAccountId": 132, // "bidOrderId": 589168494829895700, // "fee": "0.000477", // "id": 199511136, // "orderType": "MARKET", // "price": "119257.90000", // "quantity": "0.0000200000", // "realizedPnl": "-0.000352", // "side": "Sell", // "symbol": "BTC/USDT-P", // "timestamp": 1752543391 // } const marketId = this.safeString(trade, 'symbol'); market = this.safeMarket(marketId, market); const symbol = market['symbol']; const id = this.safeString(trade, 'id'); const price = this.safeString(trade, 'price'); const amount = this.safeString(trade, 'quantity'); const timestamp = this.safeIntegerProduct(trade, 'timestamp', 1000); const cost = Precise["default"].stringMul(price, amount); let side = undefined; let fee = undefined; let orderType = undefined; let orderId = undefined; let takerOrMaker = undefined; if (id === undefined) { // public trades side = this.safeStringLower(trade, 'takerSide'); takerOrMaker = 'taker'; } else { // private trades side = this.safeStringLower(trade, 'side'); fee = { 'cost': this.safeString(trade, 'fee'), 'currency': 'USDT' }; orderType = this.safeStringLower(trade, 'orderType'); if (side === 'buy') { orderId = this.safeString(trade, 'bidOrderId'); } else { orderId = this.safeString(trade, 'askOrderId'); } } return this.safeTrade({ 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'symbol': symbol, 'side': side, 'price': price, 'amount': amount, 'cost': cost, 'order': orderId, 'takerOrMaker': takerOrMaker, 'type': orderType, 'fee': fee, 'info': trade, }, market); } /** * @method * @name hibachi#fetchTrades * @description get the list of most recent trades for a particular symbol * @see https://api-doc.hibachi.xyz/#86a53bc1-d3bb-4b93-8a11-7034d4698caa * @param {string} symbol unified market symbol * @param {int} [since] timestamp in ms of the earliest trade to fetch * @param {int} [limit] the maximum amount of trades to fetch (maximum value is 100) * @param {object} [params] extra parameters specific to the hibachi api endpoint * @returns {object[]} a list of recent [trade structures] */ 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.publicGetMarketDataTrades(this.extend(request, params)); // // { // "trades": [ // { // "price": "111091.38352", // "quantity": "0.0090090093", // "takerSide": "Buy", // "timestamp": 1752095479 // }, // ] // } // const trades = this.safeList(response, 'trades', []); return this.parseTrades(trades, market); } /** * @method * @name hibachi#fetchTicker * @see https://api-doc.hibachi.xyz/#4abb30c4-e5c7-4b0f-9ade-790111dbfa47 * @description fetches a price ticker and the related information for the past 24h * @param {string} symbol unified symbol of the market * @param {object} [params] extra parameters specific to the hibachi api endpoint * @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure} */ async fetchTicker(symbol, params = {}) { await this.loadMarkets(); const market = this.market(symbol); const request = { 'symbol': market['id'], }; const rawPromises = [ this.publicGetMarketDataPrices(this.extend(request, params)), this.publicGetMarketDataStats(this.extend(request, params)), ]; const promises = await Promise.all(rawPromises); const pricesResponse = promises[0]; // { // "askPrice": "3514.650296", // "bidPrice": "3513.596112", // "fundingRateEstimation": { // "estimatedFundingRate": "0.000001", // "nextFundingTimestamp": 1712707200 // }, // "markPrice": "3514.288858", // "spotPrice": "3514.715000", // "symbol": "ETH/USDT-P", // "tradePrice": "2372.746570" // } const statsResponse = promises[1]; // { // "high24h": "3819.507827", // "low24h": "3754.474162", // "symbol": "ETH/USDT-P", // "volume24h": "23554.858590416" // } const ticker = { 'prices': pricesResponse, 'stats': statsResponse, }; return this.parseTicker(ticker, market); } parseOrderStatus(status) { const statuses = { 'PENDING': 'open', 'CHILD_PENDING': 'open', 'SCHEDULED_TWAP': 'open', 'PLACED': 'open', 'PARTIALLY_FILLED': 'open', 'FILLED': 'closed', 'CANCELLED': 'canceled', 'REJECTED': 'rejected', }; return this.safeString(statuses, status, status); } parseOrder(order, market = undefined) { const marketId = this.safeString(order, 'symbol'); market = this.safeMarket(marketId, market); const status = this.safeString(order, 'status'); const type = this.safeStringLower(order, 'orderType'); const price = this.safeString(order, 'price'); const rawSide = this.safeString(order, 'side'); let side = undefined; if (rawSide === 'BID') { side = 'buy'; } else if (rawSide === 'ASK') { side = 'sell'; } const amount = this.safeString(order, 'totalQuantity'); const remaining = this.safeString(order, 'availableQuantity'); const totalQuantity = this.safeString(order, 'totalQuantity'); const availableQuantity = this.safeString(order, 'availableQuantity'); let filled = undefined; if (totalQuantity !== undefined && availableQuantity !== undefined) { filled = Precise["default"].stringSub(totalQuantity, availableQuantity); } let timeInForce = 'GTC'; const orderFlags = this.safeValue(order, 'orderFlags'); let postOnly = false; let reduceOnly = false; if (orderFlags === 'POST_ONLY') { timeInForce = 'PO'; postOnly = true; } else if (orderFlags === 'IOC') { timeInForce = 'IOC'; } else if (orderFlags === 'REDUCE_ONLY') { reduceOnly = true; } return this.safeOrder({ 'info': order, 'id': this.safeString(order, 'orderId'), 'clientOrderId': undefined, 'datetime': undefined, 'timestamp': undefined, 'lastTradeTimestamp': undefined, 'lastUpdateTimestamp': undefined, 'status': this.parseOrderStatus(status), 'symbol': market['symbol'], 'type': type, 'timeInForce': timeInForce, 'side': side, 'price': price, 'average': undefined, 'amount': amount, 'filled': filled, 'remaining': remaining, 'cost': undefined, 'trades': undefined, 'fee': undefined, 'reduceOnly': reduceOnly, 'postOnly': postOnly, 'triggerPrice': this.safeNumber(order, 'triggerPrice'), }, market); } /** * @method * @name hibachi#fetchOrder * @description fetches information on an order made by the user * @see https://api-doc.hibachi.xyz/#096a8854-b918-4de8-8731-b2a28d26b96d * @param {string} id the order id * @param {string} symbol unified symbol of the market the order was made in * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ async fetchOrder(id, symbol = undefined, params = {}) { await this.loadMarkets(); let market = undefined; if (symbol !== undefined) { market = this.market(symbol); } const request = { 'orderId': id, 'accountId': this.getAccountId(), }; const response = await this.privateGetTradeOrder(this.extend(request, params)); return this.parseOrder(response, market); } /** * @method * @name hibachi#fetchTradingFees * @description fetch the trading fee * @param params extra parameters * @returns {object} a map of market symbols to [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure} */ async fetchTradingFees(params = {}) { await this.loadMarkets(); const request = { 'accountId': this.getAccountId(), }; const response = await this.privateGetTradeAccountInfo(this.extend(request, params)); // { // "tradeMakerFeeRate": "0.00000000", // "tradeTakerFeeRate": "0.00020000" // }, const makerFeeRate = this.safeNumber(response, 'tradeMakerFeeRate'); const takerFeeRate = this.safeNumber(response, 'tradeTakerFeeRate'); const result = {}; for (let i = 0; i < this.symbols.length; i++) { const symbol = this.symbols[i]; result[symbol] = { 'info': response, 'symbol': symbol, 'maker': makerFeeRate, 'taker': takerFeeRate, 'percentage': true, }; } return result; } orderMessage(market, nonce, feeRate, type, side, amount, price = undefined) { let sideInternal = 0; if (side === 'sell') { sideInternal = 0; } else if (side === 'buy') { sideInternal = 1; } // Converting them to internal representation: // - Quantity: Internal = External * (10^underlyingDecimals) // - Price: Internal = External * (2^32) * (10^(settlementDecimals-underlyingDecimals)) // - FeeRate: Internal = External * (10^8) const amountStr = this.amountToPrecision(this.safeString(market, 'symbol'), amount); const feeRateStr = this.numberToString(feeRate); const info = this.safeDict(market, 'info'); const underlying = '1e' + this.safeString(info, 'underlyingDecimals'); const settlement = '1e' + this.safeString(info, 'settlementDecimals'); const one = '1'; const feeRateFactor = '100000000'; // 10^8 const priceFactor = '4294967296'; // 2^32 const quantityInternal = Precise["default"].stringDiv(Precise["default"].stringMul(amountStr, underlying), one, 0); const feeRateInternal = Precise["default"].stringDiv(Precise["default"].stringMul(feeRateStr, feeRateFactor), one, 0); // Encoding const nonce16 = this.intToBase16(nonce); const noncePadded = nonce16.padStart(16, '0'); const encodedNonce = this.base16ToBinary(noncePadded); const numericId = this.intToBase16(this.safeInteger(market, 'numericId')); const numericIdPadded = numericId.padStart(8, '0'); const encodedMarketId = this.base16ToBinary(numericIdPadded); const quantity16 = this.intToBase16(this.parseToInt(quantityInternal)); const quantityPadded = quantity16.padStart(16, '0'); const encodedQuantity = this.base16ToBinary(quantityPadded); const sideInternal16 = this.intToBase16(sideInternal); const sidePadded = sideInternal16.padStart(8, '0'); const encodedSide = this.base16ToBinary(sidePadded); const feeRateInternal16 = this.intToBase16(this.parseToInt(feeRateInternal)); const feeRatePadded = feeRateInternal16.padStart(16, '0'); const encodedFeeRate = this.base16ToBinary(feeRatePadded); let encodedPrice = this.binaryConcat(); if (type === 'limit') { const priceStr = this.priceToPrecision(this.safeString(market, 'symbol'), price); const priceInternal = Precise["default"].stringDiv(Precise["default"].stringDiv(Precise["default"].stringMul(Precise["default"].stringMul(priceStr, priceFactor), settlement), underlying), one, 0); const price16 = this.intToBase16(this.parseToInt(priceInternal)); const pricePadded = price16.padStart(16, '0'); encodedPrice = this.base16ToBinary(pricePadded); } const message = this.binaryConcat(encodedNonce, encodedMarketId, encodedQuantity, encodedSide, encodedPrice, encodedFeeRate); return message; } createOrderRequest(nonce, symbol, type, side, amount, price = undefined, params = {}) { const market = this.market(symbol); const feeRate = Math.max(this.safeNumber(market, 'taker', this.safeNumber(this.options, 'defaultTakerFee', 0.00045)), this.safeNumber(market, 'maker', this.safeNumber(this.options, 'defaultMakerFee', 0.00015))); let sideInternal = ''; if (side === 'sell') { sideInternal = 'ASK'; } else if (side === 'buy') { sideInternal = 'BID'; } let priceInternal = ''; if (price) { priceInternal = this.priceToPrecision(symbol, price); } const message = this.orderMessage(market, nonce, feeRate, type, side, amount, price); const signature = this.signMessage(message, this.privateKey); const request = { 'symbol': this.safeString(market, 'id'), 'nonce': nonce, 'side': sideInternal, 'orderType': type.toUpperCase(), 'quantity': this.amountToPrecision(symbol, amount), 'price': priceInternal, 'signature': signature, 'maxFeesPercent': this.numberToString(feeRate), }; const postOnly = this.isPostOnly(type.toUpperCase() === 'MARKET', undefined, params); const reduceOnly = this.safeBool2(params, 'reduceOnly', 'reduce_only'); const timeInForce = this.safeStringLower(params, 'timeInForce'); const triggerPrice = this.safeString2(params, 'triggerPrice', 'stopPrice'); if (postOnly) { request['orderFlags'] = 'POST_ONLY'; } else if (timeInForce === 'ioc') { request['orderFlags'] = 'IOC'; } else if (reduceOnly) { request['orderFlags'] = 'REDUCE_ONLY'; } if (triggerPrice !== undefined) { request['triggerPrice'] = triggerPrice; } params = this.omit(params, ['reduceOnly', 'reduce_only', 'postOnly', 'timeInForce', 'stopPrice', 'triggerPrice']); return this.extend(request, params); } /** * @method * @name hibachi#createOrder * @description create a trade order * @see https://api-doc.hibachi.xyz/#00f6d5ad-5275-41cb-a1a8-19ed5d142124 * @param {string} symbol unified symbol of the market to create an order in * @param {string} type 'market' or 'limit' * @param {string} side 'buy' or 'sell' * @param {float} amount how much of currency you want to trade in units of base currency * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ async createOrder(symbol, type, side, amount, price = undefined, params = {}) { await this.loadMarkets(); const nonce = this.nonce(); const request = this.createOrderRequest(nonce, symbol, type, side, amount, price, params); request['accountId'] = this.getAccountId(); const response = await this.privatePostTradeOrder(request); // // { // "orderId": "578721673790138368" // } // return this.safeOrder({ 'id': this.safeString(response, 'orderId'), 'status': 'pending', }); } /** * @method * @name hibachi#createOrders * @description *contract only* create a list of trade orders * @see https://api-doc.hibachi.xyz/#c2840b9b-f02c-44ed-937d-dc2819f135b4 * @param {Array} orders list of orders to create, each object should contain the parameters required by createOrder, namely symbol, type, side, amount, price and params * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ async createOrders(orders, params = {}) { await this.loadMarkets(); const nonce = this.nonce(); const requestOrders = []; for (let i = 0; i < orders.length; i++) { const rawOrder = orders[i]; const symbol = this.safeString(rawOrder, 'symbol'); const type = this.safeString(rawOrder, 'type'); const side = this.safeString(rawOrder, 'side'); const amount = this.safeValue(rawOrder, 'amount'); const price = this.safeValue(rawOrder, 'price'); const orderParams = this.safeDict(rawOrder, 'params', {}); const orderRequest = this.createOrderRequest(nonce + i, symbol, type, side, amount, price, orderParams); orderRequest['action'] = 'place'; requestOrders.push(orderRequest); } const request = { 'accountId': this.getAccountId(), 'orders': requestOrders, }; const response = await this.privatePostTradeOrders(this.extend(request, params)); // // { "orders": [ { nonce: '1754349993908', orderId: '589642085255349248' } ] } // const ret = []; const responseOrders = this.safeList(response, 'orders'); for (let i = 0; i < responseOrders.length; i++) { const responseOrder = responseOrders[i]; ret.push(this.safeOrder({ 'info': responseOrder, 'id': this.safeString(responseOrder, 'orderId'), 'status': 'pending', })); } return ret; } editOrderRequest(nonce, id, symbol, type, side, amount = undefined, price = undefined, params = {}) { const market = this.market(symbol); const feeRate = Math.max(this.safeNumber(market, 'taker'), this.safeNumber(market, 'maker')); const message = this.orderMessage(market, nonce, feeRate, type, side, amount, price); const signature = this.signMessage(message, this.privateKey); const request = { 'orderId': id, 'nonce': nonce, 'updatedQuantity': this.amountToPrecision(symbol, amount), 'updatedPrice': this.priceToPrecision(symbol, price), 'maxFeesPercent': this.numberToString(feeRate), 'signature': signature, }; return this.extend(request, params); } /** * @method * @name hibachi#editOrder * @description edit a limit order that is not matched * @see https://api-doc.hibachi.xyz/#94d2cdaf-1c71-440f-a981-da1112824810 * @param {string} id order id * @param {string} symbol unified symbol of the market to create an order in * @param {string} type must be 'limit' * @param {string} side 'buy' or 'sell', should stay the same with original side * @param {float} amount how much of currency you want to trade in units of base currency * @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ async editOrder(id, symbol, type, side, amount = undefined, price = undefined, params = {}) { await this.loadMarkets(); const nonce = this.nonce(); const request = this.editOrderRequest(nonce, id, symbol, type, side, amount, price, params); request['accountId'] = this.getAccountId(); await this.privatePutTradeOrder(request); // At this time the response body is empty. A 200 response means the update request is accepted and sent to process // // {} // return this.safeOrder({ 'id': id, 'status': 'pending', }); } /** * @method * @name hibachi#editOrders * @description edit a list of trade orders * @see https://api-doc.hibachi.xyz/#c2840b9b-f02c-44ed-937d-dc2819f135b4 * @param {Array} orders list of orders to edit, each object should contain the parameters required by editOrder, namely id, symbol, type, side, amount, price and params * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ async editOrders(orders, params = {}) { await this.loadMarkets(); const nonce = this.nonce(); const requestOrders = []; for (let i = 0; i < orders.length; i++) { const rawOrder = orders[i]; const id = this.safeString(rawOrder, 'id'); const symbol = this.safeString(rawOrder, 'symbol'); const type = this.safeString(rawOrder, 'type'); const side = this.safeString(rawOrder, 'side'); const amount = this.safeValue(rawOrder, 'amount'); const price = this.safeValue(rawOrder, 'price'); const orderParams = this.safeDict(rawOrder, 'params', {}); const orderRequest = this.editOrderRequest(nonce + i, id, symbol, type, side, amount, price, orderParams); orderRequest['action'] = 'modify'; requestOrders.push(orderRequest); } const request = { 'accountId': this.getAccountId(), 'orders': requestOrders, }; const response = await this.privatePostTradeOrders(this.extend(request, params)); // // { "orders": [ { "orderId": "589636801329628160" } ] } // const ret = []; const responseOrders = this.safeList(response, 'orders'); for (let i = 0; i < responseOrders.length; i++) { const responseOrder = responseOrders[i]; ret.push(this.safeOrder({ 'info': responseOrder, 'id': this.safeString(responseOrder, 'orderId'), 'status': 'pending', })); } return ret; } cancelOrderRequest(id) { const bigid = this.convertToBigInt(id); const idbase16 = this.intToBase16(bigid); const idPadded = idbase16.padStart(16, '0'); const message = this.base16ToBinary(idPadded); const signature = this.signMessage(message, this.privateKey); return { 'orderId': id, 'signature': signature, }; } /** * @method * @name hibachi#cancelOrder * @see https://api-doc.hibachi.xyz/#e99c4f48-e610-4b7c-b7f6-1b4bb7af0271 * @description cancels an open order * @param {string} id order id * @param {string} symbol is unused * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ async cancelOrder(id, symbol = undefined, params = {}) { const request = this.cancelOrderRequest(id); request['accountId'] = this.getAccountId(); const response = await this.privateDeleteTradeOrder(this.extend(request, params)); // At this time the response body is empty. A 200 response means the cancel request is accepted and sent to cancel // // {} // return this.safeOrder({ 'info': response, 'id': id, 'status': 'canceled', }); } /** * @method * @name hibachi#cancelOrders * @description cancel multiple orders * @see https://api-doc.hibachi.xyz/#c2840b9b-f02c-44ed-937d-dc2819f135b4 * @param {string[]} ids order ids * @param {string} [symbol] unified market symbol, unused * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} an list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} */ async cancelOrders(ids, symbol = undefined, params = {}) { const orders = []; for (let i = 0; i < ids.length; i++) { const orderRequest = this.cancelOrderRequest(ids[i]); orderRequest['action'] = 'cancel'; orders.push(orderRequest); } const request = { 'accountId': this.getAccountId(), 'orders': orders, }; const response = await this.privatePostTradeOrders(this.extend(request, params)); // // { "orders": [ { "orderId": "589636801329628160" } ] } // const ret = []; const responseOrders = this.safeList(response, 'orders'); for (let i = 0; i < responseOrders.length; i++) { const responseOrder = responseOrders[i]; ret.push(this.safeOrder({ 'info': responseOrder, 'id': this.safeString(responseOrder, 'orderId'), 'status': 'canceled', })); } return ret; } /** * @method * @name hibachi#cancelAllOrders * @see https://api-doc.hibachi.xyz/#8ed24695-016e-49b2-a72d-7511ca921fee * @description cancel all open orders in a market * @param {string} symbol unified market symbol * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} an list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} */ async cancelAllOrders(symbol = undefined, params = {}) { await this.loadMarkets(); const nonce = this.nonce(); const nonce16 = this.intToBase16(nonce); const noncePadded = nonce16.padStart(16, '0'); const message = this.base16ToBinary(noncePadded); const signature = this.signMessage(message, this.privateKey); const request = { 'accountId': this.getAccountId(), 'nonce': nonce, 'signature': signature, }; if (symbol !== undefined) { const market = this.market(symbol); request['contractId'] = this.safeInteger(market, 'numericId'); } const response = await this.privateDeleteTradeOrders(this.extend(request, params)); // At this time the response body is empty. A 200 response means the cancel request is accepted and sent to process // // {} // return [ this.safeOrder({ 'info': response, }), ]; } encodeWithdrawMessage(amount, maxFees, address) { // Converting them to internal representation: // - Quantity: Internal = External * (10^6) // - maxFees: Internal = External * (10^6) // We only have USDT as our currency as this time const USDTAssetId = 1; const USDTFactor = '1000000'; const amountStr = this.numberToString(amount); const maxFeesStr = this.numberToString(maxFees); const one = '1'; const quantityInternal = Precise["default"].stringDiv(Precise["default"].stringMul(amountStr, USDTFactor), one, 0); const maxFeesInternal = Precise["default"].stringDiv(Precise["default"].stringMul(maxFeesStr, USDTFactor), one, 0); // Encoding const usdtAsset16 = this.intToBase16(USDTAssetId); const usdtAssetPadded = usdtAsset16.padStart(8, '0'); const encodedAssetId = this.base16ToBinary(usdtAssetPadded); const quantity16 = this.intToBase16(this.parseToInt(quantityInternal)); const quantityPadded = quantity16.padStart(16, '0'); const encodedQuantity = this.base16ToBinary(quantityPadded); const maxFees16 = this.intToBase16(this.parseToInt(maxFeesInternal)); const maxFeesPadded = maxFees16.padStart(16, '0'); const encodedMaxFees = this.base16ToBinary(maxFeesPadded); const encodedAddress = this.base16ToBinary(address); const message = this.binaryConcat(encodedAssetId, encodedQuantity, encodedMaxFees, encodedAddress); return message; } /** * @method * @name hibachi#withdraw * @description make a withdrawal * @see https://api-doc.hibachi.xyz/#6421625d-3e45-45fa-be9b-d2a0e780c090 * @param {string} code unified currency code, only support USDT * @param {float} amount the amount to withdraw * @param {string} address the address to withdraw to * @param {string} tag * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure} */ async withdraw(code, amount, address, tag = undefined, params = {}) { const withdrawAddress = address.slice(-40); // Get the withdraw fees const exchangeInfo = await this.publicGetMarketExchangeInfo(params); // { // "feeConfig": { // "depositFees": "0.004518", // "tradeMakerFeeRate": "0.00000000", // "tradeTakerFeeRate": "0.00020000", // "transferFeeRate": "0.00010000",