Files
trading_bot_v3/enhanced-global-automation.js
mindesbunister 284e1c8b8c feat: fix Safe Paper Trading display formatting and API sync
- Fixed field mapping between API and frontend (amount→positionSize, entry→entryPrice, createdAt→timestamp)
- Updated API sync function to properly convert API trade format to frontend format
- Resolved display issues: 'Invalid Date', missing entry price, missing trade size
- Added trade monitoring system and automation improvements
- Enhanced automation with simple-automation.js for reliable 24/7 operation
- Working automation now detecting 85% confidence BUY signals and executing trades
2025-08-07 16:55:41 +02:00

390 lines
13 KiB
JavaScript

#!/usr/bin/env node
/**
* Enhanced 24/7 Automation with Global Market Sentiment
* Integrates Fear & Greed Index and macro indicators for better trading decisions
*/
// Import M2 Money Supply indicator
const M2MoneySupplyIndicator = require('./m2-money-supply-indicator');
class EnhancedGlobalAutomation {
constructor() {
this.config = {
symbol: 'SOLUSD',
timeframe: '60',
intervalMinutes: 60,
autoExecuteThreshold: 60,
// API endpoint for container environment
apiHost: '192.168.0.1:9001',
// Enhanced with sentiment-based adjustments
sentimentThresholds: {
extremeFear: 75, // Lower threshold during extreme fear (more aggressive)
fear: 65, // Moderate lowering during fear
neutral: 60, // Base threshold
greed: 70, // Higher threshold during greed (more selective)
extremeGreed: 80 // Much higher threshold during extreme greed
}
};
this.cycleCount = 0;
this.m2Indicator = new M2MoneySupplyIndicator();
this.marketSentiment = {
fearGreedIndex: null,
bitcoinDominance: null,
m2Analysis: null,
marketRegime: 'UNKNOWN',
riskLevel: 'MODERATE',
lastUpdate: null
};
this.stats = {
startTime: new Date(),
totalCycles: 0,
totalTrades: 0,
sentimentAdjustments: 0,
regimeChanges: 0
};
}
async log(message) {
const timestamp = new Date().toISOString();
const logEntry = `[${timestamp}] ${message}`;
console.log(logEntry);
}
async updateMarketSentiment() {
try {
await this.log('📊 Updating global market sentiment...');
// Try to get Fear & Greed Index
const fearGreed = await this.getFearGreedIndex();
// Get Bitcoin Dominance (mock for now - in production use real API)
const btcDominance = await this.getBitcoinDominance();
// Calculate overall market regime
const regime = this.calculateMarketRegime(fearGreed, btcDominance);
// Update sentiment state
this.marketSentiment = {
fearGreedIndex: fearGreed,
bitcoinDominance: btcDominance,
marketRegime: regime.regime,
riskLevel: regime.riskLevel,
lastUpdate: new Date(),
tradingImplication: regime.tradingImplication
};
await this.log(`📈 Market Regime: ${regime.regime} | Risk: ${regime.riskLevel} | F&G: ${fearGreed || 'N/A'}`);
return this.marketSentiment;
} catch (error) {
await this.log(`❌ Sentiment update error: ${error.message}`);
return this.marketSentiment;
}
}
async getFearGreedIndex() {
try {
// Try multiple sources for Fear & Greed data
const sources = [
'https://api.alternative.me/fng/',
// Could add backup sources here
];
for (const source of sources) {
try {
const { stdout } = await execAsync(`timeout 10 curl -s "${source}"`);
const data = JSON.parse(stdout);
if (data.data && data.data[0]) {
return {
value: parseInt(data.data[0].value),
classification: data.data[0].value_classification,
timestamp: data.data[0].timestamp
};
}
} catch (error) {
continue; // Try next source
}
}
// Fallback: estimate from recent price action
return await this.estimateFearGreedFromPriceAction();
} catch (error) {
await this.log(`⚠️ Fear & Greed unavailable, using price-based estimate`);
return await this.estimateFearGreedFromPriceAction();
}
}
async estimateFearGreedFromPriceAction() {
try {
// Get recent analysis to estimate sentiment
const response = await fetch(`http://${this.config.apiHost}/api/ai-analysis/latest?symbol=${this.config.symbol}&timeframe=${this.config.timeframe}`);
const data = await response.json();
if (data.success && data.data && data.data.analysis) {
const analysis = data.data.analysis;
// Estimate Fear & Greed from technical indicators
let fearGreedEstimate = 50; // Neutral baseline
// Confidence level indicates sentiment strength
if (analysis.recommendation === 'BUY' && analysis.confidence > 70) {
fearGreedEstimate = 65; // Moderate greed
} else if (analysis.recommendation === 'SELL' && analysis.confidence > 70) {
fearGreedEstimate = 35; // Moderate fear
} else if (analysis.confidence < 50) {
fearGreedEstimate = 45; // Slight fear (uncertainty)
}
return {
value: fearGreedEstimate,
classification: this.classifyFearGreed(fearGreedEstimate),
timestamp: Date.now(),
source: 'price_action_estimate'
};
}
return { value: 50, classification: 'Neutral', timestamp: Date.now(), source: 'fallback' };
} catch (error) {
return { value: 50, classification: 'Neutral', timestamp: Date.now(), source: 'error_fallback' };
}
}
classifyFearGreed(value) {
if (value <= 25) return 'Extreme Fear';
if (value <= 45) return 'Fear';
if (value <= 55) return 'Neutral';
if (value <= 75) return 'Greed';
return 'Extreme Greed';
}
async getBitcoinDominance() {
// Mock implementation - in production, use real API like CoinGecko
// Estimate based on recent market conditions
return {
value: 52.5,
trend: 'stable',
interpretation: 'BTC holding ground vs altcoins'
};
}
calculateMarketRegime(fearGreed, btcDominance) {
let regime = 'NEUTRAL';
let riskLevel = 'MODERATE';
let tradingImplication = {};
if (fearGreed) {
const fgValue = fearGreed.value;
if (fgValue <= 25) {
regime = 'EXTREME_FEAR';
riskLevel = 'LOW'; // Low risk to buy during extreme fear
tradingImplication = {
action: 'AGGRESSIVE_BUY',
adjustedThreshold: this.config.sentimentThresholds.extremeFear,
reasoning: 'Extreme fear - contrarian opportunity'
};
} else if (fgValue <= 45) {
regime = 'FEAR';
riskLevel = 'LOW_MODERATE';
tradingImplication = {
action: 'CAUTIOUS_BUY',
adjustedThreshold: this.config.sentimentThresholds.fear,
reasoning: 'Fear present - good buying opportunity'
};
} else if (fgValue <= 55) {
regime = 'NEUTRAL';
riskLevel = 'MODERATE';
tradingImplication = {
action: 'NORMAL',
adjustedThreshold: this.config.sentimentThresholds.neutral,
reasoning: 'Balanced market - rely on technical analysis'
};
} else if (fgValue <= 75) {
regime = 'GREED';
riskLevel = 'MODERATE_HIGH';
tradingImplication = {
action: 'CAUTIOUS_SELL',
adjustedThreshold: this.config.sentimentThresholds.greed,
reasoning: 'Greed emerging - be more selective'
};
} else {
regime = 'EXTREME_GREED';
riskLevel = 'HIGH';
tradingImplication = {
action: 'DEFENSIVE',
adjustedThreshold: this.config.sentimentThresholds.extremeGreed,
reasoning: 'Extreme greed - potential market top'
};
}
}
return { regime, riskLevel, tradingImplication };
}
async start() {
await this.log('🚀 Enhanced 24/7 Automation with Global Sentiment Started');
await this.log(`📊 Base config: ${this.config.symbol} every ${this.config.intervalMinutes}m`);
// Update sentiment immediately
await this.updateMarketSentiment();
// Run first analysis cycle
await this.runEnhancedCycle();
// Schedule recurring cycles
setInterval(() => this.runEnhancedCycle().catch(console.error), this.config.intervalMinutes * 60 * 1000);
// Update sentiment every 30 minutes (more frequent than trading)
setInterval(() => this.updateMarketSentiment().catch(console.error), 30 * 60 * 1000);
await this.log(`⏰ Next cycle: ${new Date(Date.now() + this.config.intervalMinutes * 60 * 1000).toLocaleTimeString()}`);
}
async runEnhancedCycle() {
this.stats.totalCycles++;
await this.log(`🔄 Enhanced analysis cycle #${this.stats.totalCycles}`);
try {
// Update sentiment first
await this.updateMarketSentiment();
// Get current technical analysis
const response = await fetch(`http://${this.config.apiHost}/api/ai-analysis/latest?symbol=${this.config.symbol}&timeframe=${this.config.timeframe}`);
const data = await response.json();
if (data.success && data.data && data.data.analysis) {
const analysis = data.data.analysis;
// Apply sentiment-based threshold adjustment
const adjustedThreshold = this.getAdjustedThreshold();
await this.log(`📊 Technical: ${analysis.recommendation} (${analysis.confidence}%)`);
await this.log(`🎯 Threshold: ${adjustedThreshold}% (adjusted for ${this.marketSentiment.marketRegime})`);
if (analysis.confidence >= adjustedThreshold) {
await this.log(`🎯 EXECUTING: ${analysis.confidence}% ≥ ${adjustedThreshold}% (${this.marketSentiment.tradingImplication?.reasoning})`);
await this.executeEnhancedTrade(analysis);
} else {
await this.log(`⏸️ NO TRADE: ${analysis.confidence}% < ${adjustedThreshold}% (sentiment-adjusted threshold)`);
// Log why sentiment affected the decision
if (adjustedThreshold !== this.config.autoExecuteThreshold) {
this.stats.sentimentAdjustments++;
await this.log(`📈 Sentiment Impact: ${this.marketSentiment.marketRegime} adjusted threshold by ${adjustedThreshold - this.config.autoExecuteThreshold}%`);
}
}
} else {
await this.log('❌ No technical analysis available');
}
} catch (error) {
await this.log(`❌ Enhanced cycle error: ${error.message}`);
}
}
getAdjustedThreshold() {
const baseThreshold = this.config.autoExecuteThreshold;
if (!this.marketSentiment.tradingImplication) {
return baseThreshold;
}
return this.marketSentiment.tradingImplication.adjustedThreshold || baseThreshold;
}
async executeEnhancedTrade(analysis) {
try {
// Enhance trade data with sentiment context
const tradeData = {
symbol: this.config.symbol,
side: analysis.recommendation,
amount: this.calculateSentimentAdjustedAmount(),
entry: analysis.entry,
stopLoss: analysis.stopLoss,
takeProfit: analysis.takeProfit,
confidence: analysis.confidence,
reasoning: analysis.reasoning,
source: 'enhanced_24x7_sentiment',
// Enhanced fields
marketRegime: this.marketSentiment.marketRegime,
fearGreedValue: this.marketSentiment.fearGreedIndex?.value,
riskLevel: this.marketSentiment.riskLevel,
sentimentAdjustment: this.marketSentiment.tradingImplication?.reasoning
};
// Create enhanced paper trade
const response = await fetch(`http://${this.config.apiHost}/api/safe-paper-trading/create-trade`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(tradeData)
});
const result = await response.json();
if (result.success) {
this.stats.totalTrades++;
await this.log(`✅ ENHANCED TRADE: ${result.trade.id} | Regime: ${this.marketSentiment.marketRegime}`);
} else {
await this.log(`❌ TRADE FAILED: ${result.message}`);
}
} catch (error) {
await this.log(`❌ Enhanced trade execution error: ${error.message}`);
}
}
calculateSentimentAdjustedAmount() {
let baseAmount = 100;
// Adjust position size based on market sentiment
switch (this.marketSentiment.marketRegime) {
case 'EXTREME_FEAR':
return baseAmount * 1.5; // 50% larger positions during extreme fear
case 'FEAR':
return baseAmount * 1.2; // 20% larger during fear
case 'NEUTRAL':
return baseAmount; // Normal size
case 'GREED':
return baseAmount * 0.8; // 20% smaller during greed
case 'EXTREME_GREED':
return baseAmount * 0.5; // 50% smaller during extreme greed
default:
return baseAmount;
}
}
getStatus() {
const uptime = Math.floor((Date.now() - this.stats.startTime.getTime()) / 1000);
return {
isRunning: true,
config: this.config,
marketSentiment: this.marketSentiment,
stats: {
...this.stats,
uptime: `${Math.floor(uptime / 3600)}h ${Math.floor((uptime % 3600) / 60)}m`,
sentimentAdjustmentRate: `${this.stats.sentimentAdjustments}/${this.stats.totalCycles}`,
nextCycle: new Date(Date.now() + (this.config.intervalMinutes * 60 * 1000))
},
enhancement: {
sentimentIntegration: 'ACTIVE',
fearGreedTracking: this.marketSentiment.fearGreedIndex ? 'ACTIVE' : 'ESTIMATED',
riskAdjustment: 'DYNAMIC',
positionSizing: 'SENTIMENT_BASED'
}
};
}
}
// Create and start enhanced automation
const enhancedAutomation = new EnhancedGlobalAutomation();
enhancedAutomation.start();