- Added stop loss and take profit parameters to TradeParams interface - Implemented conditional order placement in executeTrade method - Added ZERO import and closePosition method to DriftTradingService - Enhanced trade API to handle stop loss/take profit parameters - Added position fetching and closing functionality to AdvancedTradingPanel - Added open positions display with close buttons - Implemented risk management calculations and UI - Added conditional order tracking in TradeResult interface
298 lines
12 KiB
TypeScript
298 lines
12 KiB
TypeScript
"use client"
|
||
import React, { useEffect, useState } from 'react'
|
||
import AutoTradingPanel from './AutoTradingPanel'
|
||
import TradingHistory from './TradingHistory'
|
||
import DeveloperSettings from './DeveloperSettings'
|
||
import AIAnalysisPanel from './AIAnalysisPanel'
|
||
import SessionStatus from './SessionStatus'
|
||
import DriftAccountStatus from './DriftAccountStatus'
|
||
import AdvancedTradingPanel from './AdvancedTradingPanel'
|
||
|
||
export default function Dashboard() {
|
||
const [positions, setPositions] = useState<any[]>([])
|
||
const [loading, setLoading] = useState(true)
|
||
const [error, setError] = useState<string | null>(null)
|
||
const [stats, setStats] = useState({
|
||
totalPnL: 0,
|
||
dailyPnL: 0,
|
||
winRate: 0,
|
||
totalTrades: 0,
|
||
accountValue: 0,
|
||
netUsdValue: 0
|
||
})
|
||
|
||
useEffect(() => {
|
||
async function fetchPositions() {
|
||
try {
|
||
setLoading(true)
|
||
|
||
// Get Drift positions
|
||
const driftRes = await fetch('/api/drift/positions')
|
||
if (driftRes.ok) {
|
||
const driftData = await driftRes.json()
|
||
if (driftData.positions) {
|
||
setPositions(driftData.positions)
|
||
|
||
// Calculate stats from Drift positions
|
||
const totalPnL = driftData.positions.reduce((sum: number, pos: any) => sum + (pos.unrealizedPnl || 0), 0)
|
||
setStats(prev => ({
|
||
...prev,
|
||
totalPnL,
|
||
dailyPnL: totalPnL * 0.1, // Approximate daily as 10% of total for demo
|
||
totalTrades: driftData.positions.length
|
||
}))
|
||
|
||
// Get account balance for account value
|
||
try {
|
||
const balanceRes = await fetch('/api/drift/balance')
|
||
if (balanceRes.ok) {
|
||
const balanceData = await balanceRes.json()
|
||
setStats(prev => ({
|
||
...prev,
|
||
accountValue: balanceData.accountValue || 0,
|
||
netUsdValue: balanceData.netUsdValue || 0
|
||
}))
|
||
}
|
||
} catch (e) {
|
||
console.warn('Could not fetch balance:', e)
|
||
}
|
||
} else {
|
||
// No positions available - set empty state
|
||
setPositions([])
|
||
setStats({
|
||
totalPnL: 0,
|
||
dailyPnL: 0,
|
||
winRate: 0,
|
||
totalTrades: 0,
|
||
accountValue: 0,
|
||
netUsdValue: 0
|
||
})
|
||
}
|
||
} else {
|
||
// API failed - set empty state
|
||
setError('Failed to connect to Drift')
|
||
setPositions([])
|
||
setStats({
|
||
totalPnL: 0,
|
||
dailyPnL: 0,
|
||
winRate: 0,
|
||
totalTrades: 0,
|
||
accountValue: 0,
|
||
netUsdValue: 0
|
||
})
|
||
}
|
||
} catch (e) {
|
||
setError('Error connecting to Drift')
|
||
console.error('Error:', e)
|
||
setPositions([])
|
||
setStats({
|
||
totalPnL: 0,
|
||
dailyPnL: 0,
|
||
winRate: 0,
|
||
totalTrades: 0,
|
||
accountValue: 0,
|
||
netUsdValue: 0
|
||
})
|
||
}
|
||
setLoading(false)
|
||
}
|
||
fetchPositions()
|
||
}, [])
|
||
|
||
return (
|
||
<div className="space-y-8">
|
||
{/* Stats Cards */}
|
||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-6 gap-6">
|
||
<div className="card card-gradient">
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<p className="text-gray-400 text-sm font-medium">Net USD Value</p>
|
||
<p className="text-2xl font-bold text-emerald-400">
|
||
${stats.netUsdValue.toFixed(2)}
|
||
</p>
|
||
</div>
|
||
<div className="w-12 h-12 bg-emerald-500/20 rounded-full flex items-center justify-center">
|
||
<span className="text-emerald-400 text-xl">💎</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="card card-gradient">
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<p className="text-gray-400 text-sm font-medium">Account Value</p>
|
||
<p className="text-2xl font-bold text-blue-400">
|
||
${stats.accountValue.toFixed(2)}
|
||
</p>
|
||
</div>
|
||
<div className="w-12 h-12 bg-blue-500/20 rounded-full flex items-center justify-center">
|
||
<span className="text-blue-400 text-xl">🏦</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="card card-gradient">
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<p className="text-gray-400 text-sm font-medium">Total P&L</p>
|
||
<p className={`text-2xl font-bold ${stats.totalPnL >= 0 ? 'text-green-400' : 'text-red-400'}`}>
|
||
{stats.totalPnL >= 0 ? '+' : ''}${stats.totalPnL.toFixed(2)}
|
||
</p>
|
||
</div>
|
||
<div className="w-12 h-12 bg-green-500/20 rounded-full flex items-center justify-center">
|
||
<span className="text-green-400 text-xl">📈</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="card card-gradient">
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<p className="text-gray-400 text-sm font-medium">Daily P&L</p>
|
||
<p className={`text-2xl font-bold ${stats.dailyPnL >= 0 ? 'text-green-400' : 'text-red-400'}`}>
|
||
{stats.dailyPnL >= 0 ? '+' : ''}${stats.dailyPnL.toFixed(2)}
|
||
</p>
|
||
</div>
|
||
<div className="w-12 h-12 bg-blue-500/20 rounded-full flex items-center justify-center">
|
||
<span className="text-blue-400 text-xl">💰</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="card card-gradient">
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<p className="text-gray-400 text-sm font-medium">Win Rate</p>
|
||
<p className="text-2xl font-bold text-cyan-400">{stats.winRate}%</p>
|
||
</div>
|
||
<div className="w-12 h-12 bg-cyan-500/20 rounded-full flex items-center justify-center">
|
||
<span className="text-cyan-400 text-xl">🎯</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="card card-gradient">
|
||
<div className="flex items-center justify-between">
|
||
<div>
|
||
<p className="text-gray-400 text-sm font-medium">Total Trades</p>
|
||
<p className="text-2xl font-bold text-purple-400">{stats.totalTrades}</p>
|
||
</div>
|
||
<div className="w-12 h-12 bg-purple-500/20 rounded-full flex items-center justify-center">
|
||
<span className="text-purple-400 text-xl">🔄</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Main Content Grid */}
|
||
<div className="grid grid-cols-1 xl:grid-cols-3 gap-8">
|
||
{/* Left Column - Controls & Account Status */}
|
||
<div className="xl:col-span-1 space-y-6">
|
||
<DriftAccountStatus />
|
||
<AdvancedTradingPanel />
|
||
<SessionStatus />
|
||
<AutoTradingPanel />
|
||
<DeveloperSettings />
|
||
</div>
|
||
|
||
{/* Right Column - Analysis & Positions */}
|
||
<div className="xl:col-span-2 space-y-6">
|
||
<AIAnalysisPanel />
|
||
|
||
{/* Open Positions */}
|
||
<div className="card card-gradient">
|
||
<div className="flex items-center justify-between mb-6">
|
||
<h2 className="text-xl font-bold text-white flex items-center">
|
||
<span className="w-2 h-2 bg-green-400 rounded-full mr-3 animate-pulse"></span>
|
||
Open Positions
|
||
</h2>
|
||
<button className="text-sm text-gray-400 hover:text-gray-300 transition-colors">
|
||
View All
|
||
</button>
|
||
</div>
|
||
|
||
{loading ? (
|
||
<div className="flex items-center justify-center py-8">
|
||
<div className="spinner"></div>
|
||
<span className="ml-2 text-gray-400">Loading positions...</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}</p>
|
||
<p className="text-gray-500 text-sm mt-2">Please check your connection and try again</p>
|
||
</div>
|
||
) : positions.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 open positions</p>
|
||
<p className="text-gray-500 text-sm mt-2">Start trading to see your positions here</p>
|
||
</div>
|
||
) : (
|
||
<div className="overflow-hidden">
|
||
<div className="overflow-x-auto">
|
||
<table className="w-full">
|
||
<thead>
|
||
<tr className="border-b border-gray-700">
|
||
<th className="text-left py-3 px-4 text-gray-400 font-medium text-sm">Asset</th>
|
||
<th className="text-left py-3 px-4 text-gray-400 font-medium text-sm">Side</th>
|
||
<th className="text-right py-3 px-4 text-gray-400 font-medium text-sm">Size</th>
|
||
<th className="text-right py-3 px-4 text-gray-400 font-medium text-sm">Entry</th>
|
||
<th className="text-right py-3 px-4 text-gray-400 font-medium text-sm">PnL</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{positions.map((pos, i) => (
|
||
<tr key={i} className="border-b border-gray-800/50 hover:bg-gray-800/30 transition-colors">
|
||
<td className="py-4 px-4">
|
||
<div className="flex items-center">
|
||
<div className="w-8 h-8 bg-gradient-to-br from-orange-400 to-orange-600 rounded-full flex items-center justify-center mr-3">
|
||
<span className="text-white text-xs font-bold">
|
||
{pos.symbol?.slice(0, 2) || '--'}
|
||
</span>
|
||
</div>
|
||
<span className="font-medium text-white">{pos.symbol || '--'}</span>
|
||
</div>
|
||
</td>
|
||
<td className="py-4 px-4">
|
||
<span className={`inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium ${
|
||
(pos.side === 'LONG' || pos.side === 'long' || pos.side === 'buy')
|
||
? 'bg-green-500/20 text-green-400'
|
||
: 'bg-red-500/20 text-red-400'
|
||
}`}>
|
||
{pos.side || '--'}
|
||
</span>
|
||
</td>
|
||
<td className="py-4 px-4 text-right font-mono text-gray-300">
|
||
{typeof pos.size === 'number' ? pos.size.toFixed(4) : '--'}
|
||
</td>
|
||
<td className="py-4 px-4 text-right font-mono text-gray-300">
|
||
${typeof pos.entryPrice === 'number' ? pos.entryPrice.toFixed(2) : '--'}
|
||
</td>
|
||
<td className="py-4 px-4 text-right">
|
||
<span className={`font-mono font-medium ${
|
||
(pos.unrealizedPnl || 0) >= 0 ? 'text-green-400' : 'text-red-400'
|
||
}`}>
|
||
{(pos.unrealizedPnl || 0) >= 0 ? '+' : ''}${typeof pos.unrealizedPnl === 'number' ? pos.unrealizedPnl.toFixed(2) : '0.00'}
|
||
</span>
|
||
</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
<TradingHistory />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|