UNPKG

n8n-nodes-dex

Version:

n8n node module for dYdX v4 trading and account access

179 lines 8.17 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TradingService = void 0; const v4_client_js_1 = require("@dydxprotocol/v4-client-js"); const bignumber_js_1 = __importDefault(require("bignumber.js")); const crypto_1 = __importDefault(require("crypto")); class TradingService { async init(network, address) { this.composite = await v4_client_js_1.CompositeClient.connect(network); this.indexer = new v4_client_js_1.IndexerClient(network.indexerConfig); this.address = address; return this; } async placeLimitOrder(mnemonic, params) { const wallet = await v4_client_js_1.LocalWallet.fromMnemonic(mnemonic, v4_client_js_1.BECH32_PREFIX); const sub = new v4_client_js_1.CompositeClient.SubaccountInfo(wallet, 0); const side = params.side === v4_client_js_1.OrderSide.BUY ? v4_client_js_1.OrderSide.BUY : v4_client_js_1.OrderSide.SELL; const tx = await this.composite.placeOrder(sub, params.market, // market side, // side - cast to any to avoid TypeScript errors params.price.toString(), // price params.size.toString(), // size v4_client_js_1.OrderType.LIMIT, // orderType v4_client_js_1.OrderTimeInForce.GTT, // timeInForce v4_client_js_1.OrderExecution.DEFAULT, // execution crypto_1.default.randomUUID(), // clientId v4_client_js_1.OrderFlags.SHORT_TERM); return { hash: tx.hash.toString(), clientId: tx.clientId?.toString() || '', }; } async placeMarketOrder(mnemonic, params) { const wallet = await v4_client_js_1.LocalWallet.fromMnemonic(mnemonic, v4_client_js_1.BECH32_PREFIX); const sub = new v4_client_js_1.CompositeClient.SubaccountInfo(wallet, 0); const side = params.side === v4_client_js_1.OrderSide.BUY ? v4_client_js_1.OrderSide.BUY : v4_client_js_1.OrderSide.SELL; const tx = await this.composite.placeOrder(sub, params.market, // market side, // side - cast to any to avoid TypeScript errors '0', // price params.size.toString(), // size v4_client_js_1.OrderType.MARKET, // orderType v4_client_js_1.OrderTimeInForce.IOC, // timeInForce v4_client_js_1.OrderExecution.DEFAULT, // execution crypto_1.default.randomUUID(), // clientId v4_client_js_1.OrderFlags.SHORT_TERM); return { hash: tx.hash.toString(), }; } async cancelOrder(params, mnemonic, accountId = 0) { const wallet = await v4_client_js_1.LocalWallet.fromMnemonic(mnemonic, v4_client_js_1.BECH32_PREFIX); const sub = new v4_client_js_1.CompositeClient.SubaccountInfo(wallet, accountId); const tx = await this.composite.cancelOrder(sub, params.clientId, v4_client_js_1.OrderFlags.SHORT_TERM, // orderFlags params.market); return { hash: tx.hash.toString(), }; } async transferBetweenSubaccounts(mnemonic, params) { const wallet = await v4_client_js_1.LocalWallet.fromMnemonic(mnemonic, v4_client_js_1.BECH32_PREFIX); const tx = await this.composite.transfer(wallet, params.fromId, params.toId, params.amount.toString()); return { hash: tx.hash.toString(), }; } async getPositions() { const subaccounts = await this.indexer.getSubaccounts(this.address); return subaccounts.map((s) => ({ subaccountId: s.subaccountNumber, positions: Object.entries(s.openPerpetualPositions || {}).map(([m, p]) => ({ market: m, size: p.size, price: p.entryPrice, status: p.status, isLong: parseFloat(p.size) > 0, leverage: new bignumber_js_1.default(p.size).multipliedBy(p.entryPrice).dividedBy(s.equity).toNumber(), })), })); } async smartPosition(params, mnemonic, accountId = 0) { // Get current positions const subaccounts = await this.indexer.getSubaccounts(this.address); const subaccount = subaccounts.find((s) => s.subaccountNumber === accountId); if (!subaccount) { throw new Error(`Subaccount ${accountId} not found`); } // Find if we already have a position for this market const positions = subaccount.openPerpetualPositions || {}; const currentPosition = positions[params.market]; const currentSize = currentPosition ? parseFloat(currentPosition.size) : 0; // If target size is same as current, do nothing if (currentSize === params.size) { return currentSize; } // Calculate the difference to achieve target position const sizeDiff = params.size - currentSize; const side = sizeDiff > 0 ? v4_client_js_1.OrderSide.BUY : v4_client_js_1.OrderSide.SELL; const size = Math.abs(sizeDiff); // Get market price and calculate 1% slippage const marketInfo = await this.getPerpetualMarket(params.market); const midPrice = (parseFloat(marketInfo.oraclePrice) * (side === v4_client_js_1.OrderSide.BUY ? 1.01 : 0.99)); // Place market order await this.placeMarketOrder(mnemonic, { market: params.market, side: side, price: midPrice, size: size, type: v4_client_js_1.OrderType.MARKET, }); return params.size; } async getAllPerpetualMarkets() { const markets = await this.indexer.getPerpetualMarkets(); return Object.values(markets); } async getPerpetualMarket(market) { const markets = await this.indexer.getPerpetualMarkets(); return markets[market]; } async getMarketTrades(market, limit = 50) { const trades = await this.indexer.getTrades({ ticker: market, limit }); return trades.trades; } async getMarketCandles(market, resolution, limit = 50) { const candles = await this.indexer.getCandles({ ticker: market, resolution, limit }); return candles.candles; } async marketSpread(market) { const orderbook = await this.indexer.getOrderbook(market); const bestBid = orderbook.bids && orderbook.bids.length > 0 ? parseFloat(orderbook.bids[0].price) : 0; const bestAsk = orderbook.asks && orderbook.asks.length > 0 ? parseFloat(orderbook.asks[0].price) : 0; // Not returning spread info in the response currently, but might be useful in the future return { bestBid: { price: bestBid, size: orderbook.bids && orderbook.bids.length > 0 ? parseFloat(orderbook.bids[0].size) : 0, }, bestAsk: { price: bestAsk, size: orderbook.asks && orderbook.asks.length > 0 ? parseFloat(orderbook.asks[0].size) : 0, } }; } async getHistoricalFundingRates(market) { const rates = await this.indexer.getHistoricalFundingRates({ ticker: market }); return rates.fundingRates; } async getSparkline(market) { // Get 24-hour candles const endTime = new Date(); const startTime = new Date(endTime.getTime() - 24 * 60 * 60 * 1000); const candles = await this.indexer.getCandles({ ticker: market, resolution: '15MINS', fromISO: startTime.toISOString(), toISO: endTime.toISOString() }); if (!candles.candles || candles.candles.length === 0) { return { ticker: market, prices: [], times: [] }; } const prices = candles.candles.map((c) => c.close); const times = candles.candles.map((c) => c.startedAt); return { ticker: market, prices, times }; } } exports.TradingService = TradingService; //# sourceMappingURL=trading.service.js.map