2026-10-28 00:30:38 -04:00 | | | #include "RuntimeStorage.mqh"
|
2026-04-14 20:08:20 -04:00 | | | //+------------------------------------------------------------------+
|
| | | //| CNewsImpactAnalyzer.mqh - Dynamic News-Based Threshold Adjustment |
|
| | | //| Analyzes news impact in real-time and adjusts trading thresholds |
|
| | | //| dynamically based on market conditions and news sentiment |
|
| | | //+------------------------------------------------------------------+
|
| | | #ifndef CNEWSIMPACTANALYZER_MQH
|
| | | #define CNEWSIMPACTANALYZER_MQH
|
| | |
|
| | | #include "CEconomicCalendar.mqh"
|
| | | #include "CErrorRecovery.mqh"
|
| | |
|
| | | // News impact state
|
| | | enum ENUM_NEWS_STATE
|
| | | {
|
| | | NEWS_STATE_QUIET = 0, // No significant news
|
| | | NEWS_STATE_WARNING = 1, // News approaching
|
| | | NEWS_STATE_ACTIVE = 2, // News happening now
|
| | | NEWS_STATE_AFTERMATH = 3 // Post-news volatility
|
| | | };
|
| | |
|
| | | // Threshold adjustment direction
|
| | | enum ENUM_THRESHOLD_ADJUSTMENT
|
| | | {
|
| | | THRESHOLD_NORMAL = 0, // No adjustment
|
| | | THRESHOLD_CONSERVATIVE = 1, // Increase thresholds (harder to trade)
|
| | | THRESHOLD_AGGRESSIVE = 2, // Decrease thresholds (easier to trade)
|
| | | THRESHOLD_BLOCK = 3 // Block trading completely
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Threshold Adjustment Result |
|
| | | //+------------------------------------------------------------------+
|
| | | struct SThresholdAdjustment
|
| | | {
|
| | | ENUM_THRESHOLD_ADJUSTMENT direction;
|
| | | double confidence_multiplier;
|
| | | double risk_multiplier;
|
| | | double position_size_multiplier;
|
| | | int sl_pips_adjustment;
|
| | | int tp_pips_adjustment;
|
| | | string reason;
|
| | | datetime effective_until;
|
| | | bool should_trade;
|
| | |
|
| | | SThresholdAdjustment()
|
| | | {
|
| | | direction = THRESHOLD_NORMAL;
|
| | | confidence_multiplier = 1.0;
|
| | | risk_multiplier = 1.0;
|
| | | position_size_multiplier = 1.0;
|
| | | sl_pips_adjustment = 0;
|
| | | tp_pips_adjustment = 0;
|
| | | reason = "";
|
| | | effective_until = 0;
|
| | | should_trade = true;
|
| | | }
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Market Response Metrics |
|
| | | //+------------------------------------------------------------------+
|
| | | struct SMarketResponse
|
| | | {
|
| | | double price_change_pct;
|
| | | double volatility_spike;
|
| | | double spread_widening;
|
| | | int volume_surge;
|
| | | bool is_trending;
|
| | | int trend_direction;
|
| | | datetime measurement_time;
|
| | |
|
| | | SMarketResponse()
|
| | | {
|
| | | price_change_pct = 0.0;
|
| | | volatility_spike = 0.0;
|
| | | spread_widening = 0.0;
|
| | | volume_surge = 0;
|
| | | is_trending = false;
|
| | | trend_direction = 0;
|
| | | measurement_time = 0;
|
| | | }
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| News Impact History Entry |
|
| | | //+------------------------------------------------------------------+
|
| | | struct SNewsImpactHistory
|
| | | {
|
| | | long event_id;
|
| | | string event_name;
|
| | | datetime event_time;
|
| | | int importance;
|
| | | double actual_value;
|
| | | double forecast_value;
|
| | | double surprise_magnitude;
|
| | | SMarketResponse market_response;
|
| | | bool profitable_to_trade;
|
| | | double ml_confidence_score;
|
| | |
|
| | | SNewsImpactHistory()
|
| | | {
|
| | | event_id = 0;
|
| | | event_name = "";
|
| | | event_time = 0;
|
| | | importance = 0;
|
| | | actual_value = 0.0;
|
| | | forecast_value = 0.0;
|
| | | surprise_magnitude = 0.0;
|
| | | profitable_to_trade = false;
|
| | | ml_confidence_score = 0.0;
|
| | | }
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| News Impact Analyzer |
|
| | | //+------------------------------------------------------------------+
|
| | | class CNewsImpactAnalyzer
|
| | | {
|
| | | private:
|
| | | static CNewsImpactAnalyzer* s_instance;
|
| | |
|
| | | // Components
|
| | | CEconomicCalendar* m_calendar;
|
| | | CErrorRecovery* m_error_recovery;
|
| | |
|
| | | // State tracking
|
| | | ENUM_NEWS_STATE m_current_state;
|
| | | datetime m_state_since;
|
| | | SThresholdAdjustment m_current_adjustment;
|
| | |
|
| | | // History for ML
|
| | | SNewsImpactHistory m_impact_history[];
|
| | | int m_history_count;
|
| | | int m_max_history;
|
| | |
|
| | | // Real-time metrics
|
| | | double m_baseline_volatility;
|
| | | double m_baseline_spread;
|
| | | datetime m_baseline_measured;
|
| | | int m_baseline_lookback_bars;
|
| | |
|
| | | // Configuration
|
| | | bool m_use_ml_predictions;
|
| | | bool m_export_for_training;
|
| | | string m_export_filename;
|
| | | int m_volatility_measurement_bars;
|
| | | double m_spread_widening_threshold;
|
| | | double m_volatility_spike_threshold;
|
| | |
|
| | | // Telemetry
|
| | | int m_analysis_count;
|
| | | int m_trade_blocked_count;
|
| | | int m_threshold_adjusted_count;
|
| | |
|
| | | public:
|
| | | CNewsImpactAnalyzer()
|
| | | {
|
| | | m_calendar = CEconomicCalendar::Instance();
|
| | | m_error_recovery = CErrorRecovery::Instance();
|
| | |
|
| | | m_current_state = NEWS_STATE_QUIET;
|
| | | m_state_since = 0;
|
| | |
|
| | | m_history_count = 0;
|
| | | m_max_history = 200;
|
| | | ArrayResize(m_impact_history, m_max_history);
|
| | |
|
| | | m_baseline_volatility = 0.0;
|
| | | m_baseline_spread = 0.0;
|
| | | m_baseline_measured = 0;
|
| | | m_baseline_lookback_bars = 20;
|
| | |
|
| | | m_use_ml_predictions = true;
|
| | | m_export_for_training = true;
|
| | | m_export_filename = "DualEA\\news_impact_training.csv";
|
| | | m_volatility_measurement_bars = 10;
|
| | | m_spread_widening_threshold = 1.5; // 150% of normal
|
| | | m_volatility_spike_threshold = 2.0; // 200% of normal
|
| | |
|
| | | m_analysis_count = 0;
|
| | | m_trade_blocked_count = 0;
|
| | | m_threshold_adjusted_count = 0;
|
| | | }
|
| | |
|
| | | ~CNewsImpactAnalyzer()
|
| | | {
|
| | | }
|
| | |
|
| | | static CNewsImpactAnalyzer* Instance()
|
| | | {
|
| | | if(s_instance == NULL)
|
| | | s_instance = new CNewsImpactAnalyzer();
|
| | | return s_instance;
|
| | | }
|
| | |
|
| | | static void Destroy()
|
| | | {
|
| | | if(s_instance != NULL)
|
| | | {
|
| | | delete s_instance;
|
| | | s_instance = NULL;
|
| | | }
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Configuration |
|
| | | //+------------------------------------------------------------------+
|
| | | void SetUseMLPredictions(bool use) { m_use_ml_predictions = use; }
|
| | | void SetExportForTraining(bool export_data) { m_export_for_training = export_data; }
|
| | | void SetBaselineLookback(int bars) { m_baseline_lookback_bars = bars; }
|
| | | void SetSpreadThreshold(double threshold) { m_spread_widening_threshold = threshold; }
|
| | | void SetVolatilityThreshold(double threshold) { m_volatility_spike_threshold = threshold; }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Main analysis entry point |
|
| | | //+------------------------------------------------------------------+
|
| | | SThresholdAdjustment AnalyzeNewsImpact(const string symbol, int buffer_minutes_before = 30, int buffer_minutes_after = 60)
|
| | | {
|
| | | m_analysis_count++;
|
| | |
|
| | | SThresholdAdjustment adjustment;
|
| | |
|
| | | // Get news impact from calendar
|
| | | SNewsImpact news_impact = m_calendar.CheckNewsImpact(symbol, buffer_minutes_before, buffer_minutes_after);
|
| | |
|
| | | // Update baseline if needed
|
| | | UpdateBaselineMetrics(symbol);
|
| | |
|
| | | // Measure current market response
|
| | | SMarketResponse response = MeasureMarketResponse(symbol);
|
| | |
|
| | | // Determine news state
|
| | | ENUM_NEWS_STATE new_state = DetermineNewsState(news_impact, response);
|
| | |
|
| | | // Handle state transition
|
| | | if(new_state != m_current_state)
|
| | | {
|
| | | Log("News state changed from " + StateToString(m_current_state) + " to " + StateToString(new_state));
|
| | | m_current_state = new_state;
|
| | | m_state_since = TimeCurrent();
|
| | | }
|
| | |
|
| | | // Calculate threshold adjustment
|
| | | adjustment = CalculateThresholdAdjustment(new_state, news_impact, response);
|
| | | m_current_adjustment = adjustment;
|
| | |
|
| | | // Update statistics
|
| | | if(!adjustment.should_trade)
|
| | | m_trade_blocked_count++;
|
| | | if(adjustment.direction != THRESHOLD_NORMAL)
|
| | | m_threshold_adjusted_count++;
|
| | |
|
| | | // Record for ML training
|
| | | if(m_export_for_training && news_impact.is_news_time)
|
| | | {
|
| | | RecordForTraining(symbol, news_impact, response, adjustment);
|
| | | }
|
| | |
|
| | | return adjustment;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Update baseline volatility and spread |
|
| | | //+------------------------------------------------------------------+
|
| | | void UpdateBaselineMetrics(const string symbol)
|
| | | {
|
| | | datetime now = TimeCurrent();
|
| | |
|
| | | // Update every 5 minutes
|
| | | if((now - m_baseline_measured) < 300)
|
| | | return;
|
| | |
|
| | | // Calculate baseline volatility (ATR)
|
| | | int atr_handle = iATR(symbol, PERIOD_CURRENT, m_baseline_lookback_bars);
|
| | | if(atr_handle != INVALID_HANDLE)
|
| | | {
|
| | | double atr[];
|
| | | if(CopyBuffer(atr_handle, 0, 0, 1, atr) == 1)
|
| | | {
|
| | | m_baseline_volatility = atr[0];
|
| | | }
|
| | | IndicatorRelease(atr_handle);
|
| | | }
|
| | |
|
| | | // Get baseline spread
|
| | | m_baseline_spread = SymbolInfoInteger(symbol, SYMBOL_SPREAD) * SymbolInfoDouble(symbol, SYMBOL_POINT);
|
| | |
|
| | | m_baseline_measured = now;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Measure current market response to news |
|
| | | //+------------------------------------------------------------------+
|
| | | SMarketResponse MeasureMarketResponse(const string symbol)
|
| | | {
|
| | | SMarketResponse response;
|
| | | response.measurement_time = TimeCurrent();
|
| | |
|
| | | // Price change over last N bars
|
| | | MqlRates rates[];
|
| | | if(CopyRates(symbol, PERIOD_CURRENT, 0, m_volatility_measurement_bars, rates) == m_volatility_measurement_bars)
|
| | | {
|
| | | double price_start = rates[0].close;
|
| | | double price_end = rates[m_volatility_measurement_bars - 1].close;
|
| | | response.price_change_pct = (price_end - price_start) / price_start * 100.0;
|
| | |
|
| | | // Calculate volatility spike
|
| | | double current_atr = 0.0;
|
| | | for(int i = 1; i < m_volatility_measurement_bars; i++)
|
| | | {
|
| | | current_atr += MathMax(rates[i].high - rates[i].low,
|
| | | MathMax(MathAbs(rates[i].high - rates[i-1].close),
|
| | | MathAbs(rates[i].low - rates[i-1].close)));
|
| | | }
|
| | | current_atr /= m_volatility_measurement_bars;
|
| | |
|
| | | if(m_baseline_volatility > 0)
|
| | | {
|
| | | response.volatility_spike = current_atr / m_baseline_volatility;
|
| | | }
|
| | |
|
| | | // Determine if trending
|
| | | double highest_high = rates[0].high;
|
| | | double lowest_low = rates[0].low;
|
| | | for(int i = 1; i < m_volatility_measurement_bars; i++)
|
| | | {
|
| | | highest_high = MathMax(highest_high, rates[i].high);
|
| | | lowest_low = MathMin(lowest_low, rates[i].low);
|
| | | }
|
| | |
|
| | | double range = highest_high - lowest_low;
|
| | | if(range > 0)
|
| | | {
|
| | | response.is_trending = MathAbs(response.price_change_pct) > (range / price_start * 100.0 * 0.6);
|
| | | response.trend_direction = (response.price_change_pct > 0) ? 1 : -1;
|
| | | }
|
| | | }
|
| | |
|
| | | // Current spread vs baseline
|
| | | double current_spread = SymbolInfoInteger(symbol, SYMBOL_SPREAD) * SymbolInfoDouble(symbol, SYMBOL_POINT);
|
| | | if(m_baseline_spread > 0)
|
| | | {
|
| | | response.spread_widening = current_spread / m_baseline_spread;
|
| | | }
|
| | |
|
| | | // Volume surge (if available)
|
| | | long volume[];
|
| | | if(CopyTickVolume(symbol, PERIOD_CURRENT, 0, m_volatility_measurement_bars, volume) == m_volatility_measurement_bars)
|
| | | {
|
| | | long avg_volume = 0;
|
| | | for(int i = 0; i < m_volatility_measurement_bars; i++)
|
| | | {
|
| | | avg_volume += volume[i];
|
| | | }
|
| | | avg_volume /= m_volatility_measurement_bars;
|
| | |
|
| | | if(avg_volume > 0)
|
| | | {
|
| | | response.volume_surge = (int)((volume[m_volatility_measurement_bars - 1] * 100) / avg_volume);
|
| | | }
|
| | | }
|
| | |
|
| | | return response;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Determine current news state |
|
| | | //+------------------------------------------------------------------+
|
| | | ENUM_NEWS_STATE DetermineNewsState(const SNewsImpact &news, const SMarketResponse &response)
|
| | | {
|
| | | // Check for active news
|
| | | if(news.is_news_time)
|
| | | {
|
| | | return NEWS_STATE_ACTIVE;
|
| | | }
|
| | |
|
| | | // Check for aftermath (high volatility after recent news)
|
| | | if(response.volatility_spike > m_volatility_spike_threshold &&
|
| | | (TimeCurrent() - m_state_since) < 1800) // Within 30 min of state change
|
| | | {
|
| | | return NEWS_STATE_AFTERMATH;
|
| | | }
|
| | |
|
| | | // Check for warning (news approaching within 15 minutes)
|
| | | if(news.next_event_time > 0)
|
| | | {
|
| | | int seconds_to_event = (int)(news.next_event_time - TimeCurrent());
|
| | | if(seconds_to_event > 0 && seconds_to_event <= 900) // 15 minutes
|
| | | {
|
| | | return NEWS_STATE_WARNING;
|
| | | }
|
| | | }
|
| | |
|
| | | return NEWS_STATE_QUIET;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Calculate threshold adjustment based on state |
|
| | | //+------------------------------------------------------------------+
|
| | | SThresholdAdjustment CalculateThresholdAdjustment(ENUM_NEWS_STATE state,
|
| | | const SNewsImpact &news,
|
| | | const SMarketResponse &response)
|
| | | {
|
| | | SThresholdAdjustment adj;
|
| | |
|
| | | switch(state)
|
| | | {
|
| | | case NEWS_STATE_QUIET:
|
| | | // Normal trading conditions
|
| | | adj.direction = THRESHOLD_NORMAL;
|
| | | adj.confidence_multiplier = 1.0;
|
| | | adj.risk_multiplier = 1.0;
|
| | | adj.position_size_multiplier = 1.0;
|
| | | adj.should_trade = true;
|
| | | adj.reason = "Normal market conditions";
|
| | | break;
|
| | |
|
| | | case NEWS_STATE_WARNING:
|
| | | // News approaching - be more conservative
|
| | | adj.direction = THRESHOLD_CONSERVATIVE;
|
| | | adj.confidence_multiplier = 1.2; // 20% harder to meet confidence
|
| | | adj.risk_multiplier = 0.8; // 20% less risk
|
| | | adj.position_size_multiplier = 0.7; // 30% smaller positions
|
| | | adj.sl_pips_adjustment = 5; // Wider SL
|
| | | adj.tp_pips_adjustment = 3; // Wider TP
|
| | | adj.should_trade = true;
|
| | | adj.reason = "News approaching: " + news.next_event_name;
|
| | | adj.effective_until = news.next_event_time + 3600; // 1 hour after event
|
| | | break;
|
| | |
|
| | | case NEWS_STATE_ACTIVE:
|
| | | // News happening now - most conservative or block
|
| | | if(news.current_importance >= EVENT_IMPORTANCE_HIGH)
|
| | | {
|
| | | adj.direction = THRESHOLD_BLOCK;
|
| | | adj.should_trade = false;
|
| | | adj.reason = "HIGH IMPACT NEWS ACTIVE: " + news.reason;
|
| | | }
|
| | | else
|
| | | {
|
| | | adj.direction = THRESHOLD_CONSERVATIVE;
|
| | | adj.confidence_multiplier = 1.5; // 50% harder
|
| | | adj.risk_multiplier = 0.5; // 50% less risk
|
| | | adj.position_size_multiplier = 0.5; // 50% smaller
|
| | | adj.sl_pips_adjustment = 10; // Much wider SL
|
| | | adj.tp_pips_adjustment = 5;
|
| | | adj.should_trade = true;
|
| | | adj.reason = "News active: " + news.reason;
|
| | | }
|
| | | adj.effective_until = TimeCurrent() + 1800; // 30 minutes
|
| | | break;
|
| | |
|
| | | case NEWS_STATE_AFTERMATH:
|
| | | // Post-news volatility - conservative
|
| | | adj.direction = THRESHOLD_CONSERVATIVE;
|
| | | adj.confidence_multiplier = 1.3;
|
| | | adj.risk_multiplier = 0.7;
|
| | | adj.position_size_multiplier = 0.6;
|
| | | adj.should_trade = true;
|
| | | adj.reason = "Post-news volatility detected";
|
| | | adj.effective_until = TimeCurrent() + 900; // 15 minutes
|
| | | break;
|
| | | }
|
| | |
|
| | | // Additional adjustments based on market response
|
| | | if(response.volatility_spike > m_volatility_spike_threshold)
|
| | | {
|
| | | adj.confidence_multiplier *= 1.2;
|
| | | adj.reason += " | High volatility detected";
|
| | | }
|
| | |
|
| | | if(response.spread_widening > m_spread_widening_threshold)
|
| | | {
|
| | | adj.confidence_multiplier *= 1.1;
|
| | | adj.position_size_multiplier *= 0.8;
|
| | | adj.reason += " | Spread widening";
|
| | | }
|
| | |
|
| | | return adj;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Record data for ML training |
|
| | | //+------------------------------------------------------------------+
|
| | | void RecordForTraining(const string symbol,
|
| | | const SNewsImpact &news,
|
| | | const SMarketResponse &response,
|
| | | const SThresholdAdjustment &adjustment)
|
| | | {
|
| | | // Build CSV line
|
| | | string line = StringFormat("%s,%s,%.4f,%.4f,%.4f,%d,%.2f,%.2f,%d,%.2f,%d,%.2f,%s\n",
|
| | | TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS),
|
| | | symbol,
|
| | | news.volatility_prediction,
|
| | | response.volatility_spike,
|
| | | response.spread_widening,
|
| | | response.volume_surge,
|
| | | response.price_change_pct,
|
| | | (double)response.is_trending,
|
| | | adjustment.direction,
|
| | | adjustment.confidence_multiplier,
|
| | | (adjustment.should_trade ? 1 : 0),
|
| | | CalculateProfitabilityScore(news, response),
|
| | | news.reason
|
| | | );
|
| | |
|
| | | // Append to training file
|
| | | int handle = FileOpen(m_export_filename,
|
2026-10-28 00:30:38 -04:00 | | | FILE_WRITE|FILE_READ|FILE_CSV|DUALEA_FILE_COMMON,
|
2026-04-14 20:08:20 -04:00 | | | ',');
|
| | |
|
| | | if(handle != INVALID_HANDLE)
|
| | | {
|
| | | FileSeek(handle, 0, SEEK_END);
|
| | |
|
| | | // Write header if new file
|
| | | if(FileSize(handle) == 0)
|
| | | {
|
| | | FileWriteString(handle,
|
| | | "timestamp,symbol,volatility_prediction,volatility_spike,spread_widening," +
|
| | | "volume_surge,price_change_pct,is_trending,threshold_direction," +
|
| | | "confidence_multiplier,should_trade,profitability_score,news_reason\n");
|
| | | }
|
| | |
|
| | | FileWriteString(handle, line);
|
| | | FileClose(handle);
|
| | | }
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Calculate profitability score for ML (0-1) |
|
| | | //+------------------------------------------------------------------+
|
| | | double CalculateProfitabilityScore(const SNewsImpact &news, const SMarketResponse &response)
|
| | | {
|
| | | // This is a heuristic - in production, this would be calculated from actual trade results
|
| | | double score = 0.5; // Neutral
|
| | |
|
| | | // Reduce score for high volatility
|
| | | if(response.volatility_spike > 2.0)
|
| | | score -= 0.2;
|
| | |
|
| | | // Reduce score for wide spreads
|
| | | if(response.spread_widening > 1.5)
|
| | | score -= 0.15;
|
| | |
|
| | | // Increase score if trending clearly
|
| | | if(response.is_trending && MathAbs(response.price_change_pct) > 0.1)
|
| | | score += 0.15;
|
| | |
|
| | | // Clamp
|
| | | if(score < 0) score = 0;
|
| | | if(score > 1) score = 1;
|
| | |
|
| | | return score;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Get current adjustment (cached) |
|
| | | //+------------------------------------------------------------------+
|
| | | SThresholdAdjustment GetCurrentAdjustment()
|
| | | {
|
| | | // Check if expired
|
| | | if(m_current_adjustment.effective_until > 0 &&
|
| | | TimeCurrent() > m_current_adjustment.effective_until)
|
| | | {
|
| | | // Reset to normal
|
| | | m_current_adjustment = SThresholdAdjustment();
|
| | | m_current_state = NEWS_STATE_QUIET;
|
| | | }
|
| | |
|
| | | return m_current_adjustment;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Apply adjustment to a confidence value |
|
| | | //+------------------------------------------------------------------+
|
| | | double ApplyToConfidence(double base_confidence)
|
| | | {
|
| | | SThresholdAdjustment adj = GetCurrentAdjustment();
|
| | | return base_confidence / adj.confidence_multiplier;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Apply adjustment to position size |
|
| | | //+------------------------------------------------------------------+
|
| | | double ApplyToPositionSize(double base_lots)
|
| | | {
|
| | | SThresholdAdjustment adj = GetCurrentAdjustment();
|
| | | return base_lots * adj.position_size_multiplier;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Check if trading should proceed |
|
| | | //+------------------------------------------------------------------+
|
| | | bool ShouldTrade()
|
| | | {
|
| | | SThresholdAdjustment adj = GetCurrentAdjustment();
|
| | | return adj.should_trade;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Statistics and reporting |
|
| | | //+------------------------------------------------------------------+
|
| | | string GetStatistics()
|
| | | {
|
| | | string stats = StringFormat(
|
| | | "=== News Impact Analyzer Statistics ===\n" +
|
| | | "Analyses performed: %d\n" +
|
| | | "Trades blocked: %d (%.1f%%)\n" +
|
| | | "Thresholds adjusted: %d (%.1f%%)\n" +
|
| | | "Current state: %s\n" +
|
| | | "Training records: %d\n" +
|
| | | "Baseline volatility: %.5f\n" +
|
| | | "Baseline spread: %.5f",
|
| | | m_analysis_count,
|
| | | m_trade_blocked_count,
|
| | | (m_analysis_count > 0 ? 100.0 * m_trade_blocked_count / m_analysis_count : 0),
|
| | | m_threshold_adjusted_count,
|
| | | (m_analysis_count > 0 ? 100.0 * m_threshold_adjusted_count / m_analysis_count : 0),
|
| | | StateToString(m_current_state),
|
| | | m_history_count,
|
| | | m_baseline_volatility,
|
| | | m_baseline_spread
|
| | | );
|
| | |
|
| | | return stats;
|
| | | }
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| Utility functions |
|
| | | //+------------------------------------------------------------------+
|
| | | string StateToString(ENUM_NEWS_STATE state)
|
| | | {
|
| | | switch(state)
|
| | | {
|
| | | case NEWS_STATE_QUIET: return "QUIET";
|
| | | case NEWS_STATE_WARNING: return "WARNING";
|
| | | case NEWS_STATE_ACTIVE: return "ACTIVE";
|
| | | case NEWS_STATE_AFTERMATH: return "AFTERMATH";
|
| | | default: return "UNKNOWN";
|
| | | }
|
| | | }
|
| | |
|
| | | void Log(const string message)
|
| | | {
|
| | | Print("[NewsImpactAnalyzer] " + message);
|
| | | }
|
| | |
|
| | | // Getters
|
| | | ENUM_NEWS_STATE GetCurrentState() const { return m_current_state; }
|
| | | bool IsActive() const { return m_current_state != NEWS_STATE_QUIET; }
|
| | | string GetCurrentReason() const { return m_current_adjustment.reason; }
|
| | | };
|
| | |
|
| | | // Static instance initialization
|
| | | CNewsImpactAnalyzer* CNewsImpactAnalyzer::s_instance = NULL;
|
| | |
|
| | | // Convenience macros
|
| | | #define ANALYZE_NEWS(symbol) \
|
| | | CNewsImpactAnalyzer::Instance().AnalyzeNewsImpact(symbol)
|
| | |
|
| | | #define GET_NEWS_ADJUSTMENT() \
|
| | | CNewsImpactAnalyzer::Instance().GetCurrentAdjustment()
|
| | |
|
| | | #define SHOULD_TRADE_NEWS() \
|
| | | CNewsImpactAnalyzer::Instance().ShouldTrade()
|
| | |
|
| | | #define APPLY_NEWS_TO_CONFIDENCE(base_conf) \
|
| | | CNewsImpactAnalyzer::Instance().ApplyToConfidence(base_conf)
|
| | |
|
| | | #define APPLY_NEWS_TO_SIZE(base_lots) \
|
| | | CNewsImpactAnalyzer::Instance().ApplyToPositionSize(base_lots)
|
| | |
|
| | | #endif // CNEWSIMPACTANALYZER_MQH
|