"use client" import React, { useState, useEffect } from 'react' interface TradeParams { symbol: string side: 'LONG' | 'SHORT' amount: number leverage: number orderType: 'MARKET' | 'LIMIT' price?: number stopLoss?: number takeProfit?: number stopLossType?: 'PRICE' | 'PERCENTAGE' takeProfitType?: 'PRICE' | 'PERCENTAGE' } interface MarketData { symbol: string price: number change24h: number } interface AccountData { totalCollateral: number freeCollateral: number leverage: number maintenanceMargin: number } interface BalanceApiResponse { totalCollateral?: number freeCollateral?: number leverage?: number marginRequirement?: number } interface TradingInfoApiResponse { totalCollateral?: number availableCollateral?: number accountLeverage?: number maintenanceMargin?: number maxPositionSize?: number requiredMargin?: number } export default function AdvancedTradingPanel() { // Trading form state const [symbol, setSymbol] = useState('SOLUSD') const [side, setSide] = useState<'LONG' | 'SHORT'>('LONG') const [orderType, setOrderType] = useState<'MARKET' | 'LIMIT'>('MARKET') const [leverage, setLeverage] = useState(1) const [positionSize, setPositionSize] = useState('') const [limitPrice, setLimitPrice] = useState('') const [loading, setLoading] = useState(false) const [result, setResult] = useState(null) // Risk Management const [enableStopLoss, setEnableStopLoss] = useState(false) const [enableTakeProfit, setEnableTakeProfit] = useState(false) const [stopLossType, setStopLossType] = useState<'PRICE' | 'PERCENTAGE'>('PERCENTAGE') const [takeProfitType, setTakeProfitType] = useState<'PRICE' | 'PERCENTAGE'>('PERCENTAGE') const [stopLossValue, setStopLossValue] = useState('') const [takeProfitValue, setTakeProfitValue] = useState('') // Market and account data const [marketData, setMarketData] = useState({ symbol: 'SOLUSD', price: 160, change24h: -2.1 }) const [accountData, setAccountData] = useState({ totalCollateral: 0, freeCollateral: 0, leverage: 0, maintenanceMargin: 0 }) const [positions, setPositions] = useState([]) const [closingPosition, setClosingPosition] = useState(null) // Calculated values const [liquidationPrice, setLiquidationPrice] = useState(0) const [requiredMargin, setRequiredMargin] = useState(0) const [maxPositionSize, setMaxPositionSize] = useState(0) const [stopLossPrice, setStopLossPrice] = useState(0) const [takeProfitPrice, setTakeProfitPrice] = useState(0) const [riskRewardRatio, setRiskRewardRatio] = useState(0) const [potentialLoss, setPotentialLoss] = useState(0) const [potentialProfit, setPotentialProfit] = useState(0) const availableSymbols = [ 'SOLUSD', 'BTCUSD', 'ETHUSD', 'DOTUSD', 'AVAXUSD', 'ADAUSD', 'MATICUSD', 'LINKUSD', 'ATOMUSD', 'NEARUSD', 'APTUSD', 'ORBSUSD', 'RNDUSD', 'WIFUSD', 'JUPUSD', 'TNSUSD', 'DOGEUSD', 'PEPE1KUSD', 'POPCATUSD', 'BOMERUSD' ] // Fetch account data on component mount useEffect(() => { fetchAccountData() fetchPositions() }, []) // Recalculate when inputs change useEffect(() => { calculateTradingMetrics() }, [positionSize, leverage, side, marketData.price, stopLossValue, takeProfitValue, stopLossType, takeProfitType, enableStopLoss, enableTakeProfit]) const fetchAccountData = async () => { try { // Fetch both balance and trading info const [balanceResponse, tradingInfoResponse] = await Promise.all([ fetch('/api/drift/balance'), fetch('/api/drift/trading-info') ]) let balanceData: BalanceApiResponse = {} let tradingInfoData: TradingInfoApiResponse = {} if (balanceResponse.ok) { balanceData = await balanceResponse.json() } if (tradingInfoResponse.ok) { tradingInfoData = await tradingInfoResponse.json() } // Combine data with fallbacks setAccountData({ totalCollateral: balanceData.totalCollateral || tradingInfoData.totalCollateral || 0, freeCollateral: balanceData.freeCollateral || tradingInfoData.availableCollateral || 0, leverage: balanceData.leverage || tradingInfoData.accountLeverage || 0, maintenanceMargin: balanceData.marginRequirement || tradingInfoData.maintenanceMargin || 0 }) // Update max position size from trading info if available if (tradingInfoData.maxPositionSize) { setMaxPositionSize(tradingInfoData.maxPositionSize) } } catch (error) { console.error('Failed to fetch account data:', error) } } const fetchPositions = async () => { try { const response = await fetch('/api/drift/positions') if (response.ok) { const data = await response.json() setPositions(data.positions || []) } } catch (error) { console.error('Failed to fetch positions:', error) } } const calculateTradingMetrics = async () => { if (!positionSize || !marketData.price) return const size = parseFloat(positionSize) const entryPrice = marketData.price const notionalValue = size * entryPrice // Try to get accurate calculations from the API first try { const response = await fetch(`/api/drift/trading-info?symbol=${symbol}&side=${side}&amount=${size}&leverage=${leverage}`) if (response.ok) { const apiData = await response.json() // Use API calculations if available if (apiData.requiredMargin !== undefined) setRequiredMargin(apiData.requiredMargin) if (apiData.maxPositionSize !== undefined) setMaxPositionSize(apiData.maxPositionSize) if (apiData.liquidationPrice !== undefined) setLiquidationPrice(apiData.liquidationPrice) } } catch (error) { console.error('Failed to fetch trading calculations from API:', error) } // Fallback to local calculations const margin = notionalValue / leverage if (requiredMargin === 0) setRequiredMargin(margin) // Calculate max position size based on available collateral if (maxPositionSize === 0) { const maxNotional = accountData.freeCollateral * leverage const maxSize = maxNotional / entryPrice setMaxPositionSize(maxSize) } // Calculate liquidation price if (liquidationPrice === 0) { // Simplified liquidation calculation (actual Drift uses more complex formula) const maintenanceMarginRate = 0.05 // 5% maintenance margin const liquidationBuffer = notionalValue * maintenanceMarginRate let liqPrice = 0 if (side === 'LONG') { // For long: liquidation when position value + margin = liquidation buffer liqPrice = entryPrice * (1 - (margin - liquidationBuffer) / notionalValue) } else { // For short: liquidation when position value - margin = liquidation buffer liqPrice = entryPrice * (1 + (margin - liquidationBuffer) / notionalValue) } setLiquidationPrice(Math.max(0, liqPrice)) } // Calculate Stop Loss and Take Profit prices let slPrice = 0 let tpPrice = 0 let potLoss = 0 let potProfit = 0 if (enableStopLoss && stopLossValue) { if (stopLossType === 'PERCENTAGE') { const slPercentage = parseFloat(stopLossValue) / 100 if (side === 'LONG') { slPrice = entryPrice * (1 - slPercentage) } else { slPrice = entryPrice * (1 + slPercentage) } } else { slPrice = parseFloat(stopLossValue) } // Calculate potential loss if (slPrice > 0) { potLoss = Math.abs(entryPrice - slPrice) * size } } if (enableTakeProfit && takeProfitValue) { if (takeProfitType === 'PERCENTAGE') { const tpPercentage = parseFloat(takeProfitValue) / 100 if (side === 'LONG') { tpPrice = entryPrice * (1 + tpPercentage) } else { tpPrice = entryPrice * (1 - tpPercentage) } } else { tpPrice = parseFloat(takeProfitValue) } // Calculate potential profit if (tpPrice > 0) { potProfit = Math.abs(tpPrice - entryPrice) * size } } setStopLossPrice(slPrice) setTakeProfitPrice(tpPrice) setPotentialLoss(potLoss) setPotentialProfit(potProfit) // Calculate Risk/Reward Ratio if (potLoss > 0 && potProfit > 0) { setRiskRewardRatio(potProfit / potLoss) } else { setRiskRewardRatio(0) } } const handleTrade = async () => { if (!positionSize || parseFloat(positionSize) <= 0) { setResult({ success: false, error: 'Please enter a valid position size' }) return } if (requiredMargin > accountData.freeCollateral) { setResult({ success: false, error: 'Insufficient collateral for this trade' }) return } if (orderType === 'LIMIT' && (!limitPrice || parseFloat(limitPrice) <= 0)) { setResult({ success: false, error: 'Please enter a valid limit price' }) return } setLoading(true) setResult(null) try { const tradeParams: TradeParams = { symbol, side, amount: parseFloat(positionSize), leverage, orderType, price: orderType === 'LIMIT' ? parseFloat(limitPrice) : undefined, stopLoss: enableStopLoss && stopLossPrice > 0 ? stopLossPrice : undefined, takeProfit: enableTakeProfit && takeProfitPrice > 0 ? takeProfitPrice : undefined, stopLossType: enableStopLoss ? stopLossType : undefined, takeProfitType: enableTakeProfit ? takeProfitType : undefined } const response = await fetch('/api/drift/trade', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(tradeParams) }) const data = await response.json() setResult(data) if (data.success) { // Refresh account data and positions after successful trade await fetchAccountData() await fetchPositions() // Reset form setPositionSize('') setLimitPrice('') } } catch (error) { setResult({ success: false, error: 'Trade execution failed' }) } setLoading(false) } const closePosition = async (symbol: string, amount?: number) => { setClosingPosition(symbol) try { const response = await fetch('/api/drift/close-position', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ symbol, amount }) }) const data = await response.json() if (data.success) { setResult({ success: true, message: `Position in ${symbol} closed successfully`, txId: data.txId }) // Refresh positions and account data await fetchPositions() await fetchAccountData() } else { setResult({ success: false, error: data.error }) } } catch (error) { setResult({ success: false, error: 'Failed to close position' }) } setClosingPosition(null) } const setMaxSize = () => { setPositionSize(maxPositionSize.toFixed(4)) } const setPercentageSize = (percentage: number) => { const size = (maxPositionSize * percentage / 100).toFixed(4) setPositionSize(size) } return (

Advanced Trading

Drift Protocol
{/* Market Info */}
${marketData.price.toFixed(2)}
= 0 ? 'text-green-400' : 'text-red-400'}`}> {marketData.change24h >= 0 ? '+' : ''}{marketData.change24h.toFixed(2)}%
{/* Side Selection */}
{/* Order Type */}
{/* Limit Price (if LIMIT order) */} {orderType === 'LIMIT' && (
setLimitPrice(e.target.value)} placeholder="Enter limit price" className="w-full bg-gray-700 text-white px-3 py-2 rounded border border-gray-600 focus:border-blue-400" />
)} {/* Leverage Slider */}
setLeverage(parseInt(e.target.value))} className="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer" />
1x 10x 20x
{/* Position Size */}
setPositionSize(e.target.value)} placeholder="Enter position size" className="w-full bg-gray-700 text-white px-3 py-2 pr-16 rounded border border-gray-600 focus:border-blue-400" />
{/* Quick Size Buttons */}
{[25, 50, 75, 100].map(pct => ( ))}
{/* Risk Management - Stop Loss */}
{enableStopLoss && (
setStopLossValue(e.target.value)} placeholder={stopLossType === 'PERCENTAGE' ? 'e.g., 5' : 'e.g., 150.00'} className="w-full bg-gray-700 text-white px-3 py-2 rounded border border-gray-600 focus:border-red-400" /> {stopLossType === 'PERCENTAGE' && (
{[2, 5, 10].map(pct => ( ))}
)} {stopLossPrice > 0 && (
Stop Loss Price: ${stopLossPrice.toFixed(2)}
)}
)}
{/* Risk Management - Take Profit */}
{enableTakeProfit && (
setTakeProfitValue(e.target.value)} placeholder={takeProfitType === 'PERCENTAGE' ? 'e.g., 10' : 'e.g., 180.00'} className="w-full bg-gray-700 text-white px-3 py-2 rounded border border-gray-600 focus:border-green-400" /> {takeProfitPrice > 0 && (
Take Profit Price: ${takeProfitPrice.toFixed(2)}
)}
)}
{/* Trading Metrics */}
Required Margin: ${requiredMargin.toFixed(2)}
Available Collateral: ${accountData.freeCollateral.toFixed(2)}
Max Position Size: {maxPositionSize.toFixed(4)}
Est. Liquidation Price: 0 ? 'text-red-400' : 'text-gray-500'}`}> {liquidationPrice > 0 ? `$${liquidationPrice.toFixed(2)}` : '--'}
{/* Risk Management Metrics */} {(enableStopLoss || enableTakeProfit) && ( <>
RISK MANAGEMENT
{enableStopLoss && potentialLoss > 0 && (
Max Loss: -${potentialLoss.toFixed(2)}
)} {enableTakeProfit && potentialProfit > 0 && (
Max Profit: +${potentialProfit.toFixed(2)}
)} {riskRewardRatio > 0 && (
Risk/Reward: = 2 ? 'text-green-400' : riskRewardRatio >= 1 ? 'text-yellow-400' : 'text-red-400' }`}> 1:{riskRewardRatio.toFixed(2)}
)} )}
{/* Open Positions */} {positions.length > 0 && (
Open Positions
{positions.length} position{positions.length !== 1 ? 's' : ''}
{positions.slice(0, 3).map((position, index) => (
{position.symbol} {position.side}
Size: {position.size.toFixed(4)} | PnL: = 0 ? 'text-green-400' : 'text-red-400'}> ${position.pnl.toFixed(2)}
))}
)} {/* Trade Button */} {/* Result Display */} {result && (
{result.success ? (
Trade Executed Successfully!
{result.txId && (
TX: {result.txId}
)}
) : (
{result.error}
)}
)}
) }