fix: resolve container networking issues for automation system

- Fixed internal API URLs from localhost:9001 to localhost:3000 in automation core files
- Updated lib/simple-automation.js: Fixed 5 baseUrl references for internal container calls
- Updated app/api/drift/consolidate-position/route.js: Fixed positions API fetch URL
- Updated app/api/drift/scale-position/route.js: Fixed 2 internal API calls (positions and orders)
- Updated lib/position-consolidator.js: Fixed 3 internal API calls (cancel-all-orders, place-order, positions)

This resolves 'Network Error' and 'fetch failed' issues that prevented automation
cycles from executing properly within Docker container environment.

Root cause: Automation was making fetch calls to external port (9001) from within
container instead of internal port (3000), causing connection failures.

Result: Automation cycles now execute successfully with proper internal API connectivity.
This commit is contained in:
mindesbunister
2025-07-28 01:19:37 +02:00
parent 236e2b0d31
commit abb8c8d7f1
4 changed files with 217 additions and 83 deletions

View File

@@ -10,7 +10,7 @@ export async function POST(request) {
console.log(`AI Analysis: ${analysis ? 'Provided - Using AI optimal levels' : 'Not provided - Using adaptive levels'}`);
// Get current position data
const positionsResponse = await fetch('http://localhost:9001/api/drift/positions');
const positionsResponse = await fetch('http://localhost:3000/api/drift/positions');
const positionsData = await positionsResponse.json();
if (!positionsData.success || !positionsData.positions.length) {

View File

@@ -29,7 +29,7 @@ export async function POST(request) {
console.log(`💰 Adding $${dcaAmount} to existing position`);
// 1. Get current position
const positionResponse = await fetch(`${process.env.INTERNAL_API_URL || 'http://localhost:9001'}/api/drift/positions`);
const positionResponse = await fetch(`${process.env.INTERNAL_API_URL || 'http://localhost:3000'}/api/drift/positions`);
const positionData = await positionResponse.json();
if (!positionData.success || positionData.positions.length === 0) {
@@ -86,7 +86,7 @@ export async function POST(request) {
// 5. Cancel existing stop loss and take profit orders
console.log('🧹 Canceling existing SL/TP orders...');
try {
const ordersResponse = await fetch(`${process.env.INTERNAL_API_URL || 'http://localhost:9001'}/api/drift/orders`);
const ordersResponse = await fetch(`${process.env.INTERNAL_API_URL || 'http://localhost:3000'}/api/drift/orders`);
const ordersData = await ordersResponse.json();
if (ordersData.success && ordersData.orders.length > 0) {

View File

@@ -243,7 +243,7 @@ class PositionConsolidator {
*/
static async cancelAllOrders() {
try {
const response = await fetch('http://localhost:9001/api/drift/cancel-all-orders', {
const response = await fetch('http://localhost:3000/api/drift/cancel-all-orders', {
method: 'POST',
headers: { 'Content-Type': 'application/json' }
});
@@ -262,7 +262,7 @@ class PositionConsolidator {
*/
static async placeConsolidatedOrder(orderParams) {
try {
const response = await fetch('http://localhost:9001/api/drift/place-order', {
const response = await fetch('http://localhost:3000/api/drift/place-order', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -297,7 +297,7 @@ class PositionConsolidator {
*/
static async getCurrentPosition() {
try {
const response = await fetch('http://localhost:9001/api/drift/positions');
const response = await fetch('http://localhost:3000/api/drift/positions');
const result = await response.json();
if (result.success && result.positions.length > 0) {

View File

@@ -123,13 +123,15 @@ class SimpleAutomation {
console.log('MODE: ' + (config.mode || 'SIMULATION'));
// Start dynamic monitoring cycle with risk-based intervals
this.currentInterval = 10 * 60 * 1000; // Start with 10 minutes
console.log('🚀 STARTING: Dynamic monitoring with scalping optimization');
this.currentInterval = 30 * 1000; // Start with 30 seconds for immediate analysis
this.startDynamicMonitoring();
// First cycle after 30 seconds
// First cycle after 5 seconds (immediate for scalping)
setTimeout(() => {
console.log('🔥 IMMEDIATE CYCLE: Starting first analysis cycle');
this.runCycle();
}, 30000);
}, 5000);
return {
success: true,
@@ -189,26 +191,50 @@ class SimpleAutomation {
this.intervalId = setTimeout(runMonitoringCycle, this.currentInterval);
}
// Determine next interval based on risk level
// Determine next interval based on risk level and position status
async getNextInterval() {
try {
// Check position monitor for current risk level
const baseUrl = process.env.INTERNAL_API_URL || 'http://localhost:9001';
const response = await fetch(`${baseUrl}/api/automation/position-monitor`); if (response.ok) {
// Check position monitor for current risk level AND position status
const baseUrl = process.env.INTERNAL_API_URL || 'http://localhost:3000';
const response = await fetch(`${baseUrl}/api/automation/position-monitor`);
if (response.ok) {
const data = await response.json();
const riskLevel = data.monitor?.riskLevel || 'NONE';
const hasPosition = data.monitor?.hasPosition;
const recommendation = data.monitor?.recommendation;
// Get timeframe-based intervals (scalping needs faster analysis)
const baseInterval = this.getTimeframeBasedIntervals();
// Risk-based multipliers for fine-tuning
// 🚨 SCALPING LOGIC: No position = IMMEDIATE re-entry scanning
if (!hasPosition && recommendation === 'START_TRADING') {
const isScalping = this.config.selectedTimeframes?.includes('5m') ||
this.config.selectedTimeframes?.includes('15m');
if (isScalping) {
// Ultra-fast intervals for scalping when no position
const scalpingInterval = 2 * 60 * 1000; // 2 minutes for immediate re-entry
console.log('🏃‍♂️ SCALPING ALERT: No position - scanning every 2 minutes for re-entry');
this.currentInterval = scalpingInterval;
return scalpingInterval;
} else {
// Fast intervals for day trading when no position
const dayTradingInterval = 5 * 60 * 1000; // 5 minutes for day trading
console.log('⚡ DAY TRADING: No position - scanning every 5 minutes for re-entry');
this.currentInterval = dayTradingInterval;
return dayTradingInterval;
}
}
// Risk-based multipliers for existing positions
let riskMultiplier;
switch (riskLevel) {
case 'CRITICAL':
riskMultiplier = 0.5; // 50% faster when critical (5min→2.5min for scalping)
riskMultiplier = 0.5; // 50% faster when critical
break;
case 'HIGH':
riskMultiplier = 0.7; // 30% faster when high risk (10min→7min for scalping)
riskMultiplier = 0.7; // 30% faster when high risk
break;
case 'MEDIUM':
riskMultiplier = 1.0; // Normal speed
@@ -218,18 +244,19 @@ class SimpleAutomation {
break;
case 'NONE':
default:
riskMultiplier = 1.0; // Normal speed when no position
// For NONE risk (no position), use base interval but don't slow down
riskMultiplier = hasPosition ? 1.0 : 0.8; // Slightly faster when no position
}
const finalInterval = Math.round(baseInterval * riskMultiplier);
const finalMinutes = finalInterval / (60 * 1000);
console.log(`📊 Risk: ${riskLevel} | Strategy: ${this.detectStrategy()} | Interval: ${finalMinutes} min`);
console.log(`📊 Risk: ${riskLevel} | Position: ${hasPosition ? 'YES' : 'NO'} | Interval: ${finalMinutes} min`);
console.log(`⚡ Optimized for ${this.getSelectedTimeframes().join(',') || 'default'} timeframes`);
const intervalMs = finalInterval;
console.log(`📊 DYNAMIC INTERVAL: Risk level ${riskLevel} → Next analysis in ${intervalMinutes} minutes`);
console.log(`📊 DYNAMIC INTERVAL: Risk level ${riskLevel} → Next analysis in ${finalMinutes} minutes`);
this.currentInterval = intervalMs;
return intervalMs;
@@ -291,18 +318,77 @@ class SimpleAutomation {
}
async performAnalysis() {
console.log(`📊 TRUE PARALLEL ANALYSIS: Starting simultaneous analysis for ${this.config.selectedTimeframes.length} timeframes...`);
console.log(`🚀 This will capture ${this.config.selectedTimeframes.length * 2} screenshots in parallel (${this.config.selectedTimeframes.length} timeframes × 2 layouts)`);
console.log(`📊 ANALYSIS CYCLE: Starting analysis for ${this.config.selectedTimeframes.length} timeframes...`);
try {
// 🚨 STEP 1: Check position monitor FIRST to determine market status
const baseUrl = process.env.INTERNAL_API_URL || 'http://localhost:3000';
let hasPosition = false;
let recommendation = 'HOLD';
let activeOrders = 0;
try {
console.log('🔍 Checking position monitor status...');
const monitorResponse = await fetch(`${baseUrl}/api/automation/position-monitor`);
if (monitorResponse.ok) {
const monitorData = await monitorResponse.json();
hasPosition = monitorData.monitor?.hasPosition || false;
recommendation = monitorData.monitor?.recommendation || 'HOLD';
activeOrders = monitorData.monitor?.orphanedOrderCleanup?.summary?.activeOrders || 0;
console.log(`📍 POSITION STATUS: ${hasPosition ? 'ACTIVE POSITION' : 'NO POSITION'}`);
console.log(`🎯 MONITOR RECOMMENDATION: ${recommendation}`);
console.log(`📋 ACTIVE ORDERS: ${activeOrders}`);
} else {
console.warn(`⚠️ Position monitor API error: ${monitorResponse.status}`);
}
} catch (monitorError) {
console.warn('⚠️ Could not fetch position monitor, using defaults:', monitorError.message);
}
// 🚨 NO POSITION SCENARIO - SCALPING MODE
if (!hasPosition) {
console.log('🏃‍♂️ SCALPING MODE: No position detected - aggressive re-entry needed');
// Clean up any remaining orders first
if (activeOrders > 0) {
console.log('🧹 CLEANUP: Canceling remaining orders before new entry...');
try {
const cleanupResponse = await fetch(`${baseUrl}/api/drift/cleanup-orders`, {
method: 'POST'
});
if (cleanupResponse.ok) {
const cleanupResult = await cleanupResponse.json();
console.log(`✅ CLEANUP COMPLETE: ${cleanupResult.summary?.totalCanceled || 0} orders canceled`);
} else {
console.warn(`⚠️ Cleanup API error: ${cleanupResponse.status}`);
}
} catch (cleanupError) {
console.warn('⚠️ Order cleanup failed:', cleanupError.message);
}
}
// For scalping, if no position, we ALWAYS analyze for immediate re-entry
if (recommendation === 'START_TRADING' || !hasPosition) {
console.log('🚀 IMMEDIATE ANALYSIS: Market is clear, scanning for entry opportunities...');
// Continue with analysis below
}
} else {
console.log('📊 POSITION MONITORING: Existing position detected, normal analysis');
}
// 🚨 STEP 2: Perform parallel analysis with better error handling
console.log(`🚀 PARALLEL SCREENSHOTS: ${this.config.selectedTimeframes.length * 2} captures starting...`);
try {
// Use correct internal port for server-side API calls
const baseUrl = process.env.INTERNAL_API_URL || 'http://localhost:9001';
const response = await fetch(`${baseUrl}/api/batch-analysis`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
symbol: this.config.symbol,
timeframes: this.config.selectedTimeframes, // All timeframes at once!
timeframes: this.config.selectedTimeframes,
layouts: ['ai', 'diy'],
analyze: true
})
@@ -337,12 +423,13 @@ class SimpleAutomation {
console.warn('⚠️ BATCH ANALYSIS: No valid data, falling back to sequential...');
return this.performSequentialAnalysis();
}
} catch (error) {
} catch (batchError) {
// Track network errors
this.stats.networkErrors++;
this.stats.consecutiveErrors++;
console.error(`❌ PARALLEL ANALYSIS FAILED (Network Error #${this.stats.networkErrors}):`, error.message);
console.error(`❌ PARALLEL ANALYSIS FAILED (Network Error #${this.stats.networkErrors}):`, batchError.message);
// If too many consecutive errors, slow down
if (this.stats.consecutiveErrors >= 3) {
@@ -352,6 +439,19 @@ class SimpleAutomation {
console.log(`🔄 FALLBACK: Using sequential analysis...`);
return this.performSequentialAnalysis();
}
} catch (error) {
// Main try-catch for the entire performAnalysis method
this.stats.consecutiveErrors++;
console.error('❌ ANALYSIS ERROR:', error.message);
// Fallback to sequential if all else fails
try {
return this.performSequentialAnalysis();
} catch (fallbackError) {
console.error('❌ SEQUENTIAL ANALYSIS ALSO FAILED:', fallbackError.message);
}
}
}
// Fallback sequential analysis method
@@ -366,7 +466,7 @@ class SimpleAutomation {
console.log(`📊 ANALYZING: ${timeframe} timeframe...`);
// Use the enhanced screenshot API for each timeframe
const baseUrl = process.env.INTERNAL_API_URL || 'http://localhost:9001';
const baseUrl = process.env.INTERNAL_API_URL || 'http://localhost:3000';
const response = await fetch(`${baseUrl}/api/enhanced-screenshot`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -457,28 +557,63 @@ class SimpleAutomation {
}
shouldExecuteTrade(analysis) {
// Always allow trade execution - the useRealDEX flag determines if it's real or simulated
console.log(`<EFBFBD> TRADE MODE: ${this.config.mode || 'SIMULATION'} - Trading ${this.config.enableTrading ? 'ENABLED' : 'DISABLED'}`);
console.log(`🎯 TRADE MODE: ${this.config.mode || 'SIMULATION'} - Trading ${this.config.enableTrading ? 'ENABLED' : 'DISABLED'}`);
const recommendation = analysis.recommendation?.toLowerCase() || '';
const confidence = analysis.confidence || 0;
// Strategy-specific confidence requirements
let minConfidence = 75;
if (this.config.selectedTimeframes?.includes('5') || this.config.selectedTimeframes?.includes('15')) {
minConfidence = 80; // Higher confidence for scalping
// 🚨 DYNAMIC CONFIDENCE BASED ON POSITION STATUS
// Check if we have an active position
let hasActivePosition = false;
try {
// This will be updated by the position monitor check in performAnalysis
// For now, assume no position if we're running scalping analysis
const isScalping = this.config.selectedTimeframes?.includes('5m') ||
this.config.selectedTimeframes?.includes('15m');
hasActivePosition = false; // Will be properly detected in position monitor
} catch (error) {
console.warn('⚠️ Could not determine position status for confidence adjustment');
}
// 🎯 AGGRESSIVE SCALPING CONFIDENCE when NO POSITION
let minConfidence = 75; // Default confidence
if (!hasActivePosition) {
// NO POSITION = Lower confidence threshold for faster re-entry
if (this.config.selectedTimeframes?.includes('5m')) {
minConfidence = 65; // Very aggressive for 5m scalping
console.log('🏃‍♂️ SCALPING MODE: Using aggressive 65% confidence (no position)');
} else if (this.config.selectedTimeframes?.includes('15m')) {
minConfidence = 70; // Aggressive for 15m scalping
console.log('⚡ SCALPING MODE: Using aggressive 70% confidence (no position)');
} else {
minConfidence = 72; // Slightly lower for day trading
console.log('📈 DAY TRADING: Using 72% confidence (no position)');
}
} else {
// EXISTING POSITION = Higher confidence threshold for additional entries
if (this.config.selectedTimeframes?.includes('5m') || this.config.selectedTimeframes?.includes('15m')) {
minConfidence = 80; // Higher confidence for scalping with existing position
console.log('⚖️ POSITION EXISTS: Using conservative 80% confidence');
} else {
minConfidence = 75; // Standard confidence for day trading
console.log('📊 POSITION EXISTS: Using standard 75% confidence');
}
}
const isHighConfidence = confidence >= minConfidence;
const isClearDirection = recommendation.includes('buy') || recommendation.includes('sell');
const isClearDirection = recommendation.includes('buy') || recommendation.includes('sell') ||
recommendation.includes('long') || recommendation.includes('short');
console.log('🎯 TRADE DECISION: ' + recommendation + ' (' + confidence + '%) - Min: ' + minConfidence + '%');
console.log(`🎯 TRADE DECISION: ${recommendation} (${confidence}%) vs Required: ${minConfidence}%`);
console.log(`✅ Will Execute: ${isHighConfidence && isClearDirection ? 'YES' : 'NO'}`);
// 🧠 RECORD AI DECISION FOR LEARNING
this.recordAIDecisionForLearning(analysis, {
recommendation,
confidence,
minConfidenceRequired: minConfidence,
hasActivePosition,
willExecute: isHighConfidence && isClearDirection
});
@@ -488,11 +623,12 @@ class SimpleAutomation {
recommendation: analysis.recommendation || 'HOLD',
confidence: confidence,
minConfidenceRequired: minConfidence,
hasActivePosition: hasActivePosition,
reasoning: analysis.reasoning || analysis.summary || 'No detailed reasoning available',
executed: false, // Will be updated if trade is executed
executed: false,
executionDetails: null,
executionError: null,
learningRecorded: true // Indicate learning system recorded this
learningRecorded: true
};
return isHighConfidence && isClearDirection;
@@ -504,7 +640,7 @@ class SimpleAutomation {
console.log('📊 Analysis data:', JSON.stringify(analysis, null, 2));
// Check if we already have a position to prevent fragmentation
const apiBaseUrl = process.env.INTERNAL_API_URL || 'http://localhost:9001';
const apiBaseUrl = process.env.INTERNAL_API_URL || 'http://localhost:3000';
const existingPositions = await fetch(`${apiBaseUrl}/api/drift/positions`);
const positionsData = await existingPositions.json();
@@ -623,9 +759,8 @@ class SimpleAutomation {
let availableBalance = 49; // fallback
try {
const baseUrl = process.env.INTERNAL_API_URL || 'http://localhost:9001';
console.log('🔧 DEBUG: Fetching balance from:', baseUrl);
const balanceResponse = await fetch(`${baseUrl}/api/drift/balance`);
console.log('🔧 DEBUG: Fetching balance from:', apiBaseUrl);
const balanceResponse = await fetch(`${apiBaseUrl}/api/drift/balance`);
const balanceData = await balanceResponse.json();
if (balanceData.success) {
accountValue = balanceData.accountValue;
@@ -680,8 +815,7 @@ class SimpleAutomation {
console.log('📊 TRADE PAYLOAD:', tradePayload);
const baseUrl = process.env.INTERNAL_API_URL || 'http://localhost:9001';
const response = await fetch(`${baseUrl}/api/trading/execute-drift`, {
const response = await fetch(`${apiBaseUrl}/api/trading/execute-drift`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(tradePayload)
@@ -743,7 +877,7 @@ class SimpleAutomation {
console.log(`💰 Adding $${dcaAmount} to existing position with AI-calculated levels`);
// Use the position scaling API
const baseUrl = process.env.INTERNAL_API_URL || 'http://localhost:9001';
const baseUrl = process.env.INTERNAL_API_URL || 'http://localhost:3000';
const response = await fetch(`${baseUrl}/api/drift/scale-position`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },