feat: Add phantom trade detection and database tracking

- Detect position size mismatches (>50% variance) after opening
- Save phantom trades to database with expectedSizeUSD, actualSizeUSD, phantomReason
- Return error from execute endpoint to prevent Position Manager tracking
- Add comprehensive documentation of phantom trade issue and solution
- Enable data collection for pattern analysis and future optimization

Fixes oracle price lag issue during volatile markets where transactions
confirm but positions don't actually open at expected size.
This commit is contained in:
mindesbunister
2025-11-04 10:34:38 +01:00
parent f682b93a1e
commit 8bc08955cc
6 changed files with 313 additions and 3 deletions

View File

@@ -29,6 +29,8 @@ export interface OpenPositionResult {
fillSize?: number
slippage?: number
error?: string
isPhantom?: boolean // Position opened but size mismatch detected
actualSizeUSD?: number // Actual position size if different from requested
}
export interface ClosePositionParams {
@@ -179,16 +181,37 @@ export async function openPosition(
const fillPrice = position.entryPrice
const slippage = Math.abs((fillPrice - oraclePrice) / oraclePrice) * 100
// CRITICAL: Validate actual position size vs expected
// Phantom trade detection: Check if position is significantly smaller than expected
const actualSizeUSD = position.size * fillPrice
const expectedSizeUSD = params.sizeUSD
const sizeRatio = actualSizeUSD / expectedSizeUSD
console.log(`💰 Fill details:`)
console.log(` Fill price: $${fillPrice.toFixed(4)}`)
console.log(` Slippage: ${slippage.toFixed(3)}%`)
console.log(` Expected size: $${expectedSizeUSD.toFixed(2)}`)
console.log(` Actual size: $${actualSizeUSD.toFixed(2)}`)
console.log(` Size ratio: ${(sizeRatio * 100).toFixed(1)}%`)
// Flag as phantom if actual size is less than 50% of expected
const isPhantom = sizeRatio < 0.5
if (isPhantom) {
console.error(`🚨 PHANTOM POSITION DETECTED!`)
console.error(` Expected: $${expectedSizeUSD.toFixed(2)}`)
console.error(` Actual: $${actualSizeUSD.toFixed(2)}`)
console.error(` This indicates the order was rejected or partially filled by Drift`)
}
return {
success: true,
transactionSignature: txSig,
fillPrice,
fillSize: baseAssetSize,
fillSize: position.size, // Use actual size from Drift, not calculated
slippage,
isPhantom,
actualSizeUSD,
}
} else {
// Position not found yet (may be DRY_RUN mode)