#!/usr/bin/env node /** * Stop Loss Decision Learning System * * This system makes the AI learn from its own decision-making process near stop loss. * It records every decision, tracks outcomes, and continuously improves decision-making. */ const { getDB } = require('./db'); class StopLossDecisionLearner { constructor() { this.decisionHistory = []; this.learningThresholds = { emergencyDistance: 1.0, highRiskDistance: 2.0, mediumRiskDistance: 5.0 }; } async getPrisma() { return await getDB(); } async log(message) { const timestamp = new Date().toISOString(); console.log(`[${timestamp}] 🧠 SL Learner: ${message}`); } /** * Record an AI decision made near stop loss for learning purposes */ async recordDecision(decisionData) { try { const decision = { id: `decision_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, tradeId: decisionData.tradeId, symbol: decisionData.symbol, decisionType: decisionData.decision, // 'HOLD', 'EXIT', 'TIGHTEN_SL', 'PARTIAL_EXIT', 'EMERGENCY_EXIT' distanceFromSL: decisionData.distanceFromSL, reasoning: decisionData.reasoning, marketConditions: { price: decisionData.currentPrice, trend: await this.analyzeMarketTrend(decisionData.symbol), volatility: await this.calculateVolatility(decisionData.symbol), volume: decisionData.volume || 'unknown', timeOfDay: new Date().getHours(), dayOfWeek: new Date().getDay() }, confidenceScore: decisionData.confidenceScore || 0.7, expectedOutcome: decisionData.expectedOutcome || 'BETTER_RESULT', decisionTimestamp: new Date(), status: 'PENDING_OUTCOME' }; // Store in database const prisma = await this.getPrisma(); await prisma.sLDecision.create({ data: { id: decision.id, tradeId: decision.tradeId, symbol: decision.symbol, decisionType: decision.decisionType, distanceFromSL: decision.distanceFromSL, reasoning: decision.reasoning, marketConditions: JSON.stringify(decision.marketConditions), confidenceScore: decision.confidenceScore, expectedOutcome: decision.expectedOutcome, decisionTimestamp: decision.decisionTimestamp, status: decision.status } }); // Keep in memory for quick access this.decisionHistory.push(decision); await this.log(`šŸ“ Recorded decision: ${decision.decisionType} at ${decision.distanceFromSL}% from SL - ${decision.reasoning}`); return decision.id; } catch (error) { await this.log(`āŒ Error recording decision: ${error.message}`); return null; } } /** * Assess the outcome of a previous decision when trade closes or conditions change */ async assessDecisionOutcome(assessmentData) { try { const { decisionId, actualOutcome, timeToOutcome, pnlImpact, additionalContext } = assessmentData; // Determine if the decision was correct const wasCorrect = this.evaluateDecisionCorrectness(actualOutcome, pnlImpact); const learningScore = this.calculateLearningScore(wasCorrect, pnlImpact, timeToOutcome); // Update decision record const prisma = await this.getPrisma(); await prisma.sLDecision.update({ where: { id: decisionId }, data: { outcome: actualOutcome, outcomeTimestamp: new Date(), timeToOutcome, pnlImpact, wasCorrect, learningScore, additionalContext: JSON.stringify(additionalContext || {}), status: 'ASSESSED' } }); // Update in-memory history const decision = this.decisionHistory.find(d => d.id === decisionId); if (decision) { Object.assign(decision, { outcome: actualOutcome, outcomeTimestamp: new Date(), wasCorrect, learningScore, status: 'ASSESSED' }); } await this.log(`āœ… Assessed decision ${decisionId}: ${wasCorrect ? 'CORRECT' : 'INCORRECT'} - Score: ${learningScore.toFixed(2)}`); // Trigger learning update await this.updateLearningModel(); return { wasCorrect, learningScore }; } catch (error) { await this.log(`āŒ Error assessing decision outcome: ${error.message}`); return null; } } /** * Analyze historical decisions to identify patterns and optimize future decisions */ async analyzeDecisionPatterns() { try { const prisma = await this.getPrisma(); const decisions = await prisma.sLDecision.findMany({ where: { status: 'ASSESSED' }, orderBy: { decisionTimestamp: 'desc' }, take: 100 // Analyze last 100 decisions }); const patterns = { successfulPatterns: [], failurePatterns: [], optimalTiming: {}, contextFactors: {}, distanceOptimization: {} }; // Analyze success patterns by decision type const decisionTypes = ['HOLD', 'EXIT', 'TIGHTEN_SL', 'PARTIAL_EXIT', 'EMERGENCY_EXIT']; for (const type of decisionTypes) { const typeDecisions = decisions.filter(d => d.decisionType === type); const successRate = typeDecisions.length > 0 ? typeDecisions.filter(d => d.wasCorrect).length / typeDecisions.length : 0; const avgScore = typeDecisions.length > 0 ? typeDecisions.reduce((sum, d) => sum + (d.learningScore || 0), 0) / typeDecisions.length : 0; if (successRate > 0.6) { // 60%+ success rate patterns.successfulPatterns.push({ decisionType: type, successRate: successRate * 100, avgScore, sampleSize: typeDecisions.length, optimalConditions: this.identifyOptimalConditions(typeDecisions.filter(d => d.wasCorrect)) }); } else if (typeDecisions.length >= 5) { patterns.failurePatterns.push({ decisionType: type, successRate: successRate * 100, avgScore, sampleSize: typeDecisions.length, commonFailureReasons: this.identifyFailureReasons(typeDecisions.filter(d => !d.wasCorrect)) }); } } // Analyze optimal distance thresholds patterns.distanceOptimization = await this.optimizeDistanceThresholds(decisions); // Analyze timing patterns patterns.optimalTiming = await this.analyzeTimingPatterns(decisions); await this.log(`šŸ“Š Pattern analysis complete: ${patterns.successfulPatterns.length} successful patterns, ${patterns.failurePatterns.length} failure patterns identified`); return patterns; } catch (error) { await this.log(`āŒ Error analyzing decision patterns: ${error.message}`); return null; } } /** * Get AI recommendation for current situation based on learned patterns */ async getSmartRecommendation(situationData) { try { const { distanceFromSL, symbol, marketConditions } = situationData; // Get historical patterns for similar situations const patterns = await this.analyzeDecisionPatterns(); const currentConditions = marketConditions || await this.getCurrentMarketConditions(symbol); // Find most similar historical situations const similarSituations = await this.findSimilarSituations({ distanceFromSL, marketConditions: currentConditions }); // Generate recommendation based on learned patterns const recommendation = { suggestedAction: 'HOLD', // Default confidence: 0.5, reasoning: 'Insufficient learning data', learningBased: false, supportingData: {} }; if (similarSituations.length >= 3) { const successfulActions = similarSituations .filter(s => s.wasCorrect) .map(s => s.decisionType); const mostSuccessfulAction = this.getMostCommonAction(successfulActions); const successRate = successfulActions.length / similarSituations.length; recommendation.suggestedAction = mostSuccessfulAction; recommendation.confidence = Math.min(0.95, successRate + 0.1); recommendation.reasoning = `Based on ${similarSituations.length} similar situations, ${mostSuccessfulAction} succeeded ${(successRate * 100).toFixed(1)}% of the time`; recommendation.learningBased = true; recommendation.supportingData = { historicalSamples: similarSituations.length, successRate: successRate * 100, avgPnlImpact: similarSituations.reduce((sum, s) => sum + (s.pnlImpact || 0), 0) / similarSituations.length }; } await this.log(`šŸŽÆ Smart recommendation: ${recommendation.suggestedAction} (${(recommendation.confidence * 100).toFixed(1)}% confidence) - ${recommendation.reasoning}`); return recommendation; } catch (error) { await this.log(`āŒ Error generating smart recommendation: ${error.message}`); return { suggestedAction: 'HOLD', confidence: 0.3, reasoning: `Error in recommendation system: ${error.message}`, learningBased: false }; } } /** * Update learning model based on new decision outcomes */ async updateLearningModel() { try { const patterns = await this.analyzeDecisionPatterns(); if (patterns && patterns.distanceOptimization) { // Update decision thresholds based on learning this.learningThresholds = { emergencyDistance: patterns.distanceOptimization.optimalEmergencyThreshold || 1.0, highRiskDistance: patterns.distanceOptimization.optimalHighRiskThreshold || 2.0, mediumRiskDistance: patterns.distanceOptimization.optimalMediumRiskThreshold || 5.0 }; await this.log(`šŸ”„ Updated learning thresholds: Emergency=${this.learningThresholds.emergencyDistance}%, High Risk=${this.learningThresholds.highRiskDistance}%, Medium Risk=${this.learningThresholds.mediumRiskDistance}%`); } return true; } catch (error) { await this.log(`āŒ Error updating learning model: ${error.message}`); return false; } } /** * Helper methods for analysis */ evaluateDecisionCorrectness(actualOutcome, pnlImpact) { // Define what constitutes a "correct" decision const correctOutcomes = [ 'BETTER_THAN_ORIGINAL_SL', 'AVOIDED_LOSS', 'IMPROVED_PROFIT', 'SUCCESSFUL_EXIT' ]; return correctOutcomes.includes(actualOutcome) || (pnlImpact && pnlImpact > 0); } calculateLearningScore(wasCorrect, pnlImpact, timeToOutcome) { let score = wasCorrect ? 0.7 : 0.3; // Base score // Adjust for P&L impact if (pnlImpact) { score += Math.min(0.2, pnlImpact / 100); // Max 0.2 bonus for positive P&L } // Adjust for timing (faster good decisions are better) if (timeToOutcome && wasCorrect) { const timingBonus = Math.max(0, 0.1 - (timeToOutcome / 3600)); // Bonus for decisions resolved within an hour score += timingBonus; } return Math.max(0, Math.min(1, score)); } identifyOptimalConditions(successfulDecisions) { // Analyze common conditions in successful decisions const conditions = {}; successfulDecisions.forEach(decision => { try { const market = JSON.parse(decision.marketConditions || '{}'); // Track successful decision contexts if (market.trend) { conditions.trend = conditions.trend || {}; conditions.trend[market.trend] = (conditions.trend[market.trend] || 0) + 1; } if (market.timeOfDay) { conditions.timeOfDay = conditions.timeOfDay || {}; const hour = market.timeOfDay; conditions.timeOfDay[hour] = (conditions.timeOfDay[hour] || 0) + 1; } } catch (error) { // Skip malformed data } }); return conditions; } identifyFailureReasons(failedDecisions) { // Analyze what went wrong in failed decisions return failedDecisions.map(decision => ({ reasoning: decision.reasoning, distanceFromSL: decision.distanceFromSL, outcome: decision.outcome, pnlImpact: decision.pnlImpact })); } async optimizeDistanceThresholds(decisions) { // Analyze optimal distance thresholds for different decision types const optimization = {}; // Group decisions by distance ranges const ranges = [ { min: 0, max: 1, label: 'emergency' }, { min: 1, max: 2, label: 'highRisk' }, { min: 2, max: 5, label: 'mediumRisk' }, { min: 5, max: 100, label: 'safe' } ]; for (const range of ranges) { const rangeDecisions = decisions.filter(d => d.distanceFromSL >= range.min && d.distanceFromSL < range.max ); if (rangeDecisions.length >= 3) { const successRate = rangeDecisions.filter(d => d.wasCorrect).length / rangeDecisions.length; const avgScore = rangeDecisions.reduce((sum, d) => sum + (d.learningScore || 0), 0) / rangeDecisions.length; optimization[`${range.label}Range`] = { successRate: successRate * 100, avgScore, sampleSize: rangeDecisions.length, optimalThreshold: this.calculateOptimalThreshold(rangeDecisions) }; } } return optimization; } calculateOptimalThreshold(decisions) { // Find the distance threshold that maximizes success rate const sortedDecisions = decisions.sort((a, b) => a.distanceFromSL - b.distanceFromSL); let bestThreshold = 1.0; let bestScore = 0; for (let i = 0; i < sortedDecisions.length - 1; i++) { const threshold = sortedDecisions[i].distanceFromSL; const aboveThreshold = sortedDecisions.slice(i); const successRate = aboveThreshold.filter(d => d.wasCorrect).length / aboveThreshold.length; if (successRate > bestScore && aboveThreshold.length >= 3) { bestScore = successRate; bestThreshold = threshold; } } return bestThreshold; } async analyzeTimingPatterns(decisions) { // Analyze when decisions work best (time of day, day of week, etc.) const timing = { timeOfDay: {}, dayOfWeek: {}, marketSession: {} }; decisions.forEach(decision => { try { const market = JSON.parse(decision.marketConditions || '{}'); const wasCorrect = decision.wasCorrect; if (market.timeOfDay !== undefined) { const hour = market.timeOfDay; timing.timeOfDay[hour] = timing.timeOfDay[hour] || { total: 0, correct: 0 }; timing.timeOfDay[hour].total++; if (wasCorrect) timing.timeOfDay[hour].correct++; } if (market.dayOfWeek !== undefined) { const day = market.dayOfWeek; timing.dayOfWeek[day] = timing.dayOfWeek[day] || { total: 0, correct: 0 }; timing.dayOfWeek[day].total++; if (wasCorrect) timing.dayOfWeek[day].correct++; } } catch (error) { // Skip malformed data } }); return timing; } async findSimilarSituations(currentSituation) { const { distanceFromSL, marketConditions } = currentSituation; const tolerance = 0.5; // 0.5% tolerance for distance matching const prisma = await this.getPrisma(); const decisions = await prisma.sLDecision.findMany({ where: { status: 'ASSESSED', distanceFromSL: { gte: distanceFromSL - tolerance, lte: distanceFromSL + tolerance } }, orderBy: { decisionTimestamp: 'desc' }, take: 20 }); return decisions; } getMostCommonAction(actions) { const counts = {}; actions.forEach(action => { counts[action] = (counts[action] || 0) + 1; }); return Object.entries(counts).reduce((a, b) => counts[a] > counts[b] ? a : b)[0] || 'HOLD'; } async analyzeMarketTrend(symbol) { // Simplified trend analysis - in real implementation, use technical indicators try { const response = await fetch(`http://localhost:9001/api/automation/position-monitor`); const data = await response.json(); if (data.success && data.monitor && data.monitor.position) { const pnl = data.monitor.position.unrealizedPnl; if (pnl > 0) return 'BULLISH'; if (pnl < 0) return 'BEARISH'; return 'SIDEWAYS'; } } catch (error) { // Fallback } return 'UNKNOWN'; } async calculateVolatility(symbol) { // Simplified volatility calculation // In real implementation, calculate based on price history return Math.random() * 0.1; // Mock volatility 0-10% } async getCurrentMarketConditions(symbol) { return { trend: await this.analyzeMarketTrend(symbol), volatility: await this.calculateVolatility(symbol), timeOfDay: new Date().getHours(), dayOfWeek: new Date().getDay() }; } /** * Generate learning insights report */ async generateLearningReport() { try { const patterns = await this.analyzeDecisionPatterns(); const report = { timestamp: new Date().toISOString(), summary: { totalDecisions: this.decisionHistory.length, successfulPatterns: patterns?.successfulPatterns?.length || 0, learningThresholds: this.learningThresholds, systemConfidence: this.calculateSystemConfidence() }, insights: patterns, recommendations: await this.generateSystemRecommendations(patterns) }; await this.log(`šŸ“Š Learning report generated: ${report.summary.totalDecisions} decisions analyzed`); return report; } catch (error) { await this.log(`āŒ Error generating learning report: ${error.message}`); return null; } } calculateSystemConfidence() { const recentDecisions = this.decisionHistory.slice(-20); // Last 20 decisions if (recentDecisions.length < 5) return 0.3; // Low confidence with insufficient data const successRate = recentDecisions.filter(d => d.wasCorrect).length / recentDecisions.length; return Math.min(0.95, successRate + 0.1); // Cap at 95% } async generateSystemRecommendations(patterns) { const recommendations = []; if (patterns?.failurePatterns?.length > 0) { patterns.failurePatterns.forEach(pattern => { recommendations.push({ type: 'IMPROVEMENT', priority: 'HIGH', message: `Consider avoiding ${pattern.decisionType} decisions - only ${pattern.successRate.toFixed(1)}% success rate`, actionable: true }); }); } if (patterns?.successfulPatterns?.length > 0) { const bestPattern = patterns.successfulPatterns.reduce((best, current) => current.successRate > best.successRate ? current : best ); recommendations.push({ type: 'OPTIMIZATION', priority: 'MEDIUM', message: `${bestPattern.decisionType} decisions show ${bestPattern.successRate.toFixed(1)}% success rate - consider using more often`, actionable: true }); } return recommendations; } } // Export for use in other modules module.exports = StopLossDecisionLearner; // Direct execution for testing if (require.main === module) { const learner = new StopLossDecisionLearner(); console.log('🧠 Stop Loss Decision Learning System'); console.log('šŸ“Š Ready to make your AI smarter with every decision!'); // Demo decision recording setTimeout(async () => { await learner.recordDecision({ tradeId: 'demo_001', symbol: 'SOL-PERP', decision: 'TIGHTEN_SL', distanceFromSL: 2.3, reasoning: 'Market showing weakness, reducing risk exposure', currentPrice: 182.45, confidenceScore: 0.8, expectedOutcome: 'BETTER_RESULT' }); const report = await learner.generateLearningReport(); console.log('\nšŸ“Š LEARNING REPORT:', JSON.stringify(report, null, 2)); }, 1000); }