Implementation:
- Added 7 orderbook fields to Trade model (spreadBps, imbalanceRatio, depths, impact, walls)
- Oracle-based estimates with 2bps spread assumption
- ENV flag: ENABLE_ORDERBOOK_LOGGING (defaults true)
- Execute wrapper lines 1037-1053 guards orderbook logic
Database:
- Direct SQL ALTER TABLE (avoided migration drift issues)
- All columns nullable DOUBLE PRECISION
- Prisma schema synced via db pull + generate
Deployment:
- Container rebuilt and deployed successfully
- All 7 columns verified accessible
- System operational, ready for live trade validation
Files changed:
- config/trading.ts (enableOrderbookLogging flag, line 127)
- types/trading.ts (orderbook interfaces)
- lib/database/trades.ts (createTrade saves orderbook data)
- app/api/trading/execute/route.ts (ENV wrapper lines 1037-1053)
- prisma/schema.prisma (7 orderbook fields)
- docs/ORDERBOOK_SHADOW_LOGGING.md (complete documentation)
Status: ✅ PRODUCTION READY - awaiting first trade for validation
245 lines
6.9 KiB
Plaintext
245 lines
6.9 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
model Trade {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
positionId String @unique
|
|
symbol String
|
|
direction String
|
|
entryPrice Float
|
|
entryTime DateTime
|
|
entrySlippage Float?
|
|
positionSizeUSD Float
|
|
leverage Float
|
|
stopLossPrice Float
|
|
softStopPrice Float?
|
|
hardStopPrice Float?
|
|
takeProfit1Price Float
|
|
takeProfit2Price Float
|
|
tp1SizePercent Float
|
|
tp2SizePercent Float
|
|
exitPrice Float?
|
|
exitTime DateTime?
|
|
exitReason String?
|
|
realizedPnL Float?
|
|
realizedPnLPercent Float?
|
|
holdTimeSeconds Int?
|
|
maxDrawdown Float?
|
|
maxGain Float?
|
|
entryOrderTx String
|
|
tp1OrderTx String?
|
|
tp2OrderTx String?
|
|
slOrderTx String?
|
|
softStopOrderTx String?
|
|
hardStopOrderTx String?
|
|
exitOrderTx String?
|
|
configSnapshot Json
|
|
signalSource String?
|
|
signalStrength String?
|
|
timeframe String?
|
|
status String @default("open")
|
|
isTestTrade Boolean @default(false)
|
|
adxAtEntry Float?
|
|
atrAtEntry Float?
|
|
basisAtEntry Float?
|
|
entrySlippagePct Float?
|
|
exitSlippagePct Float?
|
|
expectedEntryPrice Float?
|
|
expectedExitPrice Float?
|
|
fundingRateAtEntry Float?
|
|
hardSlFilled Boolean @default(false)
|
|
maxAdverseExcursion Float?
|
|
maxAdversePrice Float?
|
|
maxFavorableExcursion Float?
|
|
maxFavorablePrice Float?
|
|
slFillPrice Float?
|
|
softSlFilled Boolean @default(false)
|
|
timeToSl Int?
|
|
timeToTp1 Int?
|
|
timeToTp2 Int?
|
|
tp1FillPrice Float?
|
|
tp1Filled Boolean @default(false)
|
|
tp2FillPrice Float?
|
|
tp2Filled Boolean @default(false)
|
|
volumeAtEntry Float?
|
|
pricePositionAtEntry Float?
|
|
rsiAtEntry Float?
|
|
signalQualityScore Int?
|
|
actualSizeUSD Float?
|
|
expectedSizeUSD Float?
|
|
isPhantom Boolean @default(false)
|
|
phantomReason String?
|
|
collateralUSD Float?
|
|
signalQualityVersion String? @default("v4")
|
|
indicatorVersion String?
|
|
closeAttempts Int?
|
|
spreadBps Float?
|
|
imbalanceRatio Float?
|
|
depthBid1Usd Float?
|
|
depthAsk1Usd Float?
|
|
priceImpact1Usd Float?
|
|
bidWall Float?
|
|
askWall Float?
|
|
priceUpdates PriceUpdate[]
|
|
|
|
@@index([symbol])
|
|
@@index([createdAt])
|
|
@@index([status])
|
|
@@index([exitReason])
|
|
}
|
|
|
|
model PriceUpdate {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
tradeId String
|
|
price Float
|
|
pnl Float
|
|
pnlPercent Float
|
|
trade Trade @relation(fields: [tradeId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([tradeId])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model SystemEvent {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
eventType String
|
|
message String
|
|
details Json?
|
|
|
|
@@index([eventType])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model BlockedSignal {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
symbol String
|
|
direction String
|
|
timeframe String?
|
|
signalPrice Float
|
|
atr Float?
|
|
adx Float?
|
|
rsi Float?
|
|
volumeRatio Float?
|
|
pricePosition Float?
|
|
signalQualityScore Int
|
|
signalQualityVersion String?
|
|
scoreBreakdown Json?
|
|
minScoreRequired Int
|
|
blockReason String
|
|
blockDetails String?
|
|
priceAfter1Min Float?
|
|
priceAfter5Min Float?
|
|
priceAfter15Min Float?
|
|
priceAfter30Min Float?
|
|
wouldHitTP1 Boolean?
|
|
wouldHitTP2 Boolean?
|
|
wouldHitSL Boolean?
|
|
analysisComplete Boolean @default(false)
|
|
indicatorVersion String?
|
|
entryPrice Float @default(0)
|
|
maxFavorablePrice Float?
|
|
maxAdversePrice Float?
|
|
maxFavorableExcursion Float?
|
|
maxAdverseExcursion Float?
|
|
priceAfter1Hr Float?
|
|
priceAfter2Hr Float?
|
|
priceAfter4Hr Float?
|
|
priceAfter8Hr Float?
|
|
tp1HitTime DateTime? @map("tp1_hit_time")
|
|
tp2HitTime DateTime? @map("tp2_hit_time")
|
|
slHitTime DateTime? @map("sl_hit_time")
|
|
|
|
@@index([symbol])
|
|
@@index([createdAt])
|
|
@@index([signalQualityScore])
|
|
@@index([blockReason])
|
|
}
|
|
|
|
model StopHunt {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
originalTradeId String
|
|
symbol String
|
|
direction String
|
|
stopHuntPrice Float
|
|
originalEntryPrice Float
|
|
originalQualityScore Int
|
|
originalADX Float?
|
|
originalATR Float?
|
|
stopLossAmount Float
|
|
stopHuntTime DateTime
|
|
revengeTradeId String?
|
|
revengeExecuted Boolean @default(false)
|
|
revengeEntryPrice Float?
|
|
revengeTime DateTime?
|
|
revengeWindowExpired Boolean @default(false)
|
|
revengeExpiresAt DateTime
|
|
highestPriceAfterStop Float?
|
|
lowestPriceAfterStop Float?
|
|
revengeFailedReason String?
|
|
revengeOutcome String?
|
|
revengePnL Float?
|
|
slDistanceAtEntry Float?
|
|
firstCrossTime DateTime?
|
|
highestInZone Float?
|
|
lowestInZone Float?
|
|
zoneResetCount Int @default(0)
|
|
|
|
@@index([symbol])
|
|
@@index([revengeExecuted])
|
|
@@index([revengeWindowExpired])
|
|
@@index([stopHuntTime])
|
|
@@index([revengeOutcome])
|
|
}
|
|
|
|
model MarketData {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
symbol String
|
|
timeframe String
|
|
price Float
|
|
atr Float
|
|
adx Float
|
|
rsi Float
|
|
volumeRatio Float
|
|
pricePosition Float
|
|
maGap Float?
|
|
volume Float?
|
|
timestamp DateTime
|
|
|
|
@@index([symbol, timestamp])
|
|
@@index([createdAt])
|
|
@@index([timestamp])
|
|
}
|
|
|
|
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])
|
|
}
|