ADVANCED SYSTEM KNOWLEDGE: - Superior parallel screenshot system (60% performance gain) - AI learning system architecture and decision flow - Orphaned order cleanup integration patterns - Critical technical fixes and troubleshooting guide - Database schema best practices - Memory leak prevention strategies - AI learning system patterns and functions - Error handling best practices for trading systems - Integration patterns for position monitoring - Performance optimization rules - UI/UX consistency requirements - Critical anti-patterns to avoid - Added links to new knowledge base documents - Comprehensive documentation structure - Development guides and best practices - Performance optimizations summary - 60% screenshot performance improvement techniques - AI learning system that adapts trading decisions - Container stability and crash prevention - Frontend-backend consistency requirements - Integration strategies for existing infrastructure This documentation preserves critical insights from complex debugging sessions and provides patterns for future development.
268 lines
9.7 KiB
TypeScript
268 lines
9.7 KiB
TypeScript
'use client';
|
||
|
||
import { useState, useEffect } from 'react';
|
||
|
||
interface Position {
|
||
symbol: string;
|
||
side: string;
|
||
size: number;
|
||
entryPrice: number;
|
||
currentPrice: number;
|
||
unrealizedPnl: number;
|
||
notionalValue: number;
|
||
}
|
||
|
||
interface StopLossProximity {
|
||
stopLossPrice: number;
|
||
currentPrice: number;
|
||
distancePercent: string;
|
||
isNear: boolean;
|
||
}
|
||
|
||
interface MonitorData {
|
||
hasPosition: boolean;
|
||
position?: Position;
|
||
stopLossProximity?: StopLossProximity;
|
||
riskLevel: string;
|
||
nextAction: string;
|
||
recommendation: string;
|
||
}
|
||
|
||
interface AutomationStatus {
|
||
isActive: boolean;
|
||
mode: string;
|
||
symbol: string;
|
||
timeframes: string[];
|
||
totalCycles: number;
|
||
totalTrades: number;
|
||
lastActivity: string;
|
||
consecutiveErrors: number;
|
||
detailedStatus: string;
|
||
}
|
||
|
||
export default function PositionMonitor() {
|
||
const [monitorData, setMonitorData] = useState<MonitorData | null>(null);
|
||
const [automationStatus, setAutomationStatus] = useState<AutomationStatus | null>(null);
|
||
const [lastUpdate, setLastUpdate] = useState<Date>(new Date());
|
||
const [error, setError] = useState<string>('');
|
||
|
||
const fetchData = async () => {
|
||
try {
|
||
// Fetch position data
|
||
const positionResponse = await fetch('/api/automation/position-monitor');
|
||
const positionResult = await positionResponse.json();
|
||
|
||
// Fetch automation status
|
||
const statusResponse = await fetch('/api/automation/status');
|
||
const statusResult = await statusResponse.json();
|
||
|
||
if (positionResult.success) {
|
||
setMonitorData(positionResult.monitor);
|
||
}
|
||
|
||
setAutomationStatus(statusResult);
|
||
setLastUpdate(new Date());
|
||
setError('');
|
||
} catch (err) {
|
||
setError('Failed to fetch monitoring data');
|
||
console.error('Monitor error:', err);
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
fetchData();
|
||
const interval = setInterval(fetchData, 10000); // Update every 10 seconds
|
||
return () => clearInterval(interval);
|
||
}, []);
|
||
|
||
const getRiskColor = (risk: string) => {
|
||
switch (risk) {
|
||
case 'HIGH': return 'text-red-500';
|
||
case 'MEDIUM': return 'text-yellow-500';
|
||
case 'LOW': return 'text-green-500';
|
||
default: return 'text-gray-500';
|
||
}
|
||
};
|
||
|
||
const getStopLossStatus = (distancePercent: string) => {
|
||
const distance = parseFloat(distancePercent);
|
||
if (distance <= 2) return { icon: '🚨', text: 'DANGER', color: 'text-red-600' };
|
||
if (distance <= 5) return { icon: '⚠️', text: 'WARNING', color: 'text-yellow-600' };
|
||
if (distance <= 10) return { icon: '🟡', text: 'CAUTION', color: 'text-yellow-500' };
|
||
return { icon: '✅', text: 'SAFE', color: 'text-green-500' };
|
||
};
|
||
|
||
if (error) {
|
||
return (
|
||
<div className="bg-red-50 border border-red-200 rounded-lg p-4">
|
||
<div className="flex items-center">
|
||
<span className="text-red-500 mr-2">❌</span>
|
||
<span className="text-red-700">{error}</span>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
{/* Header */}
|
||
<div className="bg-gray-50 rounded-lg p-4">
|
||
<div className="flex justify-between items-center">
|
||
<h2 className="text-lg font-semibold text-gray-800">🔍 Position Monitor</h2>
|
||
<span className="text-sm text-gray-500">
|
||
Last update: {lastUpdate.toLocaleTimeString()}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Position Status */}
|
||
{monitorData?.hasPosition && monitorData.position ? (
|
||
<div className="bg-white border rounded-lg p-6">
|
||
<h3 className="text-lg font-medium text-gray-900 mb-4">📊 Active Position</h3>
|
||
|
||
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
|
||
<div>
|
||
<p className="text-sm text-gray-500">Symbol & Side</p>
|
||
<p className="font-medium">{monitorData.position.symbol} | {monitorData.position.side.toUpperCase()}</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-gray-500">Size</p>
|
||
<p className="font-medium">{monitorData.position.size} SOL</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-gray-500">Entry Price</p>
|
||
<p className="font-medium">${monitorData.position.entryPrice.toFixed(4)}</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-gray-500">Current Price</p>
|
||
<p className="font-medium">${monitorData.position.currentPrice.toFixed(4)}</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-gray-500">P&L</p>
|
||
<p className={`font-medium ${monitorData.position.unrealizedPnl >= 0 ? 'text-green-600' : 'text-red-600'}`}>
|
||
{monitorData.position.unrealizedPnl >= 0 ? '+' : ''}${monitorData.position.unrealizedPnl.toFixed(2)}
|
||
</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-gray-500">Notional Value</p>
|
||
<p className="font-medium">${monitorData.position.notionalValue.toFixed(2)}</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Stop Loss Section */}
|
||
{monitorData.stopLossProximity && (
|
||
<div className="mt-6 p-4 bg-gray-50 rounded-lg">
|
||
<h4 className="font-medium text-gray-900 mb-3">🛡️ Stop Loss Protection</h4>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||
<div>
|
||
<p className="text-sm text-gray-500">Stop Loss Price</p>
|
||
<p className="font-medium">${monitorData.stopLossProximity.stopLossPrice.toFixed(4)}</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-gray-500">Distance</p>
|
||
<p className="font-medium">{monitorData.stopLossProximity.distancePercent}% away</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-gray-500">Status</p>
|
||
{(() => {
|
||
const status = getStopLossStatus(monitorData.stopLossProximity.distancePercent);
|
||
return (
|
||
<p className={`font-medium ${status.color}`}>
|
||
{status.icon} {status.text}
|
||
</p>
|
||
);
|
||
})()}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="mt-3">
|
||
<p className="text-sm text-gray-500">Risk Level</p>
|
||
<p className={`font-medium ${getRiskColor(monitorData.riskLevel)}`}>
|
||
{monitorData.riskLevel}
|
||
</p>
|
||
</div>
|
||
|
||
{monitorData.stopLossProximity.isNear && (
|
||
<div className="mt-4 p-3 bg-red-100 border border-red-300 rounded-lg">
|
||
<p className="text-red-700 font-medium">🚨 ALERT: Price is near stop loss!</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
) : (
|
||
<div className="bg-white border rounded-lg p-6">
|
||
<div className="text-center py-8">
|
||
<p className="text-gray-500 text-lg">📊 No Open Positions</p>
|
||
<p className="text-gray-400 mt-2">Scanning for opportunities...</p>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Automation Status */}
|
||
<div className="bg-white border rounded-lg p-6">
|
||
<h3 className="text-lg font-medium text-gray-900 mb-4">🤖 Automation Status</h3>
|
||
|
||
{automationStatus?.isActive ? (
|
||
<div className="space-y-4">
|
||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||
<div>
|
||
<p className="text-sm text-gray-500">Status</p>
|
||
<p className="font-medium text-green-600">✅ ACTIVE</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-gray-500">Mode</p>
|
||
<p className="font-medium">{automationStatus.mode}</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-gray-500">Strategy</p>
|
||
<p className="font-medium">Scalping</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-gray-500">Symbol</p>
|
||
<p className="font-medium">{automationStatus.symbol}</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||
<div>
|
||
<p className="text-sm text-gray-500">Timeframes</p>
|
||
<p className="font-medium">{automationStatus.timeframes?.join(', ') || 'N/A'}</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-gray-500">Cycles</p>
|
||
<p className="font-medium">{automationStatus.totalCycles}</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-gray-500">Trades</p>
|
||
<p className="font-medium">{automationStatus.totalTrades}</p>
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-gray-500">Errors</p>
|
||
<p className={`font-medium ${automationStatus.consecutiveErrors > 0 ? 'text-yellow-600' : 'text-green-600'}`}>
|
||
{automationStatus.consecutiveErrors}/3
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
{automationStatus.lastActivity && (
|
||
<div>
|
||
<p className="text-sm text-gray-500">Last Activity</p>
|
||
<p className="font-medium">
|
||
{new Date(automationStatus.lastActivity).toLocaleString()}
|
||
</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
) : (
|
||
<div className="text-center py-4">
|
||
<p className="text-red-600 font-medium">❌ STOPPED</p>
|
||
<p className="text-gray-500 mt-2">{automationStatus?.detailedStatus}</p>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|