Files
trading_bot_v3/components/Dashboard.tsx
mindesbunister 71f7cd9084 Add comprehensive stop loss and take profit functionality
- 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
2025-07-13 01:31:07 +02:00

298 lines
12 KiB
TypeScript
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, { 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>
)
}