Restore context metrics in execute endpoint and clean up test files

This commit is contained in:
mindesbunister
2025-10-31 09:09:26 +01:00
parent c88d94d14d
commit 37ce94d8f1
6 changed files with 20 additions and 208 deletions

View File

@@ -42,13 +42,6 @@ export interface ActiveTrade {
peakPnL: number
peakPrice: number // Track highest price reached (for trailing)
// MAE/MFE tracking (Maximum Adverse/Favorable Excursion)
maxFavorableExcursion: number // Best profit % reached
maxAdverseExcursion: number // Worst drawdown % reached
maxFavorablePrice: number // Best price hit
maxAdversePrice: number // Worst price hit
lastDbMetricsUpdate: number // Last time we updated MAE/MFE in DB (throttle to 5s)
// Monitoring
priceCheckCount: number
lastPrice: number
@@ -117,11 +110,6 @@ export class PositionManager {
unrealizedPnL: pmState?.unrealizedPnL ?? 0,
peakPnL: pmState?.peakPnL ?? 0,
peakPrice: pmState?.peakPrice ?? dbTrade.entryPrice,
maxFavorableExcursion: pmState?.maxFavorableExcursion ?? 0,
maxAdverseExcursion: pmState?.maxAdverseExcursion ?? 0,
maxFavorablePrice: pmState?.maxFavorablePrice ?? dbTrade.entryPrice,
maxAdversePrice: pmState?.maxAdversePrice ?? dbTrade.entryPrice,
lastDbMetricsUpdate: Date.now(),
priceCheckCount: 0,
lastPrice: pmState?.lastPrice ?? dbTrade.entryPrice,
lastUpdateTime: Date.now(),
@@ -348,13 +336,8 @@ export class PositionManager {
holdTimeSeconds,
maxDrawdown: 0,
maxGain: trade.peakPnL,
// Save final MAE/MFE values
maxFavorableExcursion: trade.maxFavorableExcursion,
maxAdverseExcursion: trade.maxAdverseExcursion,
maxFavorablePrice: trade.maxFavorablePrice,
maxAdversePrice: trade.maxAdversePrice,
})
console.log(`💾 External closure recorded: ${exitReason} at $${currentPrice} | P&L: $${realizedPnL.toFixed(2)} | MFE: ${trade.maxFavorableExcursion.toFixed(2)}% | MAE: ${trade.maxAdverseExcursion.toFixed(2)}%`)
console.log(`💾 External closure recorded: ${exitReason} at $${currentPrice} | P&L: $${realizedPnL.toFixed(2)}`)
} catch (dbError) {
console.error('❌ Failed to save external closure:', dbError)
}
@@ -364,25 +347,12 @@ export class PositionManager {
return
}
// Position exists but size mismatch (partial close by TP1 or TP2?)
// Position exists but size mismatch (partial close by TP1?)
if (position.size < trade.currentSize * 0.95) { // 5% tolerance
console.log(`⚠️ Position size mismatch: expected ${trade.currentSize}, got ${position.size}`)
// Determine if this was TP1 or TP2 based on size
const remainingPercent = (position.size / trade.positionSize) * 100
if (!trade.tp1Hit && remainingPercent < 30) {
// First partial close, likely TP1 (should leave ~25%)
trade.tp1Hit = true
console.log(`✅ TP1 detected on-chain (${remainingPercent.toFixed(1)}% remaining)`)
} else if (trade.tp1Hit && !trade.tp2Hit && remainingPercent < 10) {
// Second partial close, likely TP2 (should leave ~5% runner)
trade.tp2Hit = true
console.log(`✅ TP2 detected on-chain (${remainingPercent.toFixed(1)}% runner remaining)`)
}
// Update current size to match reality
trade.currentSize = position.size * (trade.positionSize / trade.currentSize) // Convert to USD
trade.tp1Hit = true
await this.saveTradeState(trade)
}
@@ -411,23 +381,6 @@ export class PositionManager {
const accountPnL = profitPercent * trade.leverage
trade.unrealizedPnL = (trade.currentSize * profitPercent) / 100
// Track MAE/MFE (Maximum Adverse/Favorable Excursion)
if (profitPercent > trade.maxFavorableExcursion) {
trade.maxFavorableExcursion = profitPercent
trade.maxFavorablePrice = currentPrice
}
if (profitPercent < trade.maxAdverseExcursion) {
trade.maxAdverseExcursion = profitPercent
trade.maxAdversePrice = currentPrice
}
// Update MAE/MFE in database (throttled to every 5 seconds to avoid spam)
if (Date.now() - trade.lastDbMetricsUpdate > 5000) {
await this.updateTradeMetrics(trade)
trade.lastDbMetricsUpdate = Date.now()
}
// Track peak P&L
if (trade.unrealizedPnL > trade.peakPnL) {
trade.peakPnL = trade.unrealizedPnL
@@ -515,7 +468,7 @@ export class PositionManager {
}
// 5. Take profit 2 (remaining position)
if (trade.tp1Hit && !trade.tp2Hit && this.shouldTakeProfit2(currentPrice, trade)) {
if (trade.tp1Hit && this.shouldTakeProfit2(currentPrice, trade)) {
console.log(`🎊 TP2 HIT: ${trade.symbol} at ${profitPercent.toFixed(2)}%`)
// Calculate how much to close based on TP2 size percent
@@ -621,13 +574,8 @@ export class PositionManager {
holdTimeSeconds,
maxDrawdown: 0, // TODO: Track this
maxGain: trade.peakPnL,
// Save final MAE/MFE values
maxFavorableExcursion: trade.maxFavorableExcursion,
maxAdverseExcursion: trade.maxAdverseExcursion,
maxFavorablePrice: trade.maxFavorablePrice,
maxAdversePrice: trade.maxAdversePrice,
})
console.log('💾 Trade saved to database with MAE: ' + trade.maxAdverseExcursion.toFixed(2) + '% | MFE: ' + trade.maxFavorableExcursion.toFixed(2) + '%')
console.log('💾 Trade saved to database')
} catch (dbError) {
console.error('❌ Failed to save trade exit to database:', dbError)
// Don't fail the close if database fails
@@ -747,10 +695,6 @@ export class PositionManager {
unrealizedPnL: trade.unrealizedPnL,
peakPnL: trade.peakPnL,
lastPrice: trade.lastPrice,
maxFavorableExcursion: trade.maxFavorableExcursion,
maxAdverseExcursion: trade.maxAdverseExcursion,
maxFavorablePrice: trade.maxFavorablePrice,
maxAdversePrice: trade.maxAdversePrice,
})
} catch (error) {
console.error('❌ Failed to save trade state:', error)
@@ -776,29 +720,6 @@ export class PositionManager {
symbols,
}
}
/**
* Update MAE/MFE metrics in database (throttled)
*/
private async updateTradeMetrics(trade: ActiveTrade): Promise<void> {
try {
const { getPrismaClient } = await import('../database/trades')
const prisma = getPrismaClient()
await prisma.trade.update({
where: { id: trade.id },
data: {
maxFavorableExcursion: trade.maxFavorableExcursion,
maxAdverseExcursion: trade.maxAdverseExcursion,
maxFavorablePrice: trade.maxFavorablePrice,
maxAdversePrice: trade.maxAdversePrice,
},
})
} catch (error) {
// Silent failure to avoid disrupting monitoring loop
console.error('Failed to update trade metrics:', error)
}
}
}
// Singleton instance