diff --git a/Indicators/MyIndicators/Quant/AlphaBeta_Dashboard_Pro.mq5 b/Indicators/MyIndicators/Quant/AlphaBeta_Dashboard_Pro.mq5 new file mode 100644 index 0000000..7a6ce99 --- /dev/null +++ b/Indicators/MyIndicators/Quant/AlphaBeta_Dashboard_Pro.mq5 @@ -0,0 +1,439 @@ +//+------------------------------------------------------------------+ +//| AlphaBeta_Dashboard_Pro.mq5 | +//| Copyright 2026, xxxxxxxx| +//+------------------------------------------------------------------+ +#property copyright "Copyright 2026, xxxxxxxx" +#property version "1.10" // Fixed window alignment and self-reference routing +#property description "Interactive Alpha & Beta Performance Heatmap Scanner." +#property description "Click on any Symbol button to instantly switch the chart." +#property indicator_chart_window +#property indicator_buffers 0 +#property indicator_plots 0 + +#include + +//--- Input Parameters --- +input string InpCustomSymbols = ""; // Custom Symbols (Comma separated, empty for Market Watch) +input int InpMaxSymbols = 15; // Maximum Symbols to display +input ENUM_TIMEFRAMES InpTimeframe = PERIOD_H1; // Target Timeframe (Recommended: PERIOD_H1) +input int InpLookback = 60; // Rolling Lookback Window (Bars) +input string InpBenchmark = "US500"; // Global Equity Benchmark +input string InpForexBench = "DX"; // Forex Benchmark (Dollar Index) +input int InpTableX = 20; // Table X Offset (Pixels) +input int InpTableY = 60; // Table Y Offset (Pixels) +input int InpFontSize = 9; // UI Font Size +input int InpRefreshSeconds = 3; // Background Timer Fallback (Seconds) + +//--- Global Variables --- +string g_symbols[]; +int g_symbols_total = 0; +string g_prefix = ""; +bool g_updating = false; +ulong g_last_update_ms = 0; // Throttle timestamp +CMathStatisticsCalculator *g_stats = NULL; + +//+------------------------------------------------------------------+ +//| EnsureDataReady (History sync helper) | +//+------------------------------------------------------------------+ +bool EnsureDataReady(const string symbol, const ENUM_TIMEFRAMES timeframe, const int required_bars) + { + ResetLastError(); + if(!SymbolInfoInteger(symbol, SYMBOL_SELECT)) + { + SymbolSelect(symbol, true); + } + datetime times[]; + int copied = CopyTime(symbol, timeframe, 0, required_bars, times); + return (copied >= required_bars); + } + +//+------------------------------------------------------------------+ +//| GetAlphaBeta | +//| Fully synchronized and window-aligned calculation | +//+------------------------------------------------------------------+ +bool GetAlphaBeta(string symbol, ENUM_TIMEFRAMES tf, int lookback, double &out_alpha, double &out_beta) + { +// FIXED: Window size matches exactly 'lookback' to ensure 100% data alignment with standard indicator + int required_bars = lookback; + + if(!EnsureDataReady(symbol, tf, required_bars)) + return false; + + bool is_forex = IsForexPair(symbol); + string bench = is_forex ? InpForexBench : InpBenchmark; + +// FIXED: Self-Reference protection. If symbol is its own benchmark, return baseline CAPM values + if(symbol == bench) + { + out_alpha = 0.0; + out_beta = 1.0; + return true; + } + + if(!EnsureDataReady(bench, tf, required_bars)) + return false; + + double close_A[]; + datetime times[]; + + ArrayResize(close_A, required_bars); + ArrayResize(times, required_bars); + + if(CopyClose(symbol, tf, 0, required_bars, close_A) != required_bars || + CopyTime(symbol, tf, 0, required_bars, times) != required_bars) + return false; + +// High-performance chronological alignment for the benchmark close prices + double close_B[]; + ArrayResize(close_B, required_bars); + + for(int i = 0; i < required_bars; i++) + { + int shift = iBarShift(bench, tf, times[i], false); + if(shift >= 0) + { + close_B[i] = iClose(bench, tf, shift); + } + else + { + close_B[i] = (i > 0) ? close_B[i-1] : close_A[i]; + } + } + +// Calculate stationary log returns + double ret_A[], ret_B[]; + g_stats.ComputeReturns(close_A, ret_A); + g_stats.ComputeReturns(close_B, ret_B); + +// Calculate Beta + out_beta = g_stats.CalculateBeta(ret_A, ret_B); + +// Calculate cumulative returns for Alpha + int n = ArraySize(close_A); + double a_tot = (close_A[n-1] - close_A[0]) / close_A[0]; + double b_tot = (close_B[n-1] - close_B[0]) / close_B[0]; + +// Calculate Alpha + out_alpha = g_stats.CalculateAlpha(a_tot, b_tot, out_beta); + + return true; + } + +//+------------------------------------------------------------------+ +//| ParseSymbols | +//+------------------------------------------------------------------+ +void ParseSymbols() + { + ArrayFree(g_symbols); + + if(InpCustomSymbols != "" && InpCustomSymbols != NULL) + { + string temp[]; + int split = StringSplit(InpCustomSymbols, ',', temp); + int valid_count = 0; + + for(int i = 0; i < split; i++) + { + string sym = temp[i]; + StringTrimLeft(sym); + StringTrimRight(sym); + if(sym != "" && SymbolInfoInteger(sym, SYMBOL_SELECT) != 0) + { + ArrayResize(g_symbols, valid_count + 1); + g_symbols[valid_count] = sym; + valid_count++; + } + if(valid_count >= InpMaxSymbols) + break; + } + g_symbols_total = valid_count; + } + else + { + int total = SymbolsTotal(true); + int count = 0; + for(int i = 0; i < total; i++) + { + string sym = SymbolName(i, true); + if(sym != "" && sym != NULL) + { + ArrayResize(g_symbols, count + 1); + g_symbols[count] = sym; + count++; + } + if(count >= InpMaxSymbols) + break; + } + g_symbols_total = count; + } + } + +//+------------------------------------------------------------------+ +//| CreateButton | +//+------------------------------------------------------------------+ +void CreateButton(string name, string text, int x, int y, int w, int h, color bg_color, color text_color) + { + if(ObjectFind(0, name) < 0) + { + ObjectCreate(0, name, OBJ_BUTTON, 0, 0, 0); + ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER); + ObjectSetInteger(0, name, OBJPROP_FONTSIZE, InpFontSize); + ObjectSetString(0, name, OBJPROP_FONT, "Trebuchet MS"); + ObjectSetInteger(0, name, OBJPROP_BORDER_TYPE, BORDER_FLAT); + ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false); + } + ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x); + ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y); + ObjectSetInteger(0, name, OBJPROP_XSIZE, w); + ObjectSetInteger(0, name, OBJPROP_YSIZE, h); + ObjectSetString(0, name, OBJPROP_TEXT, text); + ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bg_color); + ObjectSetInteger(0, name, OBJPROP_COLOR, text_color); + } + +//+------------------------------------------------------------------+ +//| RenderDashboard | +//+------------------------------------------------------------------+ +void RenderDashboard() + { + if(g_updating || g_symbols_total <= 0) + return; + + g_updating = true; + + int col_w_sym = 104; + int col_w_val = 80; + int row_h = 22; + +//--- 1. Render Table Header + int header_y = InpTableY; + string tf_name = StringSubstr(EnumToString(InpTimeframe), 7); + CreateButton(g_prefix + "H_Sym", "Symbol (" + tf_name + ")", InpTableX, header_y, col_w_sym, row_h, clrDarkSlateGray, clrWhite); + CreateButton(g_prefix + "H_Alpha", "Alpha (" + (string)InpLookback + ")", InpTableX + col_w_sym + 2, header_y, col_w_val, row_h, clrDarkSlateGray, clrWhite); + CreateButton(g_prefix + "H_Beta", "Beta (" + (string)InpLookback + ")", InpTableX + col_w_sym + col_w_val + 4, header_y, col_w_val, row_h, clrDarkSlateGray, clrWhite); + +//--- 2. Loop and Calculate each asset row + for(int r = 0; r < g_symbols_total; r++) + { + string sym = g_symbols[r]; + int row_y = InpTableY + row_h + 2 + (r * (row_h + 2)); + + // Symbol Button (Clickable chart switcher) + CreateButton(g_prefix + "_SymBtn_" + sym, sym, InpTableX, row_y, col_w_sym, row_h, clrLightGray, clrBlack); + + double alpha = EMPTY_VALUE; + double beta = EMPTY_VALUE; + + // Calculate single-point Alpha/Beta for the current bar in micro-seconds + bool calculated = GetAlphaBeta(sym, InpTimeframe, InpLookback, alpha, beta); + + // Render Alpha and Beta cells with precise styling + RenderAlphaCell(sym, alpha, calculated, InpTableX + col_w_sym + 2, row_y, col_w_val, row_h); + RenderBetaCell(sym, beta, calculated, InpTableX + col_w_sym + col_w_val + 4, row_y, col_w_val, row_h); + } + + ChartRedraw(); + g_updating = false; + } + +//+------------------------------------------------------------------+ +//| RenderAlphaCell | +//+------------------------------------------------------------------+ +void RenderAlphaCell(string symbol, double val, bool calculated, int x, int y, int w, int h) + { + string name = g_prefix + "_" + symbol + "_Alpha"; + string text = ""; + color bg_color = clrWhite; + color text_color = clrBlack; + + if(!calculated || val == EMPTY_VALUE) + { + text = "Sync..."; + bg_color = clrWhite; + text_color = clrSilver; + } + else + { + text = DoubleToString(val, 4); + + if(val > 0.0001) + { + bg_color = clrLimeGreen; // Positive Alpha (Excess returns) + text_color = clrBlack; + } + else + if(val < -0.0001) + { + bg_color = clrCrimson; // Negative Alpha (Underperformance) + text_color = clrWhite; + } + else + { + bg_color = clrWhite; // Symmetrical/Neutral + text_color = clrDarkGray; + } + } + CreateButton(name, text, x, y, w, h, bg_color, text_color); + } + +//+------------------------------------------------------------------+ +//| RenderBetaCell | +//+------------------------------------------------------------------+ +void RenderBetaCell(string symbol, double val, bool calculated, int x, int y, int w, int h) + { + string name = g_prefix + "_" + symbol + "_Beta"; + string text = ""; + color bg_color = clrWhite; + color text_color = clrBlack; + + if(!calculated || val == EMPTY_VALUE) + { + text = "Sync..."; + bg_color = clrWhite; + text_color = clrSilver; + } + else + { + text = DoubleToString(val, 2); + + if(val > 1.20) + { + bg_color = clrOrangeRed; // High Beta (Aggressive sensitivity) + text_color = clrWhite; + } + else + if(val < 0.80) + { + bg_color = clrLightSkyBlue; // Low Beta (Defensive/uncorrelated) + text_color = clrBlack; + } + else + { + bg_color = clrGold; // Neutral Beta (Market matching) + text_color = clrBlack; + } + } + CreateButton(name, text, x, y, w, h, bg_color, text_color); + } + +//+------------------------------------------------------------------+ +//| OnInit | +//+------------------------------------------------------------------+ +int OnInit() + { + g_updating = false; + g_last_update_ms = 0; + g_prefix = StringFormat("ABD_%I64d_", ChartID()); + + ObjectsDeleteAll(0, g_prefix); + + g_stats = new CMathStatisticsCalculator(); + if(CheckPointer(g_stats) == POINTER_INVALID) + { + return INIT_FAILED; + } + + ParseSymbols(); + RenderDashboard(); + + EventSetTimer(InpRefreshSeconds); + return(INIT_SUCCEEDED); + } + +//+------------------------------------------------------------------+ +//| OnDeinit | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) + { + EventKillTimer(); + ObjectsDeleteAll(0, g_prefix); + + if(CheckPointer(g_stats) == POINTER_DYNAMIC) + { + delete g_stats; + } + Comment(""); + } + +//+------------------------------------------------------------------+ +//| OnCalculate | +//+------------------------------------------------------------------+ +int OnCalculate(const int rates_total, + const int prev_calculated, + const datetime &time[], + const double &open[], + const double &high[], + const double &low[], + const double &close[], + const long &tick_volume[], + const long &volume[], + const int &spread[]) + { +//--- Real-time high frequency tick throttling (Max 5 updates per second / 200ms) + ulong current_ms = GetTickCount64(); + if(current_ms - g_last_update_ms >= 200) + { + g_last_update_ms = current_ms; + RenderDashboard(); + } + return(rates_total); + } + +//+------------------------------------------------------------------+ +//| OnTimer | +//+------------------------------------------------------------------+ +void OnTimer() + { + RenderDashboard(); + } + +//+------------------------------------------------------------------+ +//| OnChartEvent | +//+------------------------------------------------------------------+ +void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) + { + if(id == CHARTEVENT_OBJECT_CLICK) + { + if(StringFind(sparam, g_prefix) == 0 && StringFind(sparam, "_SymBtn_") != -1) + { + string symbol = ObjectGetString(0, sparam, OBJPROP_TEXT); + if(symbol != "" && symbol != NULL) + { + ChartSetSymbolPeriod(0, symbol, _Period); + ObjectSetInteger(0, sparam, OBJPROP_STATE, false); + ChartRedraw(); + } + } + } + } + +//+------------------------------------------------------------------+ +//| IsForexPair | +//+------------------------------------------------------------------+ +bool IsForexPair(string sym) + { + if(sym == InpBenchmark || sym == InpForexBench) + return false; + + if(StringFind(sym, "USD") != -1 || StringFind(sym, "EUR") != -1 || + StringFind(sym, "GBP") != -1 || StringFind(sym, "JPY") != -1 || + StringFind(sym, "CHF") != -1 || StringFind(sym, "AUD") != -1 || + StringFind(sym, "CAD") != -1 || StringFind(sym, "NZD") != -1 || + StringFind(sym, "XAU") != -1 || StringFind(sym, "XAG") != -1) + { + if(StringFind(sym, "XTI") != -1) + return false; + if(StringFind(sym, "UKO") != -1) + return false; + if(StringFind(sym, "USO") != -1) + return false; + if(StringFind(sym, "BTC") != -1) + return false; + if(StringFind(sym, "ETH") != -1) + return false; + return true; + } + return false; + } +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+