critical: Fix startup validator reopening duplicate trades
- Group trades by symbol before validation - Keep only most recent trade per symbol - Close older duplicates with DUPLICATE_CLEANUP reason - Prevents reopening old closed trades when checking recent trades Bug: Startup validator was reopening ALL closed trades for a symbol if Drift showed one position, causing 3 trades to be tracked when only 1 actual position existed on Drift. Impact: Position Manager was tracking ghost positions, causing confusion and potential missed risk management.
This commit is contained in:
@@ -22,7 +22,16 @@ export async function initializePositionManagerOnStartup() {
|
||||
console.log('🚀 Initializing Position Manager on startup...')
|
||||
|
||||
try {
|
||||
// Validate open trades against Drift positions BEFORE starting Position Manager
|
||||
// CRITICAL: Run database sync validator FIRST to clean up duplicates
|
||||
const { validateAllOpenTrades } = await import('../database/sync-validator')
|
||||
console.log('🔍 Running database sync validation before Position Manager init...')
|
||||
const validationResult = await validateAllOpenTrades()
|
||||
|
||||
if (validationResult.ghosts > 0) {
|
||||
console.log(`✅ Cleaned up ${validationResult.ghosts} ghost/duplicate trades`)
|
||||
}
|
||||
|
||||
// Then validate open trades against Drift positions
|
||||
await validateOpenTrades()
|
||||
|
||||
const manager = await getInitializedPositionManager()
|
||||
@@ -77,10 +86,45 @@ async function validateOpenTrades() {
|
||||
|
||||
console.log(`🔍 Validating ${openTrades.length} open + ${recentlyClosedTrades.length} recently closed trades against Drift...`)
|
||||
|
||||
// CRITICAL: Group trades by symbol to handle multiple DB entries for same Drift position
|
||||
// This prevents reopening old closed trades when only the most recent should be restored
|
||||
const tradesBySymbol = new Map<string, any[]>()
|
||||
for (const trade of allTradesToCheck) {
|
||||
const existing = tradesBySymbol.get(trade.symbol) || []
|
||||
existing.push(trade)
|
||||
tradesBySymbol.set(trade.symbol, existing)
|
||||
}
|
||||
|
||||
const driftService = await initializeDriftService()
|
||||
const driftPositions = await driftService.getAllPositions() // Get all positions once
|
||||
|
||||
for (const trade of allTradesToCheck) {
|
||||
// Process each symbol's trades (keep only most recent if multiple exist)
|
||||
for (const [symbol, trades] of tradesBySymbol) {
|
||||
// Sort by creation time, newest first
|
||||
trades.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
|
||||
const mostRecentTrade = trades[0]
|
||||
const olderTrades = trades.slice(1)
|
||||
|
||||
// Close any older trades BEFORE validating the most recent
|
||||
for (const oldTrade of olderTrades) {
|
||||
if (oldTrade.exitReason === null) {
|
||||
console.log(`🗑️ Closing duplicate old trade: ${oldTrade.id} (${symbol}, created ${oldTrade.createdAt.toISOString()})`)
|
||||
await prisma.trade.update({
|
||||
where: { id: oldTrade.id },
|
||||
data: {
|
||||
status: 'closed',
|
||||
exitTime: new Date(),
|
||||
exitReason: 'DUPLICATE_CLEANUP',
|
||||
exitPrice: oldTrade.entryPrice,
|
||||
realizedPnL: 0,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Now validate only the most recent trade for this symbol
|
||||
const trade = mostRecentTrade
|
||||
|
||||
try {
|
||||
const marketConfig = getMarketConfig(trade.symbol)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user