Store signal quality score in database for future analysis

- Add signalQualityScore field to Trade model (0-100)
- Calculate quality score in execute endpoint using same logic as check-risk
- Save score with every trade for correlation analysis
- Create database migration for new field
- Enables future analysis: score vs win rate, P&L, etc.

This allows data-driven decisions on dynamic position sizing
This commit is contained in:
mindesbunister
2025-10-31 11:12:07 +01:00
parent aecdc108f6
commit 090b79a07f
4 changed files with 101 additions and 1 deletions

View File

@@ -13,6 +13,84 @@ import { getMergedConfig } from '@/config/trading'
import { getInitializedPositionManager, ActiveTrade } from '@/lib/trading/position-manager'
import { createTrade } from '@/lib/database/trades'
/**
* Calculate signal quality score (same logic as check-risk endpoint)
*/
function calculateQualityScore(params: {
atr?: number
adx?: number
rsi?: number
volumeRatio?: number
pricePosition?: number
direction: 'long' | 'short'
}): number | undefined {
// If no metrics provided, return undefined
if (!params.atr || params.atr === 0) {
return undefined
}
let score = 50 // Base score
// ATR check
if (params.atr < 0.6) {
score -= 15
} else if (params.atr > 2.5) {
score -= 20
} else {
score += 10
}
// ADX check
if (params.adx && params.adx > 0) {
if (params.adx > 25) {
score += 15
} else if (params.adx < 18) {
score -= 15
} else {
score += 5
}
}
// RSI check
if (params.rsi && params.rsi > 0) {
if (params.direction === 'long') {
if (params.rsi > 50 && params.rsi < 70) {
score += 10
} else if (params.rsi > 70) {
score -= 10
}
} else {
if (params.rsi < 50 && params.rsi > 30) {
score += 10
} else if (params.rsi < 30) {
score -= 10
}
}
}
// Volume check
if (params.volumeRatio && params.volumeRatio > 0) {
if (params.volumeRatio > 1.2) {
score += 10
} else if (params.volumeRatio < 0.8) {
score -= 10
}
}
// Price position check
if (params.pricePosition && params.pricePosition > 0) {
if (params.direction === 'long' && params.pricePosition > 90) {
score -= 15
} else if (params.direction === 'short' && params.pricePosition < 10) {
score -= 15
} else {
score += 5
}
}
return score
}
export interface ExecuteTradeRequest {
symbol: string // TradingView symbol (e.g., 'SOLUSDT')
direction: 'long' | 'short'
@@ -312,6 +390,16 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
// Save trade to database
try {
// Calculate quality score if metrics available
const qualityScore = calculateQualityScore({
atr: body.atr,
adx: body.adx,
rsi: body.rsi,
volumeRatio: body.volumeRatio,
pricePosition: body.pricePosition,
direction: body.direction,
})
await createTrade({
positionId: openResult.transactionSignature!,
symbol: driftSymbol,
@@ -341,9 +429,14 @@ export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTr
rsiAtEntry: body.rsi,
volumeAtEntry: body.volumeRatio,
pricePositionAtEntry: body.pricePosition,
signalQualityScore: qualityScore,
})
console.log('💾 Trade saved to database')
if (qualityScore !== undefined) {
console.log(`💾 Trade saved with quality score: ${qualityScore}/100`)
} else {
console.log('💾 Trade saved to database')
}
} catch (dbError) {
console.error('❌ Failed to save trade to database:', dbError)
// Don't fail the trade if database save fails

View File

@@ -51,6 +51,7 @@ export interface CreateTradeParams {
rsiAtEntry?: number
volumeAtEntry?: number
pricePositionAtEntry?: number
signalQualityScore?: number
}
export interface UpdateTradeStateParams {
@@ -131,7 +132,10 @@ export async function createTrade(params: CreateTradeParams) {
fundingRateAtEntry: params.fundingRateAtEntry,
atrAtEntry: params.atrAtEntry,
adxAtEntry: params.adxAtEntry,
rsiAtEntry: params.rsiAtEntry,
volumeAtEntry: params.volumeAtEntry,
pricePositionAtEntry: params.pricePositionAtEntry,
signalQualityScore: params.signalQualityScore,
},
})

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Trade" ADD COLUMN "signalQualityScore" INTEGER;

View File

@@ -75,6 +75,7 @@ model Trade {
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)
fundingRateAtEntry Float? // Perp funding rate at entry
basisAtEntry Float? // Perp-spot basis at entry