fix: correct PnL math and add health probe
This commit is contained in:
16
app/api/health/route.ts
Normal file
16
app/api/health/route.ts
Normal 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 }
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)}`)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user