Files
trading_bot_v3/components/TradesHistoryPanel.js
mindesbunister b0b63d5db0 Fix critical balance validation and add comprehensive trading features
- Fixed CoinGecko API rate limiting with fallback SOL price (68.11)
- Corrected internal API calls to use proper Docker container ports
- Fixed balance validation to prevent trades exceeding wallet funds
- Blocked 0.5 SOL trades with only 0.073 SOL available (~2.24)

- Added persistent storage for positions, trades, and pending orders
- Implemented limit order system with auto-fill monitoring
- Created pending orders panel and management API
- Added trades history tracking and display panel
- Enhanced position tracking with P&L calculations
- Added wallet balance validation API endpoint

- Positions stored in data/positions.json
- Trade history stored in data/trades.json
- Pending orders with auto-fill logic
- Real-time balance validation before trades

- All trades now validate against actual wallet balance
- Insufficient balance trades are properly blocked
- Added comprehensive error handling and logging
- Fixed Docker networking for internal API calls

- SPOT and leveraged trading modes
- Limit orders with price monitoring
- Stop loss and take profit support
- DEX integration with Jupiter
- Real-time position updates and P&L tracking

 Tested and verified all balance validation works correctly
2025-07-14 17:19:58 +02:00

176 lines
5.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 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>
<span className="px-2 py-1 rounded text-xs bg-blue-600 text-white">
{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>
)
}