📚 COMPREHENSIVE KNOWLEDGE DOCUMENTATION
ADVANCED SYSTEM KNOWLEDGE: - Superior parallel screenshot system (60% performance gain) - AI learning system architecture and decision flow - Orphaned order cleanup integration patterns - Critical technical fixes and troubleshooting guide - Database schema best practices - Memory leak prevention strategies - AI learning system patterns and functions - Error handling best practices for trading systems - Integration patterns for position monitoring - Performance optimization rules - UI/UX consistency requirements - Critical anti-patterns to avoid - Added links to new knowledge base documents - Comprehensive documentation structure - Development guides and best practices - Performance optimizations summary - 60% screenshot performance improvement techniques - AI learning system that adapts trading decisions - Container stability and crash prevention - Frontend-backend consistency requirements - Integration strategies for existing infrastructure This documentation preserves critical insights from complex debugging sessions and provides patterns for future development.
This commit is contained in:
530
lib/simplified-stop-loss-learner-fixed.js
Normal file
530
lib/simplified-stop-loss-learner-fixed.js
Normal file
@@ -0,0 +1,530 @@
|
||||
/**
|
||||
* 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;
|
||||
Reference in New Issue
Block a user