Files
trading_bot_v3/v4/app/api/trading/execute/route.ts
mindesbunister 1345a35680 feat: Complete Phase 2 - Autonomous Trading System
- Add Pyth Network price monitoring (WebSocket + polling fallback)
- Add Position Manager with automatic exit logic (TP1/TP2/SL)
- Implement dynamic stop-loss adjustment (breakeven + profit lock)
- Add real-time P&L tracking and multi-position support
- Create comprehensive test suite (3 test scripts)
- Add 5 detailed documentation files (2500+ lines)
- Update configuration to $50 position size for safe testing
- All Phase 2 features complete and tested

Core Components:
- v4/lib/pyth/price-monitor.ts - Real-time price monitoring
- v4/lib/trading/position-manager.ts - Autonomous position management
- v4/app/api/trading/positions/route.ts - Query positions endpoint
- v4/test-*.ts - Comprehensive testing suite

Documentation:
- PHASE_2_COMPLETE_REPORT.md - Implementation summary
- v4/PHASE_2_SUMMARY.md - Detailed feature overview
- v4/TESTING.md - Testing guide
- v4/QUICKREF_PHASE2.md - Quick reference
- install-phase2.sh - Automated installation script
2025-10-23 14:40:29 +02:00

247 lines
6.8 KiB
TypeScript

/**
* Execute Trade API Endpoint
*
* Called by n8n workflow when TradingView signal is received
* POST /api/trading/execute
*/
import { NextRequest, NextResponse } from 'next/server'
import { initializeDriftService } from '@/v4/lib/drift/client'
import { openPosition } from '@/v4/lib/drift/orders'
import { normalizeTradingViewSymbol } from '@/v4/config/trading'
import { getMergedConfig } from '@/v4/config/trading'
import { getPositionManager, ActiveTrade } from '@/v4/lib/trading/position-manager'
export interface ExecuteTradeRequest {
symbol: string // TradingView symbol (e.g., 'SOLUSDT')
direction: 'long' | 'short'
timeframe: string // e.g., '5'
signalStrength?: 'strong' | 'moderate' | 'weak'
signalPrice?: number
}
export interface ExecuteTradeResponse {
success: boolean
positionId?: string
symbol?: string
direction?: 'long' | 'short'
entryPrice?: number
positionSize?: number
stopLoss?: number
takeProfit1?: number
takeProfit2?: number
stopLossPercent?: number
tp1Percent?: number
tp2Percent?: number
entrySlippage?: number
timestamp?: string
error?: string
message?: string
}
export async function POST(request: NextRequest): Promise<NextResponse<ExecuteTradeResponse>> {
try {
// Verify authorization
const authHeader = request.headers.get('authorization')
const expectedAuth = `Bearer ${process.env.API_SECRET_KEY}`
if (!authHeader || authHeader !== expectedAuth) {
return NextResponse.json(
{
success: false,
error: 'Unauthorized',
message: 'Invalid API key',
},
{ status: 401 }
)
}
// Parse request body
const body: ExecuteTradeRequest = await request.json()
console.log('🎯 Trade execution request received:', body)
// Validate required fields
if (!body.symbol || !body.direction) {
return NextResponse.json(
{
success: false,
error: 'Missing required fields',
message: 'symbol and direction are required',
},
{ status: 400 }
)
}
// Normalize symbol
const driftSymbol = normalizeTradingViewSymbol(body.symbol)
console.log(`📊 Normalized symbol: ${body.symbol}${driftSymbol}`)
// Get trading configuration
const config = getMergedConfig()
// Initialize Drift service if not already initialized
const driftService = await initializeDriftService()
// Check account health before trading
const health = await driftService.getAccountHealth()
console.log('💊 Account health:', health)
if (health.freeCollateral <= 0) {
return NextResponse.json(
{
success: false,
error: 'Insufficient collateral',
message: `Free collateral: $${health.freeCollateral.toFixed(2)}`,
},
{ status: 400 }
)
}
// Calculate position size with leverage
const positionSizeUSD = config.positionSize * config.leverage
console.log(`💰 Opening ${body.direction} position:`)
console.log(` Symbol: ${driftSymbol}`)
console.log(` Base size: $${config.positionSize}`)
console.log(` Leverage: ${config.leverage}x`)
console.log(` Total position: $${positionSizeUSD}`)
// Open position
const openResult = await openPosition({
symbol: driftSymbol,
direction: body.direction,
sizeUSD: positionSizeUSD,
slippageTolerance: config.slippageTolerance,
})
if (!openResult.success) {
return NextResponse.json(
{
success: false,
error: 'Position open failed',
message: openResult.error,
},
{ status: 500 }
)
}
// Calculate stop loss and take profit prices
const entryPrice = openResult.fillPrice!
const stopLossPrice = calculatePrice(
entryPrice,
config.stopLossPercent,
body.direction
)
const tp1Price = calculatePrice(
entryPrice,
config.takeProfit1Percent,
body.direction
)
const tp2Price = calculatePrice(
entryPrice,
config.takeProfit2Percent,
body.direction
)
console.log('📊 Trade targets:')
console.log(` Entry: $${entryPrice.toFixed(4)}`)
console.log(` SL: $${stopLossPrice.toFixed(4)} (${config.stopLossPercent}%)`)
console.log(` TP1: $${tp1Price.toFixed(4)} (${config.takeProfit1Percent}%)`)
console.log(` TP2: $${tp2Price.toFixed(4)} (${config.takeProfit2Percent}%)`)
// Calculate emergency stop
const emergencyStopPrice = calculatePrice(
entryPrice,
config.emergencyStopPercent,
body.direction
)
// Create active trade object
const activeTrade: ActiveTrade = {
id: `trade-${Date.now()}`,
positionId: openResult.transactionSignature!,
symbol: driftSymbol,
direction: body.direction,
entryPrice,
entryTime: Date.now(),
positionSize: positionSizeUSD,
leverage: config.leverage,
stopLossPrice,
tp1Price,
tp2Price,
emergencyStopPrice,
currentSize: positionSizeUSD,
tp1Hit: false,
slMovedToBreakeven: false,
slMovedToProfit: false,
realizedPnL: 0,
unrealizedPnL: 0,
peakPnL: 0,
priceCheckCount: 0,
lastPrice: entryPrice,
lastUpdateTime: Date.now(),
}
// Add to position manager for monitoring
const positionManager = getPositionManager()
await positionManager.addTrade(activeTrade)
console.log('✅ Trade added to position manager for monitoring')
// TODO: Save trade to database (add Prisma integration later)
const response: ExecuteTradeResponse = {
success: true,
positionId: openResult.transactionSignature,
symbol: driftSymbol,
direction: body.direction,
entryPrice: entryPrice,
positionSize: positionSizeUSD,
stopLoss: stopLossPrice,
takeProfit1: tp1Price,
takeProfit2: tp2Price,
stopLossPercent: config.stopLossPercent,
tp1Percent: config.takeProfit1Percent,
tp2Percent: config.takeProfit2Percent,
entrySlippage: openResult.slippage,
timestamp: new Date().toISOString(),
}
console.log('✅ Trade executed successfully!')
return NextResponse.json(response)
} catch (error) {
console.error('❌ Trade execution error:', error)
return NextResponse.json(
{
success: false,
error: 'Internal server error',
message: error instanceof Error ? error.message : 'Unknown error',
},
{ status: 500 }
)
}
}
/**
* Helper function to calculate price based on percentage
*/
function calculatePrice(
entryPrice: number,
percent: number,
direction: 'long' | 'short'
): number {
if (direction === 'long') {
return entryPrice * (1 + percent / 100)
} else {
return entryPrice * (1 - percent / 100)
}
}