Fix trading history: implement proper P&L tracking for closed positions

- Fixed trading history not showing closed positions with positive P&L
- Implemented multi-source trading history fetching (SDK, Data API, DLOB, local DB)
- Added proper P&L calculation using unrealized PnL from Drift positions
- Enhanced TradingHistory component with error handling and sync functionality
- Added manual sync button and better status messages
- Created /api/drift/sync-trades endpoint for manual trade synchronization
- Fixed database integration to properly store and retrieve trades with P&L
- Added comprehensive fallback mechanisms for data fetching
- Improved error messages and user feedback
- Added TRADING_HISTORY_IMPROVEMENTS.md documentation

This addresses the issue where recently closed positions with positive P&L
were not appearing in the trading history section.
This commit is contained in:
mindesbunister
2025-07-13 10:18:56 +02:00
parent e1dc58e35d
commit bf9022b699
6 changed files with 780 additions and 329 deletions

View File

@@ -16,6 +16,9 @@ export default function TradingHistory() {
const [trades, setTrades] = useState<Trade[]>([])
const [loading, setLoading] = useState(true)
const [isClient, setIsClient] = useState(false)
const [error, setError] = useState<string | null>(null)
const [message, setMessage] = useState<string>('')
const [syncing, setSyncing] = useState(false)
useEffect(() => {
setIsClient(true)
@@ -26,37 +29,82 @@ export default function TradingHistory() {
return new Date(dateString).toLocaleTimeString()
}
useEffect(() => {
async function fetchTrades() {
try {
// Try Drift trading history first
const driftRes = await fetch('/api/drift/trading-history')
if (driftRes.ok) {
const data = await driftRes.json()
if (data.success && data.trades) {
setTrades(data.trades)
} else {
// No trades available
setTrades([])
}
const formatDate = (dateString: string) => {
if (!isClient) return '--/--/--'
return new Date(dateString).toLocaleDateString()
}
const handleSync = async () => {
try {
setSyncing(true)
const response = await fetch('/api/drift/sync-trades', {
method: 'POST'
})
if (response.ok) {
const data = await response.json()
setMessage(data.message || 'Sync completed')
// Refresh the trading history after sync
await fetchTrades()
} else {
setError('Failed to sync trades')
}
} catch (error) {
console.error('Sync error:', error)
setError('Error during sync')
} finally {
setSyncing(false)
}
}
const fetchTrades = async () => {
try {
setError(null)
setMessage('')
// Try Drift trading history first
const driftRes = await fetch('/api/drift/trading-history')
if (driftRes.ok) {
const data = await driftRes.json()
if (data.success && data.trades) {
setTrades(data.trades)
setMessage(data.message || `Found ${data.trades.length} trade(s)`)
} else {
// API failed - try fallback to local database
const res = await fetch('/api/trading-history')
if (res.ok) {
const data = await res.json()
setTrades(data || [])
} else {
// Both APIs failed - show empty state
setTrades([])
setTrades([])
setMessage(data.message || 'No trades available')
if (data.error) {
setError(data.error)
}
}
} catch (error) {
console.error('Failed to fetch trades:', error)
setTrades([])
} else {
// API failed - try fallback to local database
const res = await fetch('/api/trading-history')
if (res.ok) {
const data = await res.json()
setTrades(data || [])
setMessage(data?.length > 0 ? `Found ${data.length} local trade(s)` : 'No trading history available')
} else {
// Both APIs failed - show empty state
setTrades([])
setError('Failed to load trading history from both sources')
setMessage('Unable to fetch trading history. Please try again.')
}
}
} catch (error) {
console.error('Failed to fetch trades:', error)
setTrades([])
setError('Network error while fetching trades')
setMessage('Check your connection and try again.')
}
}
useEffect(() => {
async function loadTrades() {
setLoading(true)
await fetchTrades()
setLoading(false)
}
fetchTrades()
loadTrades()
}, [])
const getSideColor = (side: string) => {
@@ -86,7 +134,24 @@ export default function TradingHistory() {
</span>
Trading History
</h2>
<span className="text-xs text-gray-400">Latest {trades.length} trades</span>
<div className="flex items-center gap-3">
<span className="text-xs text-gray-400">{message || `Latest ${trades.length} trades`}</span>
<button
onClick={handleSync}
disabled={syncing || loading}
className="text-xs text-blue-400 hover:text-blue-300 transition-colors disabled:text-gray-500"
title="Sync with Drift to check for new trades"
>
{syncing ? '🔄' : '🔄 Sync'}
</button>
<button
onClick={() => window.location.reload()}
className="text-xs text-gray-400 hover:text-gray-300 transition-colors"
title="Refresh page"
>
</button>
</div>
</div>
{loading ? (
@@ -94,13 +159,31 @@ export default function TradingHistory() {
<div className="spinner"></div>
<span className="ml-2 text-gray-400">Loading trades...</span>
</div>
) : error ? (
<div className="text-center py-8">
<div className="w-16 h-16 bg-red-500/20 rounded-full flex items-center justify-center mx-auto mb-4">
<span className="text-red-400 text-2xl"></span>
</div>
<p className="text-red-400 font-medium">Error Loading Trades</p>
<p className="text-gray-500 text-sm mt-2">{error}</p>
<p className="text-gray-500 text-sm">{message}</p>
<button
onClick={() => window.location.reload()}
className="mt-4 px-4 py-2 bg-red-500/20 text-red-400 rounded-lg hover:bg-red-500/30 transition-colors"
>
Retry
</button>
</div>
) : trades.length === 0 ? (
<div className="text-center py-8">
<div className="w-16 h-16 bg-gray-700/50 rounded-full flex items-center justify-center mx-auto mb-4">
<span className="text-gray-400 text-2xl">📈</span>
</div>
<p className="text-gray-400 font-medium">No trading history</p>
<p className="text-gray-500 text-sm mt-2">Your completed trades will appear here</p>
<p className="text-gray-400 font-medium">No Trading History</p>
<p className="text-gray-500 text-sm mt-2">{message || 'Your completed trades will appear here'}</p>
<p className="text-gray-500 text-xs mt-2">
💡 If you recently closed positions with positive P&L, they may take a few minutes to appear
</p>
</div>
) : (
<div className="overflow-hidden">
@@ -156,7 +239,10 @@ export default function TradingHistory() {
</span>
</td>
<td className="py-4 px-4 text-right text-xs text-gray-400">
{formatTime(trade.executedAt)}
<div className="text-right">
<div>{formatTime(trade.executedAt)}</div>
<div className="text-gray-500">{formatDate(trade.executedAt)}</div>
</div>
</td>
</tr>
))}