feat: Position Manager persistence + order cleanup + improved stop loss

- Add Position Manager state persistence to survive restarts
  - Auto-restore open trades from database on startup
  - Save state after TP1, SL adjustments, profit locks
  - Persist to configSnapshot JSON field

- Add automatic order cancellation
  - Cancel all TP/SL orders when position fully closed
  - New cancelAllOrders() function in drift/orders.ts
  - Prevents orphaned orders after manual closes

- Improve stop loss management
  - Move SL to +0.35% after TP1 (was +0.15%)
  - Gives more breathing room for retracements
  - Still locks in half of TP1 profit

- Add database sync when Position Manager closes trades
  - Auto-update Trade record with exit data
  - Save P&L, exit reason, hold time
  - Fix analytics showing stale data

- Add trade state management functions
  - updateTradeState() for Position Manager persistence
  - getOpenTrades() for startup restoration
  - getInitializedPositionManager() for async init

- Create n8n database analytics workflows
  - Daily report workflow (automated at midnight)
  - Pattern analysis (hourly/daily performance)
  - Stop loss effectiveness analysis
  - Database analytics query workflow
  - Complete setup guide (N8N_DATABASE_SETUP.md)
This commit is contained in:
mindesbunister
2025-10-27 10:39:05 +01:00
parent f571d459e4
commit d3c04ea9c9
9 changed files with 1122 additions and 8 deletions

View File

@@ -498,6 +498,15 @@ export async function closePosition(
console.log(` Close price: $${oraclePrice.toFixed(4)}`)
console.log(` Realized P&L: $${realizedPnL.toFixed(2)}`)
// If closing 100%, cancel all remaining orders for this market
if (params.percentToClose === 100) {
console.log('🗑️ Position fully closed, cancelling remaining orders...')
const cancelResult = await cancelAllOrders(params.symbol)
if (cancelResult.success && cancelResult.cancelledCount! > 0) {
console.log(`✅ Cancelled ${cancelResult.cancelledCount} orders`)
}
}
return {
success: true,
transactionSignature: txSig,
@@ -515,6 +524,68 @@ export async function closePosition(
}
}
/**
* Cancel all open orders for a specific market
*/
export async function cancelAllOrders(
symbol: string
): Promise<{ success: boolean; cancelledCount?: number; error?: string }> {
try {
console.log(`🗑️ Cancelling all orders for ${symbol}...`)
const driftService = getDriftService()
const driftClient = driftService.getClient()
const marketConfig = getMarketConfig(symbol)
const isDryRun = process.env.DRY_RUN === 'true'
if (isDryRun) {
console.log('🧪 DRY RUN: Simulating order cancellation')
return { success: true, cancelledCount: 0 }
}
// Get user account to check for orders
const userAccount = driftClient.getUserAccount()
if (!userAccount) {
throw new Error('User account not found')
}
// Filter orders for this market
const ordersToCancel = userAccount.orders.filter(
(order: any) =>
order.marketIndex === marketConfig.driftMarketIndex &&
order.status === 0 // 0 = Open status
)
if (ordersToCancel.length === 0) {
console.log('✅ No open orders to cancel')
return { success: true, cancelledCount: 0 }
}
console.log(`📋 Found ${ordersToCancel.length} open orders to cancel`)
// Cancel all orders for this market
const txSig = await driftClient.cancelOrders(
undefined, // Cancel by market type
marketConfig.driftMarketIndex,
undefined // No specific direction filter
)
console.log(`✅ Orders cancelled! Transaction: ${txSig}`)
return {
success: true,
cancelledCount: ordersToCancel.length,
}
} catch (error) {
console.error('❌ Failed to cancel orders:', error)
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
}
}
}
/**
* Close entire position for a market
*/