From 9572b54775e693b4f166a109a40ad7e246e343d2 Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Sun, 2 Nov 2025 20:43:37 +0100 Subject: [PATCH] fix(drift): calculate realizedPnL with leverage on USD notional, not base asset - Old calculation: (closePrice - entryPrice) * sizeInBaseAsset = tiny P&L in dollars - New calculation: profitPercent * leverage * notionalUSD / 100 = correct leveraged P&L - Example: -0.13% price move * 10x leverage * $540 notional = -$7.02 (not -$0.38) - Fixes trades showing -$0.10 to -$0.77 losses when they should be -$5 to -$40 - Applied to both DRY_RUN and real execution paths --- lib/drift/orders.ts | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/lib/drift/orders.ts b/lib/drift/orders.ts index 396721b..18cfc51 100644 --- a/lib/drift/orders.ts +++ b/lib/drift/orders.ts @@ -486,9 +486,12 @@ export async function closePosition( if (isDryRun) { console.log('🧪 DRY RUN MODE: Simulating close order (not executing on blockchain)') - // Calculate realized P&L - const pnlPerUnit = oraclePrice - position.entryPrice - const realizedPnL = pnlPerUnit * sizeToClose * (position.side === 'long' ? 1 : -1) + // Calculate realized P&L with leverage (default 10x in dry run) + const profitPercent = ((oraclePrice - position.entryPrice) / position.entryPrice) * 100 * (position.side === 'long' ? 1 : -1) + const leverage = 10 // Use 10x for dry run + const accountPnLPercent = profitPercent * leverage + const closedNotional = sizeToClose * oraclePrice + const realizedPnL = (closedNotional * accountPnLPercent) / 100 const mockTxSig = `DRY_RUN_CLOSE_${Date.now()}_${Math.random().toString(36).substring(7)}` @@ -534,12 +537,32 @@ export async function closePosition( console.log('✅ Transaction confirmed on-chain') - // Calculate realized P&L - const pnlPerUnit = oraclePrice - position.entryPrice - const realizedPnL = pnlPerUnit * sizeToClose * (position.side === 'long' ? 1 : -1) + // Calculate realized P&L with leverage + // CRITICAL: P&L must account for leverage and be calculated on USD notional, not base asset size + const profitPercent = ((oraclePrice - position.entryPrice) / position.entryPrice) * 100 * (position.side === 'long' ? 1 : -1) + + // Get leverage from user account (defaults to 10x if not found) + let leverage = 10 + try { + const userAccount = driftClient.getUserAccount() + if (userAccount && userAccount.maxMarginRatio) { + // maxMarginRatio is in 1e4 scale, leverage = 1 / (margin / 10000) + leverage = 10000 / Number(userAccount.maxMarginRatio) + } + } catch (err) { + console.log('⚠️ Could not determine leverage from account, using 10x default') + } + + const accountPnLPercent = profitPercent * leverage + + // Calculate closed notional value (USD) + const closedNotional = sizeToClose * oraclePrice + const realizedPnL = (closedNotional * accountPnLPercent) / 100 console.log(`💰 Close details:`) console.log(` Close price: $${oraclePrice.toFixed(4)}`) + console.log(` Profit %: ${profitPercent.toFixed(3)}% | Account P&L (${leverage}x): ${accountPnLPercent.toFixed(2)}%`) + console.log(` Closed notional: $${closedNotional.toFixed(2)}`) console.log(` Realized P&L: $${realizedPnL.toFixed(2)}`) // If closing 100%, cancel all remaining orders for this market