"use strict"

class KrakenFuturesDemo extends Exchange {
	static meta = {
		aliases: [
			"KRAKENFUTURESDEMO",
			"KRAKENFUTURESSANDBOX",
			"KRAKENFUTURES-SANDBOX",
			"KRAKENFUTURES-DEMO",
			"KRAKENFTDEMO",
			"KRAKENFTSANDBOX",
			"KRAKENFT-SANDBOX",
			"KRAKENFT-DEMO",
		],
		endpoint: "https://demo-futures.kraken.com/derivatives",
		fields: {
			public: {
				label: "API Key",
			},
			private: {
				label: "API Secret",
			},
		},
		name: "Kraken Futures Demo",
		permissions: {
			origins: [
				"https://demo-futures.kraken.com/derivatives/api/*"
			],
		},
		subscriptions: {
			active: [],
			inactive: [],
		},
		version: "v3",
		website: "https://futures-demo.kraken.com",
		desc: "Cryptocurrency Futures Exchange by Kraken. Powerful API and Robust Indices - Go long and short with up to 50x leverage!",
		defaultSymbol: "XBTUSD",
		testSymbol: "PI_XBTUSD",
		multiAsset: "USD"+marginSuffix,
		margin: true, marginType: true, leavesQuantity: true
	}
	constructor(meta = KrakenFuturesDemo.meta) {
		super(meta)
	}

	async req(method, resource, params = {}, sign = true) {
		const headers = {"Accept": "application/json"}
		resource = "/api/" + this.meta.version + resource
		if (sign) {
			const credentials = this.getCredentials("private")
			const nonce = this.getNonce()
			const payload = serialize(params, undefined, undefined, false) + nonce + resource
			const hashBytes = CryptoJS.SHA256(payload).toString(CryptoJS.enc.Latin1)
			headers["APIKey"] = credentials.public
			headers["Authent"] = CryptoJS.HmacSHA512(CryptoJS.enc.Latin1.parse(hashBytes), CryptoJS.enc.Base64.parse(credentials.private)).toString(CryptoJS.enc.Base64)
			headers["Nonce"] = nonce
		} else this.hasAccess()

		if (method === 'PUT') {
			resource += '?'+serialize(params)
			params = null
		}
		const response = await request.call(this, method, this.meta.endpoint + resource, params, headers)
		if (!response || !response.result || response.result === 'error') {
			throw new Error(response.error || "Unknown connection error with API endpoint!")
		}
		if (response.sendStatus && response.sendStatus.status !== 'placed') {
			throw new Error(response.sendStatus.status)
		}
		return response
	}

	async time() {
		return new Date((await request.call(this, 'GET', this.meta.endpoint + "/api/" + this.meta.version + "/tickers", {_: Date.now()}, null)).serverTime).getTime()
	}

	async account(currency) {
		const data = (await this.req('GET', "/accounts")).accounts
		let balances = {}
		if (!data) {
			this.info("unable to get account info!")
			return balances
		}
		if (data.cash && data.cash.balances) {
			Object.each(data.cash.balances, (coin, amount) => {
				amount && (balances[coin.toUpperCase()] = {
					available: amount, balance: amount
				})
			})
		}
		Object.each(data, (symbol, info) => {
			if (info.currency && info.auxiliary && info.auxiliary.pv) {
				balances[symbol.split('_')[1].toUpperCase()] = {
					available: info.auxiliary.af,
					balance: info.auxiliary.pv
				}
			}
		})
		if (data.flex && data.flex.currencies) {
			Object.each(data.flex.currencies, (coin, info) => {
				info.quantity && (balances[coin.toUpperCase()+marginSuffix] = {
					available: info.available,
					balance: info.quantity
				})
			})
		}
		if (data.flex && data.flex.collateralValue) {
			balances[this.meta.multiAsset] = {
				available: data.flex.availableMargin,
				balance: data.flex.collateralValue
			}
		}
		balances[this.meta.defaultSymbol] = balances[this.meta.defaultSymbol] || {available: 0, balance: 0}
		return balances
	}

	sym(name) {
		// allow underline $$$$$ make meta info flag?
		return name?.replace(/[-/\\: ]/g, '').toUpperCase()
	}

	async symbolInfoImpl(cache) {
		const response = await this.req('GET', "/instruments", undefined, false)
		response?.instruments?.forEach(info => {
			if (info.tradeable) {
				info.currency = (info.underlying || info.symbol).split('_')[1].toUpperCase()
				info.pricePrecision = decimals(info.tickSize) + (!info.tickSize.toString().endsWith('1') ? info.tickSize % 1 : 0)
				info.quantityPrecision = info.contractValueTradePrecision !== undefined ? info.contractValueTradePrecision : 
									decimals(info.contractSize) + (!info.contractSize.toString().endsWith('1') ? info.contractSize % 1 : 0)
				info.levq = info.marginLevels && info.marginLevels.map(lev => lev.numNonContractUnits || lev.contracts || 0)
				info.levs = info.marginLevels && info.marginLevels.map(lev => 1 / (lev.initialMargin || 0.02))
				info.leverage = info.levs && info.levs[0] || 50
				info.isFlex = info.type && info.type.startsWith("flex")
				info.type = info.isFlex ? "Flexible Futures" : "Inverse Futures"
				cache[(info.symbol = info.symbol.toUpperCase())] = info
				if (info.lastTradingTime) {
					const dd = new Date(info.lastTradingTime)
					cache[info.currency + 'FGHJKMNQUVXZ'[dd.getMonth()] + (dd.getYear()+1900)] = info
				} else if (!cache[info.currency]) {
					cache[info.currency] = info
				}
			}
		})
	}

	async symbolTickerImpl(symbol) {
		let tickers = (await this.req('GET', "/tickers", {_: Date.now()}, false)).tickers
		tickers = tickers.reduce((map, ticker) => (map[ticker.symbol.toUpperCase()] = ticker, map), {})
		let ticker = tickers[symbol]

		return ticker && {
			ask: Number(ticker.ask),
			bid: Number(ticker.bid),
			mid: (Number(ticker.ask) + Number(ticker.bid)) / 2,
			mark: Number(ticker.markPrice),
			last: Number(ticker.last)
		}
	}

	maxLev(market, qty) {
		if (!market.levq || market.levq.length < 2) return market.leverage
		let i = 0
		for (const min of market.levq) {
		    if (qty < min) break
		    ++i
		}
		return market.levs[--i]
	}

	async trade(cmd, ohlc = {}) {
		const market = await this.symbolInfo(cmd.s)
		cmd._sym = market.symbol
		const currency = cmd.currency = market.isFlex ? this.meta.multiAsset : market.currency || this.meta.defaultSymbol
		const ticker = (cmd.pc && !cmd.up && this.getPrice(market.symbol)) || (await this.symbolTicker(cmd.up, market.symbol))
		if (!ticker) {
			throw new ReferenceError(`Ticker ${market.symbol} is not available!`)
		}
		this.checkSlip(cmd, ohlc, market.symbol, ticker)
		if (cmd.up) {
			this.updatePrice(market.symbol, ticker)
			if (!cmd.b && !cmd.ub) {
				return ticker
			}
		}

		let balance = {available: 0, balance: 0}
		if ((cmd.y !== "possize" && cmd.q.wasPercent()) || cmd.ub) {
			balance = cmd.bc && !cmd.ub && this.getBalance(currency)
			const balances = !balance && (await this.account(currency))
			balance = balance || balances[currency]
			this.checkBalance(cmd, balance, currency, `${market.isFlex ? 'Multi' : 'Single'} Collateral Wallet`)
			if (!cmd.bc || cmd.ub) {
				this.updateBalance(balances, currency)
				if (cmd.ub) {
					return Object.assign(ticker, balance)
				}
			}
		}

		const first = ticker[cmd.pr] || this.ohlc(cmd, ohlc, 'pr') || ((cmd.isBid && cmd.t !== "market") || (cmd.isAsk && cmd.t === "market") ? ticker.bid : ticker.ask)
		let price = (cmd.fp || cmd.p._().relative(first).minmax(cmd.minp, cmd.maxp)).resolve(market.pricePrecision)
		if (price <= 0) {
			this.warn("your p parameter might be incorrect! (price of 0 calculated, use minp= to set minimum)")
		}

		let flexLev = 0
		if (market.isFlex) {
			let info = await this.req('GET', "/leveragepreferences")
			info?.leveragePreferences && (info = info.leveragePreferences.reduce((map, info) => (map[info.symbol] = info.maxLeverage, map), {}))
			flexLev = info && info[market.symbol] || 0
		}

		const cross = !market.isFlex ? true : cmd.mt ? cmd.mt === "crossed" : cmd.l !== undefined ? cmd.l === 0 : flexLev === 0
		let cf = 1.0
		let leverage = cmd._lev = (cmd.l === 0 ? market.leverage : cmd.l) * (this.ohlc(cmd, ohlc, 'lr') || 1) || flexLev || market.leverage
		if (leverage > market.leverage) {
			cmd.l && (cf = market.leverage / leverage)
			leverage = cmd._lev = market.leverage
			this.warn(`leverage limited to ${leverage}x by instrument!`)
		}
		const available = cmd.y === "equity" ? balance.balance : balance.available
		const contracts = ohlc[cmd.y] || available * (cmd.lc || leverage) * cf * (market.isFlex ? 1/price : price / market.contractSize)

		let order = {
			symbol: market.symbol,
			side: cmd.isBid ? "buy" : "sell",
			orderType: ({post: "post", ioc: "ioc", market: "mkt"})[cmd.t] || "lmt",
			size: cmd.q._().mul(!cmd.q.wasPercent() ? (cmd.lc || leverage) * (cmd.u === "currency" ? (market.isFlex ? 1/price : price) : 1) : 1)
				.reference(contracts).mul(this.ohlc(cmd, ohlc, 'qr') || 1).add(this.addpos(cmd, ohlc)).minmax(cmd.minq, cmd.maxq).resolve(market.quantityPrecision)
		}
		if (cmd.lq && ohlc.left) {
			this.info("using leftover quantity from last order: "+ohlc.left)
			order.size = new NumberObject(ohlc.left).resolve(market.quantityPrecision)
		}
		if (order.size <= 0) this.warn("your q parameter might be incorrect! (quantity of 0 calculated, use minq= to set minimum)")
		if (cmd.r) order.reduceOnly = true
		if (cmd.t !== "market") order.limitPrice = price

		if (cmd.ts) {
			order.orderType = "trailing_stop"
			order.trailingStopDeviationUnit = cmd.ts.wasPercent() ? "PERCENT" : "QUOTE_CURRENCY"
			order.trailingStopMaxDeviation = cmd.ts.wasPercent() ? cmd.ts._().abs().mul(100).toDecimal() : cmd.ts._().reference(this.ohlc(cmd, ohlc, 'tsref', ticker, price) || first).abs().resolve(market.pricePrecision)
			order.triggerSignal = cmd.sr
			delete order.limitPrice
		} else if (cmd.tp) {
			order.orderType = "take_profit"
			order.stopPrice = cmd.tp._().relative(this.ohlc(cmd, ohlc, 'tpref', ticker, price) || first).resolve(market.pricePrecision)
			order.triggerSignal = cmd.sr
			if (cmd.t === "market") delete order.limitPrice
		} else if (cmd.sl || cmd.so) {
			const param = cmd.sl && 'slref' || 'soref'
			order.orderType = "stp"
			order.stopPrice = (cmd.sl || cmd.so)._().relative(this.ohlc(cmd, ohlc, param, ticker, price) || first).resolve(market.pricePrecision)
			order.triggerSignal = cmd.sr
			if (cmd.t === "market") delete order.limitPrice
		}

		this.info(`placing ${currency} (${market.isFlex ? 'Multi' : 'Single'} Collateral) order @ ${leverage}x${cross?' cross':' isolated'}: `+stringify(order))
		if (cmd.d) {
			this.msgDisabled(cmd)
			return order
		}

		if (cmd.l !== undefined || market.isFlex && cmd.mt) {
			if (!market.isFlex) {
				!cmd.d && this.setBalanceRaw(market.symbol, leverage)
				this.info("Single Collateral markets do not offer isolated margin – leverage parameter only used for calculation and position check!")
			} else {
				if (cross && cmd.l !== undefined) {
					!cmd.d && this.setBalanceRaw(market.symbol, leverage)
					this.info("Flexible Futures cross margin does not offer custom leverage – leverage parameter only used for calculation and position check!")
				}
				this.info(`setting contract leverage to ${cross && market.leverage || leverage}x${cross?' cross':' isolated'}...`)
				try {
					await this.req('PUT', "/leveragepreferences", cross ? {symbol: market.symbol} : {maxLeverage: leverage, symbol: market.symbol})
				} catch(ex) {
					this.warn(`couldn't set leverage, please set manually! (Error: ${ex.errors && ex.errors[0] && ex.errors[0].message || ex.status})`)
				}
			}
		}
		let result = await this.req('POST', "/sendorder", order)
		result = result.sendStatus && result.sendStatus.orderEvents && result.sendStatus.orderEvents.last()
		result = result && (result.order || result.orderTrigger || result.orderPriorExecution) || result
		cmd.id && result && (result.orderId || result.uid || result.order_id) && this.cacheId(result.orderId || result.uid || result.order_id, cmd.id)
		return result
	}

	async ordersCancel(cmd, ohlc = {}) {
		const market = await this.symbolInfo(cmd.s)
		cmd._sym = market.symbol
		if (!cmd.ch && !cmd.d && !cmd.b && !cmd.fp && cmd.t != "open" && cmd.t != "close" && cmd.r === undefined && !cmd.id && cmd.cm.wasPercent(true) && !cmd.gt && !cmd.gte && !cmd.lt && !cmd.lte) {
			this.info(`canceling all ${market.symbol} orders in bulk!`)

			let result = await this.req('POST', "/cancelallorders", {symbol: market.symbol})
			result = result?.cancelStatus?.orderEvents?.map(order => order.order || order.orderTrigger || order.orderPriorExecution)

			result?.forEach(order => this.removeId(order.orderId || order.uid || order.order_id))
			if (!result || !result.length) {
				this.msgOrdersNone()
				return false
			}
			return result
		}

		let orders = (await this.req('GET', "/openorders")).openOrders
		if (!orders || !orders.filter) {
			this.msgOrderErr()
			return false
		}
		orders = orders.filter(order => order.symbol.toUpperCase() === market.symbol)
		if (!orders.length) {
			this.msgOrdersNone()
			return false
		}

		const ids = (cmd.id || "").split(',')
		const match = cmd.cr ? false : true
		const tickers = {}
		await this.filterOrderStart(cmd, ohlc, tickers, orders)
		const totalOrders = orders.length
		orders = orders.filter(order => {
			if ((cmd.isBid && order.side !== 'buy') || (cmd.isAsk && order.side !== 'sell')) {
				return false
			}
			if (cmd.fp && cmd.fp.compare(order.limitPrice)) {
				return !match
			}
			if ((cmd.tp && order.orderType !== "take_profit") || ((cmd.sl || cmd.so) && order.orderType !== "stp")) {
				return !match
			}
			if (cmd.r !== undefined && !cmd.r != !order.reduceOnly) {
				return !match
			}
			if ((cmd.t === "open" && Number(order.stopPrice)) || (cmd.t === "close" && !Number(order.stopPrice))) {
				return !match
			}
			if (cmd.id && !this.checkId(order.order_id, ids)) {
				return !match
			}
			if (!this.filterOrder(cmd, ohlc, tickers, order)) {
				return !match
			}
			return match
		})

		if (cmd.cm._().reference(orders.length).getMax() < orders.length) {
			const sort = {
				"newest": 	["receivedTime", true],
				"oldest": 	["receivedTime", false],
				"highest": 	["limitPrice", true],
				"lowest": 	["limitPrice", false],
				"biggest": 	["unfilledSize", true],
				"smallest": ["unfilledSize", false]
			}
			cmd.cmo === 'random' ? shuffle(orders) : sortByIndex(orders, sort[cmd.cmo][0], sort[cmd.cmo][1])
			orders = orders.slice(0, cmd.cm.resolve(0))
		}

		this.msgOrders(cmd, orders, totalOrders)
		if (!orders.length || cmd.ch || cmd.d) {
			orders.length && this.msgDisabled(cmd)
			return orders
		}

		const batch = {batchOrder: orders.map(order => ({order: "cancel", order_id: order.order_id}))}
		let result = await this.req('POST', "/batchorder", {json: JSON.stringify(batch)})
		result = result?.batchStatus?.[0]?.orderEvents?.map(order => order.order || order.orderTrigger || order.orderPriorExecution)

		result?.forEach(order => this.removeId(order.orderId || order.uid || order.order_id))
		return result
	}

	async positionsClose(cmd, pos, i, ohlc = {}, ticker, market) {
		ticker = (cmd.pc && !cmd.up && this.getPrice(market.symbol)) || ticker || (await this.symbolTicker(cmd.up, market.symbol))
		if (!ticker) {
			throw new ReferenceError(`Ticker ${market.symbol} is not available!`)
		}
		ticker.pos = pos.price
		this.checkSlip(cmd, ohlc, market.symbol, ticker)
		cmd.up && this.updatePrice(market.symbol, ticker)

		const first = ticker[cmd.sl || cmd.tp || cmd.ts || cmd.so ? 'pos' : cmd.pr] || this.ohlc(cmd, ohlc, 'pr') || ((cmd.isBid && cmd.t !== "market") || (cmd.isAsk && cmd.t === "market") ? ticker.bid : ticker.ask)
		let price = (cmd.fp || cmd.p._().relative(first).minmax(cmd.minp, cmd.maxp)).resolve(market.pricePrecision)
		if (price <= 0) {
			this.warn("your p parameter might be incorrect! (price of 0 calculated, use minp= to set minimum)")
		}

		const leverage = cmd.l || pos.leverage || market.leverage
		let order = {
			symbol: market.symbol,
			side: pos.side === 'long' ? 'sell' : 'buy',
			orderType: ({post: "post", ioc: "ioc", market: "mkt"})[cmd.t] || "lmt",
			size: cmd.q._().mul(!cmd.q.wasPercent() ? (cmd.lc || leverage) * (cmd.u === "currency" ? (market.isFlex ? 1/price : price) : 1) : 1)
				.reference(pos.size).mul(this.ohlc(cmd, ohlc, 'qr') || 1).add(this.addpos(cmd, ohlc, pos)).minmax(cmd.minq, cmd.maxq).resolve(market.quantityPrecision),
			reduceOnly: cmd.r === undefined ? true : cmd.r
		}
		if (order.size <= 0) this.warn("your q parameter might be incorrect! (quantity of 0 calculated, use minq= to set minimum)")
		if (cmd.t !== "market") order.limitPrice = price

		if (cmd.ts) {
			order.orderType = "trailing_stop"
			order.trailingStopDeviationUnit = cmd.ts.wasPercent() ? "PERCENT" : "QUOTE_CURRENCY"
			order.trailingStopMaxDeviation = cmd.ts.wasPercent() ? cmd.ts._().abs().mul(100).toDecimal() : cmd.ts._().reference(this.ohlc(cmd, ohlc, 'tsref', ticker, price) || first).abs().resolve(market.pricePrecision)
			order.triggerSignal = cmd.sr
			delete order.limitPrice
		} else if (cmd.tp) {
			order.orderType = "take_profit"
			order.stopPrice = cmd.tp._().relative(this.ohlc(cmd, ohlc, 'tpref', ticker, price) || first).resolve(market.pricePrecision)
			order.triggerSignal = cmd.sr
			if (cmd.t === "market") delete order.limitPrice
		} else if (cmd.sl || cmd.so) {
			const param = cmd.sl && 'slref' || 'soref'
			order.orderType = "stp"
			order.stopPrice = (cmd.sl || cmd.so)._().relative(this.ohlc(cmd, ohlc, param, ticker, price) || first).resolve(market.pricePrecision)
			order.triggerSignal = cmd.sr
			if (cmd.t === "market") delete order.limitPrice
		}

		!cmd.ch && this.info(`placing Close Position order @ ${leverage}x: `+stringify(order))
		if (cmd.ch || cmd.d) {
			this.msgDisabled(cmd)
			return order
		}

		if (cmd.l !== undefined && !market.isFlex) {
			this.info("Single Collateral markets do not offer isolated margin – leverage parameter only used for calculation!")
		}
		let result = await this.req('POST', "/sendorder", order)
		result = result.sendStatus && result.sendStatus.orderEvents && result.sendStatus.orderEvents.last()
		result = result && (result.order || result.orderTrigger || result.orderPriorExecution) || result
		cmd.id && result && (result.orderId || result.uid || result.order_id) && this.cacheId(result.orderId || result.uid || result.order_id, cmd.id)
		return result
	}

	async positionsCloseAll(cmd, ohlc, positions) {
		const symbols = await this.symbolInfo()
		if (!positions) {
			const market = cmd.s && cmd.s !== '*' && symbols[cmd.s]
			if (cmd.s && cmd.s !== '*' && !market) {
				throw new Error(`Unknown market symbol: ${cmd.s} – Use the 'Symbols' button in the exchange settings or alert editor to browse for valid symbols!`)
			}
			positions = (await this.req('GET', "/openpositions")).openPositions
			if (!positions || !positions.filter) {
				this.msgPosErr()
				return false
			}
			const sym = market?.symbol
			positions = positions.filter(pos => ((pos.symbol = pos.symbol.toUpperCase()) === sym || !sym) && Number(pos.size))
			if (!positions.length) {
				this.msgPosNone()
				return false
			}
		}

		const tickers = {}
		for (const pos of positions) {
			if (!tickers[pos.symbol]) {
				tickers[pos.symbol] = ((cmd.up || cmd.minpl || cmd.maxpl || !cmd.s || cmd.s === '*') && (await this.symbolTicker(cmd.up, pos.symbol))) || (cmd.pc && this.getPrice(pos.symbol))
			}
		}

		const match = cmd.cr ? false : true
		await this.filterPosStart(cmd, ohlc, tickers, positions)
		const totalPos = positions.length
		positions = positions.filter(pos => {
			if ((cmd.isBid && pos.side !== 'long') || (cmd.isAsk && pos.side !== 'short')) {
				return false
			}
			if (cmd.fp && cmd.fp.compare(pos.price)) {
				return !match
			}
			const market = symbols[pos.symbol] || {}
			pos.currency = cmd.currency = market.isFlex ? this.meta.multiAsset : market.currency || this.meta.defaultSymbol
			pos.leverage = pos.maxFixedLeverage || this.getBalanceRaw()[pos.symbol] || market.leverage
			pos.margin = pos.size * (market.isFlex ? pos.price : 1/pos.price * market.contractSize) / pos.leverage
			const ticker = tickers[pos.symbol]
			pos._sizeUSD = market.isFlex ? pos.size * pos.price : pos.size
			pos._sizeCoin = market.isFlex ? pos.size : pos.size / pos.price
			pos.net = ticker ? toPrecision((pos.side === 'long' ? 1/pos.price - 1/ticker.mark : 1/ticker.mark - 1/pos.price) * pos._sizeUSD, 8) : 0
			pos.pnl = ticker ? (pos.side === 'long' ? ticker.mark / pos.price : pos.price / ticker.mark) - 1 : 0
			pos.avgEntryPrice = toPrecision(pos.price, market.pricePrecision)
			ticker && (pos.markPrice = ticker.mark)
			if (!market.isFlex || !pos.maxFixedLeverage) pos.isolated = false
			if (!this.filterPos(cmd, ohlc, tickers, pos, balance => balance * (market.isFlex ? 1/pos.price : pos.price / market.contractSize) * pos.leverage, market.quantityPrecision)) {
				return !match
			}
			return match
		})

		if (cmd.cm._().reference(positions.length).getMax() < positions.length) {
			const sort = {
				"newest": 	["fillTime", true],
				"oldest": 	["fillTime", false],
				"highest": 	["price", true],
				"lowest": 	["price", false],
				"biggest": 	["size", true],
				"smallest": ["size", false]
			}
			cmd.cmo === 'random' ? shuffle(positions) : sortByIndex(positions, sort[cmd.cmo][0], sort[cmd.cmo][1])
			positions = positions.slice(0, cmd.cm.resolve(0))
		}
		if (!positions.length || cmd.ch) {
			this.msgPos(cmd, positions, totalPos)
			return positions
		}

		let closeOrders = [], i = 0
		for (const pos of positions) {
			const order = await this.positionsClose(cmd, pos, i++, ohlc, tickers[pos.symbol], symbols[pos.symbol])
			if (order) {
				closeOrders.push(order)
				Object.assign(closeOrders.last(), Object.without(pos, ["price", "quantity", "side"]))
			}
		}
		return closeOrders
	}

/*
	async transfer(cmd, ohlc) {
	}
*/
}

class KrakenFutures extends KrakenFuturesDemo {
	static meta = Object.assign({}, super.meta, {
		aliases: [
			"KRAKENFUTURES",
			"KRAKENFT",
		],
		endpoint: "https://futures.kraken.com/derivatives",
		name: "Kraken Futures",
		permissions: {
			origins: [
				"https://futures.kraken.com/derivatives/api/*"
			],
		},
		subscriptions: {
			active: [
				"e3fdd77de3a682bfcfd8dc3000a25a5b",
				"4db6261b067dabf8ffb0924c18d95d06",
			],
			inactive: [
				"krakf1",
				"krakf2",
			],
		},
		website: "https://futures.kraken.com",
	})
	constructor(meta = KrakenFutures.meta) {
		super(meta)
	}
}

Broker.add(new KrakenFutures())
Broker.add(new KrakenFuturesDemo())
