feat: enhance position display with proper formatting and value calculation
- Fix price formatting to show exactly 2 decimal places - Display position size in SOL units (16.40 SOL) instead of incorrect dollar amount - Add new Value field showing total dollar value of position (Size × Current Price) - Improve Open Positions section with accurate financial data display - Maintain enhanced styling and responsive layout - All prices now formatted professionally with consistent decimal places
This commit is contained in:
@@ -684,10 +684,10 @@ export default function AutomationPageV2() {
|
||||
|
||||
{/* Enhanced Sidebar */}
|
||||
<div className="space-y-6">
|
||||
{/* Bot Status Card */}
|
||||
{/* Unified Trading Dashboard Card */}
|
||||
<div className="bg-gradient-to-br from-gray-900/90 via-slate-800/80 to-gray-900/90 backdrop-blur-xl p-6 rounded-2xl border border-gray-600/30 shadow-2xl">
|
||||
<div className="flex items-center space-x-3 mb-6">
|
||||
<div className={`w-12 h-12 rounded-xl flex items-center justify-center ${
|
||||
<div className={`w-14 h-14 rounded-xl flex items-center justify-center ${
|
||||
status?.isActive
|
||||
? 'bg-gradient-to-br from-green-500 to-emerald-600 shadow-lg shadow-green-500/25'
|
||||
: 'bg-gradient-to-br from-gray-600 to-gray-700'
|
||||
@@ -695,70 +695,200 @@ export default function AutomationPageV2() {
|
||||
<span className="text-2xl">{status?.isActive ? '🟢' : '⚪'}</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-white">Bot Status</h3>
|
||||
<p className="text-gray-400">Real-time monitoring</p>
|
||||
<h3 className="text-xl font-bold text-white">Trading Dashboard</h3>
|
||||
<p className="text-gray-400">Status • Positions • Risk Monitor</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between items-center p-3 bg-black/20 rounded-lg">
|
||||
<span className="text-gray-400 font-medium">Status:</span>
|
||||
<span className={`px-3 py-1 rounded-lg text-sm font-bold ${
|
||||
status?.isActive
|
||||
? 'bg-green-500/20 text-green-300 border border-green-500/30'
|
||||
: 'bg-gray-600/20 text-gray-300 border border-gray-600/30'
|
||||
}`}>
|
||||
{status?.isActive ? 'RUNNING' : 'STOPPED'}
|
||||
</span>
|
||||
{/* Bot Status Section */}
|
||||
<div className="mb-6">
|
||||
<h4 className="text-lg font-semibold text-blue-400 mb-3 flex items-center">
|
||||
<span className="mr-2">🤖</span>Bot Status
|
||||
</h4>
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between items-center p-3 bg-black/20 rounded-lg">
|
||||
<span className="text-gray-400 font-medium">Status:</span>
|
||||
<span className={`px-3 py-1 rounded-lg text-sm font-bold ${
|
||||
status?.isActive
|
||||
? 'bg-green-500/20 text-green-300 border border-green-500/30'
|
||||
: 'bg-gray-600/20 text-gray-300 border border-gray-600/30'
|
||||
}`}>
|
||||
{status?.isActive ? 'RUNNING' : 'STOPPED'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{status?.isActive && (
|
||||
<>
|
||||
<div className="flex justify-between items-center p-3 bg-black/20 rounded-lg">
|
||||
<span className="text-gray-400 font-medium">Symbol:</span>
|
||||
<span className="text-white font-bold">{status.symbol}</span>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between items-center p-3 bg-black/20 rounded-lg">
|
||||
<span className="text-gray-400 font-medium">Mode:</span>
|
||||
<span className={`px-3 py-1 rounded-lg text-sm font-bold border ${
|
||||
status.mode === 'LIVE'
|
||||
? 'bg-red-500/20 text-red-300 border-red-500/30'
|
||||
: 'bg-blue-500/20 text-blue-300 border-blue-500/30'
|
||||
}`}>
|
||||
{status.mode}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-black/20 rounded-lg">
|
||||
<div className="text-gray-400 font-medium mb-2">Timeframes:</div>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{status.timeframes?.map((tf, index) => (
|
||||
<span key={index} className="text-xs bg-cyan-500/20 text-cyan-300 px-2 py-1 rounded border border-cyan-500/30">
|
||||
{timeframes.find(t => t.value === tf)?.label || tf}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Rate Limit Warning */}
|
||||
{status?.rateLimitHit && (
|
||||
<div className="p-3 bg-gradient-to-br from-red-900/50 to-red-800/30 border border-red-600/50 rounded-lg">
|
||||
<div className="flex items-center space-x-2 mb-1">
|
||||
<span className="text-red-400 font-bold">⚠️</span>
|
||||
<span className="text-red-300 font-bold text-sm">Rate Limit Reached</span>
|
||||
</div>
|
||||
{status.rateLimitMessage && (
|
||||
<p className="text-red-200 text-xs mb-1">{status.rateLimitMessage}</p>
|
||||
)}
|
||||
<p className="text-red-100 text-xs">
|
||||
Automation stopped. Recharge OpenAI account to continue.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{status?.isActive && (
|
||||
<>
|
||||
</div>
|
||||
|
||||
{/* Position Monitor Section */}
|
||||
{monitorData && (
|
||||
<div className="mb-6">
|
||||
<h4 className="text-lg font-semibold text-purple-400 mb-3 flex items-center">
|
||||
<span className="mr-2">📊</span>Position Monitor
|
||||
</h4>
|
||||
<div className="space-y-3">
|
||||
<div className="flex justify-between items-center p-3 bg-black/20 rounded-lg">
|
||||
<span className="text-gray-400 font-medium">Symbol:</span>
|
||||
<span className="text-white font-bold text-lg">{status.symbol}</span>
|
||||
<span className="text-gray-400 font-medium">Has Position:</span>
|
||||
<span className={`px-3 py-1 rounded-lg text-sm font-bold border ${
|
||||
monitorData.hasPosition
|
||||
? 'bg-green-500/20 text-green-300 border-green-500/30'
|
||||
: 'bg-gray-600/20 text-gray-300 border-gray-600/30'
|
||||
}`}>
|
||||
{monitorData.hasPosition ? '✅ YES' : '❌ NO'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between items-center p-3 bg-black/20 rounded-lg">
|
||||
<span className="text-gray-400 font-medium">Mode:</span>
|
||||
<span className="text-gray-400 font-medium">Risk Level:</span>
|
||||
<span className={`px-3 py-1 rounded-lg text-sm font-bold border ${
|
||||
status.mode === 'LIVE'
|
||||
? 'bg-red-500/20 text-red-300 border-red-500/30'
|
||||
: 'bg-blue-500/20 text-blue-300 border-blue-500/30'
|
||||
monitorData.riskLevel === 'HIGH' ? 'bg-red-500/20 text-red-300 border-red-500/30' :
|
||||
monitorData.riskLevel === 'MEDIUM' ? 'bg-yellow-500/20 text-yellow-300 border-yellow-500/30' :
|
||||
'bg-green-500/20 text-green-300 border-green-500/30'
|
||||
}`}>
|
||||
{status.mode}
|
||||
{monitorData.riskLevel}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-black/20 rounded-lg">
|
||||
<div className="text-gray-400 font-medium mb-2">Timeframes:</div>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{status.timeframes?.map((tf, index) => (
|
||||
<span key={index} className="text-xs bg-cyan-500/20 text-cyan-300 px-2 py-1 rounded border border-cyan-500/30">
|
||||
{timeframes.find(t => t.value === tf)?.label || tf}
|
||||
</span>
|
||||
))}
|
||||
<div className="text-gray-400 text-sm mb-1">Next Action:</div>
|
||||
<div className="text-white text-sm">{monitorData.nextAction}</div>
|
||||
</div>
|
||||
|
||||
{monitorData.orphanedOrderCleanup && (
|
||||
<div className={`p-3 rounded-lg border ${
|
||||
monitorData.orphanedOrderCleanup.success
|
||||
? 'bg-green-900/30 border-green-600/50'
|
||||
: 'bg-red-900/30 border-red-600/50'
|
||||
}`}>
|
||||
<div className="text-sm font-semibold mb-1">
|
||||
{monitorData.orphanedOrderCleanup.success ? '✅ Cleanup Success' : '❌ Cleanup Failed'}
|
||||
</div>
|
||||
<div className="text-xs text-gray-300">
|
||||
{monitorData.orphanedOrderCleanup.message}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Rate Limit Warning */}
|
||||
{status?.rateLimitHit && (
|
||||
<div className="p-4 bg-gradient-to-br from-red-900/50 to-red-800/30 border-2 border-red-600/50 rounded-xl">
|
||||
<div className="flex items-center space-x-2 mb-2">
|
||||
<span className="text-red-400 font-bold text-lg">⚠️</span>
|
||||
<span className="text-red-300 font-bold">Rate Limit Reached</span>
|
||||
</div>
|
||||
{status.rateLimitMessage && (
|
||||
<p className="text-red-200 text-sm mb-2">{status.rateLimitMessage}</p>
|
||||
)}
|
||||
<p className="text-red-100 text-xs">
|
||||
Automation stopped automatically. Please recharge your OpenAI account to continue.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Open Positions Section */}
|
||||
{positions.length > 0 && (
|
||||
<div className="mb-6">
|
||||
<h4 className="text-lg font-semibold text-yellow-400 mb-3 flex items-center">
|
||||
<span className="mr-2">📈</span>Open Positions
|
||||
<span className="ml-2 text-sm bg-yellow-500/20 text-yellow-300 px-2 py-1 rounded-lg border border-yellow-500/30">
|
||||
{positions.length}
|
||||
</span>
|
||||
</h4>
|
||||
<div className="space-y-3">
|
||||
{positions.map((position, index) => (
|
||||
<div key={index} className="p-4 bg-gradient-to-r from-gray-800/50 to-gray-700/30 rounded-xl border border-gray-600/30">
|
||||
<div className="flex justify-between items-center mb-3">
|
||||
<span className="text-white font-bold">{position.symbol}</span>
|
||||
<span className={`px-2 py-1 rounded-lg text-xs font-bold border ${
|
||||
position.side === 'LONG'
|
||||
? 'bg-green-500/20 text-green-300 border-green-500/30'
|
||||
: 'bg-red-500/20 text-red-300 border-red-500/30'
|
||||
}`}>
|
||||
{position.side}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-400">Size:</span>
|
||||
<span className="text-white font-semibold">
|
||||
{position.symbol?.includes('SOL') ?
|
||||
`${parseFloat(position.size).toFixed(2)} SOL` :
|
||||
`$${parseFloat(position.size).toFixed(2)}`
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-400">Value:</span>
|
||||
<span className="text-green-400 font-bold">
|
||||
${((parseFloat(position.size) || 0) * (parseFloat(position.markPrice) || parseFloat(position.entryPrice) || 0)).toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{position.entryPrice && (
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-400">Entry:</span>
|
||||
<span className="text-white font-mono">${parseFloat(position.entryPrice).toFixed(2)}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{position.markPrice && (
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-400">Mark:</span>
|
||||
<span className="text-white font-mono">${parseFloat(position.markPrice).toFixed(2)}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{position.pnl !== undefined && (
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-400">PnL:</span>
|
||||
<span className={`font-bold ${
|
||||
position.pnl >= 0 ? 'text-green-400' : 'text-red-400'
|
||||
}`}>
|
||||
${position.pnl >= 0 ? '+' : ''}${parseFloat(position.pnl).toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Account Balance Card */}
|
||||
@@ -787,140 +917,13 @@ export default function AutomationPageV2() {
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-black/20 rounded-lg">
|
||||
<div className="text-gray-400 text-xs mb-1">Open Positions</div>
|
||||
<div className="text-gray-400 text-xs mb-1">Total Positions</div>
|
||||
<div className="text-yellow-400 font-semibold">{balance.positions || 0}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Position Monitor Card */}
|
||||
{monitorData && (
|
||||
<div className="bg-gradient-to-br from-gray-900/90 via-slate-800/80 to-gray-900/90 backdrop-blur-xl p-6 rounded-2xl border border-gray-600/30 shadow-2xl">
|
||||
<div className="flex items-center space-x-3 mb-6">
|
||||
<div className={`w-12 h-12 rounded-xl flex items-center justify-center ${
|
||||
monitorData.hasPosition
|
||||
? 'bg-gradient-to-br from-blue-500 to-indigo-600 shadow-lg shadow-blue-500/25'
|
||||
: 'bg-gradient-to-br from-gray-600 to-gray-700'
|
||||
}`}>
|
||||
<span className="text-2xl">📊</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-white">Position Monitor</h3>
|
||||
<p className="text-gray-400">Risk assessment</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="flex justify-between items-center p-3 bg-black/20 rounded-lg">
|
||||
<span className="text-gray-400 font-medium">Has Position:</span>
|
||||
<span className={`px-3 py-1 rounded-lg text-sm font-bold border ${
|
||||
monitorData.hasPosition
|
||||
? 'bg-green-500/20 text-green-300 border-green-500/30'
|
||||
: 'bg-gray-600/20 text-gray-300 border-gray-600/30'
|
||||
}`}>
|
||||
{monitorData.hasPosition ? '✅ YES' : '❌ NO'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between items-center p-3 bg-black/20 rounded-lg">
|
||||
<span className="text-gray-400 font-medium">Risk Level:</span>
|
||||
<span className={`px-3 py-1 rounded-lg text-sm font-bold border ${
|
||||
monitorData.riskLevel === 'HIGH' ? 'bg-red-500/20 text-red-300 border-red-500/30' :
|
||||
monitorData.riskLevel === 'MEDIUM' ? 'bg-yellow-500/20 text-yellow-300 border-yellow-500/30' :
|
||||
'bg-green-500/20 text-green-300 border-green-500/30'
|
||||
}`}>
|
||||
{monitorData.riskLevel}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="p-3 bg-black/20 rounded-lg">
|
||||
<div className="text-gray-400 text-sm mb-1">Next Action:</div>
|
||||
<div className="text-white text-sm">{monitorData.nextAction}</div>
|
||||
</div>
|
||||
|
||||
{monitorData.orphanedOrderCleanup && (
|
||||
<div className={`p-3 rounded-lg border ${
|
||||
monitorData.orphanedOrderCleanup.success
|
||||
? 'bg-green-900/30 border-green-600/50'
|
||||
: 'bg-red-900/30 border-red-600/50'
|
||||
}`}>
|
||||
<div className="text-sm font-semibold mb-1">
|
||||
{monitorData.orphanedOrderCleanup.success ? '✅ Cleanup Success' : '❌ Cleanup Failed'}
|
||||
</div>
|
||||
<div className="text-xs text-gray-300">
|
||||
{monitorData.orphanedOrderCleanup.message}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Open Positions Card */}
|
||||
{positions.length > 0 && (
|
||||
<div className="bg-gradient-to-br from-gray-900/90 via-slate-800/80 to-gray-900/90 backdrop-blur-xl p-6 rounded-2xl border border-gray-600/30 shadow-2xl">
|
||||
<div className="flex items-center space-x-3 mb-6">
|
||||
<div className="w-12 h-12 bg-gradient-to-br from-yellow-500 to-orange-600 rounded-xl flex items-center justify-center shadow-lg shadow-yellow-500/25">
|
||||
<span className="text-2xl">📈</span>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-white">Open Positions</h3>
|
||||
<p className="text-gray-400">{positions.length} active trade{positions.length !== 1 ? 's' : ''}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{positions.map((position, index) => (
|
||||
<div key={index} className="p-4 bg-gradient-to-r from-gray-800/50 to-gray-700/30 rounded-xl border border-gray-600/30">
|
||||
<div className="flex justify-between items-center mb-3">
|
||||
<span className="text-white font-bold text-lg">{position.symbol}</span>
|
||||
<span className={`px-3 py-1 rounded-lg text-sm font-bold border ${
|
||||
position.side === 'LONG'
|
||||
? 'bg-green-500/20 text-green-300 border-green-500/30'
|
||||
: 'bg-red-500/20 text-red-300 border-red-500/30'
|
||||
}`}>
|
||||
{position.side}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-400">Size:</span>
|
||||
<span className="text-white font-semibold">${position.size}</span>
|
||||
</div>
|
||||
|
||||
{position.entryPrice && (
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-400">Entry:</span>
|
||||
<span className="text-white font-mono">${position.entryPrice}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{position.markPrice && (
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-400">Mark:</span>
|
||||
<span className="text-white font-mono">${position.markPrice}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{position.pnl !== undefined && (
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-400">PnL:</span>
|
||||
<span className={`font-bold ${
|
||||
position.pnl >= 0 ? 'text-green-400' : 'text-red-400'
|
||||
}`}>
|
||||
${position.pnl >= 0 ? '+' : ''}${position.pnl}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user