From 0ed2e89c7e4216c0af131a805cee3499d23ea56c Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Mon, 3 Nov 2025 08:01:30 +0100 Subject: [PATCH] feat: implement per-symbol cooldown period CRITICAL: Cooldown was global across ALL symbols, causing missed opportunities Example: ETH trade at 10:00 blocked SOL trade at 10:04 (5min cooldown) Changes: - Added getLastTradeTimeForSymbol() function to query last trade per symbol - Updated check-risk endpoint to use symbol-specific cooldown - Each coin (SOL/ETH/BTC) now has independent cooldown timer - Cooldown message shows symbol: 'Must wait X min before next SOL-PERP trade' Result: Can trade ETH and SOL simultaneously without interference Example: ETH LONG at 10:00, SOL SHORT at 10:01 = both allowed --- app/api/trading/check-risk/route.ts | 20 ++++++++++---------- lib/database/trades.ts | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/app/api/trading/check-risk/route.ts b/app/api/trading/check-risk/route.ts index 0ddf277..4cdc147 100644 --- a/app/api/trading/check-risk/route.ts +++ b/app/api/trading/check-risk/route.ts @@ -8,7 +8,7 @@ import { NextRequest, NextResponse } from 'next/server' import { getMergedConfig } from '@/config/trading' import { getInitializedPositionManager } from '@/lib/trading/position-manager' -import { getLastTradeTime, getTradesInLastHour, getTodayPnL } from '@/lib/database/trades' +import { getLastTradeTime, getLastTradeTimeForSymbol, getTradesInLastHour, getTodayPnL } from '@/lib/database/trades' export interface RiskCheckRequest { symbol: string @@ -118,18 +118,18 @@ export async function POST(request: NextRequest): Promise 0) { - const timeSinceLastTrade = Date.now() - lastTradeTime.getTime() + // 3. Check cooldown period PER SYMBOL (not global) + const lastTradeTimeForSymbol = await getLastTradeTimeForSymbol(body.symbol) + if (lastTradeTimeForSymbol && config.minTimeBetweenTrades > 0) { + const timeSinceLastTrade = Date.now() - lastTradeTimeForSymbol.getTime() const cooldownMs = config.minTimeBetweenTrades * 60 * 1000 // Convert minutes to milliseconds if (timeSinceLastTrade < cooldownMs) { const remainingMs = cooldownMs - timeSinceLastTrade const remainingMinutes = Math.ceil(remainingMs / 60000) - console.log('🚫 Risk check BLOCKED: Cooldown period active', { - lastTradeTime: lastTradeTime.toISOString(), + console.log('🚫 Risk check BLOCKED: Cooldown period active for', body.symbol, { + lastTradeTime: lastTradeTimeForSymbol.toISOString(), timeSinceLastTradeMs: timeSinceLastTrade, cooldownMs, remainingMinutes, @@ -138,7 +138,7 @@ export async function POST(request: NextRequest): Promise { } } +/** + * Get the most recent trade time for a specific symbol + */ +export async function getLastTradeTimeForSymbol(symbol: string): Promise { + const prisma = getPrismaClient() + + try { + const lastTrade = await prisma.trade.findFirst({ + where: { symbol }, + orderBy: { entryTime: 'desc' }, + select: { entryTime: true }, + }) + + return lastTrade?.entryTime || null + } catch (error) { + console.error(`❌ Failed to get last trade time for ${symbol}:`, error) + return null + } +} + /** * Get the most recent trade with full details */