Files
trading_bot_v3/components/DriftAccountStatus.tsx
mindesbunister de45349baa Restore working dashboard and TradingView analysis
- Fixed layout conflicts by removing minimal layout.tsx in favor of complete layout.js
- Restored original AI Analysis page with full TradingView integration
- Connected enhanced screenshot API to real TradingView automation service
- Fixed screenshot gallery to handle both string and object formats
- Added image serving API route for screenshot display
- Resolved hydration mismatch issues with suppressHydrationWarning
- All navigation pages working (Analysis, Trading, Automation, Settings)
- TradingView automation successfully capturing screenshots from AI and DIY layouts
- Docker Compose v2 compatibility ensured

Working features:
- Homepage with hero section and status cards
- Navigation menu with Trading Bot branding
- Real TradingView screenshot capture
- AI-powered chart analysis
- Multi-layout support (AI + DIY module)
- Screenshot gallery with image serving
- API endpoints for balance, status, screenshots, trading
2025-07-14 14:21:19 +02:00

285 lines
9.9 KiB
TypeScript

"use client"
import React, { useEffect, useState } from 'react'
interface LoginStatus {
isLoggedIn: boolean
publicKey: string
userAccountExists: boolean
error?: string
}
interface AccountBalance {
totalCollateral: number
freeCollateral: number
marginRequirement: number
accountValue: number
leverage: number
availableBalance: number
}
interface Position {
symbol: string
side: 'LONG' | 'SHORT'
size: number
entryPrice: number
markPrice: number
unrealizedPnl: number
marketIndex: number
marketType: 'PERP' | 'SPOT'
}
export default function DriftAccountStatus() {
const [loginStatus, setLoginStatus] = useState<LoginStatus | null>(null)
const [balance, setBalance] = useState<AccountBalance | null>(null)
const [positions, setPositions] = useState<Position[]>([])
const [loading, setLoading] = useState(true)
const [refreshing, setRefreshing] = useState(false)
const [error, setError] = useState<string | null>(null)
const fetchAccountData = async () => {
try {
setError(null)
// Step 1: Login/Check status
const loginRes = await fetch('/api/drift/login', { method: 'POST' })
const loginData = await loginRes.json()
setLoginStatus(loginData)
if (!loginData.isLoggedIn) {
setError(loginData.error || 'Login failed')
return
}
// Check if account actually exists
if (!loginData.userAccountExists) {
setError('Drift account not initialized. Please visit app.drift.trade and deposit funds to create your account.')
return
}
// Step 2: Fetch balance
const balanceRes = await fetch('/api/drift/balance')
if (balanceRes.ok) {
const balanceData = await balanceRes.json()
// Map the API response to the expected format
const mappedBalance: AccountBalance = {
totalCollateral: balanceData.totalValue || 0,
freeCollateral: balanceData.availableBalance || 0,
marginRequirement: balanceData.marginUsed || 0,
accountValue: balanceData.totalValue || 0,
leverage: balanceData.totalValue > 0 ? (balanceData.marginUsed || 0) / balanceData.totalValue : 0,
availableBalance: balanceData.availableBalance || 0
}
setBalance(mappedBalance)
} else {
const errorData = await balanceRes.json()
setError(errorData.error || 'Failed to fetch balance')
}
// Step 3: Fetch positions
const positionsRes = await fetch('/api/drift/positions')
if (positionsRes.ok) {
const positionsData = await positionsRes.json()
// Map the API response to the expected format
const mappedPositions = (positionsData.positions || []).map((pos: any) => ({
symbol: pos.symbol,
side: (pos.side?.toUpperCase() || 'LONG') as 'LONG' | 'SHORT',
size: pos.size || 0,
entryPrice: pos.entryPrice || 0,
markPrice: pos.markPrice || 0,
unrealizedPnl: pos.unrealizedPnl || 0,
marketIndex: pos.marketIndex || 0,
marketType: 'PERP' as 'PERP' | 'SPOT'
}))
setPositions(mappedPositions)
} else {
const errorData = await positionsRes.json()
console.warn('Failed to fetch positions:', errorData.error)
}
} catch (err: any) {
setError(err.message || 'Unknown error occurred')
} finally {
setLoading(false)
setRefreshing(false)
}
}
const handleRefresh = async () => {
setRefreshing(true)
await fetchAccountData()
}
useEffect(() => {
fetchAccountData()
}, [])
if (loading) {
return (
<div className="card card-gradient">
<div className="flex items-center justify-center p-6">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-400"></div>
<span className="ml-3 text-gray-400">Loading Drift account...</span>
</div>
</div>
)
}
return (
<div className="space-y-6">
{/* Login Status */}
<div className="card card-gradient">
<div className="flex items-center justify-between mb-4">
<h2 className="text-lg font-bold text-white flex items-center">
<span className="text-xl mr-2">🌊</span>
Drift Account Status
</h2>
<button
onClick={handleRefresh}
disabled={refreshing}
className="btn-secondary btn-sm"
>
{refreshing ? '🔄' : '🔃'}
</button>
</div>
{error && (
<div className="bg-red-500/20 border border-red-500/50 rounded-lg p-4 mb-4">
<p className="text-red-400 text-sm"> {error}</p>
</div>
)}
{loginStatus && (
<div className="space-y-3">
<div className="flex items-center justify-between">
<span className="text-gray-400">Connection Status</span>
<span className={`px-2 py-1 rounded text-xs font-medium ${
loginStatus.isLoggedIn
? 'bg-green-500/20 text-green-400'
: 'bg-red-500/20 text-red-400'
}`}>
{loginStatus.isLoggedIn ? '🟢 Connected' : '🔴 Disconnected'}
</span>
</div>
<div className="flex items-center justify-between">
<span className="text-gray-400">Wallet Address</span>
<span className="text-white font-mono text-sm">
{loginStatus.publicKey ?
`${loginStatus.publicKey.slice(0, 6)}...${loginStatus.publicKey.slice(-6)}`
: 'N/A'
}
</span>
</div>
<div className="flex items-center justify-between">
<span className="text-gray-400">User Account</span>
<span className={`px-2 py-1 rounded text-xs font-medium ${
loginStatus.userAccountExists
? 'bg-green-500/20 text-green-400'
: 'bg-yellow-500/20 text-yellow-400'
}`}>
{loginStatus.userAccountExists ? 'Initialized' : 'Not Found'}
</span>
</div>
</div>
)}
</div>
{/* Account Balance */}
{balance && loginStatus?.isLoggedIn && (
<div className="card card-gradient">
<h3 className="text-lg font-bold text-white mb-4 flex items-center">
<span className="text-xl mr-2">💰</span>
Account Balance
</h3>
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-gray-400 text-sm">Total Collateral</p>
<p className="text-xl font-bold text-green-400">
${balance.totalCollateral.toFixed(2)}
</p>
</div>
<div>
<p className="text-gray-400 text-sm">Available Balance</p>
<p className="text-xl font-bold text-blue-400">
${balance.availableBalance.toFixed(2)}
</p>
</div>
<div>
<p className="text-gray-400 text-sm">Margin Used</p>
<p className="text-xl font-bold text-yellow-400">
${balance.marginRequirement.toFixed(2)}
</p>
</div>
<div>
<p className="text-gray-400 text-sm">Leverage</p>
<p className="text-xl font-bold text-purple-400">
{balance.leverage.toFixed(2)}x
</p>
</div>
</div>
</div>
)}
{/* Open Positions */}
{loginStatus?.isLoggedIn && (
<div className="card card-gradient">
<h3 className="text-lg font-bold text-white mb-4 flex items-center">
<span className="text-xl mr-2">📊</span>
Open Positions ({positions.length})
</h3>
{positions.length === 0 ? (
<div className="text-center py-6">
<p className="text-gray-400">No open positions</p>
</div>
) : (
<div className="space-y-3">
{positions.map((position, index) => (
<div key={index} className="bg-gray-800/50 rounded-lg p-4">
<div className="flex items-center justify-between mb-2">
<div className="flex items-center">
<span className="font-bold text-white">{position.symbol}</span>
<span className={`ml-2 px-2 py-1 rounded text-xs font-medium ${
position.side === 'LONG'
? 'bg-green-500/20 text-green-400'
: 'bg-red-500/20 text-red-400'
}`}>
{position.side}
</span>
</div>
<span className={`font-bold ${
position.unrealizedPnl >= 0 ? 'text-green-400' : 'text-red-400'
}`}>
{position.unrealizedPnl >= 0 ? '+' : ''}${position.unrealizedPnl.toFixed(2)}
</span>
</div>
<div className="grid grid-cols-3 gap-4 text-sm">
<div>
<span className="text-gray-400">Size: </span>
<span className="text-white">{position.size.toFixed(4)}</span>
</div>
<div>
<span className="text-gray-400">Entry: </span>
<span className="text-white">${position.entryPrice.toFixed(2)}</span>
</div>
<div>
<span className="text-gray-400">Mark: </span>
<span className="text-white">${position.markPrice.toFixed(2)}</span>
</div>
</div>
</div>
))}
</div>
)}
</div>
)}
</div>
)
}