Phase 1: Add MAE/MFE tracking and analytics schema

- Added 20+ analytics fields to Trade model (MAE/MFE, fill tracking, timing, market context, slippage)
- Implemented real-time MAE/MFE tracking in Position Manager (updates every 5s)
- Enhanced database schema with comprehensive trade analytics
- Updated all API endpoints to initialize MAE/MFE fields
- Modified updateTradeState() to persist MAE/MFE in configSnapshot

Database changes:
- maxFavorableExcursion/maxAdverseExcursion track best/worst profit %
- maxFavorablePrice/maxAdversePrice track exact price levels
- Fill tracking: tp1Filled, tp2Filled, softSlFilled, hardSlFilled
- Timing metrics: timeToTp1, timeToTp2, timeToSl
- Market context: atrAtEntry, adxAtEntry, volumeAtEntry, fundingRateAtEntry, basisAtEntry
- Slippage tracking: expectedEntryPrice, entrySlippagePct, expectedExitPrice, exitSlippagePct

Position Manager changes:
- Track MAE/MFE on every price check (2s interval)
- Throttled database updates (5s interval) via updateTradeMetrics()
- Persist MAE/MFE in trade state snapshots for recovery

Next: Phase 2 (market context capture) or Phase 3 (analytics API)
This commit is contained in:
mindesbunister
2025-10-29 20:34:03 +01:00
parent d4d2883af6
commit 65e6a8efed
7 changed files with 136 additions and 0 deletions

View File

@@ -42,6 +42,13 @@ 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
@@ -110,6 +117,11 @@ 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(),
@@ -394,6 +406,23 @@ 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
@@ -708,6 +737,10 @@ 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)
@@ -733,6 +766,29 @@ 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