/** * Simplified Stop Loss Learning System * * Simplified approach focusing on essential learning patterns * without complex statistical analysis. */ const { PrismaClient } = require('@prisma/client'); const getDB = require('./database-util'); class SimplifiedStopLossLearner { constructor() { this.learningThresholds = { emergency: 1.0, // Emergency exit at 1% from SL risk: 2.0, // High risk at 2% from SL mediumRisk: 5.0 // Medium risk at 5% from SL }; } async log(message) { console.log(`[${new Date().toISOString()}] 🧠 SL Learner: ${message}`); } /** * Record a stop loss related decision for learning */ async recordDecision(decisionData) { try { const learningRecord = { type: 'STOP_LOSS_DECISION', tradeId: decisionData.tradeId, symbol: decisionData.symbol, decision: decisionData.decision, distanceFromSL: decisionData.distanceFromSL, reasoning: decisionData.reasoning, marketConditions: decisionData.marketConditions, expectedOutcome: decisionData.expectedOutcome, timestamp: new Date().toISOString() }; const prisma = await getDB(); const record = await prisma.ai_learning_data.create({ data: { id: `sl_decision_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, userId: 'default-user', symbol: decisionData.symbol, timeframe: 'DECISION', analysisData: JSON.stringify(learningRecord), marketConditions: JSON.stringify(decisionData.marketConditions || {}), confidenceScore: 50 // Neutral starting confidence } }); await this.log(`📝 Decision recorded: ${decisionData.decision} for ${decisionData.symbol} at ${decisionData.distanceFromSL}%`); return record.id; } catch (error) { await this.log(`❌ Error recording decision: ${error.message}`); return null; } } /** * Update the outcome of a previously recorded decision */ async assessDecisionOutcome(outcomeData) { try { const prisma = await getDB(); // Find the original decision record const originalRecord = await prisma.ai_learning_data.findUnique({ where: { id: outcomeData.decisionId } }); if (!originalRecord) { await this.log(`⚠️ Original decision ${outcomeData.decisionId} not found`); return false; } // Parse the original decision data const originalDecision = JSON.parse(originalRecord.analysisData); // Create outcome record with learning data const outcomeRecord = { type: 'STOP_LOSS_OUTCOME', originalDecisionId: outcomeData.decisionId, actualOutcome: outcomeData.actualOutcome, timeToOutcome: outcomeData.timeToOutcome, pnlImpact: outcomeData.pnlImpact, wasCorrect: this.evaluateDecisionCorrectness(originalDecision, outcomeData), learningData: { originalDecision: originalDecision.decision, distanceFromSL: originalDecision.distanceFromSL, outcome: outcomeData.actualOutcome, profitability: outcomeData.pnlImpact > 0 ? 'PROFITABLE' : 'LOSS' }, timestamp: new Date().toISOString() }; await prisma.ai_learning_data.create({ data: { id: `sl_outcome_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, userId: 'default-user', symbol: originalDecision.symbol, timeframe: 'OUTCOME', analysisData: JSON.stringify(outcomeRecord), marketConditions: originalRecord.marketConditions, confidenceScore: outcomeRecord.wasCorrect ? 75 : 25 } }); await this.log(`✅ Outcome assessed for ${outcomeData.decisionId}: ${outcomeData.actualOutcome} (${outcomeRecord.wasCorrect ? 'CORRECT' : 'INCORRECT'})`); // Update learning thresholds based on outcomes await this.updateThresholdsFromOutcome(originalDecision, outcomeRecord); return true; } catch (error) { await this.log(`❌ Error assessing outcome: ${error.message}`); return false; } } /** * Evaluate if the original decision was correct based on outcome */ evaluateDecisionCorrectness(originalDecision, outcome) { const decision = originalDecision.decision; const actualOutcome = outcome.actualOutcome; const pnlImpact = outcome.pnlImpact; // Define what constitutes a "correct" decision if (decision === 'EMERGENCY_EXIT' && (actualOutcome === 'STOPPED_OUT' || pnlImpact < -50)) { return true; // Correctly identified emergency } if (decision === 'HOLD_POSITION' && pnlImpact > 0) { return true; // Correctly held profitable position } if (decision === 'ADJUST_STOP_LOSS' && actualOutcome === 'TAKE_PROFIT') { return true; // Adjustment led to profitable exit } return false; } /** * Get smart recommendation based on learned patterns */ async getSmartRecommendation(requestData) { try { const { distanceFromSL, symbol, marketConditions } = requestData; // Get historical data for similar situations const prisma = await getDB(); const similarDecisions = await prisma.ai_learning_data.findMany({ where: { symbol: symbol, analysisData: { string_contains: '"type":"STOP_LOSS_DECISION"' } }, orderBy: { createdAt: 'desc' }, take: 20 }); // Analyze patterns from similar situations let recommendation = this.getBaseRecommendation(distanceFromSL); if (similarDecisions.length >= 3) { const learnedRecommendation = await this.analyzePatterns(similarDecisions, distanceFromSL); if (learnedRecommendation) { recommendation = learnedRecommendation; } } await this.log(`🎯 Smart recommendation for ${symbol} at ${distanceFromSL}%: ${recommendation.action}`); return recommendation; } catch (error) { await this.log(`❌ Error getting smart recommendation: ${error.message}`); return this.getBaseRecommendation(distanceFromSL); } } /** * Get base recommendation using current thresholds */ getBaseRecommendation(distanceFromSL) { if (distanceFromSL <= this.learningThresholds.emergency) { return { action: 'EMERGENCY_EXIT', confidence: 0.8, reasoning: `Very close to SL (${distanceFromSL}%), emergency exit recommended` }; } else if (distanceFromSL <= this.learningThresholds.risk) { return { action: 'HIGH_ALERT', confidence: 0.7, reasoning: `Close to SL (${distanceFromSL}%), monitor closely` }; } else if (distanceFromSL <= this.learningThresholds.mediumRisk) { return { action: 'MONITOR', confidence: 0.6, reasoning: `Moderate distance from SL (${distanceFromSL}%), continue monitoring` }; } else { return { action: 'HOLD_POSITION', confidence: 0.5, reasoning: `Safe distance from SL (${distanceFromSL}%), maintain position` }; } } /** * Analyze historical patterns to improve recommendations */ async analyzePatterns(decisions, currentDistance) { const outcomes = await this.getOutcomesForDecisions(decisions); // Find decisions made at similar distances const similarDistanceDecisions = decisions.filter(d => { const data = JSON.parse(d.analysisData); const distance = data.distanceFromSL; return Math.abs(distance - currentDistance) <= 1.0; // Within 1% }); if (similarDistanceDecisions.length < 2) { return null; // Not enough similar data } // Analyze success rate of different actions at this distance const actionSuccess = {}; for (const decision of similarDistanceDecisions) { const decisionData = JSON.parse(decision.analysisData); const action = decisionData.decision; const outcome = outcomes.find(o => o.originalDecisionId === decision.id); if (outcome) { if (!actionSuccess[action]) { actionSuccess[action] = { total: 0, successful: 0 }; } actionSuccess[action].total++; if (outcome.wasCorrect) { actionSuccess[action].successful++; } } } // Find the action with highest success rate let bestAction = null; let bestSuccessRate = 0; for (const [action, stats] of Object.entries(actionSuccess)) { if (stats.total >= 2) { // Need at least 2 samples const successRate = stats.successful / stats.total; if (successRate > bestSuccessRate) { bestSuccessRate = successRate; bestAction = action; } } } if (bestAction && bestSuccessRate > 0.6) { return { action: bestAction, confidence: bestSuccessRate, reasoning: `Learned pattern: ${bestAction} successful ${Math.round(bestSuccessRate * 100)}% of time at this distance` }; } return null; } /** * Get outcomes for a set of decisions */ async getOutcomesForDecisions(decisions) { const prisma = await getDB(); const decisionIds = decisions.map(d => d.id); const outcomes = await prisma.ai_learning_data.findMany({ where: { analysisData: { string_contains: '"type":"STOP_LOSS_OUTCOME"' } } }); return outcomes.map(o => JSON.parse(o.analysisData)) .filter(outcome => decisionIds.includes(outcome.originalDecisionId)); } /** * Update learning thresholds based on outcome data */ async updateThresholdsFromOutcome(originalDecision, outcome) { // Simple threshold adjustment based on outcomes const distance = originalDecision.distanceFromSL; const wasCorrect = outcome.wasCorrect; if (!wasCorrect) { // If decision was wrong, adjust thresholds slightly if (originalDecision.decision === 'HOLD_POSITION' && outcome.actualOutcome === 'STOPPED_OUT') { // We should have exited earlier - make thresholds more conservative this.learningThresholds.emergency = Math.min(2.0, this.learningThresholds.emergency + 0.1); this.learningThresholds.risk = Math.min(3.0, this.learningThresholds.risk + 0.1); } } await this.log(`🔧 Thresholds updated: emergency=${this.learningThresholds.emergency}, risk=${this.learningThresholds.risk}`); } /** * Get current learning status and statistics */ async analyzeDecisionPatterns() { try { const prisma = await getDB(); // Get recent decisions and outcomes const decisions = await prisma.ai_learning_data.findMany({ where: { analysisData: { string_contains: '"type":"STOP_LOSS_DECISION"' }, createdAt: { gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) // Last 7 days } }, orderBy: { createdAt: 'desc' } }); const outcomes = await prisma.ai_learning_data.findMany({ where: { analysisData: { string_contains: '"type":"STOP_LOSS_OUTCOME"' }, createdAt: { gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) // Last 7 days } } }); // Analyze patterns const patterns = { totalDecisions: decisions.length, totalOutcomes: outcomes.length, successfulDecisions: outcomes.filter(o => JSON.parse(o.analysisData).wasCorrect).length, successRate: outcomes.length > 0 ? outcomes.filter(o => JSON.parse(o.analysisData).wasCorrect).length / outcomes.length : 0, learnedThresholds: this.learningThresholds }; return patterns; } catch (error) { await this.log(`❌ Error analyzing patterns: ${error.message}`); return { totalDecisions: 0, totalOutcomes: 0, successfulDecisions: 0, successRate: 0, learnedThresholds: this.learningThresholds }; } } /** * Get learning status */ async getLearningStatus() { try { const prisma = await getDB(); const totalDecisions = await prisma.ai_learning_data.count({ where: { analysisData: { string_contains: '"type":"STOP_LOSS_DECISION"' } } }); const recentDecisions = await prisma.ai_learning_data.count({ where: { analysisData: { string_contains: '"type":"STOP_LOSS_DECISION"' }, createdAt: { gte: new Date(Date.now() - 24 * 60 * 60 * 1000) // Last 24 hours } } }); return { totalDecisions, recentDecisions, thresholds: this.learningThresholds, isActive: totalDecisions > 0 }; } catch (error) { await this.log(`❌ Error getting learning status: ${error.message}`); return { totalDecisions: 0, recentDecisions: 0, thresholds: this.learningThresholds, isActive: false }; } } /** * Generate comprehensive learning report * Compatible implementation for enhanced-autonomous-risk-manager */ async generateLearningReport() { try { const status = await this.getLearningStatus(); const patterns = await this.analyzeDecisionPatterns(); // Calculate system confidence based on decisions made const systemConfidence = this.calculateSystemConfidence(status.totalDecisions, status.recentDecisions, patterns.successRate); const report = { timestamp: new Date().toISOString(), summary: { totalDecisions: status.totalDecisions, recentDecisions: status.recentDecisions, successfulPatterns: patterns.successfulDecisions, learningThresholds: this.learningThresholds, systemConfidence: systemConfidence, isActive: status.isActive, successRate: patterns.successRate }, insights: { emergencyThreshold: this.learningThresholds.emergency, riskThreshold: this.learningThresholds.risk, mediumRiskThreshold: this.learningThresholds.mediumRisk, confidenceLevel: systemConfidence > 0.7 ? 'HIGH' : systemConfidence > 0.4 ? 'MEDIUM' : 'LOW', totalOutcomes: patterns.totalOutcomes, decisionAccuracy: patterns.successRate }, recommendations: this.generateSystemRecommendations(status, patterns) }; await this.log(`📊 Learning report generated: ${report.summary.totalDecisions} decisions, ${(systemConfidence * 100).toFixed(1)}% confidence, ${(patterns.successRate * 100).toFixed(1)}% success rate`); return report; } catch (error) { await this.log(`❌ Error generating learning report: ${error.message}`); return { timestamp: new Date().toISOString(), summary: { totalDecisions: 0, recentDecisions: 0, systemConfidence: 0.0, isActive: false }, error: error.message }; } } /** * Calculate system confidence based on learning data */ calculateSystemConfidence(totalDecisions, recentDecisions, successRate = 0) { if (totalDecisions < 5) return 0.3; // Low confidence with insufficient data if (totalDecisions < 20) return 0.4 + (successRate * 0.2); // Medium-low confidence boosted by success if (totalDecisions < 50) return 0.6 + (successRate * 0.2); // Medium confidence boosted by success // High confidence with lots of data, scaled by recent activity and success rate const recentActivityFactor = Math.min(1.0, recentDecisions / 10); const successFactor = successRate || 0.5; // Default to neutral if no success data return Math.min(0.95, 0.7 + (recentActivityFactor * 0.1) + (successFactor * 0.15)); // Cap at 95% } /** * Generate system recommendations based on learning status */ generateSystemRecommendations(status, patterns) { const recommendations = []; if (status.totalDecisions < 10) { recommendations.push({ type: 'DATA_COLLECTION', message: 'Need more decision data for reliable learning', priority: 'HIGH' }); } if (status.recentDecisions < 3) { recommendations.push({ type: 'ACTIVITY_LOW', message: 'Recent trading activity is low - learning may be stale', priority: 'MEDIUM' }); } if (patterns && patterns.successRate < 0.4 && patterns.totalOutcomes >= 5) { recommendations.push({ type: 'THRESHOLD_ADJUSTMENT', message: 'Low success rate detected - consider adjusting decision thresholds', priority: 'HIGH' }); } if (status.totalDecisions >= 20 && patterns && patterns.successRate > 0.6) { recommendations.push({ type: 'SYSTEM_PERFORMING', message: 'System learning effectively with good success rate', priority: 'LOW' }); } if (status.totalDecisions >= 50) { recommendations.push({ type: 'OPTIMIZATION_READY', message: 'Sufficient data available for advanced threshold optimization', priority: 'LOW' }); } return recommendations; } } module.exports = SimplifiedStopLossLearner;