Enhancement #6 - SL Distance Validation (Data Collection Phase): - Added slDistanceAtEntry field to StopHunt schema - Calculates distance from revenge entry to stop zone (LONG vs SHORT logic) - Logs distance in dollars + × ATR multiplier - Purpose: Collect 20+ revenge trade samples for optimal multiplier analysis - Created comprehensive analysis guide with SQL queries - Decision deferred until empirical data collected Enhancement #1 - ADX Confirmation (Implementation Plan): - Documented complete 1-minute TradingView alert strategy - Storage analysis: 19.44 MB/month for 3 symbols (negligible) - Two-phase approach: Cache-only MVP → Optional DB persistence - Provided TradingView Pine Script (ready to use) - Cost breakdown: Pro subscription $49.95/month required - Benefits: Real-time ADX, pattern recognition, ML features - Implementation checklist with validation phases Files Changed: - prisma/schema.prisma: +1 field (slDistanceAtEntry) - lib/trading/stop-hunt-tracker.ts: +10 lines (distance calculation + logging) - docs/1MIN_MARKET_DATA_IMPLEMENTATION.md: NEW (comprehensive plan) - docs/ENHANCEMENT_6_ANALYSIS_GUIDE.md: NEW (SQL queries + decision matrix) Status: Enhancement #4 and #10 deployed (previous commit) Enhancement #6 data collection enabled (this commit) Awaiting 20+ revenge trades for Enhancement #6 decision
286 lines
10 KiB
Plaintext
286 lines
10 KiB
Plaintext
// Prisma Schema for Trading Bot v4
|
|
// Database: PostgreSQL
|
|
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
// Trade records for analysis and performance tracking
|
|
model Trade {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Trade identification
|
|
positionId String @unique // Transaction signature from entry order
|
|
symbol String // e.g., "SOL-PERP"
|
|
direction String // "long" or "short"
|
|
|
|
// Entry details
|
|
entryPrice Float
|
|
entryTime DateTime
|
|
entrySlippage Float?
|
|
positionSizeUSD Float // NOTIONAL position size (with leverage)
|
|
collateralUSD Float? // ACTUAL margin/collateral used (positionSizeUSD / leverage)
|
|
leverage Float
|
|
|
|
// Exit targets (planned)
|
|
stopLossPrice Float
|
|
softStopPrice Float? // Dual stop: soft stop-limit trigger
|
|
hardStopPrice Float? // Dual stop: hard stop-market trigger
|
|
takeProfit1Price Float
|
|
takeProfit2Price Float
|
|
tp1SizePercent Float
|
|
tp2SizePercent Float
|
|
|
|
// Exit details (actual)
|
|
exitPrice Float?
|
|
exitTime DateTime?
|
|
exitReason String? // "TP1", "TP2", "SL", "SOFT_SL", "HARD_SL", "manual", "emergency"
|
|
|
|
// Performance metrics
|
|
realizedPnL Float?
|
|
realizedPnLPercent Float?
|
|
holdTimeSeconds Int?
|
|
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)
|
|
rsiAtEntry Float? // RSI momentum (0-100)
|
|
volumeAtEntry Float? // Volume relative to MA
|
|
pricePositionAtEntry Float? // Price position in range (0-100%)
|
|
signalQualityScore Int? // Calculated quality score (0-100)
|
|
signalQualityVersion String? @default("v4") // Tracks which scoring logic was used
|
|
indicatorVersion String? // Pine Script version (v5, v6, etc.)
|
|
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?
|
|
tp2OrderTx String?
|
|
slOrderTx String?
|
|
softStopOrderTx String? // Dual stop: soft stop tx
|
|
hardStopOrderTx String? // Dual stop: hard stop tx
|
|
exitOrderTx String?
|
|
|
|
// Configuration snapshot
|
|
configSnapshot Json // Store settings used for this trade
|
|
|
|
// Signal data
|
|
signalSource String? // "tradingview", "manual", etc.
|
|
signalStrength String? // "strong", "moderate", "weak"
|
|
timeframe String? // "5", "15", "60"
|
|
|
|
// Status
|
|
status String @default("open") // "open", "closed", "failed", "phantom"
|
|
isTestTrade Boolean @default(false) // Flag test trades for exclusion from analytics
|
|
|
|
// Phantom trade detection
|
|
isPhantom Boolean @default(false) // Position opened but size mismatch >50%
|
|
expectedSizeUSD Float? // Expected position size (when phantom)
|
|
actualSizeUSD Float? // Actual position size from Drift (when phantom)
|
|
phantomReason String? // "ORACLE_PRICE_MISMATCH", "PARTIAL_FILL", "ORDER_REJECTED"
|
|
|
|
// Relations
|
|
priceUpdates PriceUpdate[]
|
|
|
|
@@index([symbol])
|
|
@@index([createdAt])
|
|
@@index([status])
|
|
@@index([exitReason])
|
|
}
|
|
|
|
// Real-time price updates during trade (for analysis)
|
|
model PriceUpdate {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
|
|
tradeId String
|
|
trade Trade @relation(fields: [tradeId], references: [id], onDelete: Cascade)
|
|
|
|
price Float
|
|
pnl Float
|
|
pnlPercent Float
|
|
|
|
@@index([tradeId])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
// System events and errors
|
|
model SystemEvent {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
|
|
eventType String // "error", "warning", "info", "trade_executed", etc.
|
|
message String
|
|
details Json?
|
|
|
|
@@index([eventType])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
// Blocked signals for analysis (signals that didn't pass quality checks)
|
|
model BlockedSignal {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
|
|
// Signal identification
|
|
symbol String // e.g., "SOL-PERP"
|
|
direction String // "long" or "short"
|
|
timeframe String? // "5", "15", "60"
|
|
|
|
// Price at signal time
|
|
signalPrice Float // Price when signal was generated
|
|
|
|
// Market metrics at signal time
|
|
atr Float? // ATR% at signal
|
|
adx Float? // ADX trend strength
|
|
rsi Float? // RSI momentum
|
|
volumeRatio Float? // Volume relative to average
|
|
pricePosition Float? // Position in range (0-100%)
|
|
|
|
// Quality scoring
|
|
signalQualityScore Int // 0-100 score
|
|
signalQualityVersion String? // Which scoring version
|
|
scoreBreakdown Json? // Detailed breakdown of score components
|
|
minScoreRequired Int // What threshold was used (e.g., 65)
|
|
indicatorVersion String? // Pine Script version (v5, v6, etc.)
|
|
|
|
// Block reason
|
|
blockReason String // "QUALITY_SCORE_TOO_LOW", "DUPLICATE", "COOLDOWN", "DATA_COLLECTION_ONLY", etc.
|
|
blockDetails String? // Human-readable details
|
|
|
|
// Entry tracking (for multi-timeframe analysis)
|
|
entryPrice Float @default(0) // Price at signal time
|
|
|
|
// For later analysis: track if it would have been profitable
|
|
priceAfter1Min Float? // Price 1 minute after (filled by monitoring job)
|
|
priceAfter5Min Float? // Price 5 minutes after
|
|
priceAfter15Min Float? // Price 15 minutes after
|
|
priceAfter30Min Float? // Price 30 minutes after
|
|
wouldHitTP1 Boolean? // Would TP1 have been hit?
|
|
wouldHitTP2 Boolean? // Would TP2 have been hit?
|
|
wouldHitSL Boolean? // Would SL have been hit?
|
|
|
|
// Max favorable/adverse excursion (mirror Trade model)
|
|
maxFavorablePrice Float? // Price at max profit
|
|
maxAdversePrice Float? // Price at max loss
|
|
maxFavorableExcursion Float? // Best profit % during tracking
|
|
maxAdverseExcursion Float? // Worst loss % during tracking
|
|
|
|
analysisComplete Boolean @default(false) // Has post-analysis been done?
|
|
|
|
@@index([symbol])
|
|
@@index([createdAt])
|
|
@@index([signalQualityScore])
|
|
@@index([blockReason])
|
|
}
|
|
|
|
// Stop Hunt Revenge Tracker (Nov 20, 2025)
|
|
// Tracks high-quality stop-outs and auto re-enters when stop hunt reverses
|
|
model StopHunt {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
|
|
// Original trade that got stopped out
|
|
originalTradeId String // References Trade.id
|
|
symbol String // e.g., "SOL-PERP"
|
|
direction String // "long" or "short"
|
|
|
|
// Stop hunt details
|
|
stopHuntPrice Float // Price where we got stopped out
|
|
originalEntryPrice Float // Where we originally entered
|
|
originalQualityScore Int // Must be 85+ to qualify
|
|
originalADX Float? // Trend strength at entry
|
|
originalATR Float? // Volatility at entry
|
|
stopLossAmount Float // How much we lost
|
|
stopHuntTime DateTime // When stop hunt occurred
|
|
|
|
// Revenge tracking
|
|
revengeTradeId String? // References Trade.id if revenge executed
|
|
revengeExecuted Boolean @default(false)
|
|
revengeEntryPrice Float? // Where revenge trade entered
|
|
revengeTime DateTime? // When revenge executed
|
|
revengeWindowExpired Boolean @default(false)
|
|
revengeExpiresAt DateTime // 4 hours after stop hunt
|
|
slDistanceAtEntry Float? // Distance from entry to stop zone (for Enhancement #6 analysis)
|
|
|
|
// Monitoring state
|
|
highestPriceAfterStop Float? // Track if stop hunt reverses
|
|
lowestPriceAfterStop Float? // Track if stop hunt reverses
|
|
|
|
// Zone tracking persistence (Nov 27, 2025 - Enhancement #10)
|
|
firstCrossTime DateTime? // When price entered revenge zone
|
|
lowestInZone Float? // Lowest price while in zone (LONG)
|
|
highestInZone Float? // Highest price while in zone (SHORT)
|
|
zoneResetCount Int @default(0) // How many times price left zone
|
|
|
|
// Revenge outcome tracking (Nov 27, 2025 - Enhancement #4)
|
|
revengeOutcome String? // "TP1", "TP2", "SL", "TRAILING_SL", null (pending)
|
|
revengePnL Float? // Realized P&L from revenge trade
|
|
revengeFailedReason String? // Why revenge failed: "stopped_again", "chop", "insufficient_capital"
|
|
|
|
@@index([symbol])
|
|
@@index([revengeExecuted])
|
|
@@index([revengeWindowExpired])
|
|
@@index([stopHuntTime])
|
|
@@index([revengeOutcome])
|
|
}
|
|
|
|
// Performance analytics (daily aggregates)
|
|
model DailyStats {
|
|
id String @id @default(cuid())
|
|
date DateTime @unique
|
|
|
|
tradesCount Int
|
|
winningTrades Int
|
|
losingTrades Int
|
|
totalPnL Float
|
|
totalPnLPercent Float
|
|
winRate Float
|
|
avgWin Float
|
|
avgLoss Float
|
|
profitFactor Float
|
|
maxDrawdown Float
|
|
sharpeRatio Float?
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([date])
|
|
}
|