feat: integrate AI learning system with trading automation
- AutomationWithLearning class with decision recording and outcome assessment - Enhanced API endpoints with learning status visibility - Singleton automation manager for seamless learning system integration - EnhancedAILearningPanel component for real-time learning visibility - Learning-enhanced trade execution with AI adjustments to SL/TP - Automatic decision tracking and outcome-based learning Key Features: - Records trading decisions before execution - Enhances analysis with learned patterns - Tracks trade outcomes for continuous improvement - Provides full visibility into AI decision-making process - Integrates SimplifiedStopLossLearner with real trading flow - Whether learning system is active - How many decisions are being tracked - Real-time learning statistics and insights - AI enhancements applied to trading decisions
This commit is contained in:
46
lib/automation-singleton.js
Normal file
46
lib/automation-singleton.js
Normal file
@@ -0,0 +1,46 @@
|
||||
// Singleton automation instance manager with learning integration
|
||||
let automationInstance = null;
|
||||
|
||||
async function createAutomationInstance() {
|
||||
try {
|
||||
// Try to import the learning-enhanced automation first
|
||||
const AutomationWithLearning = require('./automation-with-learning-v2.js');
|
||||
console.log('✅ Creating automation instance with AI learning system');
|
||||
return new AutomationWithLearning();
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Learning-enhanced automation not available, falling back to basic automation');
|
||||
console.warn('Error:', error.message);
|
||||
|
||||
// Fallback to basic automation
|
||||
try {
|
||||
const { simpleAutomation } = await import('./simple-automation.js');
|
||||
console.log('✅ Creating basic automation instance');
|
||||
return simpleAutomation;
|
||||
} catch (fallbackError) {
|
||||
console.error('❌ Could not create any automation instance:', fallbackError);
|
||||
throw new Error('No automation system available');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getAutomationInstance() {
|
||||
if (!automationInstance) {
|
||||
automationInstance = createAutomationInstance();
|
||||
}
|
||||
return automationInstance;
|
||||
}
|
||||
|
||||
function resetAutomationInstance() {
|
||||
if (automationInstance) {
|
||||
console.log('🔄 Resetting automation instance');
|
||||
if (typeof automationInstance.stop === 'function') {
|
||||
automationInstance.stop();
|
||||
}
|
||||
}
|
||||
automationInstance = null;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getAutomationInstance,
|
||||
resetAutomationInstance
|
||||
};
|
||||
448
lib/automation-with-learning-v2.js
Normal file
448
lib/automation-with-learning-v2.js
Normal file
@@ -0,0 +1,448 @@
|
||||
// Enhanced automation with integrated AI learning system
|
||||
|
||||
// Dynamic import for ES6 modules
|
||||
async function importSimpleAutomation() {
|
||||
try {
|
||||
const module = await import('./simple-automation.js');
|
||||
// Extract the class from the singleton instance
|
||||
return module.simpleAutomation.constructor;
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Could not import SimpleAutomation, using fallback');
|
||||
// Fallback - create a basic class structure
|
||||
return class BasicAutomation {
|
||||
constructor() {
|
||||
this.isRunning = false;
|
||||
this.config = null;
|
||||
this.stats = { totalCycles: 0, totalTrades: 0 };
|
||||
}
|
||||
|
||||
async start(config) {
|
||||
return { success: false, message: 'Base automation not available' };
|
||||
}
|
||||
|
||||
async stop() {
|
||||
return { success: true, message: 'Stopped' };
|
||||
}
|
||||
|
||||
getStatus() {
|
||||
return { isRunning: this.isRunning, stats: this.stats };
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Import learning system
|
||||
async function importSimplifiedStopLossLearner() {
|
||||
try {
|
||||
const { SimplifiedStopLossLearner } = await import('./simplified-stop-loss-learner-fixed.js');
|
||||
return SimplifiedStopLossLearner;
|
||||
} catch (error) {
|
||||
console.warn('⚠️ SimplifiedStopLossLearner not available, continuing without learning');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class AutomationWithLearning {
|
||||
constructor() {
|
||||
this.baseAutomation = null;
|
||||
this.learner = null;
|
||||
this.activeTrades = new Map(); // Track active trades for outcome assessment
|
||||
this.learningDecisions = new Map(); // Track decisions for learning
|
||||
this.learningEnabled = false;
|
||||
this.isRunning = false;
|
||||
this.config = null;
|
||||
this.stats = { totalCycles: 0, totalTrades: 0 };
|
||||
}
|
||||
|
||||
async initializeBaseAutomation() {
|
||||
if (!this.baseAutomation) {
|
||||
const SimpleAutomationClass = await importSimpleAutomation();
|
||||
this.baseAutomation = new SimpleAutomationClass();
|
||||
}
|
||||
return this.baseAutomation;
|
||||
}
|
||||
|
||||
async initializeLearningSystem() {
|
||||
try {
|
||||
const SimplifiedStopLossLearnerClass = await importSimplifiedStopLossLearner();
|
||||
if (SimplifiedStopLossLearnerClass) {
|
||||
this.learner = new SimplifiedStopLossLearnerClass();
|
||||
console.log('✅ AI Learning System initialized successfully');
|
||||
|
||||
// Generate initial learning report
|
||||
if (typeof this.learner.generateLearningReport === 'function') {
|
||||
const report = await this.learner.generateLearningReport();
|
||||
console.log('📊 Current Learning Status:', report.summary);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Could not initialize learning system:', error.message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async start(config) {
|
||||
try {
|
||||
// Initialize base automation
|
||||
await this.initializeBaseAutomation();
|
||||
|
||||
// Initialize learning system
|
||||
await this.initializeLearningSystem();
|
||||
|
||||
// Use base automation if available, otherwise implement basic functionality
|
||||
let result;
|
||||
if (this.baseAutomation && typeof this.baseAutomation.start === 'function') {
|
||||
result = await this.baseAutomation.start(config);
|
||||
} else {
|
||||
// Basic start functionality
|
||||
this.isRunning = true;
|
||||
this.config = config;
|
||||
result = { success: true, message: 'Basic automation started' };
|
||||
}
|
||||
|
||||
if (result.success && this.learner) {
|
||||
console.log('🧠 AI LEARNING SYSTEM: Activated and ready to learn from trades');
|
||||
console.log('📊 The system will now record decisions and improve over time');
|
||||
this.learningEnabled = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('❌ Error starting automation with learning:', error);
|
||||
return { success: false, message: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async stop() {
|
||||
try {
|
||||
let result;
|
||||
if (this.baseAutomation && typeof this.baseAutomation.stop === 'function') {
|
||||
result = await this.baseAutomation.stop();
|
||||
} else {
|
||||
this.isRunning = false;
|
||||
result = { success: true, message: 'Basic automation stopped' };
|
||||
}
|
||||
|
||||
this.learningEnabled = false;
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('❌ Error stopping automation:', error);
|
||||
return { success: false, message: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
getStatus() {
|
||||
let baseStatus;
|
||||
if (this.baseAutomation && typeof this.baseAutomation.getStatus === 'function') {
|
||||
baseStatus = this.baseAutomation.getStatus();
|
||||
} else {
|
||||
baseStatus = {
|
||||
isRunning: this.isRunning,
|
||||
config: this.config,
|
||||
stats: this.stats
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...baseStatus,
|
||||
learningSystem: {
|
||||
enabled: this.learningEnabled,
|
||||
hasLearner: !!this.learner,
|
||||
activeDecisions: this.activeTrades.size,
|
||||
trackedTrades: Array.from(this.activeTrades.keys())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async getLearningStatus() {
|
||||
if (!this.learningEnabled || !this.learner) {
|
||||
return {
|
||||
enabled: false,
|
||||
message: 'Learning system not available'
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
let report = null;
|
||||
|
||||
// Try to get comprehensive learning report
|
||||
if (typeof this.learner.generateLearningReport === 'function') {
|
||||
report = await this.learner.generateLearningReport();
|
||||
} else if (typeof this.learner.getLearningStatus === 'function') {
|
||||
report = await this.learner.getLearningStatus();
|
||||
}
|
||||
|
||||
return {
|
||||
enabled: true,
|
||||
learningActive: this.learningEnabled,
|
||||
activeDecisions: this.activeTrades.size,
|
||||
report: report || { summary: { message: 'Learning system active but no data available yet' } }
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error getting learning status:', error);
|
||||
return {
|
||||
enabled: true,
|
||||
learningActive: this.learningEnabled,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async executeTrade(analysis) {
|
||||
try {
|
||||
console.log('🧠 LEARNING-ENHANCED TRADE EXECUTION');
|
||||
|
||||
// Record decision before execution for learning
|
||||
let decisionId = null;
|
||||
if (this.learningEnabled && this.learner) {
|
||||
decisionId = await this.recordTradingDecision(analysis);
|
||||
}
|
||||
|
||||
// Enhance analysis with learning-based adjustments
|
||||
const enhancedAnalysis = await this.enhanceAnalysisWithLearning(analysis);
|
||||
|
||||
// Execute the trade using enhanced analysis
|
||||
let tradeResult;
|
||||
if (this.baseAutomation && typeof this.baseAutomation.executeTrade === 'function') {
|
||||
tradeResult = await this.baseAutomation.executeTrade(enhancedAnalysis);
|
||||
} else {
|
||||
// Basic trade execution simulation
|
||||
tradeResult = {
|
||||
success: true,
|
||||
message: 'Trade simulated (learning system active)',
|
||||
simulation: true
|
||||
};
|
||||
}
|
||||
|
||||
// Track trade for outcome assessment
|
||||
if (tradeResult.success && decisionId) {
|
||||
this.trackTradeForLearning(decisionId, tradeResult, enhancedAnalysis);
|
||||
}
|
||||
|
||||
return tradeResult;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error in learning-enhanced trade execution:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async recordTradingDecision(analysis) {
|
||||
if (!this.learner || typeof this.learner.recordDecision !== 'function') {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const decisionData = {
|
||||
symbol: this.config?.symbol || 'SOLUSD',
|
||||
recommendation: analysis.recommendation,
|
||||
confidence: analysis.confidence,
|
||||
stopLoss: this.extractStopLoss(analysis),
|
||||
takeProfit: this.extractTakeProfit(analysis),
|
||||
entryPrice: analysis.entry?.price || analysis.currentPrice,
|
||||
marketConditions: {
|
||||
timeframe: this.config?.selectedTimeframes || ['1h'],
|
||||
analysis: analysis.reasoning || analysis.summary
|
||||
},
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
const decisionId = await this.learner.recordDecision(decisionData);
|
||||
console.log(`📝 LEARNING: Recorded decision ${decisionId}`);
|
||||
return decisionId;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error recording decision:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async enhanceAnalysisWithLearning(analysis) {
|
||||
if (!this.learningEnabled || !this.learner) {
|
||||
console.log('📊 Using original analysis (learning not available)');
|
||||
return analysis;
|
||||
}
|
||||
|
||||
try {
|
||||
// Get learning-based recommendations if available
|
||||
if (typeof this.learner.getSmartRecommendation === 'function') {
|
||||
const currentPrice = analysis.entry?.price || analysis.currentPrice || 178;
|
||||
const originalStopLoss = this.extractStopLoss(analysis);
|
||||
|
||||
if (originalStopLoss) {
|
||||
const distanceFromSL = Math.abs(currentPrice - originalStopLoss);
|
||||
|
||||
const learningRecommendation = await this.learner.getSmartRecommendation({
|
||||
symbol: this.config?.symbol || 'SOLUSD',
|
||||
distanceFromSL,
|
||||
marketConditions: {
|
||||
recommendation: analysis.recommendation,
|
||||
confidence: analysis.confidence,
|
||||
timeframe: this.config?.selectedTimeframes || ['1h']
|
||||
}
|
||||
});
|
||||
|
||||
if (learningRecommendation && learningRecommendation.confidence > 70) {
|
||||
console.log(`🧠 LEARNING ENHANCEMENT: ${learningRecommendation.action} (${learningRecommendation.confidence}% confidence)`);
|
||||
console.log(`💡 Reasoning: ${learningRecommendation.reasoning}`);
|
||||
|
||||
// Apply learning-based adjustments
|
||||
if (learningRecommendation.action === 'TIGHTEN_STOP_LOSS') {
|
||||
analysis = this.adjustStopLoss(analysis, learningRecommendation.suggestedValue);
|
||||
} else if (learningRecommendation.action === 'ADJUST_TAKE_PROFIT') {
|
||||
analysis = this.adjustTakeProfit(analysis, learningRecommendation.suggestedValue);
|
||||
}
|
||||
|
||||
// Add learning metadata to analysis
|
||||
analysis.learningEnhanced = true;
|
||||
analysis.learningConfidence = learningRecommendation.confidence;
|
||||
analysis.learningReasoning = learningRecommendation.reasoning;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return analysis;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error enhancing analysis with learning:', error);
|
||||
return analysis; // Return original analysis if learning fails
|
||||
}
|
||||
}
|
||||
|
||||
adjustStopLoss(analysis, newStopLoss) {
|
||||
if (newStopLoss) {
|
||||
if (analysis.stopLoss && typeof analysis.stopLoss === 'object') {
|
||||
analysis.stopLoss.price = newStopLoss;
|
||||
} else {
|
||||
analysis.stopLoss = newStopLoss;
|
||||
}
|
||||
console.log(`🔧 LEARNING ADJUSTMENT: Stop-loss adjusted to $${newStopLoss}`);
|
||||
}
|
||||
return analysis;
|
||||
}
|
||||
|
||||
adjustTakeProfit(analysis, newTakeProfit) {
|
||||
if (newTakeProfit) {
|
||||
if (analysis.takeProfits && analysis.takeProfits.tp1) {
|
||||
analysis.takeProfits.tp1.price = newTakeProfit;
|
||||
} else if (analysis.takeProfit) {
|
||||
analysis.takeProfit = newTakeProfit;
|
||||
} else {
|
||||
analysis.takeProfit = newTakeProfit;
|
||||
}
|
||||
console.log(`🎯 LEARNING ADJUSTMENT: Take-profit adjusted to $${newTakeProfit}`);
|
||||
}
|
||||
return analysis;
|
||||
}
|
||||
|
||||
trackTradeForLearning(decisionId, tradeResult, analysis) {
|
||||
if (!decisionId) return;
|
||||
|
||||
this.activeTrades.set(decisionId, {
|
||||
tradeResult,
|
||||
analysis,
|
||||
timestamp: new Date().toISOString(),
|
||||
symbol: this.config?.symbol || 'SOLUSD'
|
||||
});
|
||||
|
||||
console.log(`📊 LEARNING: Tracking trade ${decisionId} for outcome assessment`);
|
||||
|
||||
// Set up outcome assessment check (will check position status later)
|
||||
setTimeout(() => {
|
||||
this.assessTradeOutcome(decisionId);
|
||||
}, 300000); // Check after 5 minutes
|
||||
}
|
||||
|
||||
async assessTradeOutcome(decisionId) {
|
||||
if (!this.learningEnabled || !this.learner || !this.activeTrades.has(decisionId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const tradeInfo = this.activeTrades.get(decisionId);
|
||||
|
||||
// Check current position status via API
|
||||
const baseUrl = process.env.INTERNAL_API_URL || 'http://localhost:3000';
|
||||
const response = await fetch(`${baseUrl}/api/automation/position-monitor`);
|
||||
const positionData = await response.json();
|
||||
|
||||
let outcome = null;
|
||||
let actualPnL = 0;
|
||||
|
||||
if (positionData.success) {
|
||||
// Determine outcome based on position status
|
||||
if (!positionData.monitor.hasPosition) {
|
||||
// Position closed - check if it was profitable
|
||||
outcome = 'POSITION_CLOSED';
|
||||
|
||||
// Try to get actual P&L from position history
|
||||
try {
|
||||
const historyResponse = await fetch(`${baseUrl}/api/drift/position-history`);
|
||||
const historyData = await historyResponse.json();
|
||||
|
||||
if (historyData.success && historyData.trades.length > 0) {
|
||||
// Find the most recent trade
|
||||
const recentTrade = historyData.trades[0];
|
||||
actualPnL = recentTrade.pnl;
|
||||
outcome = actualPnL > 0 ? 'WIN' : 'LOSS';
|
||||
}
|
||||
} catch (historyError) {
|
||||
console.warn('⚠️ Could not fetch trade history for outcome assessment');
|
||||
}
|
||||
} else {
|
||||
// Position still open - check if it's profitable
|
||||
const currentPnL = positionData.monitor.position?.unrealizedPnl || 0;
|
||||
actualPnL = currentPnL;
|
||||
outcome = currentPnL > 0 ? 'CURRENTLY_PROFITABLE' : 'CURRENTLY_LOSING';
|
||||
}
|
||||
}
|
||||
|
||||
// Record outcome for learning
|
||||
if (outcome && typeof this.learner.assessDecisionOutcome === 'function') {
|
||||
const outcomeData = {
|
||||
decisionId,
|
||||
outcome,
|
||||
actualPnL,
|
||||
timestamp: new Date().toISOString(),
|
||||
positionInfo: positionData.monitor
|
||||
};
|
||||
|
||||
await this.learner.assessDecisionOutcome(outcomeData);
|
||||
console.log(`🧠 LEARNING: Assessed outcome for decision ${decisionId}: ${outcome} (P&L: $${actualPnL.toFixed(2)})`);
|
||||
|
||||
// Clean up tracked trade
|
||||
this.activeTrades.delete(decisionId);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error assessing trade outcome:', error);
|
||||
}
|
||||
}
|
||||
|
||||
extractStopLoss(analysis) {
|
||||
if (analysis.stopLoss && typeof analysis.stopLoss === 'object') {
|
||||
return analysis.stopLoss.price;
|
||||
} else if (analysis.stopLoss && typeof analysis.stopLoss === 'number') {
|
||||
return analysis.stopLoss;
|
||||
} else if (analysis.levels?.stopLoss) {
|
||||
return analysis.levels.stopLoss;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
extractTakeProfit(analysis) {
|
||||
if (analysis.takeProfits && analysis.takeProfits.tp1?.price) {
|
||||
return analysis.takeProfits.tp1.price;
|
||||
} else if (analysis.takeProfit && typeof analysis.takeProfit === 'number') {
|
||||
return analysis.takeProfit;
|
||||
} else if (analysis.levels?.takeProfit) {
|
||||
return analysis.levels.takeProfit;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AutomationWithLearning;
|
||||
455
lib/automation-with-learning.js
Normal file
455
lib/automation-with-learning.js
Normal file
@@ -0,0 +1,455 @@
|
||||
// Enhanced automation with integrated AI learning system
|
||||
|
||||
// Dynamic import for ES6 modules
|
||||
async function importSimpleAutomation() {
|
||||
try {
|
||||
const module = await import('./simple-automation.js');
|
||||
// Extract the class from the singleton instance
|
||||
return module.simpleAutomation.constructor;
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Could not import SimpleAutomation, using fallback');
|
||||
// Fallback - create a basic class structure
|
||||
return class BasicAutomation {
|
||||
constructor() {
|
||||
this.isRunning = false;
|
||||
this.config = null;
|
||||
this.stats = { totalCycles: 0, totalTrades: 0 };
|
||||
}
|
||||
|
||||
async start(config) {
|
||||
return { success: false, message: 'Base automation not available' };
|
||||
}
|
||||
|
||||
async stop() {
|
||||
return { success: true, message: 'Stopped' };
|
||||
}
|
||||
|
||||
getStatus() {
|
||||
return { isRunning: this.isRunning, stats: this.stats };
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Import learning system
|
||||
async function importSimplifiedStopLossLearner() {
|
||||
try {
|
||||
const { SimplifiedStopLossLearner } = await import('./simplified-stop-loss-learner-fixed.js');
|
||||
return SimplifiedStopLossLearner;
|
||||
} catch (error) {
|
||||
console.warn('⚠️ SimplifiedStopLossLearner not available, continuing without learning');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class AutomationWithLearning {
|
||||
constructor() {
|
||||
this.baseAutomation = null;
|
||||
this.learner = null;
|
||||
this.activeTrades = new Map(); // Track active trades for outcome assessment
|
||||
this.learningDecisions = new Map(); // Track decisions for learning
|
||||
this.learningEnabled = false;
|
||||
this.isRunning = false;
|
||||
this.config = null;
|
||||
this.stats = { totalCycles: 0, totalTrades: 0 };
|
||||
}
|
||||
|
||||
async initializeBaseAutomation() {
|
||||
if (!this.baseAutomation) {
|
||||
const SimpleAutomationClass = await importSimpleAutomation();
|
||||
this.baseAutomation = new SimpleAutomationClass();
|
||||
}
|
||||
return this.baseAutomation;
|
||||
}
|
||||
|
||||
async start(config) {
|
||||
try {
|
||||
// Initialize base automation
|
||||
await this.initializeBaseAutomation();
|
||||
|
||||
// Initialize learning system
|
||||
await this.initializeLearningSystem();
|
||||
|
||||
// Use base automation if available, otherwise implement basic functionality
|
||||
let result;
|
||||
if (this.baseAutomation && typeof this.baseAutomation.start === 'function') {
|
||||
result = await this.baseAutomation.start(config);
|
||||
} else {
|
||||
// Basic start functionality
|
||||
this.isRunning = true;
|
||||
this.config = config;
|
||||
result = { success: true, message: 'Basic automation started' };
|
||||
}
|
||||
|
||||
if (result.success && this.learner) {
|
||||
console.log('🧠 AI LEARNING SYSTEM: Activated and ready to learn from trades');
|
||||
console.log('📊 The system will now record decisions and improve over time');
|
||||
this.learningEnabled = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('❌ Error starting automation with learning:', error);
|
||||
return { success: false, message: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async stop() {
|
||||
try {
|
||||
let result;
|
||||
if (this.baseAutomation && typeof this.baseAutomation.stop === 'function') {
|
||||
result = await this.baseAutomation.stop();
|
||||
} else {
|
||||
this.isRunning = false;
|
||||
result = { success: true, message: 'Basic automation stopped' };
|
||||
}
|
||||
|
||||
this.learningEnabled = false;
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('❌ Error stopping automation:', error);
|
||||
return { success: false, message: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
getStatus() {
|
||||
let baseStatus;
|
||||
if (this.baseAutomation && typeof this.baseAutomation.getStatus === 'function') {
|
||||
baseStatus = this.baseAutomation.getStatus();
|
||||
} else {
|
||||
baseStatus = {
|
||||
isRunning: this.isRunning,
|
||||
config: this.config,
|
||||
stats: this.stats
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...baseStatus,
|
||||
learningSystem: {
|
||||
enabled: this.learningEnabled,
|
||||
hasLearner: !!this.learner,
|
||||
activeDecisions: this.activeTrades.size,
|
||||
trackedTrades: Array.from(this.activeTrades.keys())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = AutomationWithLearning;
|
||||
|
||||
async initializeLearningSystem() {
|
||||
try {
|
||||
const SimplifiedStopLossLearnerClass = await importSimplifiedStopLossLearner();
|
||||
if (SimplifiedStopLossLearnerClass) {
|
||||
this.learner = new SimplifiedStopLossLearnerClass();
|
||||
console.log('✅ AI Learning System initialized successfully');
|
||||
|
||||
// Generate initial learning report
|
||||
if (typeof this.learner.generateLearningReport === 'function') {
|
||||
const report = await this.learner.generateLearningReport();
|
||||
console.log('📊 Current Learning Status:', report.summary);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Could not initialize learning system:', error.message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async executeTrade(analysis) {
|
||||
try {
|
||||
console.log('🧠 LEARNING-ENHANCED TRADE EXECUTION');
|
||||
|
||||
// Record decision before execution for learning
|
||||
let decisionId = null;
|
||||
if (this.learningEnabled && this.learner) {
|
||||
decisionId = await this.recordTradingDecision(analysis);
|
||||
}
|
||||
|
||||
// Enhance analysis with learning-based adjustments
|
||||
const enhancedAnalysis = await this.enhanceAnalysisWithLearning(analysis);
|
||||
|
||||
// Execute the trade using enhanced analysis
|
||||
const tradeResult = await super.executeTrade(enhancedAnalysis);
|
||||
|
||||
// Track trade for outcome assessment
|
||||
if (tradeResult.success && decisionId) {
|
||||
this.trackTradeForLearning(decisionId, tradeResult, enhancedAnalysis);
|
||||
}
|
||||
|
||||
return tradeResult;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error in learning-enhanced trade execution:', error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
async recordTradingDecision(analysis) {
|
||||
if (!this.learner || typeof this.learner.recordDecision !== 'function') {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const decisionData = {
|
||||
symbol: this.config.symbol,
|
||||
recommendation: analysis.recommendation,
|
||||
confidence: analysis.confidence,
|
||||
stopLoss: this.extractStopLoss(analysis),
|
||||
takeProfit: this.extractTakeProfit(analysis),
|
||||
entryPrice: analysis.entry?.price || analysis.currentPrice,
|
||||
marketConditions: {
|
||||
timeframe: this.config.selectedTimeframes,
|
||||
analysis: analysis.reasoning || analysis.summary
|
||||
},
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
const decisionId = await this.learner.recordDecision(decisionData);
|
||||
console.log(`📝 LEARNING: Recorded decision ${decisionId}`);
|
||||
return decisionId;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error recording decision:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async enhanceAnalysisWithLearning(analysis) {
|
||||
if (!this.learningEnabled || !this.learner) {
|
||||
console.log('📊 Using original analysis (learning not available)');
|
||||
return analysis;
|
||||
}
|
||||
|
||||
try {
|
||||
// Get learning-based recommendations if available
|
||||
if (typeof this.learner.getSmartRecommendation === 'function') {
|
||||
const currentPrice = analysis.entry?.price || analysis.currentPrice || 178;
|
||||
const originalStopLoss = this.extractStopLoss(analysis);
|
||||
|
||||
if (originalStopLoss) {
|
||||
const distanceFromSL = Math.abs(currentPrice - originalStopLoss);
|
||||
|
||||
const learningRecommendation = await this.learner.getSmartRecommendation({
|
||||
symbol: this.config.symbol,
|
||||
distanceFromSL,
|
||||
marketConditions: {
|
||||
recommendation: analysis.recommendation,
|
||||
confidence: analysis.confidence,
|
||||
timeframe: this.config.selectedTimeframes
|
||||
}
|
||||
});
|
||||
|
||||
if (learningRecommendation && learningRecommendation.confidence > 70) {
|
||||
console.log(`🧠 LEARNING ENHANCEMENT: ${learningRecommendation.action} (${learningRecommendation.confidence}% confidence)`);
|
||||
console.log(`💡 Reasoning: ${learningRecommendation.reasoning}`);
|
||||
|
||||
// Apply learning-based adjustments
|
||||
if (learningRecommendation.action === 'TIGHTEN_STOP_LOSS') {
|
||||
analysis = this.adjustStopLoss(analysis, learningRecommendation.suggestedValue);
|
||||
} else if (learningRecommendation.action === 'ADJUST_TAKE_PROFIT') {
|
||||
analysis = this.adjustTakeProfit(analysis, learningRecommendation.suggestedValue);
|
||||
}
|
||||
|
||||
// Add learning metadata to analysis
|
||||
analysis.learningEnhanced = true;
|
||||
analysis.learningConfidence = learningRecommendation.confidence;
|
||||
analysis.learningReasoning = learningRecommendation.reasoning;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return analysis;
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error enhancing analysis with learning:', error);
|
||||
return analysis; // Return original analysis if learning fails
|
||||
}
|
||||
}
|
||||
|
||||
adjustStopLoss(analysis, newStopLoss) {
|
||||
if (newStopLoss) {
|
||||
if (analysis.stopLoss && typeof analysis.stopLoss === 'object') {
|
||||
analysis.stopLoss.price = newStopLoss;
|
||||
} else {
|
||||
analysis.stopLoss = newStopLoss;
|
||||
}
|
||||
console.log(`🔧 LEARNING ADJUSTMENT: Stop-loss adjusted to $${newStopLoss}`);
|
||||
}
|
||||
return analysis;
|
||||
}
|
||||
|
||||
adjustTakeProfit(analysis, newTakeProfit) {
|
||||
if (newTakeProfit) {
|
||||
if (analysis.takeProfits && analysis.takeProfits.tp1) {
|
||||
analysis.takeProfits.tp1.price = newTakeProfit;
|
||||
} else if (analysis.takeProfit) {
|
||||
analysis.takeProfit = newTakeProfit;
|
||||
} else {
|
||||
analysis.takeProfit = newTakeProfit;
|
||||
}
|
||||
console.log(`🎯 LEARNING ADJUSTMENT: Take-profit adjusted to $${newTakeProfit}`);
|
||||
}
|
||||
return analysis;
|
||||
}
|
||||
|
||||
trackTradeForLearning(decisionId, tradeResult, analysis) {
|
||||
if (!decisionId) return;
|
||||
|
||||
this.activeTrades.set(decisionId, {
|
||||
tradeResult,
|
||||
analysis,
|
||||
timestamp: new Date().toISOString(),
|
||||
symbol: this.config.symbol
|
||||
});
|
||||
|
||||
console.log(`📊 LEARNING: Tracking trade ${decisionId} for outcome assessment`);
|
||||
|
||||
// Set up outcome assessment check (will check position status later)
|
||||
setTimeout(() => {
|
||||
this.assessTradeOutcome(decisionId);
|
||||
}, 300000); // Check after 5 minutes
|
||||
}
|
||||
|
||||
async assessTradeOutcome(decisionId) {
|
||||
if (!this.learningEnabled || !this.learner || !this.activeTrades.has(decisionId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const tradeInfo = this.activeTrades.get(decisionId);
|
||||
|
||||
// Check current position status via API
|
||||
const baseUrl = process.env.INTERNAL_API_URL || 'http://localhost:3000';
|
||||
const response = await fetch(`${baseUrl}/api/automation/position-monitor`);
|
||||
const positionData = await response.json();
|
||||
|
||||
let outcome = null;
|
||||
let actualPnL = 0;
|
||||
|
||||
if (positionData.success) {
|
||||
// Determine outcome based on position status
|
||||
if (!positionData.monitor.hasPosition) {
|
||||
// Position closed - check if it was profitable
|
||||
outcome = 'POSITION_CLOSED';
|
||||
|
||||
// Try to get actual P&L from position history
|
||||
try {
|
||||
const historyResponse = await fetch(`${baseUrl}/api/drift/position-history`);
|
||||
const historyData = await historyResponse.json();
|
||||
|
||||
if (historyData.success && historyData.trades.length > 0) {
|
||||
// Find the most recent trade
|
||||
const recentTrade = historyData.trades[0];
|
||||
actualPnL = recentTrade.pnl;
|
||||
outcome = actualPnL > 0 ? 'WIN' : 'LOSS';
|
||||
}
|
||||
} catch (historyError) {
|
||||
console.warn('⚠️ Could not fetch trade history for outcome assessment');
|
||||
}
|
||||
} else {
|
||||
// Position still open - check if it's profitable
|
||||
const currentPnL = positionData.monitor.position?.unrealizedPnl || 0;
|
||||
actualPnL = currentPnL;
|
||||
outcome = currentPnL > 0 ? 'CURRENTLY_PROFITABLE' : 'CURRENTLY_LOSING';
|
||||
}
|
||||
}
|
||||
|
||||
// Record outcome for learning
|
||||
if (outcome && typeof this.learner.assessDecisionOutcome === 'function') {
|
||||
const outcomeData = {
|
||||
decisionId,
|
||||
outcome,
|
||||
actualPnL,
|
||||
timestamp: new Date().toISOString(),
|
||||
positionInfo: positionData.monitor
|
||||
};
|
||||
|
||||
await this.learner.assessDecisionOutcome(outcomeData);
|
||||
console.log(`🧠 LEARNING: Assessed outcome for decision ${decisionId}: ${outcome} (P&L: $${actualPnL.toFixed(2)})`);
|
||||
|
||||
// Clean up tracked trade
|
||||
this.activeTrades.delete(decisionId);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error assessing trade outcome:', error);
|
||||
}
|
||||
}
|
||||
|
||||
extractStopLoss(analysis) {
|
||||
if (analysis.stopLoss && typeof analysis.stopLoss === 'object') {
|
||||
return analysis.stopLoss.price;
|
||||
} else if (analysis.stopLoss && typeof analysis.stopLoss === 'number') {
|
||||
return analysis.stopLoss;
|
||||
} else if (analysis.levels?.stopLoss) {
|
||||
return analysis.levels.stopLoss;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
extractTakeProfit(analysis) {
|
||||
if (analysis.takeProfits && analysis.takeProfits.tp1?.price) {
|
||||
return analysis.takeProfits.tp1.price;
|
||||
} else if (analysis.takeProfit && typeof analysis.takeProfit === 'number') {
|
||||
return analysis.takeProfit;
|
||||
} else if (analysis.levels?.takeProfit) {
|
||||
return analysis.levels.takeProfit;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async getLearningStatus() {
|
||||
if (!this.learningEnabled || !this.learner) {
|
||||
return {
|
||||
enabled: false,
|
||||
message: 'Learning system not available'
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
let report = null;
|
||||
|
||||
// Try to get comprehensive learning report
|
||||
if (typeof this.learner.generateLearningReport === 'function') {
|
||||
report = await this.learner.generateLearningReport();
|
||||
} else if (typeof this.learner.getLearningStatus === 'function') {
|
||||
report = await this.learner.getLearningStatus();
|
||||
}
|
||||
|
||||
return {
|
||||
enabled: true,
|
||||
learningActive: this.learningEnabled,
|
||||
activeDecisions: this.activeTrades.size,
|
||||
report: report || { summary: { message: 'Learning system active but no data available yet' } }
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error getting learning status:', error);
|
||||
return {
|
||||
enabled: true,
|
||||
learningActive: this.learningEnabled,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Override getStatus to include learning information
|
||||
getStatus() {
|
||||
const baseStatus = super.getStatus();
|
||||
|
||||
return {
|
||||
...baseStatus,
|
||||
learningSystem: {
|
||||
enabled: this.learningEnabled,
|
||||
hasLearner: !!this.learner,
|
||||
activeDecisions: this.activeTrades.size,
|
||||
trackedTrades: Array.from(this.activeTrades.keys())
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AutomationWithLearning;
|
||||
Reference in New Issue
Block a user