Implement risk checks: cooldown, hourly limit, and daily drawdown

Implemented 3 critical risk checks in /api/trading/check-risk:

1. Daily Drawdown Check
   - Blocks trades if today's P&L < maxDailyDrawdown
   - Prevents catastrophic daily losses
   - Currently: -0 limit (configurable via MAX_DAILY_DRAWDOWN)

2. Hourly Trade Limit
   - Blocks trades if tradesInLastHour >= maxTradesPerHour
   - Prevents overtrading / algorithm malfunction
   - Currently: 20 trades/hour (configurable via MAX_TRADES_PER_HOUR)

3. Cooldown Period
   - Blocks trades if timeSinceLastTrade < minTimeBetweenTrades
   - Enforces breathing room between trades
   - Uses minutes (not seconds) thanks to previous commit
   - Currently: 0 min = disabled (configurable via MIN_TIME_BETWEEN_TRADES)

Added database helper functions:
- getLastTradeTime() - Returns timestamp of most recent trade
- getTradesInLastHour() - Counts trades in last 60 minutes
- getTodayPnL() - Sums realized P&L since midnight

All checks include detailed logging with values and thresholds.
Risk check called by n8n workflow before every trade execution.
This commit is contained in:
mindesbunister
2025-10-30 10:50:08 +01:00
parent b7b0fb9bb2
commit 7c4adff4e4
2 changed files with 132 additions and 6 deletions

View File

@@ -251,6 +251,78 @@ export async function getOpenTrades() {
}
}
/**
* Get the most recent trade entry time (for cooldown checking)
*/
export async function getLastTradeTime(): Promise<Date | null> {
const prisma = getPrismaClient()
try {
const lastTrade = await prisma.trade.findFirst({
orderBy: { entryTime: 'desc' },
select: { entryTime: true },
})
return lastTrade?.entryTime || null
} catch (error) {
console.error('❌ Failed to get last trade time:', error)
return null
}
}
/**
* Get count of trades in the last hour
*/
export async function getTradesInLastHour(): Promise<number> {
const prisma = getPrismaClient()
try {
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000)
const count = await prisma.trade.count({
where: {
entryTime: {
gte: oneHourAgo,
},
},
})
return count
} catch (error) {
console.error('❌ Failed to get trades in last hour:', error)
return 0
}
}
/**
* Get total P&L for today
*/
export async function getTodayPnL(): Promise<number> {
const prisma = getPrismaClient()
try {
const startOfDay = new Date()
startOfDay.setHours(0, 0, 0, 0)
const result = await prisma.trade.aggregate({
where: {
entryTime: {
gte: startOfDay,
},
status: 'closed',
},
_sum: {
realizedPnL: true,
},
})
return result._sum.realizedPnL || 0
} catch (error) {
console.error('❌ Failed to get today PnL:', error)
return 0
}
}
/**
* Add price update for a trade (for tracking max gain/drawdown)
*/