Files
trading_bot_v3/lib/simple-automation.js
mindesbunister 416f72181e feat: enhance paper trading with comprehensive AI analysis and learning insights
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
2025-08-02 17:56:02 +02:00

1442 lines
60 KiB
JavaScript
Raw Permalink 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 };