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...')
|
console.log('🚀 Initializing Position Manager on startup...')
|
||||||
|
|
||||||
try {
|
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()
|
await validateOpenTrades()
|
||||||
|
|
||||||
const manager = await getInitializedPositionManager()
|
const manager = await getInitializedPositionManager()
|
||||||
@@ -77,10 +86,45 @@ async function validateOpenTrades() {
|
|||||||
|
|
||||||
console.log(`🔍 Validating ${openTrades.length} open + ${recentlyClosedTrades.length} recently closed trades against Drift...`)
|
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 driftService = await initializeDriftService()
|
||||||
const driftPositions = await driftService.getAllPositions() // Get all positions once
|
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 {
|
try {
|
||||||
const marketConfig = getMarketConfig(trade.symbol)
|
const marketConfig = getMarketConfig(trade.symbol)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user