Files
trading_bot_v3/lib/simple-automation.js

1442 lines
60 KiB
JavaScript
Raw Blame History

// 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 };