feat: Complete global market sentiment integration with Fear & Greed Index
- Enhanced 24/7 automation with sentiment-based threshold adjustments - Multi-asset global trader supporting BTC, ETH, SOL, ADA - Comprehensive sentiment indicators guide with 8 categories - Global sentiment API providing real-time market regime detection - Fear & Greed Index integration with fallback estimation - Sentiment-adjusted confidence thresholds and position sizing - Successfully executed first sentiment-aware trade (GREED regime) - Market regime classification: EXTREME_FEAR to EXTREME_GREED - Trading threshold adjustments based on market psychology
This commit is contained in:
380
enhanced-global-automation.js
Normal file
380
enhanced-global-automation.js
Normal file
@@ -0,0 +1,380 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Enhanced 24/7 Automation with Global Market Sentiment
|
||||
* Integrates Fear & Greed Index and macro indicators for better trading decisions
|
||||
*/
|
||||
|
||||
const { exec } = require('child_process');
|
||||
const { promisify } = require('util');
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
class EnhancedGlobalAutomation {
|
||||
constructor() {
|
||||
this.config = {
|
||||
symbol: 'SOLUSD',
|
||||
timeframe: '60',
|
||||
intervalMinutes: 60,
|
||||
autoExecuteThreshold: 60,
|
||||
// Enhanced with sentiment-based adjustments
|
||||
sentimentThresholds: {
|
||||
extremeFear: 75, // Lower threshold during extreme fear (more aggressive)
|
||||
fear: 65, // Slightly lower during fear
|
||||
neutral: 60, // Normal threshold
|
||||
greed: 70, // Higher threshold during greed (more conservative)
|
||||
extremeGreed: 80 // Much higher threshold during extreme greed
|
||||
}
|
||||
};
|
||||
|
||||
this.marketSentiment = {
|
||||
fearGreedIndex: null,
|
||||
bitcoinDominance: 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 { stdout } = await execAsync(`curl -s "http://localhost:9001/api/ai-analysis/latest?symbol=${this.config.symbol}&timeframe=${this.config.timeframe}"`);
|
||||
const data = JSON.parse(stdout);
|
||||
|
||||
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 { stdout } = await execAsync(`curl -s "http://localhost:9001/api/ai-analysis/latest?symbol=${this.config.symbol}&timeframe=${this.config.timeframe}"`);
|
||||
const data = JSON.parse(stdout);
|
||||
|
||||
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 curlData = JSON.stringify(tradeData).replace(/"/g, '\\"');
|
||||
const { stdout: tradeResult } = await execAsync(`curl -s -X POST http://localhost:9001/api/safe-paper-trading/create-trade -H "Content-Type: application/json" -d "${curlData}"`);
|
||||
|
||||
const result = JSON.parse(tradeResult);
|
||||
|
||||
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();
|
||||
Reference in New Issue
Block a user