wip: Emergency order restoration endpoint (has singleton issues)
- Created /api/trading/place-exit-orders endpoint - Created restore-orders.mjs script - Issue: Next.js creates separate Drift instances per route - Workaround: Use /api/trading/cancel-orders to remove orphaned orders Current situation: - 32 orphaned orders existed and were cancelled - Position Manager should auto-place new orders - Manual order placement endpoint needs refactoring
This commit is contained in:
2
.env
2
.env
@@ -376,7 +376,7 @@ TRAILING_STOP_ACTIVATION=0.4
|
|||||||
MIN_QUALITY_SCORE=60
|
MIN_QUALITY_SCORE=60
|
||||||
SOLANA_ENABLED=true
|
SOLANA_ENABLED=true
|
||||||
SOLANA_POSITION_SIZE=100
|
SOLANA_POSITION_SIZE=100
|
||||||
SOLANA_LEVERAGE=10
|
SOLANA_LEVERAGE=1
|
||||||
SOLANA_USE_PERCENTAGE_SIZE=true
|
SOLANA_USE_PERCENTAGE_SIZE=true
|
||||||
ETHEREUM_ENABLED=false
|
ETHEREUM_ENABLED=false
|
||||||
ETHEREUM_POSITION_SIZE=50
|
ETHEREUM_POSITION_SIZE=50
|
||||||
|
|||||||
84
app/api/trading/place-exit-orders/route.ts
Normal file
84
app/api/trading/place-exit-orders/route.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import { NextRequest, NextResponse } from 'next/server'
|
||||||
|
import { placeExitOrders } from '@/lib/drift/orders'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emergency endpoint to manually place exit orders
|
||||||
|
* Use when orders vanish but position still exists
|
||||||
|
*/
|
||||||
|
export async function POST(req: NextRequest) {
|
||||||
|
try {
|
||||||
|
// Auth check
|
||||||
|
const authHeader = req.headers.get('authorization')
|
||||||
|
const apiSecret = process.env.API_SECRET_KEY
|
||||||
|
|
||||||
|
if (!authHeader || !apiSecret || !authHeader.includes(apiSecret)) {
|
||||||
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = await req.json()
|
||||||
|
const {
|
||||||
|
symbol,
|
||||||
|
direction,
|
||||||
|
entryPrice,
|
||||||
|
tp1Price,
|
||||||
|
tp2Price,
|
||||||
|
slPrice,
|
||||||
|
positionSizeUSD,
|
||||||
|
tp1SizePercent = 75,
|
||||||
|
tp2SizePercent = 0,
|
||||||
|
} = body
|
||||||
|
|
||||||
|
// Validate required fields
|
||||||
|
if (!symbol || !direction || !entryPrice || !tp1Price || !tp2Price || !slPrice || !positionSizeUSD) {
|
||||||
|
return NextResponse.json({
|
||||||
|
success: false,
|
||||||
|
error: 'Missing required fields'
|
||||||
|
}, { status: 400 })
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('🛡️ Manually placing exit orders for existing position...')
|
||||||
|
console.log(` ${symbol} ${direction} @ $${entryPrice}`)
|
||||||
|
console.log(` TP1: $${tp1Price} (${tp1SizePercent}%)`)
|
||||||
|
console.log(` TP2: $${tp2Price} (${tp2SizePercent}%)`)
|
||||||
|
console.log(` SL: $${slPrice}`)
|
||||||
|
|
||||||
|
// Initialize Drift service BEFORE calling placeExitOrders
|
||||||
|
const { initializeDriftService } = await import('@/lib/drift/client')
|
||||||
|
await initializeDriftService()
|
||||||
|
console.log('✅ Drift service ready for order placement')
|
||||||
|
|
||||||
|
const result = await placeExitOrders({
|
||||||
|
symbol,
|
||||||
|
direction,
|
||||||
|
entryPrice,
|
||||||
|
tp1Price,
|
||||||
|
tp2Price,
|
||||||
|
stopLossPrice: slPrice,
|
||||||
|
positionSizeUSD,
|
||||||
|
tp1SizePercent,
|
||||||
|
tp2SizePercent,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
console.log('✅ Exit orders placed successfully!')
|
||||||
|
return NextResponse.json({
|
||||||
|
success: true,
|
||||||
|
signatures: result.signatures,
|
||||||
|
message: 'Exit orders placed on-chain'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.error('❌ Failed to place exit orders:', result.error)
|
||||||
|
return NextResponse.json({
|
||||||
|
success: false,
|
||||||
|
error: result.error
|
||||||
|
}, { status: 500 })
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Error in place-exit-orders:', error)
|
||||||
|
return NextResponse.json({
|
||||||
|
success: false,
|
||||||
|
error: error instanceof Error ? error.message : 'Unknown error'
|
||||||
|
}, { status: 500 })
|
||||||
|
}
|
||||||
|
}
|
||||||
75
scripts/restore-orders.mjs
Normal file
75
scripts/restore-orders.mjs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Emergency script to restore on-chain exit orders
|
||||||
|
* Use when orders vanish but Position Manager is still tracking
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { PrismaClient } from '@prisma/client'
|
||||||
|
|
||||||
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
|
async function restoreOrders() {
|
||||||
|
console.log('🔍 Checking for open trades without orders...')
|
||||||
|
|
||||||
|
const openTrades = await prisma.trade.findMany({
|
||||||
|
where: { exitReason: null },
|
||||||
|
})
|
||||||
|
|
||||||
|
if (openTrades.length === 0) {
|
||||||
|
console.log('✅ No open trades found')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const trade of openTrades) {
|
||||||
|
console.log(`\n📊 Trade: ${trade.symbol} ${trade.direction} @ $${trade.entryPrice}`)
|
||||||
|
console.log(` TP1: $${trade.takeProfit1Price}`)
|
||||||
|
console.log(` TP2: $${trade.takeProfit2Price}`)
|
||||||
|
console.log(` SL: $${trade.stopLossPrice}`)
|
||||||
|
console.log(` Size: $${trade.positionSizeUSD}`)
|
||||||
|
|
||||||
|
// Make API call to place orders
|
||||||
|
const API_SECRET_KEY = process.env.API_SECRET_KEY
|
||||||
|
if (!API_SECRET_KEY) {
|
||||||
|
console.error('❌ API_SECRET_KEY not set in environment')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch('http://localhost:3001/api/trading/place-exit-orders', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${API_SECRET_KEY}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
symbol: trade.symbol,
|
||||||
|
direction: trade.direction,
|
||||||
|
entryPrice: trade.entryPrice,
|
||||||
|
tp1Price: trade.takeProfit1Price,
|
||||||
|
tp2Price: trade.takeProfit2Price,
|
||||||
|
slPrice: trade.stopLossPrice,
|
||||||
|
positionSizeUSD: trade.positionSizeUSD,
|
||||||
|
tp1SizePercent: 75,
|
||||||
|
tp2SizePercent: 0, // TP2-as-runner
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = await response.json()
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
console.log('✅ Orders placed successfully!')
|
||||||
|
console.log(` Signatures: ${result.signatures?.slice(0, 2).join(', ')}...`)
|
||||||
|
} else {
|
||||||
|
console.error('❌ Failed to place orders:', result.error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreOrders()
|
||||||
|
.then(() => {
|
||||||
|
console.log('\n✅ Done')
|
||||||
|
process.exit(0)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('❌ Error:', error)
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user