Files
trading_bot_v3/components/TradesHistoryPanel.js
mindesbunister 77eb727f8d Fix spot trade logic: remove incorrect positions and enhance trade history display
- 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
2025-07-16 11:36:58 +02:00

199 lines
6.5 KiB
JavaScript
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, 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>
)
}