Files
trading_bot_v3/lib/simple-automation.js
mindesbunister 167d7ff5bc feat: implement comprehensive AI decision display and reasoning panel
Major Features Added:
- Complete AI decision tracking system with detailed reasoning display
- Prominent gradient-styled AI reasoning panel on automation-v2 page
- Test AI decision generator with realistic trading scenarios
- Enhanced decision transparency showing entry/exit logic and leverage calculations

- Fixed orphaned order cleanup to preserve reduce-only SL/TP orders
- Integrated AI leverage calculator with 100x capability (up from 10x limit)
- Added lastDecision property to automation status for UI display
- Enhanced position monitoring with better cleanup triggers

- Beautiful gradient-styled AI Trading Analysis panel
- Color-coded confidence levels and recommendation displays
- Detailed breakdown of entry strategy, stop loss logic, and take profit targets
- Real-time display of AI leverage reasoning with safety buffer explanations
- Test AI button for demonstration of decision-making process

- SL/TP orders now execute properly (fixed cleanup interference)
- AI calculates sophisticated leverage (8.8x-42.2x vs previous 1x hardcoded)
- Complete decision audit trail with execution details
- Risk management transparency with liquidation safety calculations

- Why This Decision? - Prominent reasoning section
- Entry & Exit Strategy - Price levels with color coding
- AI Leverage Decision - Detailed calculation explanations
- Execution status with success/failure indicators
- Transaction IDs and comprehensive trade details

All systems now provide full transparency of AI decision-making process.
2025-07-26 22:41:55 +02:00

619 lines
24 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Simple automation service for basic start/stop functionality
// 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.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 simple monitoring cycle (10 minutes for safety)
this.intervalId = setInterval(() => {
this.runCycle();
}, 10 * 60 * 1000); // 10 minutes
// First cycle after 30 seconds
setTimeout(() => {
this.runCycle();
}, 30000);
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) {
clearInterval(this.intervalId);
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 };
}
}
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) {
clearInterval(this.intervalId);
this.intervalId = null;
}
return;
}
console.log(`⚠️ Error ${this.stats.consecutiveErrors}/3 - Will retry next cycle`);
}
}
async performAnalysis() {
console.log(`📊 TRUE PARALLEL ANALYSIS: Starting simultaneous analysis for ${this.config.selectedTimeframes.length} timeframes...`);
console.log(`🚀 This will capture ${this.config.selectedTimeframes.length * 2} screenshots in parallel (${this.config.selectedTimeframes.length} timeframes × 2 layouts)`);
try {
// Use internal container port for server-side API calls
const baseUrl = process.env.INTERNAL_API_URL || 'http://localhost:3000';
const response = await fetch(`${baseUrl}/api/batch-analysis`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
symbol: this.config.symbol,
timeframes: this.config.selectedTimeframes, // All timeframes at once!
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 (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 (error) {
// Track network errors
this.stats.networkErrors++;
this.stats.consecutiveErrors++;
console.error(`❌ PARALLEL ANALYSIS FAILED (Network Error #${this.stats.networkErrors}):`, error.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();
}
}
// 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 (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
};
}
shouldExecuteTrade(analysis) {
// Always allow trade execution - the useRealDEX flag determines if it's real or simulated
console.log(`<EFBFBD> TRADE MODE: ${this.config.mode || 'SIMULATION'} - Trading ${this.config.enableTrading ? 'ENABLED' : 'DISABLED'}`);
const recommendation = analysis.recommendation?.toLowerCase() || '';
const confidence = analysis.confidence || 0;
// Strategy-specific confidence requirements
let minConfidence = 75;
if (this.config.selectedTimeframes?.includes('5') || this.config.selectedTimeframes?.includes('15')) {
minConfidence = 80; // Higher confidence for scalping
}
const isHighConfidence = confidence >= minConfidence;
const isClearDirection = recommendation.includes('buy') || recommendation.includes('sell');
console.log('🎯 TRADE DECISION: ' + recommendation + ' (' + confidence + '%) - Min: ' + minConfidence + '%');
// Store decision data for UI display
this.lastDecision = {
timestamp: new Date().toISOString(),
recommendation: analysis.recommendation || 'HOLD',
confidence: confidence,
minConfidenceRequired: minConfidence,
reasoning: analysis.reasoning || analysis.summary || 'No detailed reasoning available',
executed: false, // Will be updated if trade is executed
executionDetails: null,
executionError: null
};
return isHighConfidence && isClearDirection;
}
async executeTrade(analysis) {
try {
console.log('💰 EXECUTING TRADE...');
console.log('📊 Analysis data:', JSON.stringify(analysis, null, 2));
// 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 };
}
// 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, ''));
}
console.log(`🎯 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 {
const baseUrl = process.env.INTERNAL_API_URL || 'http://localhost:3000';
console.log('🔧 DEBUG: Fetching balance from:', baseUrl);
const balanceResponse = await fetch(`${baseUrl}/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}`);
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`);
// 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 baseUrl = process.env.INTERNAL_API_URL || 'http://localhost:3000';
const response = await fetch(`${baseUrl}/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;
// 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);
// 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 };
}
}
getStatus() {
const baseStatus = {
isActive: this.isRunning,
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
...this.stats
};
// 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;
}
}
// Export singleton instance
const simpleAutomation = new SimpleAutomation();
export { simpleAutomation };