🔧 CRITICAL FIX: Price Data Sync & Position Monitor Enhancement
Fixed major price data sync issues: - Removed hardcoded price (77.63) from position monitor - Added real-time oracle data instead of stale TWAP pricing - Implemented cache-busting headers for fresh data - Updated fallback prices to current market levels - Real-time P&L tracking with trend indicators (📈📉➡️) - Enhanced stop loss proximity alerts with color-coded risk levels - Analysis progress indicators during automation cycles - Performance metrics (runtime, cycles, trades, errors) - Fresh data validation and improved error handling - Price accuracy: 77.63 → 84.47 (matches Drift UI) - P&L accuracy: -.91 → -.59 (correct calculation) - Risk assessment: CRITICAL → MEDIUM (proper evaluation) - Stop loss distance: 0.91% → 4.8% (safe distance) - CLI monitor script with 8-second updates - Web dashboard component (PositionMonitor.tsx) - Real-time automation status tracking - Database and error monitoring improvements This fixes the automation showing false emergency alerts when position was actually performing normally.
This commit is contained in:
@@ -23,9 +23,192 @@ class EnhancedAutonomousRiskManager {
|
||||
this.pendingDecisions = new Map(); // Track decisions awaiting outcomes
|
||||
this.activeSetups = new Map(); // Track R/R setups for outcome learning
|
||||
this.lastAnalysis = null;
|
||||
this.baseApiUrl = this.detectApiUrl(); // Docker-aware API URL
|
||||
this.lastScreenshotAnalysis = null; // Track when we last analyzed screenshots
|
||||
this.screenshotAnalysisThreshold = 3.5; // Only analyze screenshots when < 3.5% from SL (demo: was 3.0)
|
||||
this.screenshotAnalysisInterval = 2 * 60 * 1000; // Don't analyze more than once every 2 minutes (demo: was 5)
|
||||
}
|
||||
|
||||
async log(message) {
|
||||
/**
|
||||
* Detect the correct API URL based on environment
|
||||
* Returns localhost for host environment, gateway IP for Docker
|
||||
*/
|
||||
detectApiUrl() {
|
||||
try {
|
||||
// Check if running inside Docker container
|
||||
const fs = require('fs');
|
||||
if (fs.existsSync('/.dockerenv')) {
|
||||
// Get the default gateway IP from /proc/net/route
|
||||
try {
|
||||
const routeData = fs.readFileSync('/proc/net/route', 'utf8');
|
||||
const lines = routeData.split('\n');
|
||||
for (const line of lines) {
|
||||
const parts = line.trim().split(/\s+/);
|
||||
// Look for default route (destination 00000000)
|
||||
if (parts[1] === '00000000' && parts[2] && parts[2] !== '00000000') {
|
||||
// Convert hex gateway to IP
|
||||
const gatewayHex = parts[2];
|
||||
const ip = [
|
||||
parseInt(gatewayHex.substr(6, 2), 16),
|
||||
parseInt(gatewayHex.substr(4, 2), 16),
|
||||
parseInt(gatewayHex.substr(2, 2), 16),
|
||||
parseInt(gatewayHex.substr(0, 2), 16)
|
||||
].join('.');
|
||||
return `http://${ip}:9001`;
|
||||
}
|
||||
}
|
||||
// Fallback to known gateway IP
|
||||
return 'http://192.168.160.1:9001';
|
||||
} catch (routeError) {
|
||||
// Fallback to the known gateway IP for this Docker setup
|
||||
return 'http://192.168.160.1:9001';
|
||||
}
|
||||
}
|
||||
|
||||
// Check hostname (Docker containers often have specific hostnames)
|
||||
const os = require('os');
|
||||
const hostname = os.hostname();
|
||||
if (hostname && hostname.length === 12 && /^[a-f0-9]+$/.test(hostname)) {
|
||||
// Same gateway detection for hostname-based detection
|
||||
try {
|
||||
const routeData = fs.readFileSync('/proc/net/route', 'utf8');
|
||||
const lines = routeData.split('\n');
|
||||
for (const line of lines) {
|
||||
const parts = line.trim().split(/\s+/);
|
||||
if (parts[1] === '00000000' && parts[2] && parts[2] !== '00000000') {
|
||||
const gatewayHex = parts[2];
|
||||
const ip = [
|
||||
parseInt(gatewayHex.substr(6, 2), 16),
|
||||
parseInt(gatewayHex.substr(4, 2), 16),
|
||||
parseInt(gatewayHex.substr(2, 2), 16),
|
||||
parseInt(gatewayHex.substr(0, 2), 16)
|
||||
].join('.');
|
||||
return `http://${ip}:9001`;
|
||||
}
|
||||
}
|
||||
return 'http://192.168.160.1:9001';
|
||||
} catch (routeError) {
|
||||
return 'http://192.168.160.1:9001';
|
||||
}
|
||||
}
|
||||
|
||||
// Default to localhost for host environment
|
||||
return 'http://localhost:9001';
|
||||
} catch (error) {
|
||||
// Fallback to localhost if detection fails
|
||||
return 'http://localhost:9001';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we should trigger screenshot analysis based on risk level
|
||||
*/
|
||||
shouldTriggerScreenshotAnalysis(distancePercent) {
|
||||
// Only trigger when approaching critical levels
|
||||
if (distancePercent > this.screenshotAnalysisThreshold) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't analyze too frequently
|
||||
if (this.lastScreenshotAnalysis) {
|
||||
const timeSinceLastAnalysis = Date.now() - this.lastScreenshotAnalysis.getTime();
|
||||
if (timeSinceLastAnalysis < this.screenshotAnalysisInterval) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request screenshot analysis from the main trading system
|
||||
*/
|
||||
async requestScreenshotAnalysis(symbol) {
|
||||
try {
|
||||
this.lastScreenshotAnalysis = new Date();
|
||||
|
||||
await this.log(`📸 Requesting chart analysis for ${symbol} - risk level requires visual confirmation`);
|
||||
|
||||
// Use the enhanced screenshot API with analysis
|
||||
const response = await HttpUtil.post(`${this.baseApiUrl}/api/enhanced-screenshot`, {
|
||||
symbol: symbol,
|
||||
timeframes: ['1h'], // Focus on primary timeframe for speed
|
||||
layouts: ['ai'], // Only AI layout for faster analysis
|
||||
analyze: true,
|
||||
reason: 'RISK_MANAGEMENT_ANALYSIS'
|
||||
});
|
||||
|
||||
if (response.success && response.analysis) {
|
||||
await this.log(`✅ Chart analysis complete: ${response.analysis.recommendation} (${response.analysis.confidence}% confidence)`);
|
||||
|
||||
return {
|
||||
recommendation: response.analysis.recommendation,
|
||||
confidence: response.analysis.confidence,
|
||||
marketSentiment: response.analysis.marketSentiment,
|
||||
keyLevels: response.analysis.keyLevels,
|
||||
reasoning: response.analysis.reasoning,
|
||||
supportNearby: this.detectNearbySupport(response.analysis, symbol),
|
||||
resistanceNearby: this.detectNearbyResistance(response.analysis, symbol),
|
||||
technicalStrength: this.assessTechnicalStrength(response.analysis),
|
||||
timestamp: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (error) {
|
||||
await this.log(`❌ Error in screenshot analysis: ${error.message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if there's strong support near current price
|
||||
*/
|
||||
detectNearbySupport(analysis, symbol) {
|
||||
if (!analysis.keyLevels?.support) return false;
|
||||
|
||||
// Get current price from last position data
|
||||
const currentPrice = this.lastAnalysis?.monitor?.position?.currentPrice || 0;
|
||||
if (!currentPrice) return false;
|
||||
|
||||
// Check if any support level is within 2% of current price
|
||||
return analysis.keyLevels.support.some(supportLevel => {
|
||||
const distance = Math.abs(currentPrice - supportLevel) / currentPrice;
|
||||
return distance < 0.02; // Within 2%
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if there's resistance near current price
|
||||
*/
|
||||
detectNearbyResistance(analysis, symbol) {
|
||||
if (!analysis.keyLevels?.resistance) return false;
|
||||
|
||||
const currentPrice = this.lastAnalysis?.monitor?.position?.currentPrice || 0;
|
||||
if (!currentPrice) return false;
|
||||
|
||||
return analysis.keyLevels.resistance.some(resistanceLevel => {
|
||||
const distance = Math.abs(currentPrice - resistanceLevel) / currentPrice;
|
||||
return distance < 0.02; // Within 2%
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Assess overall technical strength from chart analysis
|
||||
*/
|
||||
assessTechnicalStrength(analysis) {
|
||||
let strength = 'NEUTRAL';
|
||||
|
||||
if (analysis.confidence > 80 && analysis.marketSentiment === 'BULLISH') {
|
||||
strength = 'STRONG_BULLISH';
|
||||
} else if (analysis.confidence > 80 && analysis.marketSentiment === 'BEARISH') {
|
||||
strength = 'STRONG_BEARISH';
|
||||
} else if (analysis.confidence > 60) {
|
||||
strength = `MODERATE_${analysis.marketSentiment}`;
|
||||
}
|
||||
|
||||
return strength;
|
||||
} async log(message) {
|
||||
const timestamp = new Date().toISOString();
|
||||
console.log(`[${timestamp}] 🤖 Enhanced Risk AI: ${message}`);
|
||||
}
|
||||
@@ -49,6 +232,14 @@ class EnhancedAutonomousRiskManager {
|
||||
// Update thresholds based on learning
|
||||
await this.updateThresholdsFromLearning();
|
||||
|
||||
// SMART SCREENSHOT ANALYSIS TRIGGER
|
||||
// Only analyze screenshots when approaching critical levels
|
||||
let chartAnalysis = null;
|
||||
if (this.shouldTriggerScreenshotAnalysis(distance)) {
|
||||
await this.log(`📸 Triggering screenshot analysis - distance: ${distance}%`);
|
||||
chartAnalysis = await this.requestScreenshotAnalysis(position.symbol);
|
||||
}
|
||||
|
||||
// Get AI recommendation based on learned patterns
|
||||
const smartRecommendation = await this.learner.getSmartRecommendation({
|
||||
distanceFromSL: distance,
|
||||
@@ -57,7 +248,8 @@ class EnhancedAutonomousRiskManager {
|
||||
price: position.entryPrice, // Current price context
|
||||
unrealizedPnl: position.unrealizedPnl,
|
||||
side: position.side
|
||||
}
|
||||
},
|
||||
chartAnalysis: chartAnalysis // Include visual analysis if available
|
||||
});
|
||||
|
||||
let decision;
|
||||
@@ -129,6 +321,49 @@ class EnhancedAutonomousRiskManager {
|
||||
|
||||
await this.log(`⚠️ HIGH RISK: Position ${distance}% from stop loss`);
|
||||
|
||||
// Check if we have recent chart analysis data
|
||||
const chartAnalysis = smartRecommendation.chartAnalysis;
|
||||
|
||||
// Use chart analysis to make smarter decisions
|
||||
if (chartAnalysis) {
|
||||
await this.log(`📊 Using chart analysis: ${chartAnalysis.technicalStrength} sentiment, ${chartAnalysis.confidence}% confidence`);
|
||||
|
||||
// If there's strong support nearby and bullish sentiment, consider holding
|
||||
if (chartAnalysis.supportNearby &&
|
||||
chartAnalysis.technicalStrength.includes('BULLISH') &&
|
||||
chartAnalysis.confidence > 70 &&
|
||||
position.side === 'long') {
|
||||
|
||||
await this.log(`🛡️ Strong support detected near ${position.currentPrice} - holding position with tighter stop`);
|
||||
return {
|
||||
action: 'TIGHTEN_STOP_LOSS',
|
||||
reasoning: `Chart shows strong support nearby (${chartAnalysis.reasoning}). Tightening stop instead of exiting.`,
|
||||
confidence: chartAnalysis.confidence / 100,
|
||||
urgency: 'HIGH',
|
||||
chartEnhanced: true,
|
||||
parameters: {
|
||||
newStopLossDistance: distance * 0.8 // Tighten by 20%
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// If chart shows weakness, exit more aggressively
|
||||
if (chartAnalysis.technicalStrength.includes('BEARISH') && chartAnalysis.confidence > 60) {
|
||||
await this.log(`📉 Chart shows weakness - executing defensive exit`);
|
||||
return {
|
||||
action: 'PARTIAL_EXIT',
|
||||
reasoning: `Chart analysis shows bearish signals (${chartAnalysis.reasoning}). Reducing exposure.`,
|
||||
confidence: chartAnalysis.confidence / 100,
|
||||
urgency: 'HIGH',
|
||||
chartEnhanced: true,
|
||||
parameters: {
|
||||
exitPercentage: 70, // More aggressive exit
|
||||
keepStopLoss: true
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Check learning recommendation
|
||||
if (smartRecommendation.learningBased && smartRecommendation.confidence > 0.7) {
|
||||
return {
|
||||
@@ -478,7 +713,7 @@ class EnhancedAutonomousRiskManager {
|
||||
async checkPositionStatus(symbol) {
|
||||
// Check if position is still active
|
||||
try {
|
||||
const data = await HttpUtil.get('http://localhost:9001/api/automation/position-monitor');
|
||||
const data = await HttpUtil.get(`${this.baseApiUrl}/api/automation/position-monitor`);
|
||||
|
||||
if (data.success && data.monitor?.hasPosition && data.monitor.position?.symbol === symbol) {
|
||||
return data.monitor;
|
||||
@@ -547,7 +782,7 @@ class EnhancedAutonomousRiskManager {
|
||||
|
||||
async getCurrentPositionStatus(symbol) {
|
||||
try {
|
||||
const data = await HttpUtil.get('http://localhost:9001/api/automation/position-monitor');
|
||||
const data = await HttpUtil.get(`${this.baseApiUrl}/api/automation/position-monitor`);
|
||||
|
||||
if (data.success && data.monitor?.hasPosition) {
|
||||
return {
|
||||
@@ -604,7 +839,7 @@ class EnhancedAutonomousRiskManager {
|
||||
async analyzeMarketConditions(symbol) {
|
||||
// Enhanced market analysis for better decision making
|
||||
try {
|
||||
const data = await HttpUtil.get('http://localhost:9001/api/automation/position-monitor');
|
||||
const data = await HttpUtil.get(`${this.baseApiUrl}/api/automation/position-monitor`);
|
||||
|
||||
if (data.success && data.monitor?.position) {
|
||||
const pnl = data.monitor.position.unrealizedPnl;
|
||||
@@ -651,7 +886,7 @@ class EnhancedAutonomousRiskManager {
|
||||
|
||||
try {
|
||||
// Check current positions
|
||||
const data = await HttpUtil.get('http://localhost:9001/api/automation/position-monitor');
|
||||
const data = await HttpUtil.get(`${this.baseApiUrl}/api/automation/position-monitor`);
|
||||
|
||||
if (data.success) {
|
||||
const decision = await this.analyzePosition(data.monitor);
|
||||
|
||||
Reference in New Issue
Block a user