UNPKG

@jalmonter/ccxt

Version:

A JavaScript / TypeScript / Python / C# / PHP cryptocurrency trading library with support for 130+ exchanges

1,134 lines 105 kB
// --------------------------------------------------------------------------- import Exchange from './abstract/krakenfutures.js'; import { TICK_SIZE } from './base/functions/number.js'; import { ArgumentsRequired, AuthenticationError, BadRequest, ContractUnavailable, DDoSProtection, DuplicateOrderId, ExchangeError, ExchangeNotAvailable, InsufficientFunds, InvalidNonce, InvalidOrder, OrderImmediatelyFillable, OrderNotFillable, OrderNotFound, RateLimitExceeded } from './base/errors.js'; import { Precise } from './base/Precise.js'; import { sha256 } from './static_dependencies/noble-hashes/sha256.js'; import { sha512 } from './static_dependencies/noble-hashes/sha512.js'; // --------------------------------------------------------------------------- /** * @class krakenfutures * @augments Exchange */ export default class krakenfutures extends Exchange { describe() { return this.deepExtend(super.describe(), { 'id': 'krakenfutures', 'name': 'Kraken Futures', 'countries': ['US'], 'version': 'v3', 'userAgent': undefined, 'rateLimit': 600, 'pro': true, 'has': { 'CORS': undefined, 'spot': false, 'margin': false, 'swap': true, 'future': true, 'option': false, 'cancelAllOrders': true, 'cancelOrder': true, 'cancelOrders': true, 'createMarketOrder': false, 'createOrder': true, 'editOrder': true, 'fetchBalance': true, 'fetchBorrowRateHistories': false, 'fetchBorrowRateHistory': false, 'fetchClosedOrders': undefined, 'fetchCrossBorrowRate': false, 'fetchCrossBorrowRates': false, 'fetchFundingHistory': undefined, 'fetchFundingRate': 'emulated', 'fetchFundingRateHistory': true, 'fetchFundingRates': true, 'fetchIndexOHLCV': false, 'fetchIsolatedBorrowRate': false, 'fetchIsolatedBorrowRates': false, 'fetchIsolatedPositions': false, 'fetchLeverage': true, 'fetchLeverageTiers': true, 'fetchMarketLeverageTiers': 'emulated', 'fetchMarkets': true, 'fetchMarkOHLCV': true, 'fetchMyTrades': true, 'fetchOHLCV': true, 'fetchOpenOrders': true, 'fetchOrder': false, 'fetchOrderBook': true, 'fetchOrders': false, 'fetchPositions': true, 'fetchPremiumIndexOHLCV': false, 'fetchTickers': true, 'fetchTrades': true, 'setLeverage': true, 'setMarginMode': false, 'transfer': true, }, 'urls': { 'test': { 'public': 'https://demo-futures.kraken.com/derivatives/api/', 'private': 'https://demo-futures.kraken.com/derivatives/api/', 'charts': 'https://demo-futures.kraken.com/api/charts/', 'www': 'https://demo-futures.kraken.com', }, 'logo': 'https://user-images.githubusercontent.com/24300605/81436764-b22fd580-9172-11ea-9703-742783e6376d.jpg', 'api': { 'charts': 'https://futures.kraken.com/api/charts/', 'history': 'https://futures.kraken.com/api/history/', 'feeschedules': 'https://futures.kraken.com/api/feeschedules/', 'public': 'https://futures.kraken.com/derivatives/api/', 'private': 'https://futures.kraken.com/derivatives/api/', }, 'www': 'https://futures.kraken.com/', 'doc': [ 'https://docs.futures.kraken.com/#introduction', ], 'fees': 'https://support.kraken.com/hc/en-us/articles/360022835771-Transaction-fees-and-rebates-for-Kraken-Futures', 'referral': undefined, }, 'api': { 'public': { 'get': [ 'feeschedules', 'instruments', 'orderbook', 'tickers', 'history', 'historicalfundingrates', ], }, 'private': { 'get': [ 'feeschedules/volumes', 'openpositions', 'notifications', 'accounts', 'openorders', 'recentorders', 'fills', 'transfers', 'leveragepreferences', 'pnlpreferences', ], 'post': [ 'sendorder', 'editorder', 'cancelorder', 'transfer', 'batchorder', 'cancelallorders', 'cancelallordersafter', 'withdrawal', // for futures wallet -> kraken spot wallet ], 'put': [ 'leveragepreferences', 'pnlpreferences', ], }, 'charts': { 'get': [ '{price_type}/{symbol}/{interval}', ], }, 'history': { 'get': [ 'orders', 'executions', 'triggers', 'accountlogcsv', 'market/{symbol}/orders', 'market/{symbol}/executions', ], }, }, 'fees': { 'trading': { 'tierBased': false, 'percentage': true, 'maker': this.parseNumber('-0.0002'), 'taker': this.parseNumber('0.00075'), }, }, 'exceptions': { 'exact': { 'apiLimitExceeded': RateLimitExceeded, 'marketUnavailable': ContractUnavailable, 'requiredArgumentMissing': BadRequest, 'unavailable': ExchangeNotAvailable, 'authenticationError': AuthenticationError, 'accountInactive': ExchangeError, 'invalidAccount': BadRequest, 'invalidAmount': BadRequest, 'insufficientFunds': InsufficientFunds, 'Bad Request': BadRequest, 'Unavailable': InsufficientFunds, 'invalidUnit': BadRequest, 'Json Parse Error': ExchangeError, 'nonceBelowThreshold': InvalidNonce, 'nonceDuplicate': InvalidNonce, 'notFound': BadRequest, 'Server Error': ExchangeError, 'unknownError': ExchangeError, }, 'broad': { 'invalidArgument': BadRequest, 'nonceBelowThreshold': InvalidNonce, 'nonceDuplicate': InvalidNonce, }, }, 'precisionMode': TICK_SIZE, 'options': { 'access': { 'history': { 'GET': { 'orders': 'private', 'executions': 'private', 'triggers': 'private', 'accountlogcsv': 'private', }, }, }, 'settlementCurrencies': { 'flex': ['USDT', 'BTC', 'USD', 'GBP', 'EUR', 'USDC'], }, 'symbol': { 'quoteIds': ['USD', 'XBT'], 'reversed': false, }, 'versions': { 'public': { 'GET': { 'historicalfundingrates': 'v4', }, }, 'charts': { 'GET': { '{price_type}/{symbol}/{interval}': 'v1', }, }, 'history': { 'GET': { 'orders': 'v2', 'executions': 'v2', 'triggers': 'v2', 'accountlogcsv': 'v2', }, }, }, }, 'timeframes': { '1m': '1m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '4h': '4h', '12h': '12h', '1d': '1d', '1w': '1w', }, }); } async fetchMarkets(params = {}) { /** * @method * @name krakenfutures#fetchMarkets * @description Fetches the available trading markets from the exchange, Multi-collateral markets are returned as linear markets, but can be settled in multiple currencies * @see https://docs.futures.kraken.com/#http-api-trading-v3-api-instrument-details-get-instruments * @param {object} [params] exchange specific params * @returns An array of market structures */ const response = await this.publicGetInstruments(params); // // { // "result": "success", // "instruments": [ // { // "symbol": "fi_ethusd_180928", // "type": "futures_inverse", // futures_vanilla // spot index // "underlying": "rr_ethusd", // "lastTradingTime": "2018-09-28T15:00:00.000Z", // "tickSize": 0.1, // "contractSize": 1, // "tradeable": true, // "marginLevels": [ // { // "contracts":0, // "initialMargin":0.02, // "maintenanceMargin":0.01 // }, // { // "contracts":250000, // "initialMargin":0.04, // "maintenanceMargin":0.02 // }, // ... // ], // "isin": "GB00JVMLMP88", // "retailMarginLevels": [ // { // "contracts": 0, // "initialMargin": 0.5, // "maintenanceMargin": 0.25 // } // ], // "tags": [], // }, // { // "symbol": "in_xbtusd", // "type": "spot index", // "tradeable":false // } // ] // "serverTime": "2018-07-19T11:32:39.433Z" // } // const instruments = this.safeValue(response, 'instruments', []); const result = []; for (let i = 0; i < instruments.length; i++) { const market = instruments[i]; const id = this.safeString(market, 'symbol'); const marketType = this.safeString(market, 'type'); let type = undefined; const index = (marketType.indexOf(' index') >= 0); let linear = undefined; let inverse = undefined; let expiry = undefined; if (!index) { linear = (marketType.indexOf('_vanilla') >= 0); inverse = !linear; const settleTime = this.safeString(market, 'lastTradingTime'); type = (settleTime === undefined) ? 'swap' : 'future'; expiry = this.parse8601(settleTime); } else { type = 'index'; } const swap = (type === 'swap'); const future = (type === 'future'); let symbol = id; const split = id.split('_'); const splitMarket = this.safeString(split, 1); const baseId = splitMarket.slice(0, splitMarket.length - 3); const quoteId = 'usd'; // always USD const base = this.safeCurrencyCode(baseId); const quote = this.safeCurrencyCode(quoteId); // swap == perpetual let settle = undefined; let settleId = undefined; const amountPrecision = this.parseNumber(this.parsePrecision(this.safeString(market, 'contractValueTradePrecision', '0'))); const pricePrecision = this.safeNumber(market, 'tickSize'); const contract = (swap || future || index); const swapOrFutures = (swap || future); if (swapOrFutures) { const exchangeType = this.safeString(market, 'type'); if (exchangeType === 'futures_inverse') { settle = base; settleId = baseId; inverse = true; } else { settle = quote; settleId = quoteId; inverse = false; } linear = !inverse; symbol = base + '/' + quote + ':' + settle; if (future) { symbol = symbol + '-' + this.yymmdd(expiry); } } result.push({ 'id': id, 'symbol': symbol, 'base': base, 'quote': quote, 'settle': settle, 'baseId': baseId, 'quoteId': quoteId, 'settleId': settleId, 'type': type, 'spot': false, 'margin': false, 'swap': swap, 'future': future, 'option': false, 'index': index, 'active': undefined, 'contract': contract, 'linear': linear, 'inverse': inverse, 'contractSize': this.safeNumber(market, 'contractSize'), 'maintenanceMarginRate': undefined, 'expiry': expiry, 'expiryDatetime': this.iso8601(expiry), 'strike': undefined, 'optionType': undefined, 'precision': { 'amount': amountPrecision, 'price': pricePrecision, }, 'limits': { 'leverage': { 'min': undefined, 'max': undefined, }, 'amount': { 'min': undefined, 'max': undefined, }, 'price': { 'min': undefined, 'max': undefined, }, 'cost': { 'min': undefined, 'max': undefined, }, }, 'created': this.parse8601(this.safeString(market, 'openingDate')), 'info': market, }); } const settlementCurrencies = this.options['settlementCurrencies']['flex']; const currencies = []; for (let i = 0; i < settlementCurrencies.length; i++) { const code = settlementCurrencies[i]; currencies.push({ 'id': code.toLowerCase(), 'numericId': undefined, 'code': code, 'precision': undefined, }); } this.currencies = this.deepExtend(currencies, this.currencies); return result; } async fetchOrderBook(symbol, limit = undefined, params = {}) { /** * @method * @name krakenfutures#fetchOrderBook * @see https://docs.futures.kraken.com/#http-api-trading-v3-api-market-data-get-orderbook * @description Fetches a list of open orders in a market * @param {string} symbol Unified market symbol * @param {int} [limit] Not used by krakenfutures * @param {object} [params] exchange specific params * @returns An [order book structure]{@link https://docs.ccxt.com/#/?id=order-book-structure} */ await this.loadMarkets(); const market = this.market(symbol); const request = { 'symbol': market['id'], }; const response = await this.publicGetOrderbook(this.extend(request, params)); // // { // "result": "success", // "serverTime": "2016-02-25T09:45:53.818Z", // "orderBook": { // "bids": [ // [ // 4213, // 2000, // ], // [ // 4210, // 4000, // ], // ... // ], // "asks": [ // [ // 4218, // 4000, // ], // [ // 4220, // 5000, // ], // ... // ], // }, // } // const timestamp = this.parse8601(response['serverTime']); return this.parseOrderBook(response['orderBook'], symbol, timestamp); } async fetchTickers(symbols = undefined, params = {}) { /** * @method * @name krakenfutures#fetchTickers * @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market * @see https://docs.futures.kraken.com/#http-api-trading-v3-api-market-data-get-tickers * @param {string[]} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned * @param {object} [params] extra parameters specific to the exchange API endpoint * @returns {object} an array of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure} */ await this.loadMarkets(); const response = await this.publicGetTickers(params); // // { // "result": "success", // "tickers": [ // { // "tag": 'semiannual', // 'month', 'quarter', "perpetual", "semiannual", // "pair": "ETH:USD", // "symbol": "fi_ethusd_220624", // "markPrice": "2925.72", // "bid": "2923.8", // "bidSize": "16804", // "ask": "2928.65", // "askSize": "1339", // "vol24h": "860493", // "openInterest": "3023363.00000000", // "open24h": "3021.25", // "indexPrice": "2893.71", // "last": "2942.25", // "lastTime": "2022-02-18T14:08:15.578Z", // "lastSize": "151", // "suspended": false // }, // { // "symbol": "in_xbtusd", // "rr_xbtusd", // "last": "40411", // "lastTime": "2022-02-18T14:16:28.000Z" // }, // ... // ], // "serverTime": "2022-02-18T14:16:29.440Z" // } // const tickers = this.safeValue(response, 'tickers'); return this.parseTickers(tickers, symbols); } parseTicker(ticker, market = undefined) { // // { // "tag": 'semiannual', // 'month', 'quarter', "perpetual", "semiannual", // "pair": "ETH:USD", // "symbol": "fi_ethusd_220624", // "markPrice": "2925.72", // "bid": "2923.8", // "bidSize": "16804", // "ask": "2928.65", // "askSize": "1339", // "vol24h": "860493", // "openInterest": "3023363.00000000", // "open24h": "3021.25", // "indexPrice": "2893.71", // "last": "2942.25", // "lastTime": "2022-02-18T14:08:15.578Z", // "lastSize": "151", // "suspended": false // } // // { // "symbol": "in_xbtusd", // "rr_xbtusd", // "last": "40411", // "lastTime": "2022-02-18T14:16:28.000Z" // } // const marketId = this.safeString(ticker, 'symbol'); market = this.safeMarket(marketId, market); const symbol = market['symbol']; const timestamp = this.parse8601(this.safeString(ticker, 'lastTime')); const open = this.safeString(ticker, 'open24h'); const last = this.safeString(ticker, 'last'); const change = Precise.stringSub(last, open); const percentage = Precise.stringMul(Precise.stringDiv(change, open), '100'); const average = Precise.stringDiv(Precise.stringAdd(open, last), '2'); const volume = this.safeString(ticker, 'vol24h'); let baseVolume = undefined; let quoteVolume = undefined; const isIndex = this.safeValue(market, 'index', false); if (!isIndex) { if (market['linear']) { baseVolume = volume; } else if (market['inverse']) { quoteVolume = volume; } } return this.safeTicker({ 'symbol': symbol, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'high': undefined, 'low': undefined, 'bid': this.safeString(ticker, 'bid'), 'bidVolume': this.safeString(ticker, 'bidSize'), 'ask': this.safeString(ticker, 'ask'), 'askVolume': this.safeString(ticker, 'askSize'), 'vwap': undefined, 'open': open, 'close': last, 'last': last, 'previousClose': undefined, 'change': change, 'percentage': percentage, 'average': average, 'baseVolume': baseVolume, 'quoteVolume': quoteVolume, 'info': ticker, }); } async fetchOHLCV(symbol, timeframe = '1m', since = undefined, limit = undefined, params = {}) { /** * @method * @name kraken#fetchOHLCV * @see https://docs.futures.kraken.com/#http-api-charts-candles * @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market * @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 {boolean} [params.paginate] default false, when true will automatically paginate by calling this endpoint multiple times. See in the docs all the [availble parameters](https://github.com/ccxt/ccxt/wiki/Manual#pagination-params) * @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume */ await this.loadMarkets(); const market = this.market(symbol); let paginate = false; [paginate, params] = this.handleOptionAndParams(params, 'fetchOHLCV', 'paginate'); if (paginate) { return await this.fetchPaginatedCallDeterministic('fetchOHLCV', symbol, since, limit, timeframe, params, 5000); } const request = { 'symbol': market['id'], 'price_type': this.safeString(params, 'price', 'trade'), 'interval': this.timeframes[timeframe], }; params = this.omit(params, 'price'); if (since !== undefined) { const duration = this.parseTimeframe(timeframe); request['from'] = this.parseToInt(since / 1000); if (limit === undefined) { limit = 5000; } else if (limit > 5000) { throw new BadRequest(this.id + ' fetchOHLCV() limit cannot exceed 5000'); } const toTimestamp = this.sum(request['from'], limit * duration - 1); const currentTimestamp = this.seconds(); request['to'] = Math.min(toTimestamp, currentTimestamp); } else if (limit !== undefined) { if (limit > 5000) { throw new BadRequest(this.id + ' fetchOHLCV() limit cannot exceed 5000'); } const duration = this.parseTimeframe(timeframe); request['to'] = this.seconds(); request['from'] = this.parseToInt(request['to'] - (duration * limit)); } const response = await this.chartsGetPriceTypeSymbolInterval(this.extend(request, params)); // // { // "candles": [ // { // "time": 1645198500000, // "open": "309.15000000000", // "high": "309.15000000000", // "low": "308.70000000000", // "close": "308.85000000000", // "volume": 0 // } // ], // "more_candles": true // } // const candles = this.safeValue(response, 'candles'); return this.parseOHLCVs(candles, market, timeframe, since, limit); } parseOHLCV(ohlcv, market = undefined) { // // { // "time": 1645198500000, // "open": "309.15000000000", // "high": "309.15000000000", // "low": "308.70000000000", // "close": "308.85000000000", // "volume": 0 // } // return [ this.safeInteger(ohlcv, 'time'), this.safeNumber(ohlcv, 'open'), this.safeNumber(ohlcv, 'high'), this.safeNumber(ohlcv, 'low'), this.safeNumber(ohlcv, 'close'), this.safeNumber(ohlcv, 'volume'), // trading volume, undefined for mark or index price ]; } async fetchTrades(symbol, since = undefined, limit = undefined, params = {}) { /** * @method * @name krakenfutures#fetchTrades * @see https://docs.futures.kraken.com/#http-api-trading-v3-api-market-data-get-trade-history * @description Fetch a history of filled trades that this account has made * @param {string} symbol Unified CCXT market symbol * @param {int} [since] Timestamp in ms of earliest trade. Not used by krakenfutures except in combination with params.until * @param {int} [limit] Total number of trades, cannot exceed 100 * @param {object} [params] Exchange specific params * @param {int} [params.until] Timestamp in ms of latest trade * @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 An array of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure} */ await this.loadMarkets(); let paginate = false; [paginate, params] = this.handleOptionAndParams(params, 'fetchTrades', 'paginate'); if (paginate) { return await this.fetchPaginatedCallDynamic('fetchTrades', symbol, since, limit, params); } const market = this.market(symbol); const request = { 'symbol': market['id'], }; const until = this.safeInteger(params, 'until'); if (until !== undefined) { request['lastTime'] = this.iso8601(until); } // // { // "result": "success", // "history": [ // { // "time": "2022-03-18T04:55:37.692Z", // "trade_id": 100, // "price": 0.7921, // "size": 1068, // "side": "sell", // "type": "fill", // "uid": "6c5da0b0-f1a8-483f-921f-466eb0388265" // }, // ... // ], // "serverTime": "2022-03-18T06:39:18.056Z" // } // const response = await this.publicGetHistory(this.extend(request, params)); const history = this.safeValue(response, 'history'); return this.parseTrades(history, market, since, limit); } parseTrade(trade, market = undefined) { // // fetchTrades (public) // // { // "time": "2019-02-14T09:25:33.920Z", // "trade_id": 100, // "price": 3574, // "size": 100, // "side": "buy", // "type": "fill" // fill, liquidation, assignment, termination // "uid": "11c3d82c-9e70-4fe9-8115-f643f1b162d4" // } // // fetchMyTrades (private) // // { // "fillTime": "2016-02-25T09:47:01.000Z", // "order_id": "c18f0c17-9971-40e6-8e5b-10df05d422f0", // "fill_id": "522d4e08-96e7-4b44-9694-bfaea8fe215e", // "cliOrdId": "d427f920-ec55-4c18-ba95-5fe241513b30", // OPTIONAL // "symbol": "fi_xbtusd_180615", // "side": "buy", // "size": 2000, // "price": 4255, // "fillType": "maker" // taker, takerAfterEdit, maker, liquidation, assignee // } // // execution report (createOrder, editOrder) // // { // "executionId": "e1ec9f63-2338-4c44-b40a-43486c6732d7", // "price": 7244.5, // "amount": 10, // "orderPriorEdit": null, // "orderPriorExecution": { // "orderId": "61ca5732-3478-42fe-8362-abbfd9465294", // "cliOrdId": null, // "type": "lmt", // "symbol": "pi_xbtusd", // "side": "buy", // "quantity": 10, // "filled": 0, // "limitPrice": 7500, // "reduceOnly": false, // "timestamp": "2019-12-11T17:17:33.888Z", // "lastUpdateTimestamp": "2019-12-11T17:17:33.888Z" // }, // "takerReducedQuantity": null, // "type": "EXECUTION" // } // const timestamp = this.parse8601(this.safeString2(trade, 'time', 'fillTime')); const price = this.safeString(trade, 'price'); const amount = this.safeString2(trade, 'size', 'amount', '0.0'); let id = this.safeString2(trade, 'uid', 'fill_id'); if (id === undefined) { id = this.safeString(trade, 'executionId'); } let order = this.safeString(trade, 'order_id'); let symbolId = this.safeString(trade, 'symbol'); let side = this.safeString(trade, 'side'); let type = undefined; const priorEdit = this.safeValue(trade, 'orderPriorEdit'); const priorExecution = this.safeValue(trade, 'orderPriorExecution'); if (priorExecution !== undefined) { order = this.safeString(priorExecution, 'orderId'); symbolId = this.safeString(priorExecution, 'symbol'); side = this.safeString(priorExecution, 'side'); type = this.safeString(priorExecution, 'type'); } else if (priorEdit !== undefined) { order = this.safeString(priorEdit, 'orderId'); symbolId = this.safeString(priorEdit, 'symbol'); side = this.safeString(priorEdit, 'type'); type = this.safeString(priorEdit, 'type'); } if (type !== undefined) { type = this.parseOrderType(type); } let symbol = undefined; if (symbolId !== undefined) { market = this.safeValue(this.markets_by_id, symbolId); if (market === undefined) { symbol = symbolId; } } symbol = this.safeString(market, 'symbol', symbol); let cost = undefined; if ((amount !== undefined) && (price !== undefined) && (market !== undefined)) { const linear = this.safeValue(market, 'linear'); if (linear) { cost = Precise.stringMul(amount, price); // in quote } else { cost = Precise.stringDiv(amount, price); // in base } const contractSize = this.safeString(market, 'contractSize'); cost = Precise.stringMul(cost, contractSize); } let takerOrMaker = undefined; const fillType = this.safeString(trade, 'fillType'); if (fillType !== undefined) { if (fillType.indexOf('taker') >= 0) { takerOrMaker = 'taker'; } else if (fillType.indexOf('maker') >= 0) { takerOrMaker = 'maker'; } } return this.safeTrade({ 'info': trade, 'id': id, 'timestamp': timestamp, 'datetime': this.iso8601(timestamp), 'symbol': symbol, 'order': order, 'type': type, 'side': side, 'takerOrMaker': takerOrMaker, 'price': price, 'amount': amount, 'cost': cost, 'fee': undefined, }); } createOrderRequest(symbol, type, side, amount, price = undefined, params = {}) { type = this.safeString(params, 'orderType', type); const timeInForce = this.safeString(params, 'timeInForce'); const stopPrice = this.safeString(params, 'stopPrice'); let postOnly = false; [postOnly, params] = this.handlePostOnly(type === 'market', type === 'post', params); const clientOrderId = this.safeString2(params, 'clientOrderId', 'cliOrdId'); params = this.omit(params, ['clientOrderId', 'cliOrdId']); if ((type === 'stp' || type === 'take_profit') && stopPrice === undefined) { throw new ArgumentsRequired(this.id + ' createOrder requires params.stopPrice when type is ' + type); } if (stopPrice !== undefined && type !== 'take_profit') { type = 'stp'; } else if (postOnly) { type = 'post'; } else if (timeInForce === 'ioc') { type = 'ioc'; } else if (type === 'limit') { type = 'lmt'; } else if (type === 'market') { type = 'mkt'; } const request = { 'orderType': type, 'symbol': this.marketId(symbol), 'side': side, 'size': amount, }; if (price !== undefined) { request['limitPrice'] = price; } if (clientOrderId !== undefined) { request['cliOrdId'] = clientOrderId; } return this.extend(request, params); } async createOrder(symbol, type, side, amount, price = undefined, params = {}) { /** * @method * @name krakenfutures#createOrder * @description Create an order on the exchange * @param {string} symbol market symbol * @param {string} type One of 'limit', 'market', 'take_profit' * @param {string} side buy or sell * @param {int} amount Contract quantity * @param {float} [price] Limit order price * @param {float} [params.stopPrice] The stop price associated with a stop or take profit order, Required if orderType is stp or take_profit, Must not have more than 2 decimal places, Note that for stop orders, limitPrice denotes the worst price at which the stop or take_profit order can get filled at. If no limitPrice is provided the stop or take_profit order will trigger a market order, * @param {bool} [params.reduceOnly] Set as true if you wish the order to only reduce an existing position, Any order which increases an existing position will be rejected, Default false, * @param {bool} [params.postOnly] Set as true if you wish to make a postOnly order, Default false * @param {string} [params.triggerSignal] If placing a stp or take_profit, the signal used for trigger, One of: 'mark', 'index', 'last', last is market price * @param {string} [params.cliOrdId] UUID The order identity that is specified from the user, It must be globally unique * @param {string} [params.clientOrderId] UUID The order identity that is specified from the user, It must be globally unique */ await this.loadMarkets(); const orderRequest = this.createOrderRequest(symbol, type, side, amount, price, params); const response = await this.privatePostSendorder(orderRequest); // // { // "result": "success", // "sendStatus": { // "order_id": "salf320-e337-47ac-b345-30sdfsalj", // "status": "placed", // "receivedTime": "2022-02-28T19:32:17.122Z", // "orderEvents": [ // { // "order": { // "orderId": "salf320-e337-47ac-b345-30sdfsalj", // "cliOrdId": null, // "type": "lmt", // "symbol": "pi_xrpusd", // "side": "buy", // "quantity": 1, // "filled": 0, // "limitPrice": 0.7, // "reduceOnly": false, // "timestamp": "2022-02-28T19:32:17.122Z", // "lastUpdateTimestamp": "2022-02-28T19:32:17.122Z" // }, // "reducedQuantity": null, // "type": "PLACE" // } // ] // }, // "serverTime": "2022-02-28T19:32:17.122Z" // } // const sendStatus = this.safeValue(response, 'sendStatus'); const status = this.safeString(sendStatus, 'status'); this.verifyOrderActionSuccess(status, 'createOrder', ['filled']); return this.parseOrder(sendStatus); } async createOrders(orders, params = {}) { /** * @method * @name krakenfutures#createOrders * @description create a list of trade orders * @see https://docs.futures.kraken.com/#http-api-trading-v3-api-order-management-batch-order-management * @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 * @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ await this.loadMarkets(); const ordersRequests = []; for (let i = 0; i < orders.length; i++) { const rawOrder = orders[i]; const marketId = 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.safeValue(rawOrder, 'params', {}); const extendedParams = this.extend(orderParams, params); // the request does not accept extra params since it's a list, so we're extending each order with the common params if (!('order_tag' in extendedParams)) { // order tag is mandatory so we will generate one if not provided extendedParams['order_tag'] = this.sum(i, 1).toString(); // sequential counter } extendedParams['order'] = 'send'; const orderRequest = this.createOrderRequest(marketId, type, side, amount, price, extendedParams); ordersRequests.push(orderRequest); } const request = { 'batchOrder': ordersRequests, }; const response = await this.privatePostBatchorder(this.extend(request, params)); // // { // "result": "success", // "serverTime": "2023-10-24T08:40:57.339Z", // "batchStatus": [ // { // "status": "requiredArgumentMissing", // "orderEvents": [] // }, // { // "status": "requiredArgumentMissing", // "orderEvents": [] // } // ] // } // const data = this.safeValue(response, 'batchStatus', []); return this.parseOrders(data); } async editOrder(id, symbol, type, side, amount = undefined, price = undefined, params = {}) { /** * @method * @name krakenfutures#editOrder * @see https://docs.futures.kraken.com/#http-api-trading-v3-api-order-management-edit-order * @description Edit an open order on the exchange * @param {string} id order id * @param {string} symbol Not used by Krakenfutures * @param {string} type Not used by Krakenfutures * @param {string} side Not used by Krakenfutures * @param {float} amount Order size * @param {float} [price] Price to fill order at * @param {object} [params] Exchange specific params * @returns An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ await this.loadMarkets(); const request = { 'orderId': id, }; if (amount !== undefined) { request['size'] = amount; } if (price !== undefined) { request['limitPrice'] = price; } const response = await this.privatePostEditorder(this.extend(request, params)); const status = this.safeString(response['editStatus'], 'status'); this.verifyOrderActionSuccess(status, 'editOrder', ['filled']); const order = this.parseOrder(response['editStatus']); order['info'] = response; return order; } async cancelOrder(id, symbol = undefined, params = {}) { /** * @method * @name krakenfutures#cancelOrder * @see https://docs.futures.kraken.com/#http-api-trading-v3-api-order-management-cancel-order * @description Cancel an open order on the exchange * @param {string} id Order id * @param {string} symbol Not used by Krakenfutures * @param {object} [params] Exchange specific params * @returns An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure} */ await this.loadMarkets(); const response = await this.privatePostCancelorder(this.extend({ 'order_id': id }, params)); const status = this.safeString(this.safeValue(response, 'cancelStatus', {}), 'status'); this.verifyOrderActionSuccess(status, 'cancelOrder'); let order = {}; if ('cancelStatus' in response) { order = this.parseOrder(response['cancelStatus']); } return this.extend({ 'info': response }, order); } async cancelOrders(ids, symbol = undefined, params = {}) { /** * @method * @name krakenfutures#cancelOrders * @description cancel multiple orders * @see https://docs.futures.kraken.com/#http-api-trading-v3-api-order-management-batch-order-management * @param {string[]} ids order ids * @param {string} [symbol] unified market symbol * @param {object} [params] extra parameters specific to the exchange API endpoint * * EXCHANGE SPECIFIC PARAMETERS * @param {string[]} [params.clientOrderIds] max length 10 e.g. ["my_id_1","my_id_2"] * @returns {object} an list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} */ await this.loadMarkets(); const orders = []; const clientOrderIds = this.safeValue(params, 'clientOrderIds', []); const clientOrderIdsLength = clientOrderIds.length; if (clientOrderIdsLength > 0) { for (let i = 0; i < clientOrderIds.length; i++) { orders.push({ 'order': 'cancel', 'cliOrdId': clientOrderIds[i] }); } } else { for (let i = 0; i < ids.length; i++) { orders.push({ 'order': 'cancel', 'order_id': ids[i] }); } } const request = { 'batchOrder': orders, }; const response = await this.privatePostBatchorder(this.extend(request, params)); // { // "result": "success", // "serverTime": "2023-10-23T16:36:51.327Z", // "batchStatus": [ // { // "status": "cancelled", // "order_id": "101c2327-f12e-45f2-8445-7502b87afc0b", // "orderEvents": [ // { // "uid": "101c2327-f12e-45f2-8445-7502b87afc0b", // "order": { // "orderId": "101c2327-f12e-45f2-8445-7502b87afc0b", // "cliOrdId": null, // "type": "lmt", // "symbol": "PF_LTCUSD", // "side": "buy", // "quantity": "0.10000000000", // "filled": "0E-11", // "limitPrice": "50.00000000000", // "reduceOnly": false, // "timestamp": "2023-10-20T10:29:13.005Z", // "lastUpdateTimestamp": "2023-10-20T10:29:13.005Z" // }, // "type": "CANCEL" // } // ] // } // ] // } const batchStatus = this.safeValue(response, 'batchStatus', []); return this.parseOrders(batchStatus); } async cancelAllOrders(symbol = undefined, params = {}) { /** * @method * @name krakenfutures#cancelAllOrders * @see https://docs.futures.kraken.com/#http-api-trading-v3-api-order-management-cancel-all-orders * @description Cancels all orders on the exchange, including trigger orders * @param {str} symbol Unified market symbol * @param {dict} [params] Exchange specific params * @returns Response from exchange api */ const request = {}; if (symbol !== undefined) { request['symbol'] = this.marketId(symbol); } const response = await this.privatePostCancelallorders(this.extend(request, params)); return response; } async fetchOpenOrders(symbol = undefined, since = undefined, limit = undefined, params = {}) { /** * @method * @name krakenfutures#fetchOpenOrders * @see https://docs.futures.kraken.com/#http-api-trading-v3-api-order-management-get-open-orders * @description Gets all open orders, including trigger orders, for an account from the exchange api * @param {string} symbol Unified market symbol * @param {int} [since] Timestamp (ms) of earliest order. (Not used by kraken api but filtered internally by CCXT) * @param {int} [limit] How many orders to return. (Not used by kraken api but filtered internally by CCXT) * @param {object} [params] Exchange specific parameters * @returns An array of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure} */ await this.loadMarkets(); let market = undefined; if (symbol !=