/** * Drift Order Execution * * Handles opening and closing positions with market orders */ import { getDriftService } from './client' import { getMarketConfig } from '../../config/trading' import { MarketType, PositionDirection, OrderType, OrderParams, } from '@drift-labs/sdk' export interface OpenPositionParams { symbol: string // e.g., 'SOL-PERP' direction: 'long' | 'short' sizeUSD: number // USD notional size slippageTolerance: number // Percentage (e.g., 1.0 for 1%) } export interface OpenPositionResult { success: boolean transactionSignature?: string fillPrice?: number fillSize?: number slippage?: number error?: string } export interface ClosePositionParams { symbol: string percentToClose: number // 0-100 slippageTolerance: number } export interface ClosePositionResult { success: boolean transactionSignature?: string closePrice?: number closedSize?: number realizedPnL?: number error?: string } /** * Open a position with a market order */ export async function openPosition( params: OpenPositionParams ): Promise { try { console.log('📊 Opening position:', params) const driftService = getDriftService() const marketConfig = getMarketConfig(params.symbol) const driftClient = driftService.getClient() // Get current oracle price const oraclePrice = await driftService.getOraclePrice(marketConfig.driftMarketIndex) console.log(`💰 Current ${params.symbol} price: $${oraclePrice.toFixed(4)}`) // Calculate position size in base asset const baseAssetSize = params.sizeUSD / oraclePrice // Validate minimum order size if (baseAssetSize < marketConfig.minOrderSize) { throw new Error( `Order size ${baseAssetSize.toFixed(4)} is below minimum ${marketConfig.minOrderSize}` ) } // Calculate worst acceptable price (with slippage) const slippageMultiplier = params.direction === 'long' ? 1 + (params.slippageTolerance / 100) : 1 - (params.slippageTolerance / 100) const worstPrice = oraclePrice * slippageMultiplier console.log(`📝 Order details:`) console.log(` Size: ${baseAssetSize.toFixed(4)} ${params.symbol.split('-')[0]}`) console.log(` Notional: $${params.sizeUSD.toFixed(2)}`) console.log(` Oracle price: $${oraclePrice.toFixed(4)}`) console.log(` Worst price (${params.slippageTolerance}% slippage): $${worstPrice.toFixed(4)}`) // Prepare order parameters const orderParams: OrderParams = { orderType: OrderType.MARKET, marketIndex: marketConfig.driftMarketIndex, marketType: MarketType.PERP, direction: params.direction === 'long' ? PositionDirection.LONG : PositionDirection.SHORT, baseAssetAmount: BigInt(Math.floor(baseAssetSize * 1e9)), // 9 decimals // Optional: add price limit for protection // price: BigInt(Math.floor(worstPrice * 1e6)), // 6 decimals } // Place market order console.log('🚀 Placing market order...') const txSig = await driftClient.placeAndTakePerpOrder(orderParams) console.log(`✅ Order placed! Transaction: ${txSig}`) // Wait for confirmation await driftClient.txSender.confirmTransaction(txSig) console.log('✅ Transaction confirmed') // Get actual fill price from position const position = await driftService.getPosition(marketConfig.driftMarketIndex) if (!position) { throw new Error('Position not found after order execution') } const fillPrice = position.entryPrice const slippage = Math.abs((fillPrice - oraclePrice) / oraclePrice) * 100 console.log(`💰 Fill details:`) console.log(` Fill price: $${fillPrice.toFixed(4)}`) console.log(` Slippage: ${slippage.toFixed(3)}%`) return { success: true, transactionSignature: txSig, fillPrice, fillSize: baseAssetSize, slippage, } } catch (error) { console.error('❌ Failed to open position:', error) return { success: false, error: error instanceof Error ? error.message : 'Unknown error', } } } /** * Close a position (partially or fully) with a market order */ export async function closePosition( params: ClosePositionParams ): Promise { try { console.log('📊 Closing position:', params) const driftService = getDriftService() const marketConfig = getMarketConfig(params.symbol) const driftClient = driftService.getClient() // Get current position const position = await driftService.getPosition(marketConfig.driftMarketIndex) if (!position || position.side === 'none') { throw new Error(`No active position for ${params.symbol}`) } // Calculate size to close const sizeToClose = position.size * (params.percentToClose / 100) console.log(`📝 Close order details:`) console.log(` Current position: ${position.size.toFixed(4)} ${position.side}`) console.log(` Closing: ${params.percentToClose}% (${sizeToClose.toFixed(4)})`) console.log(` Entry price: $${position.entryPrice.toFixed(4)}`) console.log(` Unrealized P&L: $${position.unrealizedPnL.toFixed(2)}`) // Get current oracle price const oraclePrice = await driftService.getOraclePrice(marketConfig.driftMarketIndex) console.log(` Current price: $${oraclePrice.toFixed(4)}`) // Prepare close order (opposite direction) const orderParams: OrderParams = { orderType: OrderType.MARKET, marketIndex: marketConfig.driftMarketIndex, marketType: MarketType.PERP, direction: position.side === 'long' ? PositionDirection.SHORT : PositionDirection.LONG, baseAssetAmount: BigInt(Math.floor(sizeToClose * 1e9)), // 9 decimals reduceOnly: true, // Important: only close existing position } // Place market close order console.log('🚀 Placing market close order...') const txSig = await driftClient.placeAndTakePerpOrder(orderParams) console.log(`✅ Close order placed! Transaction: ${txSig}`) // Wait for confirmation await driftClient.txSender.confirmTransaction(txSig) console.log('✅ Transaction confirmed') // Calculate realized P&L const pnlPerUnit = oraclePrice - position.entryPrice const realizedPnL = pnlPerUnit * sizeToClose * (position.side === 'long' ? 1 : -1) console.log(`💰 Close details:`) console.log(` Close price: $${oraclePrice.toFixed(4)}`) console.log(` Realized P&L: $${realizedPnL.toFixed(2)}`) return { success: true, transactionSignature: txSig, closePrice: oraclePrice, closedSize: sizeToClose, realizedPnL, } } catch (error) { console.error('❌ Failed to close position:', error) return { success: false, error: error instanceof Error ? error.message : 'Unknown error', } } } /** * Close entire position for a market */ export async function closeEntirePosition( symbol: string, slippageTolerance: number = 1.0 ): Promise { return closePosition({ symbol, percentToClose: 100, slippageTolerance, }) } /** * Emergency close all positions */ export async function emergencyCloseAll(): Promise<{ success: boolean results: Array<{ symbol: string result: ClosePositionResult }> }> { console.log('🚨 EMERGENCY: Closing all positions') try { const driftService = getDriftService() const positions = await driftService.getAllPositions() if (positions.length === 0) { console.log('✅ No positions to close') return { success: true, results: [] } } const results = [] for (const position of positions) { console.log(`🔴 Emergency closing ${position.symbol}...`) const result = await closeEntirePosition(position.symbol, 2.0) // Allow 2% slippage results.push({ symbol: position.symbol, result, }) } console.log('✅ Emergency close complete') return { success: true, results, } } catch (error) { console.error('❌ Emergency close failed:', error) return { success: false, results: [], } } }