2025-10-16 18:03:47 -04:00 | | | //+------------------------------------------------------------------+
|
| | | //| NuclearOptimization.mqh - 100-Level MQL5-Compatible Optimization |
|
| | | //| Copyright DualEA - Nuclear Grade Implementation |
|
| | | //| Version 3.0.0 |
|
| | | //+------------------------------------------------------------------+
|
| | | #property copyright "DualEA - Nuclear Grade Implementation"
|
| | | #property version "3.0.0"
|
| | | #property strict
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| MEMORY-OPTIMIZED CONFIGURATION BLOCK |
|
| | | //+------------------------------------------------------------------+
|
| | | struct SConfigBlock {
|
| | | string signature; // "DUAL"
|
| | | int version; // 3
|
| | | int strategy_count; // 23
|
| | | int risk_profile; // 0-100 (85 for AGGRESSIVE)
|
| | | double max_drawdown; // 0.15 default
|
| | | double kelly_fraction; // 0.25 default
|
| | | int correlation_window; // 252
|
| | | int volatility_window; // 21
|
| | | bool enable_ml; // true
|
| | | bool enable_news_filter; // true
|
| | | bool enable_regime_switch; // true
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
2026-02-24 12:47:37 -05:00 | | | //| RING BUFFER - MQL5 Compatible String-Based Implementation |
|
| | | //| FIXED: Using string array instead of SPolicyState (not defined) |
|
2025-10-16 18:03:47 -04:00 | | | //+------------------------------------------------------------------+
|
| | | class CRingBuffer {
|
| | | private:
|
2026-02-24 12:47:37 -05:00 | | | string m_buffer[1024]; // Store serialized data as strings
|
2025-10-16 18:03:47 -04:00 | | | int m_capacity;
|
| | | int m_head;
|
| | | int m_tail;
|
| | | int m_count;
|
| | |
|
| | | public:
|
2026-02-24 12:47:37 -05:00 | | | CRingBuffer(int capacity = 1024) : m_capacity(capacity), m_head(0), m_tail(0), m_count(0) {
|
| | | if(capacity > 1024) m_capacity = 1024; // Cap at 1024
|
2025-10-16 18:03:47 -04:00 | | | }
|
| | |
|
| | | ~CRingBuffer() {
|
2026-02-24 12:47:37 -05:00 | | | // No ArrayFree needed for fixed array
|
2025-10-16 18:03:47 -04:00 | | | }
|
| | |
|
2026-02-24 12:47:37 -05:00 | | | bool Push(const string &item) {
|
2025-10-16 18:03:47 -04:00 | | | if(m_count >= m_capacity) return false;
|
| | |
|
| | | m_buffer[m_tail] = item;
|
| | | m_tail = (m_tail + 1) % m_capacity;
|
| | | m_count++;
|
| | | return true;
|
| | | }
|
| | |
|
2026-02-24 12:47:37 -05:00 | | | bool Pop(string &item) {
|
2025-10-16 18:03:47 -04:00 | | | if(m_count <= 0) return false;
|
| | |
|
| | | item = m_buffer[m_head];
|
| | | m_head = (m_head + 1) % m_capacity;
|
| | | m_count--;
|
| | | return true;
|
| | | }
|
| | |
|
| | | int Count() const { return m_count; }
|
| | | bool IsFull() const { return m_count >= m_capacity; }
|
| | | bool IsEmpty() const { return m_count <= 0; }
|
2026-02-24 12:47:37 -05:00 | | | void Clear() { m_head = 0; m_tail = 0; m_count = 0; }
|
2025-10-16 18:03:47 -04:00 | | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| OPTIMIZED CORRELATION ENGINE - 23-Strategy Matrix |
|
| | | //+------------------------------------------------------------------+
|
| | | class COptimizedCorrelationEngine {
|
| | | private:
|
| | | double m_correlation_cache[23][23];
|
| | | double m_price_matrix[23][252]; // 23 strategies × 252-day window
|
| | | int m_window_size;
|
| | | int m_num_strategies;
|
| | | datetime m_last_update;
|
| | |
|
| | | public:
|
| | | COptimizedCorrelationEngine() {
|
| | | m_window_size = 252;
|
| | | m_num_strategies = 23;
|
| | | m_last_update = 0;
|
| | | ArrayInitialize(m_correlation_cache, 0.0);
|
| | | ArrayInitialize(m_price_matrix, 0.0);
|
| | | }
|
| | |
|
| | | // Optimized Pearson correlation with cache - compute inline from source matrix
|
| | | // First dimension must be dynamic in MQL5 function parameters
|
| | | void CalculateCorrelations(const double &prices[][252]) {
|
| | | for(int i = 0; i < m_num_strategies; i++) {
|
| | | for(int j = i; j < m_num_strategies; j++) {
|
| | | double sum_x = 0, sum_y = 0, sum_xy = 0;
|
| | | double sum_x2 = 0, sum_y2 = 0;
|
| | | int n = m_window_size;
|
| | | for(int k = 0; k < n; k++) {
|
| | | double x = prices[i][k];
|
| | | double y = prices[j][k];
|
| | | sum_x += x; sum_y += y; sum_xy += x * y; sum_x2 += x * x; sum_y2 += y * y;
|
| | | }
|
| | | double numerator = n * sum_xy - sum_x * sum_y;
|
| | | double denominator = MathSqrt((n * sum_x2 - sum_x * sum_x) * (n * sum_y2 - sum_y * sum_y));
|
| | | double corr = (denominator != 0.0) ? numerator / denominator : 0.0;
|
| | | m_correlation_cache[i][j] = corr;
|
| | | m_correlation_cache[j][i] = corr;
|
| | | }
|
| | | }
|
| | | m_last_update = TimeCurrent();
|
| | | }
|
| | |
|
| | | // Removed row-slice PearsonCorrelation; inlined in CalculateCorrelations
|
| | | double PearsonCorrelation(const double &x[], const double &y[], int n) { return 0.0; }
|
| | |
|
| | | double GetCorrelation(int strategy_i, int strategy_j) {
|
| | | if(strategy_i >= 0 && strategy_i < 23 && strategy_j >= 0 && strategy_j < 23)
|
| | | return m_correlation_cache[strategy_i][strategy_j];
|
| | | return 0.0;
|
| | | }
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| ADVANCED RISK METRICS - VaR & Expected Shortfall |
|
| | | //+------------------------------------------------------------------+
|
| | | struct SRiskMetrics {
|
| | | double var_95; // 5% Value at Risk
|
| | | double expected_shortfall; // Conditional VaR (CVaR)
|
| | | double max_drawdown; // Maximum drawdown
|
| | | double sharpe_ratio; // Risk-adjusted return
|
| | | double calmar_ratio; // Return/MaxDD ratio
|
| | | double sortino_ratio; // Downside deviation ratio
|
| | | datetime timestamp;
|
| | | };
|
| | |
|
| | | class CRiskMetricsCalculator {
|
| | | private:
|
| | | double m_returns[];
|
| | | int m_window;
|
| | |
|
| | | public:
|
| | | CRiskMetricsCalculator(int window = 100) : m_window(window) {
|
| | | ArrayResize(m_returns, m_window);
|
| | | ArrayInitialize(m_returns, 0.0);
|
| | | }
|
| | |
|
| | | SRiskMetrics CalculateMetrics(const double &equity_curve[]) {
|
| | | SRiskMetrics metrics;
|
| | | int n = ArraySize(equity_curve);
|
| | |
|
| | | // Calculate returns
|
| | | CalculateReturns(equity_curve);
|
| | |
|
| | | // Sort returns for VaR calculation
|
| | | double sorted_returns[];
|
| | | ArrayCopy(sorted_returns, m_returns);
|
| | | ArraySort(sorted_returns);
|
| | |
|
| | | // Value at Risk (95th percentile)
|
| | | int var_index = (int)(n * 0.05);
|
| | | metrics.var_95 = MathAbs(sorted_returns[var_index]);
|
| | |
|
| | | // Expected Shortfall (CVaR) - average of worst 5%
|
| | | double es_sum = 0;
|
| | | for(int i = 0; i < var_index; i++) {
|
| | | es_sum += MathAbs(sorted_returns[i]);
|
| | | }
|
| | | metrics.expected_shortfall = es_sum / var_index;
|
| | |
|
| | | // Maximum Drawdown
|
| | | metrics.max_drawdown = CalculateMaxDrawdown(equity_curve);
|
| | |
|
| | | // Sharpe Ratio
|
| | | double mean_return = 0, std_return = 0;
|
| | | CalculateMeanStd(m_returns, mean_return, std_return);
|
| | | metrics.sharpe_ratio = (std_return > 0) ? (mean_return / std_return) * MathSqrt(252) : 0;
|
| | |
|
| | | // Calmar Ratio
|
| | | double annual_return = mean_return * 252;
|
| | | metrics.calmar_ratio = (metrics.max_drawdown > 0) ? annual_return / metrics.max_drawdown : 0;
|
| | |
|
| | | // Sortino Ratio (downside deviation only)
|
| | | metrics.sortino_ratio = CalculateSortinoRatio(m_returns);
|
| | |
|
| | | metrics.timestamp = TimeCurrent();
|
| | | return metrics;
|
| | | }
|
| | |
|
| | | private:
|
| | | void CalculateReturns(const double &equity[]) {
|
| | | int n = MathMin(ArraySize(equity), m_window);
|
| | | ArrayResize(m_returns, n - 1);
|
| | |
|
| | | for(int i = 1; i < n; i++) {
|
| | | m_returns[i-1] = (equity[i] - equity[i-1]) / equity[i-1];
|
| | | }
|
| | | }
|
| | |
|
| | | double CalculateMaxDrawdown(const double &equity[]) {
|
| | | int n = ArraySize(equity);
|
| | | double max_dd = 0;
|
| | | double peak = equity[0];
|
| | |
|
| | | for(int i = 1; i < n; i++) {
|
| | | if(equity[i] > peak) {
|
| | | peak = equity[i];
|
| | | } else {
|
| | | double dd = (peak - equity[i]) / peak;
|
| | | if(dd > max_dd) max_dd = dd;
|
| | | }
|
| | | }
|
| | | return max_dd;
|
| | | }
|
| | |
|
| | | void CalculateMeanStd(const double &data[], double &mean, double &std) {
|
| | | int n = ArraySize(data);
|
| | | mean = 0;
|
| | | for(int i = 0; i < n; i++) mean += data[i];
|
| | | mean /= n;
|
| | |
|
| | | double variance = 0;
|
| | | for(int i = 0; i < n; i++) {
|
| | | double diff = data[i] - mean;
|
| | | variance += diff * diff;
|
| | | }
|
| | | std = MathSqrt(variance / n);
|
| | | }
|
| | |
|
| | | double CalculateSortinoRatio(const double &returns[]) {
|
| | | int n = ArraySize(returns);
|
| | | double mean = 0, downside_sum = 0;
|
| | | int downside_count = 0;
|
| | |
|
| | | for(int i = 0; i < n; i++) {
|
| | | mean += returns[i];
|
| | | if(returns[i] < 0) {
|
| | | downside_sum += returns[i] * returns[i];
|
| | | downside_count++;
|
| | | }
|
| | | }
|
| | | mean /= n;
|
| | |
|
| | | if(downside_count == 0) return 0;
|
| | | double downside_dev = MathSqrt(downside_sum / downside_count);
|
| | | return (downside_dev > 0) ? (mean / downside_dev) * MathSqrt(252) : 0;
|
| | | }
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| KELLY CRITERION POSITION SIZER |
|
| | | //+------------------------------------------------------------------+
|
| | | class CKellyPositionSizer {
|
| | | private:
|
| | | double m_kelly_fraction; // Safety factor (0.25 = quarter Kelly)
|
| | | double m_max_position_size; // Maximum position size cap
|
| | | double m_min_position_size; // Minimum position size floor
|
| | |
|
| | | public:
|
| | | CKellyPositionSizer(double kelly_fraction = 0.25, double max_size = 0.10, double min_size = 0.01) {
|
| | | m_kelly_fraction = kelly_fraction;
|
| | | m_max_position_size = max_size;
|
| | | m_min_position_size = min_size;
|
| | | }
|
| | |
|
| | | double CalculatePositionSize(double win_rate, double avg_win, double avg_loss, double current_equity) {
|
| | | // Full Kelly formula: f = (p*W - (1-p)*L) / (W*L)
|
| | | // Where: p = win_rate, W = avg_win, L = avg_loss
|
| | |
|
| | | if(avg_win <= 0 || avg_loss <= 0) return m_min_position_size;
|
| | |
|
| | | double p = win_rate;
|
| | | double q = 1.0 - win_rate;
|
| | |
|
| | | // Kelly percentage
|
| | | double kelly = (p * avg_win - q * avg_loss) / (avg_win * avg_loss);
|
| | |
|
| | | // Apply safety fraction
|
| | | kelly *= m_kelly_fraction;
|
| | |
|
| | | // Clamp to safe range
|
| | | kelly = MathMax(0.0, MathMin(kelly, m_max_position_size));
|
| | |
|
| | | if(kelly < m_min_position_size) return 0.0; // Don't trade if Kelly too small
|
| | |
|
| | | return current_equity * kelly;
|
| | | }
|
| | |
|
| | | double CalculateOptimalF(const double &returns[], double current_equity) {
|
| | | // Optimal F method (Ralph Vince)
|
| | | int n = ArraySize(returns);
|
| | | if(n == 0) return m_min_position_size;
|
| | |
|
| | | double max_f = 0;
|
| | | double max_twrr = -999999;
|
| | |
|
| | | // Search for optimal f between 0 and 1
|
| | | for(double f = 0.01; f <= 0.50; f += 0.01) {
|
| | | double twrr = 1.0; // Terminal Wealth Relative Return
|
| | |
|
| | | for(int i = 0; i < n; i++) {
|
| | | twrr *= (1.0 + f * returns[i]);
|
| | | }
|
| | |
|
| | | if(twrr > max_twrr) {
|
| | | max_twrr = twrr;
|
| | | max_f = f;
|
| | | }
|
| | | }
|
| | |
|
| | | // Apply safety fraction
|
| | | max_f *= m_kelly_fraction;
|
| | | max_f = MathMax(m_min_position_size, MathMin(max_f, m_max_position_size));
|
| | |
|
| | | return current_equity * max_f;
|
| | | }
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| CONFIGURATION MANAGER - JSON-Based |
|
| | | //+------------------------------------------------------------------+
|
| | | class CConfigurationManager {
|
| | | private:
|
| | | SConfigBlock m_config;
|
| | | string m_config_file;
|
| | |
|
| | | public:
|
| | | CConfigurationManager(string config_path = "DualEA\\config\\nuclear_config.json") {
|
| | | m_config_file = config_path;
|
| | | LoadDefaultConfig();
|
| | | }
|
| | |
|
| | | void LoadDefaultConfig() {
|
| | | m_config.signature = "DUAL";
|
| | | m_config.version = 3;
|
| | | m_config.strategy_count = 23;
|
| | | m_config.risk_profile = 85; // AGGRESSIVE
|
| | | m_config.max_drawdown = 0.15;
|
| | | m_config.kelly_fraction = 0.25;
|
| | | m_config.correlation_window = 252;
|
| | | m_config.volatility_window = 21;
|
| | | m_config.enable_ml = true;
|
| | | m_config.enable_news_filter = true;
|
| | | m_config.enable_regime_switch = true;
|
| | | }
|
| | |
|
| | | bool SaveConfig() {
|
| | | int handle = FileOpen(m_config_file, FILE_WRITE|FILE_TXT);
|
| | | if(handle == INVALID_HANDLE) return false;
|
| | |
|
| | | FileWriteString(handle, "{\n");
|
| | | FileWriteString(handle, " \"signature\": \"" + m_config.signature + "\",\n");
|
| | | FileWriteString(handle, " \"version\": " + IntegerToString(m_config.version) + ",\n");
|
| | | FileWriteString(handle, " \"strategy_count\": " + IntegerToString(m_config.strategy_count) + ",\n");
|
| | | FileWriteString(handle, " \"risk_profile\": " + IntegerToString(m_config.risk_profile) + ",\n");
|
| | | FileWriteString(handle, " \"max_drawdown\": " + DoubleToString(m_config.max_drawdown, 2) + ",\n");
|
| | | FileWriteString(handle, " \"kelly_fraction\": " + DoubleToString(m_config.kelly_fraction, 2) + ",\n");
|
| | | FileWriteString(handle, " \"correlation_window\": " + IntegerToString(m_config.correlation_window) + ",\n");
|
| | | FileWriteString(handle, " \"volatility_window\": " + IntegerToString(m_config.volatility_window) + ",\n");
|
| | | FileWriteString(handle, " \"enable_ml\": " + (m_config.enable_ml ? "true" : "false") + ",\n");
|
| | | FileWriteString(handle, " \"enable_news_filter\": " + (m_config.enable_news_filter ? "true" : "false") + ",\n");
|
| | | FileWriteString(handle, " \"enable_regime_switch\": " + (m_config.enable_regime_switch ? "true" : "false") + "\n");
|
| | | FileWriteString(handle, "}\n");
|
| | |
|
| | | FileClose(handle);
|
| | | return true;
|
| | | }
|
| | |
|
| | | SConfigBlock GetConfig() const { return m_config; }
|
| | | void SetRiskProfile(int profile) { m_config.risk_profile = profile; }
|
| | | void SetKellyFraction(double fraction) { m_config.kelly_fraction = fraction; }
|
| | | void SetMaxDrawdown(double dd) { m_config.max_drawdown = dd; }
|
| | | };
|
| | |
|
| | | //+------------------------------------------------------------------+
|
| | | //| POLICY STATE MANAGEMENT |
|
| | | //+------------------------------------------------------------------+
|
| | | struct SPolicyState {
|
| | | double weights[23];
|
| | | double bias;
|
| | | double learning_rate;
|
| | | int epoch;
|
| | | string model_hash;
|
| | | datetime timestamp;
|
| | | };
|
| | |
|
| | | class CPolicyStateManager {
|
| | | private:
|
2026-02-24 12:47:37 -05:00 | | | CRingBuffer m_policy_queue; // Non-template - stores serialized policy states
|
| | | string m_current_state_serialized;
|
2025-10-16 18:03:47 -04:00 | | |
|
| | | public:
|
2026-02-24 12:47:37 -05:00 | | | CPolicyStateManager() : m_policy_queue(1024) {}
|
2025-10-16 18:03:47 -04:00 | | |
|
2026-02-24 12:47:37 -05:00 | | | bool UpdateState(const string &serialized_state) {
|
| | | m_current_state_serialized = serialized_state;
|
| | | return m_policy_queue.Push(serialized_state);
|
2025-10-16 18:03:47 -04:00 | | | }
|
| | |
|
2026-02-24 12:47:37 -05:00 | | | string GetCurrentStateSerialized() const { return m_current_state_serialized; }
|
2025-10-16 18:03:47 -04:00 | | | };
|