From 65e6a8efede1d6747edc1f838492317c9df56162 Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Wed, 29 Oct 2025 20:34:03 +0100 Subject: [PATCH] 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) --- app/api/trading/execute/route.ts | 5 ++ app/api/trading/test-db/route.ts | 5 ++ app/api/trading/test/route.ts | 5 ++ lib/database/trades.ts | 8 +++ lib/trading/position-manager.ts | 56 +++++++++++++++++++ .../migration.sql | 24 ++++++++ prisma/schema.prisma | 33 +++++++++++ 7 files changed, 136 insertions(+) create mode 100644 prisma/migrations/20251029192059_add_mae_mfe_and_market_context/migration.sql diff --git a/app/api/trading/execute/route.ts b/app/api/trading/execute/route.ts index 23d0633..c8de56e 100644 --- a/app/api/trading/execute/route.ts +++ b/app/api/trading/execute/route.ts @@ -241,6 +241,11 @@ export async function POST(request: NextRequest): Promise 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 { + 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 diff --git a/prisma/migrations/20251029192059_add_mae_mfe_and_market_context/migration.sql b/prisma/migrations/20251029192059_add_mae_mfe_and_market_context/migration.sql new file mode 100644 index 0000000..31645b6 --- /dev/null +++ b/prisma/migrations/20251029192059_add_mae_mfe_and_market_context/migration.sql @@ -0,0 +1,24 @@ +-- AlterTable +ALTER TABLE "Trade" ADD COLUMN "adxAtEntry" DOUBLE PRECISION, +ADD COLUMN "atrAtEntry" DOUBLE PRECISION, +ADD COLUMN "basisAtEntry" DOUBLE PRECISION, +ADD COLUMN "entrySlippagePct" DOUBLE PRECISION, +ADD COLUMN "exitSlippagePct" DOUBLE PRECISION, +ADD COLUMN "expectedEntryPrice" DOUBLE PRECISION, +ADD COLUMN "expectedExitPrice" DOUBLE PRECISION, +ADD COLUMN "fundingRateAtEntry" DOUBLE PRECISION, +ADD COLUMN "hardSlFilled" BOOLEAN NOT NULL DEFAULT false, +ADD COLUMN "maxAdverseExcursion" DOUBLE PRECISION, +ADD COLUMN "maxAdversePrice" DOUBLE PRECISION, +ADD COLUMN "maxFavorableExcursion" DOUBLE PRECISION, +ADD COLUMN "maxFavorablePrice" DOUBLE PRECISION, +ADD COLUMN "slFillPrice" DOUBLE PRECISION, +ADD COLUMN "softSlFilled" BOOLEAN NOT NULL DEFAULT false, +ADD COLUMN "timeToSl" INTEGER, +ADD COLUMN "timeToTp1" INTEGER, +ADD COLUMN "timeToTp2" INTEGER, +ADD COLUMN "tp1FillPrice" DOUBLE PRECISION, +ADD COLUMN "tp1Filled" BOOLEAN NOT NULL DEFAULT false, +ADD COLUMN "tp2FillPrice" DOUBLE PRECISION, +ADD COLUMN "tp2Filled" BOOLEAN NOT NULL DEFAULT false, +ADD COLUMN "volumeAtEntry" DOUBLE PRECISION; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index fb5fe04..ef46ec2 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -49,6 +49,39 @@ model Trade { maxDrawdown Float? // Peak to valley during trade maxGain Float? // Peak gain reached + // MAE/MFE Analysis (Maximum Adverse/Favorable Excursion) + maxFavorableExcursion Float? // Best profit % reached during trade + maxAdverseExcursion Float? // Worst drawdown % during trade + maxFavorablePrice Float? // Best price hit (direction-aware) + maxAdversePrice Float? // Worst price hit (direction-aware) + + // Exit details - which levels actually filled + tp1Filled Boolean @default(false) + tp2Filled Boolean @default(false) + softSlFilled Boolean @default(false) + hardSlFilled Boolean @default(false) + tp1FillPrice Float? + tp2FillPrice Float? + slFillPrice Float? + + // Timing metrics + timeToTp1 Int? // Seconds from entry to TP1 fill + timeToTp2 Int? // Seconds from entry to TP2 fill + timeToSl Int? // Seconds from entry to SL hit + + // Market context at entry + atrAtEntry Float? // ATR% when trade opened + adxAtEntry Float? // ADX trend strength (0-50) + volumeAtEntry Float? // Volume relative to MA + fundingRateAtEntry Float? // Perp funding rate at entry + basisAtEntry Float? // Perp-spot basis at entry + + // Slippage tracking + expectedEntryPrice Float? // Target entry from signal + entrySlippagePct Float? // Actual slippage % + expectedExitPrice Float? // Which TP/SL should have hit + exitSlippagePct Float? // Exit slippage % + // Order signatures entryOrderTx String tp1OrderTx String?