- Remove incorrect open positions for spot swaps (instant settlements) - Add DELETE API route for position removal (/api/trading/positions/[positionId]) - Update existing SOL/USDC trade to clearly mark as SPOT_SWAP - Enhance TradesHistoryPanel with visual trade type indicators: * SPOT_SWAP: Purple badge with ⚡ icon * MARKET: Blue badge with 📈 icon * LIMIT: Orange badge with 🎯 icon * STOP: Red badge with 🛑 icon - Add trade history update functionality for modifying existing trades - Fix container communication URLs in execute-dex route Result: Spot trades no longer create open positions, trade history clearly shows trade types
199 lines
6.5 KiB
JavaScript
199 lines
6.5 KiB
JavaScript
'use client'
|
||
import React, { useState, useEffect } from 'react'
|
||
|
||
export default function TradesHistoryPanel() {
|
||
const [trades, setTrades] = useState([])
|
||
const [loading, setLoading] = useState(true)
|
||
|
||
useEffect(() => {
|
||
fetchTrades()
|
||
// Refresh trades every 15 seconds
|
||
const interval = setInterval(fetchTrades, 15000)
|
||
return () => clearInterval(interval)
|
||
}, [])
|
||
|
||
const fetchTrades = async () => {
|
||
try {
|
||
const response = await fetch('/api/trading/history')
|
||
const data = await response.json()
|
||
|
||
if (data.success) {
|
||
setTrades(data.trades || [])
|
||
}
|
||
} catch (error) {
|
||
console.error('Failed to fetch trades:', error)
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
|
||
const clearHistory = async () => {
|
||
try {
|
||
const response = await fetch('/api/trading/history', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ action: 'clear' })
|
||
})
|
||
|
||
if (response.ok) {
|
||
setTrades([])
|
||
}
|
||
} catch (error) {
|
||
console.error('Failed to clear history:', error)
|
||
}
|
||
}
|
||
|
||
const getTradeTypeInfo = (trade) => {
|
||
switch (trade.type) {
|
||
case 'SPOT_SWAP':
|
||
return { label: 'SPOT SWAP', color: 'bg-purple-600', icon: '⚡' }
|
||
case 'market':
|
||
return { label: 'MARKET', color: 'bg-blue-600', icon: '📈' }
|
||
case 'limit':
|
||
return { label: 'LIMIT', color: 'bg-orange-600', icon: '🎯' }
|
||
case 'stop':
|
||
return { label: 'STOP', color: 'bg-red-600', icon: '🛑' }
|
||
default:
|
||
return { label: trade.type?.toUpperCase() || 'TRADE', color: 'bg-gray-600', icon: '💱' }
|
||
}
|
||
}
|
||
|
||
const getSideColor = (side) => {
|
||
return side === 'BUY' ? 'text-green-400' : 'text-red-400'
|
||
}
|
||
|
||
const getPnlColor = (pnl) => {
|
||
if (pnl === null || pnl === 0) return 'text-gray-400'
|
||
return pnl > 0 ? 'text-green-400' : 'text-red-400'
|
||
}
|
||
|
||
const formatCurrency = (amount) => {
|
||
return new Intl.NumberFormat('en-US', {
|
||
style: 'currency',
|
||
currency: 'USD',
|
||
minimumFractionDigits: 2,
|
||
maximumFractionDigits: 4
|
||
}).format(amount)
|
||
}
|
||
|
||
const formatTime = (timestamp) => {
|
||
return new Date(timestamp).toLocaleTimeString('en-US', {
|
||
hour: '2-digit',
|
||
minute: '2-digit',
|
||
second: '2-digit'
|
||
})
|
||
}
|
||
|
||
if (loading) {
|
||
return (
|
||
<div className="card card-gradient p-6">
|
||
<h2 className="text-xl font-bold text-white mb-4">Recent Trades</h2>
|
||
<div className="text-gray-400">Loading trades...</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<div className="card card-gradient p-6">
|
||
<div className="flex items-center justify-between mb-4">
|
||
<h2 className="text-xl font-bold text-white">Recent Trades</h2>
|
||
<div className="flex gap-2">
|
||
<button
|
||
onClick={fetchTrades}
|
||
className="text-blue-400 hover:text-blue-300 text-sm"
|
||
>
|
||
🔄 Refresh
|
||
</button>
|
||
{trades.length > 0 && (
|
||
<button
|
||
onClick={clearHistory}
|
||
className="text-red-400 hover:text-red-300 text-sm"
|
||
>
|
||
🗑️ Clear
|
||
</button>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{trades.length === 0 ? (
|
||
<div className="text-center py-8">
|
||
<div className="text-gray-400 mb-2">📈 No trades yet</div>
|
||
<div className="text-sm text-gray-500">Execute a trade to see history here</div>
|
||
</div>
|
||
) : (
|
||
<div className="space-y-3 max-h-96 overflow-y-auto">
|
||
{trades.map((trade) => (
|
||
<div
|
||
key={trade.id}
|
||
className="bg-gray-800 rounded-lg p-4 border border-gray-600"
|
||
>
|
||
<div className="flex items-center justify-between mb-2">
|
||
<div className="flex items-center space-x-3">
|
||
<span className="text-white font-medium">{trade.symbol}</span>
|
||
<span className={`px-2 py-1 rounded text-xs font-medium ${
|
||
trade.side === 'BUY'
|
||
? 'bg-green-600 text-white'
|
||
: 'bg-red-600 text-white'
|
||
}`}>
|
||
{trade.side}
|
||
</span>
|
||
{(() => {
|
||
const typeInfo = getTradeTypeInfo(trade)
|
||
return (
|
||
<span className={`px-2 py-1 rounded text-xs font-medium text-white ${typeInfo.color}`}>
|
||
{typeInfo.icon} {typeInfo.label}
|
||
</span>
|
||
)
|
||
})()}
|
||
<span className="px-2 py-1 rounded text-xs bg-gray-700 text-gray-300">
|
||
{trade.dex}
|
||
</span>
|
||
</div>
|
||
<div className="text-xs text-gray-400">
|
||
{formatTime(trade.timestamp)}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 text-sm">
|
||
<div>
|
||
<div className="text-gray-400">Amount</div>
|
||
<div className="text-white font-medium">{trade.amount}</div>
|
||
</div>
|
||
<div>
|
||
<div className="text-gray-400">Price</div>
|
||
<div className="text-white font-medium">{formatCurrency(trade.price)}</div>
|
||
</div>
|
||
<div>
|
||
<div className="text-gray-400">Status</div>
|
||
<div className="text-green-400 font-medium">{trade.status}</div>
|
||
</div>
|
||
{trade.pnl !== null && trade.pnl !== undefined && (
|
||
<div>
|
||
<div className="text-gray-400">P&L</div>
|
||
<div className={`font-medium ${getPnlColor(trade.pnl)}`}>
|
||
{trade.pnl >= 0 ? '+' : ''}{formatCurrency(trade.pnl)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Additional Info */}
|
||
<div className="mt-3 flex justify-between items-center text-xs">
|
||
<div className="text-gray-500">
|
||
TX: {trade.txId?.substring(0, 12)}...
|
||
{trade.notes && (
|
||
<span className="ml-2 text-yellow-400">• {trade.notes}</span>
|
||
)}
|
||
</div>
|
||
<div className="text-gray-500">
|
||
Fee: ${trade.fee?.toFixed(4) || '0.0000'}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
)
|
||
}
|