fix: correct PnL math and add health probe

This commit is contained in:
mindesbunister
2025-11-05 07:58:27 +01:00
parent 02193b7dce
commit cbb6592153
4 changed files with 33 additions and 18 deletions

16
app/api/health/route.ts Normal file
View File

@@ -0,0 +1,16 @@
import { NextResponse } from 'next/server'
/**
* Health check endpoint for Docker HEALTHCHECK
* Returns 200 OK if the server is running
*/
export async function GET() {
return NextResponse.json(
{
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
},
{ status: 200 }
)
}

View File

@@ -240,9 +240,10 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
// Save the closure to database // Save the closure to database
try { try {
const holdTimeSeconds = Math.floor((Date.now() - oppositePosition.entryTime) / 1000) const holdTimeSeconds = Math.floor((Date.now() - oppositePosition.entryTime) / 1000)
const profitPercent = ((closeResult.closePrice! - oppositePosition.entryPrice) / oppositePosition.entryPrice) * 100 const priceProfitPercent = oppositePosition.direction === 'long'
const accountPnL = profitPercent * oppositePosition.leverage * (oppositePosition.direction === 'long' ? 1 : -1) ? ((closeResult.closePrice! - oppositePosition.entryPrice) / oppositePosition.entryPrice) * 100
const realizedPnL = (oppositePosition.currentSize * accountPnL) / 100 : ((oppositePosition.entryPrice - closeResult.closePrice!) / oppositePosition.entryPrice) * 100
const realizedPnL = closeResult.realizedPnL ?? (oppositePosition.currentSize * priceProfitPercent) / 100
await updateTradeExit({ await updateTradeExit({
positionId: oppositePosition.positionId, positionId: oppositePosition.positionId,

View File

@@ -519,15 +519,15 @@ export async function closePosition(
// Calculate realized P&L with leverage (default 10x in dry run) // 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 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 closedNotional = sizeToClose * oraclePrice
const realizedPnL = (closedNotional * accountPnLPercent) / 100 const realizedPnL = (closedNotional * profitPercent) / 100
const accountPnLPercent = profitPercent * 10 // display using default leverage
const mockTxSig = `DRY_RUN_CLOSE_${Date.now()}_${Math.random().toString(36).substring(7)}` const mockTxSig = `DRY_RUN_CLOSE_${Date.now()}_${Math.random().toString(36).substring(7)}`
console.log(`💰 Simulated close:`) console.log(`💰 Simulated close:`)
console.log(` Close price: $${oraclePrice.toFixed(4)}`) console.log(` Close price: $${oraclePrice.toFixed(4)}`)
console.log(` Profit %: ${profitPercent.toFixed(3)}% → Account P&L (10x): ${accountPnLPercent.toFixed(2)}%`)
console.log(` Realized P&L: $${realizedPnL.toFixed(2)}`) console.log(` Realized P&L: $${realizedPnL.toFixed(2)}`)
return { return {
@@ -584,11 +584,10 @@ export async function closePosition(
console.log('⚠️ Could not determine leverage from account, using 10x default') console.log('⚠️ Could not determine leverage from account, using 10x default')
} }
const accountPnLPercent = profitPercent * leverage
// Calculate closed notional value (USD) // Calculate closed notional value (USD)
const closedNotional = sizeToClose * oraclePrice const closedNotional = sizeToClose * oraclePrice
const realizedPnL = (closedNotional * accountPnLPercent) / 100 const realizedPnL = (closedNotional * profitPercent) / 100
const accountPnLPercent = profitPercent * leverage
console.log(`💰 Close details:`) console.log(`💰 Close details:`)
console.log(` Close price: $${oraclePrice.toFixed(4)}`) console.log(` Close price: $${oraclePrice.toFixed(4)}`)

View File

@@ -350,15 +350,15 @@ export class PositionManager {
await this.saveTradeState(trade) await this.saveTradeState(trade)
// CRITICAL: Don't return early! Continue monitoring the runner position
// The trailing stop logic at line 732 needs to run
} else { } else {
// Partial fill detected but unclear which TP - just update size // Partial fill detected but unclear which TP - just update size
console.log(`⚠️ Unknown partial fill detected - updating tracked size to $${positionSizeUSD.toFixed(2)}`) console.log(`⚠️ Unknown partial fill detected - updating tracked size to $${positionSizeUSD.toFixed(2)}`)
trade.currentSize = positionSizeUSD trade.currentSize = positionSizeUSD
await this.saveTradeState(trade) await this.saveTradeState(trade)
} }
// Continue monitoring the remaining position
return
} }
// CRITICAL: Check for entry price mismatch (NEW position opened) // CRITICAL: Check for entry price mismatch (NEW position opened)
@@ -380,10 +380,10 @@ export class PositionManager {
trade.lastPrice, trade.lastPrice,
trade.direction trade.direction
) )
const accountPnL = profitPercent * trade.leverage const accountPnLPercent = profitPercent * trade.leverage
const estimatedPnL = (trade.currentSize * accountPnL) / 100 const estimatedPnL = (trade.currentSize * profitPercent) / 100
console.log(`💰 Estimated P&L for lost trade: ${profitPercent.toFixed(2)}% price → ${accountPnL.toFixed(2)}% account → $${estimatedPnL.toFixed(2)} realized`) console.log(`💰 Estimated P&L for lost trade: ${profitPercent.toFixed(2)}% price → ${accountPnLPercent.toFixed(2)}% account → $${estimatedPnL.toFixed(2)} realized`)
try { try {
await updateTradeExit({ await updateTradeExit({
@@ -450,8 +450,7 @@ export class PositionManager {
currentPrice, currentPrice,
trade.direction trade.direction
) )
const accountPnL = profitPercent * trade.leverage realizedPnL = (sizeForPnL * profitPercent) / 100
realizedPnL = (sizeForPnL * accountPnL) / 100
} }
// Determine exit reason from trade state and P&L // Determine exit reason from trade state and P&L
@@ -708,7 +707,7 @@ export class PositionManager {
} }
// 5. Take profit 2 (remaining position) // 5. Take profit 2 (remaining position)
if (trade.tp1Hit && this.shouldTakeProfit2(currentPrice, trade)) { if (trade.tp1Hit && !trade.tp2Hit && this.shouldTakeProfit2(currentPrice, trade)) {
console.log(`🎊 TP2 HIT: ${trade.symbol} at ${profitPercent.toFixed(2)}%`) console.log(`🎊 TP2 HIT: ${trade.symbol} at ${profitPercent.toFixed(2)}%`)
// Calculate how much to close based on TP2 size percent // Calculate how much to close based on TP2 size percent