New Features: - 📊 Detailed Market Analysis Panel (similar to pro trading interface) * Market sentiment, recommendation, resistance/support levels * Detailed trading setup with entry/exit points * Risk management with R:R ratios and confirmation triggers * Technical indicators (RSI, OBV, VWAP) analysis - 🧠 AI Learning Insights Panel * Real-time learning status and success rates * Winner/Loser trade outcome tracking * AI reflection messages explaining what was learned * Current thresholds and pattern recognition data - 🔮 AI Database Integration * Shows what AI learned from previous trades * Current confidence thresholds and risk parameters * Pattern recognition for symbol/timeframe combinations * Next trade adjustments based on learning - 🎓 Intelligent Learning from Outcomes * Automatic trade outcome analysis (winner/loser) * AI generates learning insights from each trade result * Confidence adjustment based on trade performance * Pattern reinforcement or correction based on results - Beautiful gradient panels with color-coded sections - Clear winner/loser indicators with visual feedback - Expandable detailed analysis view - Real-time learning progress tracking - Completely isolated paper trading (no real money risk) - Real market data integration for authentic learning - Safe practice environment with professional analysis tools This provides a complete AI learning trading simulation where users can: 1. Get real market analysis with detailed reasoning 2. Execute safe paper trades with zero risk 3. See immediate feedback on trade outcomes 4. Learn from AI reflections and insights 5. Understand how AI adapts and improves over time
1442 lines
60 KiB
JavaScript
1442 lines
60 KiB
JavaScript
// Simple automation service for basic start/stop functionality
|
||
|
||
import { SimplifiedStopLossLearner } from './simplified-stop-loss-learner-fixed.js';
|
||
|
||
// Import AI Leverage Calculator for dynamic leverage
|
||
async function importAILeverageCalculator() {
|
||
try {
|
||
const { AILeverageCalculator } = await import('./ai-leverage-calculator.js');
|
||
return AILeverageCalculator;
|
||
} catch (error) {
|
||
console.warn('⚠️ AI Leverage Calculator not available, using default leverage');
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// Import Enhanced Risk Manager with Learning for intelligent beach mode operation
|
||
async function importEnhancedRiskManager() {
|
||
try {
|
||
const EnhancedAutonomousRiskManager = require('./enhanced-autonomous-risk-manager.js');
|
||
return EnhancedAutonomousRiskManager;
|
||
} catch (error) {
|
||
console.warn('⚠️ Enhanced Risk Manager not available, falling back to stable monitor');
|
||
// Fallback to stable risk monitor
|
||
try {
|
||
const StableRiskMonitor = require('./stable-risk-monitor.js');
|
||
return StableRiskMonitor;
|
||
} catch (fallbackError) {
|
||
console.warn('⚠️ Stable Risk Monitor also not available, using basic monitoring');
|
||
return null;
|
||
}
|
||
}
|
||
}
|
||
|
||
class SimpleAutomation {
|
||
constructor() {
|
||
this.isRunning = false;
|
||
this.config = null;
|
||
this.intervalId = null;
|
||
this.riskManager = null; // Autonomous AI Risk Manager
|
||
this.lastDecision = null; // Store last AI decision for UI display
|
||
this.lastDCATime = 0; // Track last DCA execution time
|
||
this.dcaCooldownHours = 2; // Minimum 2 hours between DCA trades
|
||
|
||
// Initialize AI Learning System
|
||
this.learner = new SimplifiedStopLossLearner();
|
||
console.log('🧠 AI Learning System initialized');
|
||
|
||
this.stats = {
|
||
totalCycles: 0,
|
||
totalTrades: 0,
|
||
startTime: null,
|
||
lastActivity: null,
|
||
status: 'Stopped',
|
||
networkErrors: 0,
|
||
consecutiveErrors: 0
|
||
};
|
||
}
|
||
|
||
async start(config) {
|
||
try {
|
||
if (this.isRunning) {
|
||
return { success: false, message: 'Automation already running' };
|
||
}
|
||
|
||
// Validate basic config
|
||
if (!config.selectedTimeframes || config.selectedTimeframes.length === 0) {
|
||
return { success: false, message: 'At least one timeframe required' };
|
||
}
|
||
|
||
this.config = config;
|
||
this.isRunning = true;
|
||
this.stats.startTime = new Date().toISOString();
|
||
this.stats.status = 'Running';
|
||
|
||
console.log('✅ AUTOMATION STATUS: isRunning =', this.isRunning);
|
||
console.log('🎯 LIVE TRADING:', this.config.enableTrading ? 'ENABLED' : 'DISABLED');
|
||
this.stats.totalCycles = 0;
|
||
|
||
// Initialize Enhanced AI Risk Manager with Learning Capabilities
|
||
try {
|
||
const EnhancedRiskManagerClass = await importEnhancedRiskManager();
|
||
if (EnhancedRiskManagerClass) {
|
||
this.riskManager = new EnhancedRiskManagerClass();
|
||
console.log('🧠 ENHANCED BEACH MODE: AI learning system activated');
|
||
console.log('🎯 System will learn from every decision and improve over time');
|
||
|
||
// Start enhanced autonomous operation
|
||
setTimeout(() => {
|
||
if (this.riskManager && this.riskManager.beachMode) {
|
||
this.riskManager.beachMode();
|
||
console.log('🏖️ Full autonomous operation with AI learning active');
|
||
}
|
||
}, 2000);
|
||
}
|
||
} catch (error) {
|
||
console.log('🔄 Continuing without enhanced autonomous risk monitoring');
|
||
console.error('Risk manager initialization error:', error.message);
|
||
}
|
||
|
||
// Auto-enable trading when in LIVE mode
|
||
if (config.mode === 'LIVE') {
|
||
this.config.enableTrading = true;
|
||
console.log('🔥 LIVE TRADING ENABLED: Real trades will be executed');
|
||
} else {
|
||
this.config.enableTrading = false;
|
||
console.log('📊 SIMULATION MODE: Trades will be simulated only');
|
||
}
|
||
|
||
// Detect strategy
|
||
const timeframes = config.selectedTimeframes;
|
||
let strategy = 'General';
|
||
|
||
const isScalping = timeframes.includes('5') || timeframes.includes('15') || timeframes.includes('30');
|
||
const isDayTrading = timeframes.includes('60') || timeframes.includes('120');
|
||
const isSwingTrading = timeframes.includes('240') || timeframes.includes('D');
|
||
|
||
if (isScalping) strategy = 'Scalping';
|
||
else if (isDayTrading) strategy = 'Day Trading';
|
||
else if (isSwingTrading) strategy = 'Swing Trading';
|
||
|
||
console.log('SIMPLE AUTOMATION: Starting ' + strategy + ' strategy');
|
||
console.log('TIMEFRAMES: [' + timeframes.join(', ') + ']');
|
||
console.log('MODE: ' + (config.mode || 'SIMULATION'));
|
||
|
||
// Start dynamic monitoring cycle with risk-based intervals
|
||
console.log('🚀 STARTING: Dynamic monitoring with scalping optimization');
|
||
this.currentInterval = 30 * 1000; // Start with 30 seconds for immediate analysis
|
||
this.startDynamicMonitoring();
|
||
|
||
// First cycle after 5 seconds (immediate for scalping)
|
||
setTimeout(() => {
|
||
console.log('🔥 IMMEDIATE CYCLE: Starting first analysis cycle');
|
||
this.runCycle();
|
||
}, 5000);
|
||
|
||
return {
|
||
success: true,
|
||
message: strategy + ' automation started successfully',
|
||
strategy: strategy,
|
||
timeframes: timeframes
|
||
};
|
||
|
||
} catch (error) {
|
||
console.error('Failed to start automation:', error);
|
||
return { success: false, message: 'Failed to start automation: ' + error.message };
|
||
}
|
||
}
|
||
|
||
async stop() {
|
||
try {
|
||
console.log('🛑 STOPPING AUTOMATION...');
|
||
this.isRunning = false;
|
||
this.stats.status = 'Stopped';
|
||
console.log('✅ AUTOMATION STATUS: isRunning =', this.isRunning);
|
||
|
||
if (this.intervalId) {
|
||
clearTimeout(this.intervalId); // Changed from clearInterval to clearTimeout
|
||
this.intervalId = null;
|
||
}
|
||
|
||
// Stop risk monitor if running
|
||
if (this.riskManager && this.riskManager.stop) {
|
||
this.riskManager.stop();
|
||
console.log('🏖️ Beach mode monitoring stopped');
|
||
}
|
||
|
||
console.log('SIMPLE AUTOMATION: Stopped');
|
||
|
||
return { success: true, message: 'Automation stopped successfully' };
|
||
} catch (error) {
|
||
console.error('Failed to stop automation:', error);
|
||
return { success: false, message: 'Failed to stop automation: ' + error.message };
|
||
}
|
||
}
|
||
|
||
// Dynamic monitoring with risk-based intervals
|
||
startDynamicMonitoring() {
|
||
const runMonitoringCycle = async () => {
|
||
if (!this.isRunning) return;
|
||
|
||
await this.runCycle();
|
||
|
||
// Get current risk level from position monitor
|
||
const nextInterval = await this.getNextInterval();
|
||
|
||
// Schedule next cycle with dynamic interval
|
||
this.intervalId = setTimeout(runMonitoringCycle, nextInterval);
|
||
};
|
||
|
||
// Start the dynamic cycle
|
||
this.intervalId = setTimeout(runMonitoringCycle, this.currentInterval);
|
||
}
|
||
|
||
// Determine next interval based on risk level and position status
|
||
async getNextInterval() {
|
||
try {
|
||
// 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();
|
||
|
||
// 🚨 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
|
||
break;
|
||
case 'HIGH':
|
||
riskMultiplier = 0.7; // 30% faster when high risk
|
||
break;
|
||
case 'MEDIUM':
|
||
riskMultiplier = 1.0; // Normal speed
|
||
break;
|
||
case 'LOW':
|
||
riskMultiplier = 1.5; // 50% slower when low risk
|
||
break;
|
||
case 'NONE':
|
||
default:
|
||
// 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} | 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 ${finalMinutes} minutes`);
|
||
|
||
this.currentInterval = intervalMs;
|
||
return intervalMs;
|
||
}
|
||
} catch (error) {
|
||
console.warn('⚠️ Failed to get risk level for dynamic interval, using default 10 minutes:', error.message);
|
||
}
|
||
|
||
// Fallback to 10 minutes
|
||
this.currentInterval = 10 * 60 * 1000;
|
||
return this.currentInterval;
|
||
}
|
||
|
||
async runCycle() {
|
||
try {
|
||
// Check if automation should still be running
|
||
if (!this.isRunning) {
|
||
console.log('⏹️ AUTOMATION STOPPED: Skipping cycle');
|
||
return;
|
||
}
|
||
|
||
this.stats.totalCycles++;
|
||
this.stats.lastActivity = new Date().toISOString();
|
||
|
||
console.log('🔄 AUTOMATION CYCLE ' + this.stats.totalCycles + ': ' + (this.config?.symbol || 'SOLUSD'));
|
||
console.log('⏰ TIME: ' + new Date().toLocaleTimeString());
|
||
console.log('📊 STRATEGY: ' + (this.config.selectedTimeframes?.join(', ') || 'N/A') + ' timeframes');
|
||
|
||
if (this.config) {
|
||
// Perform actual analysis using enhanced screenshot API
|
||
await this.performAnalysis();
|
||
|
||
// Reset error counter on successful cycle
|
||
this.stats.consecutiveErrors = 0;
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('❌ CRITICAL ERROR in automation cycle:', error);
|
||
console.error('❌ Stack trace:', error.stack);
|
||
|
||
// Increment error counter
|
||
this.stats.consecutiveErrors = (this.stats.consecutiveErrors || 0) + 1;
|
||
|
||
// If too many consecutive errors, stop automation
|
||
if (this.stats.consecutiveErrors >= 3) {
|
||
console.error('🚨 TOO MANY ERRORS: Stopping automation after', this.stats.consecutiveErrors, 'consecutive failures');
|
||
this.isRunning = false;
|
||
this.stats.status = 'Stopped due to errors';
|
||
|
||
if (this.intervalId) {
|
||
clearTimeout(this.intervalId); // Changed from clearInterval to clearTimeout
|
||
this.intervalId = null;
|
||
}
|
||
return;
|
||
}
|
||
|
||
console.log(`⚠️ Error ${this.stats.consecutiveErrors}/3 - Will retry next cycle`);
|
||
}
|
||
}
|
||
|
||
async performAnalysis() {
|
||
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');
|
||
|
||
// 🧹 MANDATORY CLEANUP: Always clear ALL orders before re-entry (regardless of count)
|
||
console.log('🧹 MANDATORY CLEANUP: Clearing all orphaned orders before re-entry...');
|
||
try {
|
||
const cleanupResponse = await fetch(`${baseUrl}/api/drift/cleanup-orders`, {
|
||
method: 'POST'
|
||
});
|
||
if (cleanupResponse.ok) {
|
||
const cleanupResult = await cleanupResponse.json();
|
||
const totalCanceled = cleanupResult.summary?.totalCanceled || 0;
|
||
const activeOrdersAfter = cleanupResult.summary?.activeOrders || 0;
|
||
|
||
console.log(`✅ CLEANUP COMPLETE: ${totalCanceled} orders canceled`);
|
||
console.log(`📊 CLEANUP RESULT: ${activeOrdersAfter} orders remaining`);
|
||
|
||
// Additional safety check - if orders still remain, try cancel-all endpoint
|
||
if (activeOrdersAfter > 0) {
|
||
console.log('🔄 ADDITIONAL CLEANUP: Found remaining orders, using cancel-all...');
|
||
const cancelAllResponse = await fetch(`${baseUrl}/api/drift/cancel-all-orders`, {
|
||
method: 'POST'
|
||
});
|
||
if (cancelAllResponse.ok) {
|
||
const cancelAllResult = await cancelAllResponse.json();
|
||
console.log(`✅ CANCEL-ALL COMPLETE: ${cancelAllResult.totalCanceled || 0} additional orders canceled`);
|
||
}
|
||
}
|
||
} else {
|
||
console.warn(`⚠️ Cleanup API error: ${cleanupResponse.status}`);
|
||
}
|
||
} catch (cleanupError) {
|
||
console.warn('⚠️ Order cleanup failed:', cleanupError.message);
|
||
|
||
// Fallback: Try direct cancel-all as backup
|
||
try {
|
||
console.log('🔄 FALLBACK CLEANUP: Attempting direct cancel-all...');
|
||
const fallbackResponse = await fetch(`${baseUrl}/api/drift/cancel-all-orders`, {
|
||
method: 'POST'
|
||
});
|
||
if (fallbackResponse.ok) {
|
||
const fallbackResult = await fallbackResponse.json();
|
||
console.log(`✅ FALLBACK COMPLETE: ${fallbackResult.totalCanceled || 0} orders canceled`);
|
||
}
|
||
} catch (fallbackError) {
|
||
console.warn('⚠️ Fallback cleanup also failed:', fallbackError.message);
|
||
}
|
||
}
|
||
|
||
// 🔍 VERIFY CLEANUP: Wait a moment and verify orders are cleared
|
||
await new Promise(resolve => setTimeout(resolve, 1000)); // 1 second delay
|
||
|
||
// For scalping, if no position, we ALWAYS analyze for immediate re-entry
|
||
if (recommendation === 'START_TRADING' || !hasPosition) {
|
||
console.log('🚀 IMMEDIATE ANALYSIS: Market cleared, 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 {
|
||
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,
|
||
layouts: ['ai', 'diy'],
|
||
analyze: true
|
||
})
|
||
});
|
||
|
||
if (!response.ok) {
|
||
console.warn(`⚠️ BATCH API ERROR: ${response.status}, falling back to sequential...`);
|
||
return this.performSequentialAnalysis();
|
||
}
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success && result.analysis) {
|
||
// Reset consecutive error counter on success
|
||
this.stats.consecutiveErrors = 0;
|
||
|
||
console.log(`✅ PARALLEL ANALYSIS COMPLETE: ${result.totalScreenshots} screenshots captured`);
|
||
console.log(`📊 COMBINED Recommendation: ${result.analysis.recommendation}`);
|
||
console.log(`🎯 COMBINED Confidence: ${result.analysis.confidence}%`);
|
||
console.log(`⏰ Timeframes analyzed: ${result.timeframes.join(', ')}`);
|
||
|
||
// Check if we should execute a trade based on combined analysis
|
||
if (await this.shouldExecuteTrade(result.analysis)) {
|
||
console.log('💰 TRADE SIGNAL: Executing trade...');
|
||
await this.executeTrade(result.analysis);
|
||
} else {
|
||
console.log('📈 SIGNAL: Combined analysis complete, no trade executed');
|
||
}
|
||
|
||
return;
|
||
} else {
|
||
console.warn('⚠️ BATCH ANALYSIS: No valid data, falling back to sequential...');
|
||
return this.performSequentialAnalysis();
|
||
}
|
||
|
||
} catch (batchError) {
|
||
// Track network errors
|
||
this.stats.networkErrors++;
|
||
this.stats.consecutiveErrors++;
|
||
|
||
console.error(`❌ PARALLEL ANALYSIS FAILED (Network Error #${this.stats.networkErrors}):`, batchError.message);
|
||
|
||
// If too many consecutive errors, slow down
|
||
if (this.stats.consecutiveErrors >= 3) {
|
||
console.warn(`⚠️ HIGH NETWORK ERROR COUNT: ${this.stats.consecutiveErrors} consecutive failures. System will continue but may slow down.`);
|
||
}
|
||
|
||
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
|
||
async performSequentialAnalysis() {
|
||
try {
|
||
console.log('📸 SEQUENTIAL ANALYSIS: Starting...');
|
||
|
||
const allResults = [];
|
||
|
||
// Analyze each timeframe separately
|
||
for (const timeframe of this.config.selectedTimeframes) {
|
||
console.log(`📊 ANALYZING: ${timeframe} timeframe...`);
|
||
|
||
// Use the enhanced screenshot API for each timeframe
|
||
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' },
|
||
body: JSON.stringify({
|
||
symbol: this.config.symbol,
|
||
timeframe: timeframe, // Send one timeframe at a time
|
||
layouts: ['ai', 'diy'],
|
||
analyze: true
|
||
})
|
||
});
|
||
|
||
if (!response.ok) {
|
||
console.log(`⚠️ TIMEFRAME ${timeframe}: API error ${response.status}`);
|
||
continue;
|
||
}
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.analysis) {
|
||
console.log(`✅ TIMEFRAME ${timeframe}: ${result.analysis.recommendation} (${result.analysis.confidence}%)`);
|
||
allResults.push({
|
||
timeframe: timeframe,
|
||
analysis: result.analysis,
|
||
screenshots: result.screenshots
|
||
});
|
||
} else {
|
||
console.log(`⚠️ TIMEFRAME ${timeframe}: No analysis data`);
|
||
}
|
||
}
|
||
|
||
if (allResults.length > 0) {
|
||
console.log('✅ MULTI-TIMEFRAME ANALYSIS COMPLETE:');
|
||
|
||
// Combine results for overall decision
|
||
const combinedAnalysis = this.combineTimeframeAnalysis(allResults);
|
||
|
||
console.log('📊 COMBINED Recommendation: ' + combinedAnalysis.recommendation);
|
||
console.log('🎯 COMBINED Confidence: ' + combinedAnalysis.confidence + '%');
|
||
console.log('⏰ Timeframes analyzed: ' + allResults.map(r => r.timeframe).join(', '));
|
||
|
||
// Check if we should execute a trade based on combined analysis
|
||
if (await this.shouldExecuteTrade(combinedAnalysis)) {
|
||
console.log('💰 TRADE SIGNAL: Executing trade...');
|
||
await this.executeTrade(combinedAnalysis);
|
||
} else {
|
||
console.log('📈 SIGNAL: Combined analysis complete, no trade executed');
|
||
}
|
||
} else {
|
||
console.log('⚠️ ANALYSIS: No valid analysis data from any timeframe');
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('❌ ANALYSIS ERROR:', error.message);
|
||
}
|
||
}
|
||
|
||
combineTimeframeAnalysis(results) {
|
||
if (results.length === 1) {
|
||
return results[0].analysis;
|
||
}
|
||
|
||
// Simple combination logic - can be enhanced
|
||
const recommendations = results.map(r => r.analysis.recommendation?.toLowerCase() || 'hold');
|
||
const confidences = results.map(r => r.analysis.confidence || 0);
|
||
|
||
// Count votes for each recommendation
|
||
const votes = {};
|
||
recommendations.forEach(rec => {
|
||
votes[rec] = (votes[rec] || 0) + 1;
|
||
});
|
||
|
||
// Get majority recommendation
|
||
const majorityRec = Object.keys(votes).reduce((a, b) => votes[a] > votes[b] ? a : b);
|
||
|
||
// Average confidence, but boost if multiple timeframes agree
|
||
const avgConfidence = confidences.reduce((sum, conf) => sum + conf, 0) / confidences.length;
|
||
const agreementBonus = votes[majorityRec] > 1 ? 10 : 0; // +10% if multiple agree
|
||
const finalConfidence = Math.min(95, avgConfidence + agreementBonus);
|
||
|
||
console.log(`🔄 CONSENSUS: ${majorityRec.toUpperCase()} from ${votes[majorityRec]}/${results.length} timeframes`);
|
||
|
||
return {
|
||
recommendation: majorityRec,
|
||
confidence: Math.round(finalConfidence),
|
||
reasoning: `Multi-timeframe consensus from ${results.length} timeframes (${results.map(r => r.timeframe).join(', ')})`,
|
||
timeframeResults: results
|
||
};
|
||
}
|
||
|
||
async shouldExecuteTrade(analysis) {
|
||
console.log(`🎯 TRADE MODE: ${this.config.mode || 'SIMULATION'} - Trading ${this.config.enableTrading ? 'ENABLED' : 'DISABLED'}`);
|
||
|
||
const recommendation = analysis.recommendation?.toLowerCase() || '';
|
||
const confidence = analysis.confidence || 0;
|
||
|
||
// 🚨 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');
|
||
}
|
||
|
||
// 🎯 IMPROVED CONFIDENCE THRESHOLDS - Higher standards to reduce losses
|
||
let minConfidence = 80; // Increased default confidence
|
||
|
||
if (!hasActivePosition) {
|
||
// NO POSITION = Still require high confidence for quality entries
|
||
if (this.config.selectedTimeframes?.includes('1h')) {
|
||
minConfidence = 75; // 1h timeframe - medium confidence
|
||
console.log('📊 1H TRADING: Using 75% confidence threshold (no position)');
|
||
} else if (this.config.selectedTimeframes?.includes('4h')) {
|
||
minConfidence = 70; // 4h timeframe - lower confidence OK due to higher reliability
|
||
console.log('📈 4H TRADING: Using 70% confidence threshold (no position)');
|
||
} else if (this.config.selectedTimeframes?.includes('5m')) {
|
||
minConfidence = 80; // 5m requires very high confidence due to noise
|
||
console.log('⚡ 5M SCALPING: Using high 80% confidence (noisy timeframe)');
|
||
} else if (this.config.selectedTimeframes?.includes('15m')) {
|
||
minConfidence = 78; // 15m also requires high confidence
|
||
console.log('🏃♂️ 15M SCALPING: Using high 78% confidence (noisy timeframe)');
|
||
} 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') ||
|
||
recommendation.includes('long') || recommendation.includes('short');
|
||
|
||
// 🧠 GET AI LEARNING RECOMMENDATION TO INFLUENCE DECISION
|
||
let finalWillExecute = isHighConfidence && isClearDirection;
|
||
let learningInfluence = null;
|
||
|
||
try {
|
||
const learningRec = await this.getAILearningRecommendation(analysis);
|
||
if (learningRec) {
|
||
learningInfluence = learningRec;
|
||
console.log(`🧠 AI LEARNING INPUT: ${learningRec.action} (${(learningRec.confidence * 100).toFixed(1)}% confidence)`);
|
||
console.log(`📚 Learning Reasoning: ${learningRec.reasoning}`);
|
||
|
||
// Adjust decision based on learning
|
||
if (learningRec.action === 'HOLD_POSITION' && learningRec.confidence > 0.7) {
|
||
console.log('🧠 AI Learning suggests HOLD - reducing execution likelihood');
|
||
finalWillExecute = finalWillExecute && (confidence >= (minConfidence + 10)); // Require 10% higher confidence
|
||
} else if (learningRec.action === 'EXECUTE_TRADE' && learningRec.confidence > 0.7) {
|
||
console.log('🧠 AI Learning suggests EXECUTE - lowering confidence threshold');
|
||
finalWillExecute = (confidence >= (minConfidence - 5)) && isClearDirection; // Allow 5% lower confidence
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.log('⚠️ Learning recommendation error:', error.message);
|
||
}
|
||
|
||
console.log(`🎯 TRADE DECISION: ${recommendation} (${confidence}%) vs Required: ${minConfidence}%`);
|
||
console.log(`🧠 Learning Influence: ${learningInfluence ? learningInfluence.action : 'None'}`);
|
||
console.log(`✅ Final Decision: ${finalWillExecute ? 'EXECUTE' : 'HOLD'}`);
|
||
|
||
// 🧠 RECORD AI DECISION FOR LEARNING
|
||
this.recordAIDecisionForLearning(analysis, {
|
||
recommendation,
|
||
confidence,
|
||
minConfidenceRequired: minConfidence,
|
||
hasActivePosition,
|
||
willExecute: finalWillExecute,
|
||
learningInfluence: learningInfluence
|
||
});
|
||
|
||
// Store decision data for UI display
|
||
this.lastDecision = {
|
||
timestamp: new Date().toISOString(),
|
||
recommendation: analysis.recommendation || 'HOLD',
|
||
confidence: confidence,
|
||
minConfidenceRequired: minConfidence,
|
||
hasActivePosition: hasActivePosition,
|
||
reasoning: analysis.reasoning || analysis.summary || 'No detailed reasoning available',
|
||
executed: false,
|
||
executionDetails: null,
|
||
executionError: null,
|
||
learningRecorded: true,
|
||
learningInfluence: learningInfluence
|
||
};
|
||
|
||
return finalWillExecute;
|
||
}
|
||
|
||
async executeTrade(analysis) {
|
||
try {
|
||
console.log('💰 EXECUTING TRADE...');
|
||
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:3000';
|
||
const existingPositions = await fetch(`${apiBaseUrl}/api/drift/positions`);
|
||
const positionsData = await existingPositions.json();
|
||
|
||
if (positionsData.success && positionsData.positions.length > 0) {
|
||
console.log('🔍 EXISTING POSITION DETECTED - Checking DCA scaling opportunity...');
|
||
|
||
// Check DCA cooldown period to prevent over-execution
|
||
const currentTime = Date.now();
|
||
const timeSinceLastDCA = (currentTime - this.lastDCATime) / (1000 * 60 * 60); // Hours
|
||
|
||
if (timeSinceLastDCA < this.dcaCooldownHours) {
|
||
const remainingCooldown = (this.dcaCooldownHours - timeSinceLastDCA).toFixed(1);
|
||
console.log(`⏰ DCA COOLDOWN ACTIVE - ${remainingCooldown} hours remaining`);
|
||
console.log('🛡️ Preventing DCA over-execution that caused 24+ orders');
|
||
return {
|
||
success: false,
|
||
error: `DCA cooldown active - ${remainingCooldown} hours remaining`,
|
||
existingPosition: positionsData.positions[0],
|
||
cooldownRemaining: remainingCooldown
|
||
};
|
||
}
|
||
|
||
const currentPosition = positionsData.positions[0];
|
||
console.log('📊 Current position:', currentPosition);
|
||
console.log('✅ DCA cooldown passed - executing POSITION SCALING DCA...');
|
||
|
||
// Check if analysis direction matches existing position
|
||
const recommendation = analysis.recommendation?.toLowerCase() || '';
|
||
let analysisDirection = '';
|
||
|
||
if (recommendation.includes('buy')) {
|
||
analysisDirection = 'buy';
|
||
} else if (recommendation.includes('sell')) {
|
||
analysisDirection = 'sell';
|
||
} else {
|
||
return {
|
||
success: false,
|
||
error: `Invalid recommendation for DCA: ${recommendation}`,
|
||
existingPosition: currentPosition,
|
||
suggestedAction: 'Cannot determine direction from analysis'
|
||
};
|
||
}
|
||
|
||
const positionDirection = currentPosition.side.toLowerCase();
|
||
|
||
if (analysisDirection === 'buy' && positionDirection === 'long') {
|
||
console.log('🎯 SCALING LONG POSITION - Adding to existing long position');
|
||
return await this.executePositionScaling(analysis, this.config.tradingAmount || 49);
|
||
} else if (analysisDirection === 'sell' && positionDirection === 'short') {
|
||
console.log('🎯 SCALING SHORT POSITION - Adding to existing short position');
|
||
return await this.executePositionScaling(analysis, this.config.tradingAmount || 49);
|
||
} else {
|
||
console.log('🔄 DIRECTION MISMATCH - Analysis suggests opposite direction');
|
||
console.log(` Position: ${positionDirection.toUpperCase()} | Analysis: ${analysisDirection.toUpperCase()}`);
|
||
return {
|
||
success: false,
|
||
error: `Direction mismatch: Position is ${positionDirection}, analysis suggests ${analysisDirection}`,
|
||
existingPosition: currentPosition,
|
||
suggestedAction: 'Consider position consolidation or exit strategy'
|
||
};
|
||
}
|
||
}
|
||
|
||
// Map analysis recommendation to trading side
|
||
const recommendation = analysis.recommendation?.toLowerCase() || '';
|
||
let side = '';
|
||
|
||
if (recommendation.includes('buy')) {
|
||
side = 'BUY';
|
||
} else if (recommendation.includes('sell')) {
|
||
side = 'SELL';
|
||
} else {
|
||
console.log('❌ TRADE SKIP: Invalid recommendation - ' + recommendation);
|
||
return { success: false, error: 'Invalid recommendation: ' + recommendation };
|
||
}
|
||
|
||
console.log(`🔧 DEBUG: Side variable initialized as: "${side}" from recommendation: "${recommendation}"`);
|
||
|
||
// Validate side is properly set
|
||
if (!side || (side !== 'BUY' && side !== 'SELL')) {
|
||
console.log('❌ TRADE ERROR: Side variable not properly initialized');
|
||
return { success: false, error: 'Side variable initialization error' };
|
||
}
|
||
|
||
// Extract stop loss and take profit from analysis
|
||
let stopLoss = null;
|
||
let takeProfit = null;
|
||
|
||
// Try to extract from the structured analysis format
|
||
if (analysis.stopLoss && typeof analysis.stopLoss === 'object') {
|
||
stopLoss = analysis.stopLoss.price;
|
||
} else if (analysis.stopLoss && typeof analysis.stopLoss === 'number') {
|
||
stopLoss = analysis.stopLoss;
|
||
}
|
||
|
||
// Extract take profit - prefer tp1 if available
|
||
if (analysis.takeProfits && typeof analysis.takeProfits === 'object') {
|
||
if (analysis.takeProfits.tp1 && analysis.takeProfits.tp1.price) {
|
||
takeProfit = analysis.takeProfits.tp1.price;
|
||
} else if (analysis.takeProfits.tp2 && analysis.takeProfits.tp2.price) {
|
||
takeProfit = analysis.takeProfits.tp2.price;
|
||
}
|
||
} else if (analysis.takeProfit && typeof analysis.takeProfit === 'number') {
|
||
takeProfit = analysis.takeProfit;
|
||
}
|
||
|
||
// Fallback: try to extract from nested levels object
|
||
if (!stopLoss && analysis.levels) {
|
||
stopLoss = analysis.levels.stopLoss || analysis.levels.stop;
|
||
}
|
||
if (!takeProfit && analysis.levels) {
|
||
takeProfit = analysis.levels.takeProfit || analysis.levels.target;
|
||
}
|
||
|
||
// Parse numeric values if they're strings
|
||
if (stopLoss && typeof stopLoss === 'string') {
|
||
stopLoss = parseFloat(stopLoss.replace(/[^0-9.]/g, ''));
|
||
}
|
||
if (takeProfit && typeof takeProfit === 'string') {
|
||
takeProfit = parseFloat(takeProfit.replace(/[^0-9.]/g, ''));
|
||
}
|
||
|
||
// 🛡️ FALLBACK RISK MANAGEMENT: Ensure SL/TP always exist
|
||
if (!stopLoss || !takeProfit) {
|
||
console.log('⚠️ Missing AI-calculated SL/TP, generating fallback values...');
|
||
const currentPrice = analysis.entry?.price || analysis.currentPrice || 178;
|
||
|
||
if (!stopLoss) {
|
||
// Fallback: 1.5% stop loss for scalping
|
||
if (side === 'BUY' || side === 'LONG') {
|
||
stopLoss = currentPrice * 0.985; // 1.5% below entry
|
||
} else {
|
||
stopLoss = currentPrice * 1.015; // 1.5% above entry
|
||
}
|
||
console.log(`🔧 Generated fallback stop loss: $${stopLoss.toFixed(4)} (1.5%)`);
|
||
}
|
||
|
||
if (!takeProfit) {
|
||
// Fallback: 3% take profit for scalping (2:1 risk/reward)
|
||
if (side === 'BUY' || side === 'LONG') {
|
||
takeProfit = currentPrice * 1.03; // 3% above entry
|
||
} else {
|
||
takeProfit = currentPrice * 0.97; // 3% below entry
|
||
}
|
||
console.log(`🔧 Generated fallback take profit: $${takeProfit.toFixed(4)} (3%)`);
|
||
}
|
||
}
|
||
|
||
console.log(`🎯 Final trade levels - SL: ${stopLoss}, TP: ${takeProfit}`);
|
||
|
||
// Calculate optimal leverage using AI Leverage Calculator
|
||
let optimalLeverage = 1; // Default fallback
|
||
let leverageResult = null; // Store leverage calculation result for UI display
|
||
console.log('🔧 DEBUG: Starting leverage calculation...');
|
||
try {
|
||
const AILeverageCalculator = await importAILeverageCalculator();
|
||
console.log('🔧 DEBUG: AI Leverage Calculator imported:', !!AILeverageCalculator);
|
||
if (AILeverageCalculator && stopLoss) {
|
||
console.log('🔧 DEBUG: Both calculator and stopLoss available, proceeding...');
|
||
// Get current price from analysis
|
||
const currentPrice = analysis.entry?.price || analysis.currentPrice || 178; // fallback price
|
||
|
||
// Get real account data from Drift API
|
||
let accountValue = 49; // fallback
|
||
let availableBalance = 49; // fallback
|
||
|
||
try {
|
||
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;
|
||
availableBalance = balanceData.availableBalance;
|
||
console.log(`💰 Real account data: $${accountValue.toFixed(2)} total, $${availableBalance.toFixed(2)} available`);
|
||
} else {
|
||
console.log('🔧 DEBUG: Balance API returned error:', balanceData);
|
||
}
|
||
} catch (balanceError) {
|
||
console.warn('⚠️ Failed to get real balance, using fallback:', balanceError.message);
|
||
}
|
||
|
||
console.log(`🧮 Calculating optimal leverage: Entry=$${currentPrice}, StopLoss=$${stopLoss}`);
|
||
console.log(`🔧 DEBUG: Side variable before leverage calc: "${side}"`);
|
||
|
||
const leverageCalcResult = AILeverageCalculator.calculateOptimalLeverage({
|
||
accountValue,
|
||
availableBalance,
|
||
entryPrice: currentPrice,
|
||
stopLossPrice: stopLoss,
|
||
side: side === 'BUY' ? 'long' : 'short',
|
||
maxLeverageAllowed: 100, // Drift platform max (can go up to 101x)
|
||
safetyBuffer: 0.10 // 10% safety buffer
|
||
});
|
||
|
||
// Store the result for UI display
|
||
leverageResult = leverageCalcResult;
|
||
optimalLeverage = leverageCalcResult.recommendedLeverage;
|
||
|
||
optimalLeverage = leverageCalcResult.recommendedLeverage;
|
||
console.log(`🎯 AI Calculated Leverage: ${optimalLeverage.toFixed(1)}x (Risk: ${leverageCalcResult.riskAssessment})`);
|
||
console.log(`📊 Leverage Details: ${leverageCalcResult.reasoning}`);
|
||
} else {
|
||
console.log('🔧 DEBUG: Skipping leverage calc - Calculator:', !!AILeverageCalculator, 'StopLoss:', !!stopLoss);
|
||
}
|
||
} catch (leverageError) {
|
||
console.warn('⚠️ Leverage calculation failed, using default 1x:', leverageError.message);
|
||
}
|
||
|
||
console.log(`🔧 DEBUG: Final leverage to use: ${optimalLeverage}x`);
|
||
|
||
// 🧹 MANDATORY CLEANUP: Clear all orders before new trade execution
|
||
console.log('🧹 PRE-TRADE CLEANUP: Ensuring no orphaned orders before new trade...');
|
||
try {
|
||
const cleanupResponse = await fetch(`${apiBaseUrl}/api/drift/cleanup-orders`, {
|
||
method: 'POST'
|
||
});
|
||
if (cleanupResponse.ok) {
|
||
const cleanupResult = await cleanupResponse.json();
|
||
const totalCanceled = cleanupResult.summary?.totalCanceled || 0;
|
||
console.log(`✅ PRE-TRADE CLEANUP: ${totalCanceled} orders cleared before trade execution`);
|
||
|
||
// Additional safety: If orders still exist, use cancel-all
|
||
if (cleanupResult.summary?.activeOrders > 0) {
|
||
console.log('🔄 ADDITIONAL CLEANUP: Remaining orders found, using cancel-all...');
|
||
const cancelAllResponse = await fetch(`${apiBaseUrl}/api/drift/cancel-all-orders`, {
|
||
method: 'POST'
|
||
});
|
||
if (cancelAllResponse.ok) {
|
||
const cancelAllResult = await cancelAllResponse.json();
|
||
console.log(`✅ CANCEL-ALL COMPLETE: ${cancelAllResult.totalCanceled || 0} additional orders cleared`);
|
||
}
|
||
}
|
||
} else {
|
||
console.warn(`⚠️ Pre-trade cleanup failed: ${cleanupResponse.status}`);
|
||
}
|
||
|
||
// Brief delay to ensure cleanup is processed
|
||
await new Promise(resolve => setTimeout(resolve, 500));
|
||
} catch (cleanupError) {
|
||
console.warn('⚠️ Pre-trade cleanup error:', cleanupError.message);
|
||
}
|
||
|
||
// ✅ PROCEED DIRECTLY TO TRADE EXECUTION (Risk management removed)
|
||
console.log('<27> PROCEEDING WITH TRADE EXECUTION...');
|
||
console.log(` Entry: $${analysis.entry?.price || analysis.currentPrice || 0}`);
|
||
console.log(` SL: $${stopLoss?.toFixed(2) || 'N/A'}`);
|
||
console.log(` TP: $${takeProfit?.toFixed(2) || 'N/A'}`);
|
||
console.log(` Leverage: ${optimalLeverage}x`);
|
||
|
||
// Use the trading API with proper fields for Drift
|
||
const tradePayload = {
|
||
symbol: this.config.symbol,
|
||
side: side,
|
||
amount: this.config.tradingAmount || 49, // Use available balance
|
||
leverage: optimalLeverage, // Use AI-calculated optimal leverage
|
||
stopLoss: stopLoss,
|
||
takeProfit: takeProfit,
|
||
useRealDEX: true, // Enable LIVE trading always
|
||
analysis: analysis // Include analysis for reference
|
||
};
|
||
|
||
console.log('📊 TRADE PAYLOAD:', tradePayload);
|
||
|
||
const response = await fetch(`${apiBaseUrl}/api/trading/execute-drift`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(tradePayload)
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
console.log('✅ TRADE EXECUTED: ' + result.message);
|
||
this.stats.totalTrades = (this.stats.totalTrades || 0) + 1;
|
||
this.stats.successfulTrades = (this.stats.successfulTrades || 0) + 1;
|
||
|
||
// Log the successful trade for live analysis panel
|
||
try {
|
||
const baseUrl = process.env.INTERNAL_API_URL || 'http://localhost:3000';
|
||
|
||
// Use actual execution data from the trade result
|
||
let actualEntryPrice = 0;
|
||
if (result.executionPrice) {
|
||
actualEntryPrice = result.executionPrice;
|
||
} else if (result.price) {
|
||
actualEntryPrice = result.price;
|
||
} else if (analysis.entry?.price) {
|
||
actualEntryPrice = analysis.entry.price;
|
||
} else if (analysis.currentPrice) {
|
||
actualEntryPrice = analysis.currentPrice;
|
||
}
|
||
|
||
await fetch(`${baseUrl}/api/automation/live-decisions`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
type: 'TRADE_EXECUTED',
|
||
action: side?.toUpperCase() || 'UNKNOWN',
|
||
symbol: this.config.symbol,
|
||
blocked: false,
|
||
executed: true,
|
||
confidence: analysis.confidence || 0,
|
||
entryPrice: actualEntryPrice,
|
||
stopLoss: stopLoss,
|
||
takeProfit: takeProfit,
|
||
leverage: optimalLeverage,
|
||
amount: tradePayload.amount,
|
||
reasoning: analysis.reasoning || 'No reasoning provided',
|
||
aiLeverageReasoning: leverageResult ? leverageResult.reasoning : 'AI leverage calculation not available',
|
||
txId: result.transactionId || result.signature,
|
||
timestamp: new Date().toISOString(),
|
||
cycle: this.stats.totalCycles
|
||
})
|
||
});
|
||
} catch (logError) {
|
||
console.warn('⚠️ Failed to log executed trade:', logError.message);
|
||
}
|
||
|
||
// Update DCA timestamp to prevent over-execution
|
||
this.lastDCATime = Date.now();
|
||
console.log(`⏰ DCA cooldown activated - Next DCA possible in ${this.dcaCooldownHours} hours`);
|
||
|
||
// 🧠 TRACK SUCCESSFUL TRADE OUTCOME FOR LEARNING
|
||
await this.trackTradeOutcomeForLearning(result);
|
||
|
||
// Update last decision with execution details
|
||
if (this.lastDecision) {
|
||
this.lastDecision.executed = true;
|
||
this.lastDecision.executionDetails = {
|
||
side: side,
|
||
amount: tradePayload.amount,
|
||
leverage: optimalLeverage,
|
||
currentPrice: analysis.entry?.price || analysis.currentPrice,
|
||
stopLoss: stopLoss,
|
||
takeProfit: takeProfit,
|
||
aiReasoning: leverageResult ? leverageResult.reasoning : 'AI leverage calculation not available',
|
||
txId: result.transactionId || result.signature,
|
||
aiStopLossPercent: analysis.stopLossPercent || 'Not specified'
|
||
};
|
||
}
|
||
} else {
|
||
console.log('❌ TRADE FAILED: ' + result.error);
|
||
|
||
// 🧠 TRACK FAILED TRADE OUTCOME FOR LEARNING
|
||
await this.trackTradeOutcomeForLearning(result);
|
||
|
||
// Update last decision with execution error
|
||
if (this.lastDecision) {
|
||
this.lastDecision.executed = false;
|
||
this.lastDecision.executionError = result.error || 'Unknown execution error';
|
||
}
|
||
}
|
||
|
||
return result;
|
||
} catch (error) {
|
||
console.error('❌ TRADE ERROR:', error.message);
|
||
return { success: false, error: error.message };
|
||
}
|
||
}
|
||
|
||
// Position Scaling DCA - Increase existing position size with adjusted SL/TP
|
||
async executePositionScaling(analysis, dcaAmount) {
|
||
try {
|
||
console.log('🎯 EXECUTING POSITION SCALING DCA...');
|
||
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:3000';
|
||
const response = await fetch(`${baseUrl}/api/drift/scale-position`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
dcaAmount: dcaAmount,
|
||
analysis: analysis // Pass AI analysis for optimal SL/TP levels
|
||
})
|
||
});
|
||
|
||
const result = await response.json();
|
||
|
||
if (result.success) {
|
||
console.log('✅ POSITION SCALING SUCCESSFUL');
|
||
console.log(`📊 Old: ${result.scalingResult.originalSize.toFixed(4)} @ $${result.scalingResult.originalEntryPrice.toFixed(4)}`);
|
||
console.log(`📈 New: ${result.scalingResult.newTotalSize.toFixed(4)} @ $${result.scalingResult.newAveragePrice.toFixed(4)}`);
|
||
console.log(`🛡️ Stop Loss: $${result.scalingResult.newStopLoss.toFixed(4)}`);
|
||
console.log(`🎯 Take Profit: $${result.scalingResult.newTakeProfit.toFixed(4)}`);
|
||
|
||
// 🧠 TRACK SUCCESSFUL POSITION SCALING FOR LEARNING
|
||
await this.trackTradeOutcomeForLearning(result);
|
||
|
||
// Update stats and DCA timestamp
|
||
this.stats.totalTrades = (this.stats.totalTrades || 0) + 1;
|
||
this.stats.successfulTrades = (this.stats.successfulTrades || 0) + 1;
|
||
this.lastDCATime = Date.now();
|
||
|
||
console.log(`⏰ DCA cooldown activated - Next DCA possible in ${this.dcaCooldownHours} hours`);
|
||
|
||
// Update last decision with scaling details
|
||
if (this.lastDecision) {
|
||
this.lastDecision.executed = true;
|
||
this.lastDecision.executionDetails = {
|
||
type: 'POSITION_SCALING',
|
||
dcaAmount: dcaAmount,
|
||
originalSize: result.scalingResult.originalSize,
|
||
newTotalSize: result.scalingResult.newTotalSize,
|
||
originalEntryPrice: result.scalingResult.originalEntryPrice,
|
||
newAveragePrice: result.scalingResult.newAveragePrice,
|
||
newStopLoss: result.scalingResult.newStopLoss,
|
||
newTakeProfit: result.scalingResult.newTakeProfit,
|
||
usedAILevels: result.scalingResult.usedAILevels,
|
||
txIds: {
|
||
dcaTx: result.scalingResult.dcaTxId,
|
||
stopLossTx: result.scalingResult.stopLossTxId,
|
||
takeProfitTx: result.scalingResult.takeProfitTxId
|
||
}
|
||
};
|
||
}
|
||
} else {
|
||
console.log('❌ POSITION SCALING FAILED:', result.error);
|
||
|
||
// Update last decision with error
|
||
if (this.lastDecision) {
|
||
this.lastDecision.executed = false;
|
||
this.lastDecision.executionError = result.error || 'Position scaling failed';
|
||
}
|
||
}
|
||
|
||
return result;
|
||
} catch (error) {
|
||
console.error('❌ POSITION SCALING ERROR:', error.message);
|
||
return { success: false, error: error.message };
|
||
}
|
||
}
|
||
|
||
async getStatus() {
|
||
const baseStatus = {
|
||
isRunning: this.isRunning, // Changed from isActive to isRunning
|
||
isActive: this.isRunning, // Keep both for compatibility
|
||
mode: this.config?.mode || 'SIMULATION',
|
||
enableTrading: this.config?.enableTrading || false,
|
||
tradingStatus: this.config?.enableTrading ? 'REAL TRADES' : 'SIMULATED ONLY',
|
||
symbol: this.config?.symbol || 'SOLUSD',
|
||
timeframes: this.config?.selectedTimeframes || [],
|
||
automationType: 'SIMPLE',
|
||
lastDecision: this.lastDecision, // Include last AI decision for UI display
|
||
config: this.config, // Include config for debugging
|
||
...this.stats
|
||
};
|
||
|
||
// Add AI Learning Status
|
||
try {
|
||
const learningInsights = await this.getAILearningInsights();
|
||
baseStatus.aiLearning = {
|
||
available: learningInsights.available,
|
||
systemConfidence: learningInsights.report?.summary?.systemConfidence || 0,
|
||
totalDecisions: learningInsights.report?.summary?.totalDecisions || 0,
|
||
successRate: learningInsights.report?.summary?.successRate || 0,
|
||
phase: this.getAILearningPhase(learningInsights.report?.summary?.totalDecisions || 0)
|
||
};
|
||
} catch (error) {
|
||
baseStatus.aiLearning = {
|
||
available: false,
|
||
error: error.message
|
||
};
|
||
}
|
||
|
||
// Add more descriptive status based on running state
|
||
if (this.isRunning) {
|
||
baseStatus.detailedStatus = 'Running - Monitoring for trade opportunities';
|
||
baseStatus.nextAction = 'Next analysis cycle in progress';
|
||
} else {
|
||
baseStatus.detailedStatus = 'Stopped - No monitoring active';
|
||
baseStatus.nextAction = 'Start automation to begin monitoring';
|
||
}
|
||
|
||
return baseStatus;
|
||
}
|
||
|
||
// Helper method to determine AI learning phase
|
||
getAILearningPhase(totalDecisions) {
|
||
if (totalDecisions < 5) return 'INITIAL';
|
||
if (totalDecisions < 20) return 'LEARNING';
|
||
if (totalDecisions < 50) return 'DEVELOPING';
|
||
return 'EXPERT';
|
||
}
|
||
|
||
// Get intervals based on trading timeframes (scalping needs faster analysis)
|
||
getTimeframeBasedIntervals() {
|
||
const timeframes = this.getSelectedTimeframes();
|
||
|
||
// Detect if this is scalping (5m, 15m, 30m)
|
||
const isScalping = timeframes.some(tf => ['5', '5m', '15', '15m', '30', '30m'].includes(tf));
|
||
const isDayTrading = timeframes.some(tf => ['60', '1h', '120', '2h'].includes(tf));
|
||
const isSwingTrading = timeframes.some(tf => ['240', '4h', '1D', '1d'].includes(tf));
|
||
|
||
if (isScalping) {
|
||
console.log('🎯 SCALPING DETECTED: Using faster 10-minute intervals (was 30-90)');
|
||
return 10 * 60 * 1000; // 10 minutes for scalping - fast enough for 5m charts
|
||
} else if (isDayTrading) {
|
||
console.log('⚡ DAY TRADING DETECTED: Using 20-minute intervals');
|
||
return 20 * 60 * 1000; // 20 minutes for day trading
|
||
} else if (isSwingTrading) {
|
||
console.log('📈 SWING TRADING DETECTED: Using 45-minute intervals');
|
||
return 45 * 60 * 1000; // 45 minutes for swing trading
|
||
} else {
|
||
// Unknown/mixed strategy - use moderate interval
|
||
console.log('📊 MIXED STRATEGY: Using 30-minute intervals');
|
||
return 30 * 60 * 1000; // 30 minutes default
|
||
}
|
||
}
|
||
|
||
// Get selected timeframes from config
|
||
getSelectedTimeframes() {
|
||
return this.config?.timeframes || this.config?.selectedTimeframes || ['1h'];
|
||
}
|
||
|
||
// Detect trading strategy from timeframes
|
||
detectStrategy() {
|
||
const timeframes = this.getSelectedTimeframes();
|
||
const isScalping = timeframes.some(tf => ['5', '5m', '15', '15m', '30', '30m'].includes(tf));
|
||
const isDayTrading = timeframes.some(tf => ['60', '1h', '120', '2h'].includes(tf));
|
||
const isSwingTrading = timeframes.some(tf => ['240', '4h', '1D', '1d'].includes(tf));
|
||
|
||
if (isScalping) return 'Scalping';
|
||
if (isDayTrading) return 'Day Trading';
|
||
if (isSwingTrading) return 'Swing Trading';
|
||
return 'Mixed';
|
||
}
|
||
|
||
// 🧠 AI LEARNING INTEGRATION METHODS
|
||
|
||
/**
|
||
* Record AI decision for learning system
|
||
*/
|
||
async recordAIDecisionForLearning(analysis, decisionContext) {
|
||
try {
|
||
if (!this.learner || typeof this.learner.recordDecision !== 'function') {
|
||
console.log('⚠️ Learning system not available - skipping decision recording');
|
||
return null;
|
||
}
|
||
|
||
const decisionData = {
|
||
tradeId: `trade_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
||
symbol: this.config?.symbol || 'SOLUSD',
|
||
decision: decisionContext.willExecute ? 'EXECUTE_TRADE' : 'HOLD_POSITION',
|
||
confidence: decisionContext.confidence,
|
||
recommendation: decisionContext.recommendation,
|
||
reasoning: analysis.reasoning || analysis.summary || 'AI analysis recommendation',
|
||
marketConditions: {
|
||
timeframes: this.config?.selectedTimeframes || ['1h'],
|
||
strategy: this.detectStrategy(),
|
||
minConfidenceRequired: decisionContext.minConfidenceRequired
|
||
},
|
||
expectedOutcome: decisionContext.willExecute ? 'PROFITABLE_TRADE' : 'WAIT_BETTER_OPPORTUNITY',
|
||
aiLevels: {
|
||
stopLoss: analysis.stopLoss?.price || analysis.stopLoss,
|
||
takeProfit: analysis.takeProfits?.tp1?.price || analysis.takeProfit,
|
||
entry: analysis.entry?.price || analysis.currentPrice
|
||
}
|
||
};
|
||
|
||
const decisionId = await this.learner.recordDecision(decisionData);
|
||
console.log(`🧠 AI Decision recorded for learning: ${decisionData.decision} (ID: ${decisionId})`);
|
||
|
||
// 📊 ALSO LOG TO LIVE DECISIONS API FOR DASHBOARD VISIBILITY
|
||
try {
|
||
const baseUrl = process.env.INTERNAL_API_URL || 'http://localhost:3000';
|
||
|
||
// Extract better entry price from analysis
|
||
let entryPrice = 0;
|
||
if (analysis.entry?.price) {
|
||
entryPrice = analysis.entry.price;
|
||
} else if (analysis.currentPrice) {
|
||
entryPrice = analysis.currentPrice;
|
||
} else if (analysis.entry && typeof analysis.entry === 'number') {
|
||
entryPrice = analysis.entry;
|
||
}
|
||
|
||
await fetch(`${baseUrl}/api/automation/live-decisions`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
type: 'AI_DECISION',
|
||
action: decisionData.recommendation?.toUpperCase() || decisionData.decision,
|
||
symbol: decisionData.symbol,
|
||
blocked: !decisionContext.willExecute,
|
||
executed: decisionContext.willExecute,
|
||
confidence: decisionData.confidence,
|
||
entryPrice: entryPrice,
|
||
stopLoss: decisionData.aiLevels.stopLoss,
|
||
takeProfit: decisionData.aiLevels.takeProfit,
|
||
reasoning: decisionData.reasoning,
|
||
timestamp: new Date().toISOString(),
|
||
cycle: this.stats.totalCycles,
|
||
learningDecisionId: decisionId
|
||
})
|
||
});
|
||
console.log(`📊 AI Decision logged to live-decisions API: ${decisionData.decision} with entry: $${entryPrice}`);
|
||
} catch (apiError) {
|
||
console.warn('⚠️ Failed to log decision to live-decisions API:', apiError.message);
|
||
}
|
||
|
||
// Store decision ID for later outcome tracking
|
||
if (this.lastDecision) {
|
||
this.lastDecision.learningDecisionId = decisionId;
|
||
}
|
||
|
||
return decisionId;
|
||
} catch (error) {
|
||
console.error('❌ Error recording AI decision for learning:', error.message);
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Track trade outcome for learning system
|
||
*/
|
||
async trackTradeOutcomeForLearning(executionResult, decisionId = null) {
|
||
try {
|
||
if (!this.learner || typeof this.learner.assessDecisionOutcome !== 'function') {
|
||
console.log('⚠️ Learning system not available - skipping outcome tracking');
|
||
return;
|
||
}
|
||
|
||
const targetDecisionId = decisionId || this.lastDecision?.learningDecisionId;
|
||
if (!targetDecisionId) {
|
||
console.log('⚠️ No decision ID available for outcome tracking');
|
||
return;
|
||
}
|
||
|
||
const outcomeData = {
|
||
decisionId: targetDecisionId,
|
||
actualOutcome: executionResult.success ? 'TRADE_EXECUTED' : 'TRADE_FAILED',
|
||
timeToOutcome: Date.now() - new Date(this.lastDecision?.timestamp || Date.now()).getTime(),
|
||
pnlImpact: executionResult.success ? 0 : -10, // Will be updated later with actual P&L
|
||
executionDetails: executionResult,
|
||
marketConditions: {
|
||
timestamp: new Date().toISOString(),
|
||
symbol: this.config?.symbol || 'SOLUSD'
|
||
}
|
||
};
|
||
|
||
const success = await this.learner.assessDecisionOutcome(outcomeData);
|
||
if (success) {
|
||
console.log(`🧠 Trade outcome recorded for learning: ${outcomeData.actualOutcome}`);
|
||
}
|
||
} catch (error) {
|
||
console.error('❌ Error tracking trade outcome for learning:', error.message);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Get AI learning insights and recommendations
|
||
*/
|
||
async getAILearningInsights() {
|
||
try {
|
||
if (!this.learner) {
|
||
return {
|
||
available: false,
|
||
message: 'Learning system not initialized'
|
||
};
|
||
}
|
||
|
||
// Check if learning methods are available
|
||
if (typeof this.learner.generateLearningReport === 'function') {
|
||
const report = await this.learner.generateLearningReport();
|
||
return {
|
||
available: true,
|
||
report: report,
|
||
type: 'FULL_REPORT'
|
||
};
|
||
} else if (typeof this.learner.getLearningStatus === 'function') {
|
||
const status = await this.learner.getLearningStatus();
|
||
return {
|
||
available: true,
|
||
report: status,
|
||
type: 'BASIC_STATUS'
|
||
};
|
||
} else {
|
||
return {
|
||
available: false,
|
||
message: 'Learning methods not available'
|
||
};
|
||
}
|
||
} catch (error) {
|
||
console.error('❌ Error getting AI learning insights:', error.message);
|
||
return {
|
||
available: false,
|
||
error: error.message
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Use AI learning to improve trade decisions
|
||
*/
|
||
async getAILearningRecommendation(analysis) {
|
||
try {
|
||
if (!this.learner || typeof this.learner.getSmartRecommendation !== 'function') {
|
||
console.log('🧠 Smart recommendations not available - using standard analysis');
|
||
return null;
|
||
}
|
||
|
||
const requestData = {
|
||
symbol: this.config?.symbol || 'SOLUSD',
|
||
confidence: analysis.confidence || 0,
|
||
recommendation: analysis.recommendation,
|
||
marketConditions: {
|
||
timeframes: this.config?.selectedTimeframes || ['1h'],
|
||
strategy: this.detectStrategy()
|
||
},
|
||
aiLevels: {
|
||
stopLoss: analysis.stopLoss?.price || analysis.stopLoss,
|
||
takeProfit: analysis.takeProfits?.tp1?.price || analysis.takeProfit
|
||
}
|
||
};
|
||
|
||
const learningRec = await this.learner.getSmartRecommendation(requestData);
|
||
if (learningRec && learningRec.confidence > 0.6) {
|
||
console.log(`🧠 AI Learning Recommendation: ${learningRec.action} (${(learningRec.confidence * 100).toFixed(1)}% confidence)`);
|
||
console.log(`📚 Learning Reasoning: ${learningRec.reasoning}`);
|
||
return learningRec;
|
||
}
|
||
|
||
return null;
|
||
} catch (error) {
|
||
console.error('❌ Error getting AI learning recommendation:', error.message);
|
||
return null;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Export singleton instance
|
||
const simpleAutomation = new SimpleAutomation();
|
||
export { simpleAutomation };
|