Features: - Autonomous trading system with Drift Protocol on Solana - Real-time position monitoring with Pyth price feeds - Dynamic stop-loss and take-profit management - n8n workflow integration for TradingView signals - Beautiful web UI for settings management - REST API for trade execution and monitoring - Next.js 15 with standalone output mode - TypeScript with strict typing - Docker containerization with multi-stage builds - PostgreSQL database for trade history - Singleton pattern for Drift client connection pooling - BN.js for BigNumber handling (Drift SDK requirement) - Configurable stop-loss and take-profit levels - Breakeven trigger and profit locking - Daily loss limits and trade cooldowns - Slippage tolerance controls - DRY_RUN mode for safe testing - Real-time risk calculator - Interactive sliders for all parameters - Live preview of trade outcomes - Position sizing and leverage controls - Beautiful gradient design with Tailwind CSS - POST /api/trading/execute - Execute trades - POST /api/trading/close - Close positions - GET /api/trading/positions - Monitor active trades - GET /api/trading/check-risk - Validate trade signals - GET /api/settings - View configuration - POST /api/settings - Update configuration - Fixed Borsh serialization errors (simplified order params) - Resolved RPC rate limiting with singleton pattern - Fixed BigInt vs BN type mismatches - Corrected order execution flow - Improved position state management - Complete setup guides - Docker deployment instructions - n8n workflow configuration - API reference documentation - Risk management guidelines - Runs on port 3001 (external), 3000 (internal) - Uses Helius RPC for optimal performance - Production-ready with error handling - Health monitoring and logging
134 lines
3.5 KiB
TypeScript
134 lines
3.5 KiB
TypeScript
/**
|
|
* Get Active Positions API Endpoint
|
|
*
|
|
* Returns all currently monitored positions
|
|
* GET /api/trading/positions
|
|
*/
|
|
|
|
import { NextRequest, NextResponse } from 'next/server'
|
|
import { getPositionManager } from '@/lib/trading/position-manager'
|
|
|
|
export interface PositionsResponse {
|
|
success: boolean
|
|
monitoring: {
|
|
isActive: boolean
|
|
tradeCount: number
|
|
symbols: string[]
|
|
}
|
|
positions: Array<{
|
|
id: string
|
|
symbol: string
|
|
direction: 'long' | 'short'
|
|
entryPrice: number
|
|
currentPrice: number
|
|
entryTime: string
|
|
positionSize: number
|
|
currentSize: number
|
|
leverage: number
|
|
stopLoss: number
|
|
takeProfit1: number
|
|
takeProfit2: number
|
|
tp1Hit: boolean
|
|
slMovedToBreakeven: boolean
|
|
realizedPnL: number
|
|
unrealizedPnL: number
|
|
peakPnL: number
|
|
profitPercent: number
|
|
accountPnL: number
|
|
priceChecks: number
|
|
ageMinutes: number
|
|
}>
|
|
}
|
|
|
|
export async function GET(request: NextRequest): Promise<NextResponse<PositionsResponse>> {
|
|
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,
|
|
monitoring: { isActive: false, tradeCount: 0, symbols: [] },
|
|
positions: [],
|
|
} as any,
|
|
{ status: 401 }
|
|
)
|
|
}
|
|
|
|
const positionManager = getPositionManager()
|
|
const status = positionManager.getStatus()
|
|
const trades = positionManager.getActiveTrades()
|
|
|
|
const positions = trades.map(trade => {
|
|
const profitPercent = calculateProfitPercent(
|
|
trade.entryPrice,
|
|
trade.lastPrice,
|
|
trade.direction
|
|
)
|
|
|
|
const accountPnL = profitPercent * trade.leverage
|
|
const ageMinutes = Math.floor((Date.now() - trade.entryTime) / 60000)
|
|
|
|
return {
|
|
id: trade.id,
|
|
symbol: trade.symbol,
|
|
direction: trade.direction,
|
|
entryPrice: trade.entryPrice,
|
|
currentPrice: trade.lastPrice,
|
|
entryTime: new Date(trade.entryTime).toISOString(),
|
|
positionSize: trade.positionSize,
|
|
currentSize: trade.currentSize,
|
|
leverage: trade.leverage,
|
|
stopLoss: trade.stopLossPrice,
|
|
takeProfit1: trade.tp1Price,
|
|
takeProfit2: trade.tp2Price,
|
|
tp1Hit: trade.tp1Hit,
|
|
slMovedToBreakeven: trade.slMovedToBreakeven,
|
|
realizedPnL: trade.realizedPnL,
|
|
unrealizedPnL: trade.unrealizedPnL,
|
|
peakPnL: trade.peakPnL,
|
|
profitPercent: profitPercent,
|
|
accountPnL: accountPnL,
|
|
priceChecks: trade.priceCheckCount,
|
|
ageMinutes,
|
|
}
|
|
})
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
monitoring: {
|
|
isActive: status.isMonitoring,
|
|
tradeCount: status.activeTradesCount,
|
|
symbols: status.symbols,
|
|
},
|
|
positions,
|
|
})
|
|
|
|
} catch (error) {
|
|
console.error('❌ Error fetching positions:', error)
|
|
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
monitoring: { isActive: false, tradeCount: 0, symbols: [] },
|
|
positions: [],
|
|
} as any,
|
|
{ status: 500 }
|
|
)
|
|
}
|
|
}
|
|
|
|
function calculateProfitPercent(
|
|
entryPrice: number,
|
|
currentPrice: number,
|
|
direction: 'long' | 'short'
|
|
): number {
|
|
if (direction === 'long') {
|
|
return ((currentPrice - entryPrice) / entryPrice) * 100
|
|
} else {
|
|
return ((entryPrice - currentPrice) / entryPrice) * 100
|
|
}
|
|
}
|