Fix Prisma Decimal type handling in version comparison API
- Changed numeric fields from typed as number to 'any' in raw query results - Properly convert Prisma Decimal/BigInt types to JavaScript numbers - Fixes TypeError: e.totalPnL.toFixed is not a function - All numeric values (totalPnL, avgPnL, avgADX, etc.) now converted with Number() Issue: Prisma returns Decimal objects from aggregation queries which don't have toFixed() method. Frontend expects plain numbers for .toFixed(2) formatting.
This commit is contained in:
@@ -36,18 +36,19 @@ export async function GET() {
|
|||||||
version: string | null
|
version: string | null
|
||||||
trades: bigint
|
trades: bigint
|
||||||
wins: bigint
|
wins: bigint
|
||||||
total_pnl: number
|
total_pnl: any
|
||||||
avg_pnl: number
|
avg_pnl: any
|
||||||
avg_quality_score: number | null
|
avg_quality_score: any
|
||||||
avg_mfe: number | null
|
avg_mfe: any
|
||||||
avg_mae: number | null
|
avg_mae: any
|
||||||
}>>`
|
}>>`
|
||||||
SELECT
|
SELECT
|
||||||
COALESCE("signalQualityVersion", 'v1') as version,
|
COALESCE("signalQualityVersion", 'v1') as version,
|
||||||
COUNT(*) as trades,
|
COUNT(*) as trades,
|
||||||
SUM(CASE WHEN "realizedPnL" > 0 THEN 1 ELSE 0 END) as wins,
|
SUM(CASE WHEN "realizedPnL" > 0 THEN 1 ELSE 0 END) as wins,
|
||||||
ROUND(SUM("realizedPnL")::numeric, 2) as total_pnl,
|
SUM("realizedPnL") as total_pnl,
|
||||||
ROUND(AVG("realizedPnL")::numeric, 2) as avg_pnl,
|
AVG("realizedPnL") as avg_pnl,
|
||||||
|
AVG("realizedPnL") as avg_pnl,
|
||||||
ROUND(AVG("signalQualityScore")::numeric, 1) as avg_quality_score,
|
ROUND(AVG("signalQualityScore")::numeric, 1) as avg_quality_score,
|
||||||
ROUND(AVG("maxFavorableExcursion")::numeric, 2) as avg_mfe,
|
ROUND(AVG("maxFavorableExcursion")::numeric, 2) as avg_mfe,
|
||||||
ROUND(AVG("maxAdverseExcursion")::numeric, 2) as avg_mae
|
ROUND(AVG("maxAdverseExcursion")::numeric, 2) as avg_mae
|
||||||
@@ -63,10 +64,10 @@ export async function GET() {
|
|||||||
const extremePositionStats = await prisma.$queryRaw<Array<{
|
const extremePositionStats = await prisma.$queryRaw<Array<{
|
||||||
version: string | null
|
version: string | null
|
||||||
count: bigint
|
count: bigint
|
||||||
avg_adx: number | null
|
avg_adx: any
|
||||||
weak_adx_count: bigint
|
weak_adx_count: bigint
|
||||||
wins: bigint
|
wins: bigint
|
||||||
avg_pnl: number
|
avg_pnl: any
|
||||||
}>>`
|
}>>`
|
||||||
SELECT
|
SELECT
|
||||||
COALESCE("signalQualityVersion", 'v1') as version,
|
COALESCE("signalQualityVersion", 'v1') as version,
|
||||||
@@ -74,7 +75,7 @@ export async function GET() {
|
|||||||
ROUND(AVG("adxAtEntry")::numeric, 1) as avg_adx,
|
ROUND(AVG("adxAtEntry")::numeric, 1) as avg_adx,
|
||||||
COUNT(*) FILTER (WHERE "adxAtEntry" < 18) as weak_adx_count,
|
COUNT(*) FILTER (WHERE "adxAtEntry" < 18) as weak_adx_count,
|
||||||
SUM(CASE WHEN "realizedPnL" > 0 THEN 1 ELSE 0 END) as wins,
|
SUM(CASE WHEN "realizedPnL" > 0 THEN 1 ELSE 0 END) as wins,
|
||||||
ROUND(AVG("realizedPnL")::numeric, 2) as avg_pnl
|
AVG("realizedPnL") as avg_pnl
|
||||||
FROM "Trade"
|
FROM "Trade"
|
||||||
WHERE "exitReason" IS NOT NULL
|
WHERE "exitReason" IS NOT NULL
|
||||||
AND "exitReason" NOT LIKE '%CLEANUP%'
|
AND "exitReason" NOT LIKE '%CLEANUP%'
|
||||||
@@ -100,17 +101,17 @@ export async function GET() {
|
|||||||
version: stat.version || 'v1',
|
version: stat.version || 'v1',
|
||||||
tradeCount: trades,
|
tradeCount: trades,
|
||||||
winRate: trades > 0 ? Math.round((wins / trades) * 100 * 10) / 10 : 0,
|
winRate: trades > 0 ? Math.round((wins / trades) * 100 * 10) / 10 : 0,
|
||||||
totalPnL: stat.total_pnl,
|
totalPnL: Number(stat.total_pnl) || 0,
|
||||||
avgPnL: stat.avg_pnl,
|
avgPnL: Number(stat.avg_pnl) || 0,
|
||||||
avgQualityScore: stat.avg_quality_score,
|
avgQualityScore: stat.avg_quality_score ? Number(stat.avg_quality_score) : null,
|
||||||
avgMFE: stat.avg_mfe,
|
avgMFE: stat.avg_mfe ? Number(stat.avg_mfe) : null,
|
||||||
avgMAE: stat.avg_mae,
|
avgMAE: stat.avg_mae ? Number(stat.avg_mae) : null,
|
||||||
extremePositions: {
|
extremePositions: {
|
||||||
count: extremeCount,
|
count: extremeCount,
|
||||||
avgADX: extremeStats?.avg_adx || null,
|
avgADX: extremeStats?.avg_adx ? Number(extremeStats.avg_adx) : null,
|
||||||
weakADXCount: extremeStats ? Number(extremeStats.weak_adx_count) : 0,
|
weakADXCount: extremeStats ? Number(extremeStats.weak_adx_count) : 0,
|
||||||
winRate: extremeCount > 0 ? Math.round((extremeWins / extremeCount) * 100 * 10) / 10 : 0,
|
winRate: extremeCount > 0 ? Math.round((extremeWins / extremeCount) * 100 * 10) / 10 : 0,
|
||||||
avgPnL: extremeStats?.avg_pnl || 0,
|
avgPnL: extremeStats?.avg_pnl ? Number(extremeStats.avg_pnl) : 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user