Files
trading_bot_v3/app/chart-trading-demo/page.tsx
mindesbunister f0845d0fd1 restore: Complete Jupiter-style trading interface from git history
Fully restored complete trading page with all components:
- Complete Jupiter Perps-style UI with coin selection dropdown
- Real wallet balance integration and live trading capabilities
- Position management with leverage controls (1x-100x)
- Complete trade form with 'You're paying' and 'You're receiving' sections
- Working dropdowns for token selection with balance display
- Professional position table with P&L tracking
- Real trade execution with backend API integration
- Stop Loss and Take Profit functionality
- Risk warnings for leveraged positions
- MAX button for using full balance
- Live market data integration
- Temporary chart placeholder (SimpleChart to be added)

All form sections complete, builds successfully, ready for testing
2025-07-16 16:04:04 +02:00

777 lines
34 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import React, { useState, useRef, useEffect } from 'react'
// import SimpleChart from '../../components/SimpleChart'
interface Position {
id: string
symbol: string
side: 'BUY' | 'SELL'
amount: number
entryPrice: number
stopLoss: number
takeProfit: number
currentPrice: number
unrealizedPnl: number
leverage: number
}
export default function ChartTradingDemo() {
const [selectedSymbol, setSelectedSymbol] = useState('SOL')
const [leverage, setLeverage] = useState(1)
const [payingAmount, setPayingAmount] = useState('')
const [payingToken, setPayingToken] = useState('USDC')
const [receivingAmount, setReceivingAmount] = useState('')
const [stopLoss, setStopLoss] = useState('')
const [takeProfit, setTakeProfit] = useState('')
const [isSymbolDropdownOpen, setIsSymbolDropdownOpen] = useState(false)
const [isPayingTokenDropdownOpen, setIsPayingTokenDropdownOpen] = useState(false)
const dropdownRef = useRef<HTMLDivElement>(null)
const payingTokenDropdownRef = useRef<HTMLDivElement>(null)
const [positions, setPositions] = useState<Position[]>([])
const [isLoading, setIsLoading] = useState(false)
const [tradeStatus, setTradeStatus] = useState<string>('')
const [liveBalances, setLiveBalances] = useState<{[key: string]: number}>({})
const [marketPrices, setMarketPrices] = useState<{[key: string]: {price: number, change: number}}>({});
const symbols = [
{ symbol: 'SOL', name: 'Solana', price: marketPrices.SOL?.price || 166.21, change: marketPrices.SOL?.change || 2.45, icon: '🟣' },
{ symbol: 'BTC', name: 'Bitcoin', price: marketPrices.BTC?.price || 42150.00, change: marketPrices.BTC?.change || -1.23, icon: '🟠' },
{ symbol: 'ETH', name: 'Ethereum', price: marketPrices.ETH?.price || 2580.50, change: marketPrices.ETH?.change || 3.12, icon: '⬜' },
{ symbol: 'BONK', name: 'Bonk', price: marketPrices.BONK?.price || 0.00003456, change: marketPrices.BONK?.change || 15.67, icon: '🐕' },
{ symbol: 'JUP', name: 'Jupiter', price: marketPrices.JUP?.price || 0.8234, change: marketPrices.JUP?.change || 5.43, icon: '🪐' },
{ symbol: 'WIF', name: 'dogwifhat', price: marketPrices.WIF?.price || 3.42, change: marketPrices.WIF?.change || -8.21, icon: '🧢' },
]
const payingTokens = [
{ symbol: 'USDC', name: 'USD Coin', balance: liveBalances.USDC || 0, icon: '💵' },
{ symbol: 'USDT', name: 'Tether', balance: liveBalances.USDT || 0, icon: '💰' },
{ symbol: 'SOL', name: 'Solana', balance: liveBalances.SOL || 0, icon: '🟣' },
{ symbol: 'BTC', name: 'Bitcoin', balance: liveBalances.BTC || 0, icon: '🟠' },
{ symbol: 'ETH', name: 'Ethereum', balance: liveBalances.ETH || 0, icon: '⬜' },
]
// Close dropdown when clicking outside
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsSymbolDropdownOpen(false)
}
if (payingTokenDropdownRef.current && !payingTokenDropdownRef.current.contains(event.target as Node)) {
setIsPayingTokenDropdownOpen(false)
}
}
document.addEventListener('mousedown', handleClickOutside)
return () => {
document.removeEventListener('mousedown', handleClickOutside)
}
}, [])
// Load real wallet balances and positions
useEffect(() => {
const loadRealData = async () => {
try {
// Fetch real wallet balances
const balanceResponse = await fetch('/api/wallet/balance')
if (balanceResponse.ok) {
const balanceData = await balanceResponse.json()
if (balanceData.success && balanceData.balance?.positions) {
// Convert positions array to balances object
const balances: {[key: string]: number} = {}
balanceData.balance.positions.forEach((position: any) => {
balances[position.symbol] = position.amount
})
setLiveBalances(balances)
console.log('✅ Loaded wallet balances:', balances)
}
}
// Fetch real positions
const positionsResponse = await fetch('/api/trading/positions')
if (positionsResponse.ok) {
const positionsData = await positionsResponse.json()
if (positionsData.success) {
setPositions(positionsData.positions || [])
}
}
// Fetch market prices
const pricesResponse = await fetch('/api/prices')
if (pricesResponse.ok) {
const pricesData = await pricesResponse.json()
if (pricesData.success) {
setMarketPrices(pricesData.prices || {})
}
}
} catch (error) {
console.error('Error loading real data:', error)
setTradeStatus('Error loading wallet data')
}
}
loadRealData()
// Refresh data every 30 seconds
const interval = setInterval(loadRealData, 30000)
return () => clearInterval(interval)
}, [])
const getCurrentSymbolData = () => {
return symbols.find(s => s.symbol === selectedSymbol) || symbols[0]
}
const getCurrentPayingTokenData = () => {
const tokenData = payingTokens.find(t => t.symbol === payingToken) || payingTokens[0]
console.log(`Getting token data for ${payingToken}:`, tokenData, 'Live balances:', liveBalances)
return tokenData
}
// Calculate receiving amount based on paying amount
useEffect(() => {
if (payingAmount && payingToken === 'USDC') {
const symbolData = getCurrentSymbolData()
const symbolPrice = symbolData.price
const receiving = parseFloat(payingAmount) / symbolPrice
setReceivingAmount(receiving.toFixed(6))
} else if (payingAmount && payingToken === selectedSymbol) {
setReceivingAmount(payingAmount)
} else {
setReceivingAmount('')
}
}, [payingAmount, payingToken, selectedSymbol])
const handleTrade = async (side: 'BUY' | 'SELL') => {
const amount = parseFloat(payingAmount)
if (!amount || amount <= 0) {
alert('Please enter a valid amount')
return
}
setIsLoading(true)
setTradeStatus(`Executing ${side} order...`)
try {
// Execute real trade based on leverage
const tradeEndpoint = leverage > 1 ? '/api/trading/execute-perp' : '/api/trading/execute-dex'
const tradePayload = {
symbol: selectedSymbol,
side,
amount,
stopLoss: stopLoss ? parseFloat(stopLoss) : undefined,
takeProfit: takeProfit ? parseFloat(takeProfit) : undefined,
leverage: leverage > 1 ? leverage : undefined,
fromCoin: payingToken,
toCoin: selectedSymbol,
useRealDEX: true
}
console.log('Executing trade:', tradePayload)
const response = await fetch(tradeEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(tradePayload)
})
const result = await response.json()
if (result.success) {
setTradeStatus(`${side} order executed successfully!`)
// Refresh positions and balances
const positionsResponse = await fetch('/api/trading/positions')
if (positionsResponse.ok) {
const positionsData = await positionsResponse.json()
if (positionsData.success) {
setPositions(positionsData.positions || [])
}
}
const balanceResponse = await fetch('/api/wallet/balance')
if (balanceResponse.ok) {
const balanceData = await balanceResponse.json()
if (balanceData.success && balanceData.balance?.positions) {
// Convert positions array to balances object
const balances: {[key: string]: number} = {}
balanceData.balance.positions.forEach((position: any) => {
balances[position.symbol] = position.amount
})
setLiveBalances(balances)
}
}
// Clear form
setPayingAmount('')
setReceivingAmount('')
setStopLoss('')
setTakeProfit('')
alert(`${side} order executed successfully!\nTransaction: ${result.signature || 'Completed'}`)
} else {
setTradeStatus(`Trade failed: ${result.error || 'Unknown error'}`)
alert(`❌ Trade failed: ${result.message || result.error || 'Unknown error'}`)
}
} catch (error) {
console.error('Trade execution error:', error)
setTradeStatus('Trade execution failed')
alert('❌ Trade execution failed. Please try again.')
} finally {
setIsLoading(false)
setTimeout(() => setTradeStatus(''), 5000)
}
}
const handleClosePosition = async (positionId: string) => {
const position = positions.find(pos => pos.id === positionId)
if (!position) return
if (!confirm(`Are you sure you want to close ${position.symbol} ${position.side} position?`)) {
return
}
setIsLoading(true)
setTradeStatus('Closing position...')
try {
const response = await fetch(`/api/trading/positions/${positionId}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
})
const result = await response.json()
if (result.success) {
setTradeStatus('Position closed successfully!')
// Refresh positions and balances
const positionsResponse = await fetch('/api/trading/positions')
if (positionsResponse.ok) {
const positionsData = await positionsResponse.json()
if (positionsData.success) {
setPositions(positionsData.positions || [])
}
}
const balanceResponse = await fetch('/api/wallet/balance')
if (balanceResponse.ok) {
const balanceData = await balanceResponse.json()
if (balanceData.success && balanceData.balance?.positions) {
// Convert positions array to balances object
const balances: {[key: string]: number} = {}
balanceData.balance.positions.forEach((position: any) => {
balances[position.symbol] = position.amount
})
setLiveBalances(balances)
}
}
alert(`✅ Position closed successfully!\nP&L: ${result.realizedPnl ? (result.realizedPnl >= 0 ? '+' : '') + '$' + result.realizedPnl.toFixed(2) : 'Calculated at settlement'}`)
} else {
setTradeStatus(`Failed to close position: ${result.error}`)
alert(`❌ Failed to close position: ${result.message || result.error}`)
}
} catch (error) {
console.error('Position close error:', error)
setTradeStatus('Failed to close position')
alert('❌ Failed to close position. Please try again.')
} finally {
setIsLoading(false)
setTimeout(() => setTradeStatus(''), 5000)
}
}
return (
<>
<style jsx>{`
.slider::-webkit-slider-thumb {
appearance: none;
height: 20px;
width: 20px;
border-radius: 50%;
background: #eab308;
border: 2px solid #1f2937;
cursor: pointer;
box-shadow: 0 0 0 1px #374151;
}
.slider::-moz-range-thumb {
height: 20px;
width: 20px;
border-radius: 50%;
background: #eab308;
border: 2px solid #1f2937;
cursor: pointer;
box-shadow: 0 0 0 1px #374151;
}
.slider::-webkit-slider-track {
height: 8px;
border-radius: 4px;
}
.slider::-moz-range-track {
height: 8px;
border-radius: 4px;
}
`}</style>
<div className="h-screen bg-gray-900 flex flex-col">
{/* Top Bar */}
<div className="bg-gray-800 border-b border-gray-700 p-4">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-6">
<h1 className="text-xl font-bold text-white">Live Trading Terminal</h1>
{/* Symbol Selector Dropdown */}
<div className="relative" ref={dropdownRef}>
<button
onClick={() => setIsSymbolDropdownOpen(!isSymbolDropdownOpen)}
className="flex items-center space-x-3 bg-gray-700 hover:bg-gray-600 rounded-lg px-4 py-2 text-white transition-all border border-gray-600"
>
<span className="text-lg">{getCurrentSymbolData().icon}</span>
<div className="text-left">
<div className="font-medium">{selectedSymbol}/USDC</div>
<div className="text-xs text-gray-400">{getCurrentSymbolData().name}</div>
</div>
<div className="text-right">
<div className="text-sm font-mono">${getCurrentSymbolData().price.toLocaleString()}</div>
<div className={`text-xs ${getCurrentSymbolData().change >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{getCurrentSymbolData().change >= 0 ? '+' : ''}{getCurrentSymbolData().change}%
</div>
</div>
<svg className={`w-4 h-4 transition-transform ${isSymbolDropdownOpen ? 'rotate-180' : ''}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
{/* Dropdown Menu */}
{isSymbolDropdownOpen && (
<div className="absolute top-full left-0 mt-2 w-80 bg-gray-800 border border-gray-600 rounded-lg shadow-xl z-50 max-h-80 overflow-y-auto">
<div className="p-2">
<div className="text-xs text-gray-400 uppercase tracking-wide px-3 py-2 border-b border-gray-700">
Select Trading Pair
</div>
{symbols.map(({ symbol, name, price, change, icon }) => (
<button
key={symbol}
onClick={() => {
setSelectedSymbol(symbol)
setIsSymbolDropdownOpen(false)
}}
className={`w-full flex items-center space-x-3 px-3 py-3 text-left rounded-lg transition-all ${
selectedSymbol === symbol
? 'bg-blue-600/20 border border-blue-500/30'
: 'hover:bg-gray-700'
}`}
>
<span className="text-lg">{icon}</span>
<div className="flex-1">
<div className="flex items-center justify-between">
<div>
<div className="font-medium text-white">{symbol}/USDC</div>
<div className="text-xs text-gray-400">{name}</div>
</div>
<div className="text-right">
<div className="text-sm font-mono text-white">
${price < 1 ? price.toFixed(8) : price.toLocaleString()}
</div>
<div className={`text-xs ${change >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{change >= 0 ? '+' : ''}{change}%
</div>
</div>
</div>
</div>
{selectedSymbol === symbol && (
<div className="w-2 h-2 bg-blue-500 rounded-full"></div>
)}
</button>
))}
</div>
</div>
)}
</div>
</div>
{/* Market Status */}
<div className="flex items-center space-x-4 text-sm">
<div className="flex items-center space-x-2">
<div className={`w-2 h-2 rounded-full animate-pulse ${isLoading ? 'bg-yellow-400' : 'bg-green-400'}`}></div>
<span className="text-gray-300">{isLoading ? 'Processing...' : 'Live Trading'}</span>
</div>
{tradeStatus && (
<div className="text-blue-400 text-xs">
{tradeStatus}
</div>
)}
<div className="text-gray-400">
{new Date().toLocaleTimeString()}
</div>
</div>
</div>
</div>
{/* Main Trading Interface */}
<div className="flex-1 flex">
{/* Chart Area (70% width) */}
<div className="flex-1 p-4">
<div className="bg-gray-800 rounded-lg p-4 h-full flex items-center justify-center">
<div className="text-gray-400 text-center">
<div className="text-lg mb-2">📊</div>
<div>Chart Component Loading...</div>
<div className="text-sm">Symbol: {selectedSymbol}</div>
<div className="text-sm">Positions: {positions.length}</div>
</div>
</div>
</div>
{/* Trading Panel (30% width) */}
<div className="w-96 border-l border-gray-700 p-4 space-y-4">
<div className="bg-gray-800 rounded-lg p-4">
<h3 className="text-white text-lg font-semibold mb-4">{selectedSymbol}/USDC</h3>
<div className="text-2xl font-bold text-white mb-2">
${getCurrentSymbolData().price.toLocaleString()}
</div>
<div className={`text-sm ${getCurrentSymbolData().change >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{getCurrentSymbolData().change >= 0 ? '+' : ''}{getCurrentSymbolData().change}%
</div>
{/* Leverage Selector */}
<div className="mt-6">
<div className="flex items-center justify-between mb-3">
<label className="block text-gray-400 text-sm">Leverage</label>
<div className="text-sm text-yellow-400 font-medium">
{leverage}x
</div>
</div>
{/* Leverage Slider */}
<div className="relative">
<input
type="range"
min="1"
max="100"
value={leverage}
onChange={(e) => setLeverage(parseInt(e.target.value))}
className="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer slider"
style={{
background: `linear-gradient(to right, #eab308 0%, #eab308 ${((leverage - 1) / 99) * 100}%, #374151 ${((leverage - 1) / 99) * 100}%, #374151 100%)`
}}
/>
{/* Leverage Marks */}
<div className="flex justify-between mt-2 px-1">
{[1, 2, 5, 10, 20, 50, 100].map((mark) => (
<button
key={mark}
onClick={() => setLeverage(mark)}
className={`text-xs transition-all ${
leverage === mark
? 'text-yellow-400 font-medium'
: 'text-gray-500 hover:text-gray-300'
}`}
>
{mark}x
</button>
))}
</div>
</div>
{/* Leverage Warning */}
{leverage > 10 && (
<div className="mt-2 flex items-center space-x-2 text-xs text-orange-400">
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clipRule="evenodd" />
</svg>
<span>High leverage increases liquidation risk</span>
</div>
)}
</div>
{/* Quick Trade Buttons */}
<div className="mt-6 space-y-3">
<button
onClick={() => handleTrade('BUY')}
disabled={!payingAmount || isLoading}
className="w-full bg-green-600 hover:bg-green-700 disabled:bg-gray-600 disabled:cursor-not-allowed text-white py-3 px-4 rounded-lg font-medium transition-all"
>
{isLoading ? 'Processing...' : `Long/Buy ${leverage > 1 ? `(${leverage}x)` : ''}`}
</button>
<button
onClick={() => handleTrade('SELL')}
disabled={!payingAmount || isLoading}
className="w-full bg-red-600 hover:bg-red-700 disabled:bg-gray-600 disabled:cursor-not-allowed text-white py-3 px-4 rounded-lg font-medium transition-all"
>
{isLoading ? 'Processing...' : `Short/Sell ${leverage > 1 ? `(${leverage}x)` : ''}`}
</button>
</div>
{/* Trade Form */}
<div className="mt-6 space-y-4">
{/* You're Paying Section */}
<div>
<label className="block text-gray-400 text-sm mb-2">You're paying</label>
<div className="bg-gray-700 rounded-lg p-3 border border-gray-600">
<div className="flex items-center justify-between">
<input
type="number"
value={payingAmount}
onChange={(e) => setPayingAmount(e.target.value)}
placeholder="0.00"
step="0.01"
min="0"
className="bg-transparent text-white text-lg font-medium focus:outline-none flex-1 mr-3"
/>
{/* Paying Token Selector */}
<div className="relative" ref={payingTokenDropdownRef}>
<button
onClick={() => setIsPayingTokenDropdownOpen(!isPayingTokenDropdownOpen)}
className="flex items-center space-x-2 bg-gray-600 hover:bg-gray-500 rounded-lg px-3 py-2 transition-all"
>
<span className="text-sm">{getCurrentPayingTokenData().icon}</span>
<span className="text-white font-medium">{payingToken}</span>
<svg className={`w-4 h-4 text-gray-300 transition-transform ${isPayingTokenDropdownOpen ? 'rotate-180' : ''}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
{/* Paying Token Dropdown */}
{isPayingTokenDropdownOpen && (
<div className="absolute top-full right-0 mt-2 w-64 bg-gray-800 border border-gray-600 rounded-lg shadow-xl z-50">
<div className="p-2">
{payingTokens.map(({ symbol, name, balance, icon }) => (
<button
key={symbol}
onClick={() => {
setPayingToken(symbol)
setIsPayingTokenDropdownOpen(false)
}} className={`w-full flex items-center justify-between px-3 py-2 text-left rounded-lg transition-all ${
payingToken === symbol
? 'bg-blue-600/20 border border-blue-500/30'
: 'hover:bg-gray-700'
}`}
>
<div className="flex items-center space-x-2">
<span className="text-sm">{icon}</span>
<div>
<div className="text-white font-medium">{symbol}</div>
<div className="text-xs text-gray-400">{name}</div>
</div>
</div>
<div className="text-xs text-gray-400">
{balance.toFixed(balance < 1 ? 6 : 2)}
</div>
</button>
))}
</div>
</div>
)}
</div>
</div>
{/* Balance and MAX button */}
<div className="flex items-center justify-between mt-2 text-xs text-gray-400">
<span>Balance: {getCurrentPayingTokenData().balance.toFixed(getCurrentPayingTokenData().balance < 1 ? 6 : 2)} {payingToken}</span>
<button
onClick={() => setPayingAmount(getCurrentPayingTokenData().balance.toString())}
disabled={getCurrentPayingTokenData().balance === 0}
className="text-blue-400 hover:text-blue-300 disabled:text-gray-500 disabled:cursor-not-allowed"
>
MAX
</button>
</div>
{getCurrentPayingTokenData().balance === 0 && (
<div className="text-xs text-red-400 mt-1">
⚠️ No {payingToken} balance available
</div>
)}
{payingAmount && parseFloat(payingAmount) > getCurrentPayingTokenData().balance && (
<div className="text-xs text-red-400 mt-1">
⚠️ Insufficient balance ({getCurrentPayingTokenData().balance.toFixed(getCurrentPayingTokenData().balance < 1 ? 6 : 2)} {payingToken} available)
</div>
)}
</div>
{leverage > 1 && payingAmount && (
<div className="text-xs text-yellow-400 mt-1">
Position value: ${(parseFloat(payingAmount) * leverage).toLocaleString()} ({leverage}x leverage)
</div>
)}
</div>
{/* You're Receiving Section */}
<div>
<label className="block text-gray-400 text-sm mb-2">You're receiving</label>
<div className="bg-gray-700 rounded-lg p-3 border border-gray-600">
<div className="flex items-center justify-between">
<div className="text-lg font-medium text-white">
{receivingAmount || '0.00'}
</div>
<div className="flex items-center space-x-2 bg-gray-600 rounded-lg px-3 py-2">
<span className="text-sm">{getCurrentSymbolData().icon}</span>
<span className="text-white font-medium">{selectedSymbol}</span>
</div>
</div>
<div className="text-xs text-gray-400 mt-2">
Rate: 1 {payingToken} = {payingToken === 'USDC' ? (1 / getCurrentSymbolData().price).toFixed(8) : '1.00'} {selectedSymbol}
</div>
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-gray-400 text-sm mb-2">Stop Loss</label>
<input
type="number"
value={stopLoss}
onChange={(e) => setStopLoss(e.target.value)}
placeholder="Optional"
step="0.01"
min="0"
className="w-full bg-gray-700 text-white rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
<div>
<label className="block text-gray-400 text-sm mb-2">Take Profit</label>
<input
type="number"
value={takeProfit}
onChange={(e) => setTakeProfit(e.target.value)}
placeholder="Optional"
step="0.01"
min="0"
className="w-full bg-gray-700 text-white rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
</div>
{/* Risk Information */}
{payingAmount && leverage > 1 && (
<div className="bg-yellow-900/20 border border-yellow-600/20 rounded-lg p-3">
<div className="text-yellow-400 text-xs font-medium mb-1"> Leveraged Position Risk</div>
<div className="text-xs text-gray-300">
Position value: ${(parseFloat(payingAmount) * leverage).toLocaleString()} ({leverage}x leverage)
</div>
<div className="text-xs text-gray-300">
Liquidation risk: High with {leverage}x leverage
</div>
</div>
)}
</div>
</div>
</div>
</div>
{/* Bottom Panel - Positions */}
<div className="border-t border-gray-700 bg-gray-800">
<div className="p-4">
<div className="flex items-center justify-between mb-4">
<div className="flex space-x-6">
<button className="text-white font-medium border-b-2 border-blue-500 pb-2">
Positions ({positions.length})
</button>
<button className="text-gray-400 hover:text-white pb-2">
Orders (0)
</button>
<button className="text-gray-400 hover:text-white pb-2">
History
</button>
</div>
{positions.length > 0 && (
<div className="text-sm text-gray-400">
Total P&L: <span className={`${
positions.reduce((sum, pos) => sum + pos.unrealizedPnl, 0) >= 0
? 'text-green-400' : 'text-red-400'
}`}>
{positions.reduce((sum, pos) => sum + pos.unrealizedPnl, 0) >= 0 ? '+' : ''}
${positions.reduce((sum, pos) => sum + pos.unrealizedPnl, 0).toFixed(2)}
</span>
</div>
)}
</div>
{/* Positions Table */}
<div className="max-h-48 overflow-y-auto">
{isLoading && positions.length === 0 && (
<div className="text-center py-8 text-gray-400">
<div className="inline-block animate-spin rounded-full h-6 w-6 border-b-2 border-blue-400 mr-2"></div>
Loading positions...
</div>
)}
{!isLoading && positions.length === 0 ? (
<div className="text-center py-8 text-gray-400">
No open positions
</div>
) : (
<div className="space-y-2">
{positions.map((position: Position) => (
<div
key={position.id}
className="bg-gray-900 rounded-lg p-4 flex items-center justify-between"
>
<div className="flex items-center space-x-4">
<div className={`w-3 h-3 rounded-full ${
position.side === 'BUY' ? 'bg-green-400' : 'bg-red-400'
}`}></div>
<div>
<div className="text-white font-medium">
{position.symbol} {position.side}
{position.leverage > 1 && (
<span className="ml-2 text-yellow-400 text-sm">
{position.leverage}x
</span>
)}
</div>
<div className="text-sm text-gray-400">
Size: {position.amount} Entry: ${position.entryPrice?.toFixed(2)}
</div>
<div className="text-xs text-red-400">
SL: ${position.stopLoss.toFixed(2)}
</div>
<div className="text-xs text-green-400">
TP: ${position.takeProfit.toFixed(2)}
</div>
</div>
</div>
<div className="text-right">
<div className="text-white font-medium">
${(position.amount * position.currentPrice).toFixed(2)}
</div>
<div className={`text-sm ${
position.unrealizedPnl >= 0 ? 'text-green-400' : 'text-red-400'
}`}>
{position.unrealizedPnl >= 0 ? '+' : ''}${(position.unrealizedPnl || 0).toFixed(2)}
</div>
{position.leverage > 1 && (
<div className="text-xs text-yellow-400">
{position.leverage}x Leveraged
</div>
)}
</div>
<div className="flex space-x-2">
<button
onClick={() => handleClosePosition(position.id)}
disabled={isLoading}
className="px-3 py-1 bg-red-600 text-white rounded text-sm hover:bg-red-700 disabled:bg-gray-600 disabled:cursor-not-allowed transition-all"
>
{isLoading ? 'Processing...' : 'Close'}
</button>
</div>
</div>
))}
</div>
)}
</div>
</div>
</div>
</div>
</>
)
}