Files
mql5/Indicators/MyIndicators/Symbol_Switcher_Pro.mq5
T

299 lines
11 KiB
Plaintext

//+------------------------------------------------------------------+
//| Symbol_Switcher_Pro.mq5 |
//| Copyright 2026, xxxxxxxx|
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, xxxxxxxx"
#property version "1.30"
#property description "Ultra-Lightweight 3x9 Multi-Asset Symbol Switcher Matrix."
#property description "Features per-button solid opaque shielding, persistent Z-order forefront priority, and 1-click chart switching."
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots 0
//--- Anchor Corner Enum
enum ENUM_SWITCHER_CORNER
{
CORNER_BOTTOM_LEFT = CORNER_LEFT_LOWER, // Anchor: Bottom-Left Corner
CORNER_TOP_LEFT = CORNER_LEFT_UPPER // Anchor: Top-Left Corner
};
//--- Grid Dimensional Constants
#define GRID_COLUMNS 3
#define GRID_ROWS 9
#define MAX_BUTTONS 27
//--- Input Parameters ---
input group "--- Asset Selection Settings ---"
input string InpCustomSymbols = ""; // Custom Symbols (Comma separated, empty for Market Watch)
input group "--- Dashboard Placement & Sizing ---"
input ENUM_SWITCHER_CORNER InpAnchorCorner = CORNER_BOTTOM_LEFT; // Dashboard Anchor Corner
input int InpTableX = 20; // Offset X (Pixels from Left)
input int InpTableY = 30; // Offset Y (Pixels from Corner)
input int InpButtonWidth = 85; // Button Width (Pixels)
input int InpButtonHeight = 22; // Button Height (Pixels)
input int InpGap = 2; // Gap between cells (Pixels)
input int InpFontSize = 9; // Font Size
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
input group "--- Color Customization (V-Score Dashboard Palette) ---"
input color InpColorActiveBg = clrDeepSkyBlue; // Active Symbol Background
input color InpColorActiveTxt = clrWhite; // Active Symbol Text
input color InpColorIdleBg = clrLightGray; // Inactive Symbol Background (Solid Gray)
input color InpColorIdleTxt = clrBlack; // Inactive Symbol Text
//--- Internal State
string g_symbols[];
int g_symbols_count = 0;
string g_prefix = "";
string g_active_sym = "";
ulong g_last_check_ms = 0;
//+------------------------------------------------------------------+
//| Parse Symbols from Input String or Market Watch |
//+------------------------------------------------------------------+
void ParseSymbols()
{
ArrayFree(g_symbols);
g_symbols_count = 0;
// Mode A: Parse User-Defined Comma-Separated List
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 >= MAX_BUTTONS)
break;
}
g_symbols_count = valid_count;
}
else // Mode B: Collect from Active Market Watch
{
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 >= MAX_BUTTONS)
break;
}
g_symbols_count = count;
}
}
//+------------------------------------------------------------------+
//| Create Dual-Layer Button with Opaque Raster Shield (Z-Order 1000)|
//+------------------------------------------------------------------+
void CreateButtonWithShield(const int index, const string sym, const int x, const int y, const int w, const int h, const bool is_active)
{
string shield_name = g_prefix + "Shld_" + IntegerToString(index);
string btn_name = g_prefix + "Btn_" + IntegerToString(index);
color bg_col = is_active ? InpColorActiveBg : InpColorIdleBg;
color txt_col = is_active ? InpColorActiveTxt : InpColorIdleTxt;
// 1. Layer 1: Solid Opaque Raster Shield (Obliterates any underlying chart objects)
if(ObjectFind(0, shield_name) < 0)
{
ObjectCreate(0, shield_name, OBJ_RECTANGLE_LABEL, 0, 0, 0);
ObjectSetInteger(0, shield_name, OBJPROP_CORNER, InpAnchorCorner);
ObjectSetInteger(0, shield_name, OBJPROP_BORDER_TYPE, BORDER_FLAT);
ObjectSetInteger(0, shield_name, OBJPROP_SELECTABLE, false);
}
ObjectSetInteger(0, shield_name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, shield_name, OBJPROP_YDISTANCE, y);
ObjectSetInteger(0, shield_name, OBJPROP_XSIZE, w);
ObjectSetInteger(0, shield_name, OBJPROP_YSIZE, h);
ObjectSetInteger(0, shield_name, OBJPROP_BGCOLOR, bg_col);
ObjectSetInteger(0, shield_name, OBJPROP_COLOR, bg_col); // Invisible matching border
ObjectSetInteger(0, shield_name, OBJPROP_BACK, false); // Strict foreground
ObjectSetInteger(0, shield_name, OBJPROP_ZORDER, 999); // Directly below button
// 2. Layer 2: Interactive Flat Button (Carries Text & Click Events)
if(ObjectFind(0, btn_name) < 0)
{
ObjectCreate(0, btn_name, OBJ_BUTTON, 0, 0, 0);
ObjectSetInteger(0, btn_name, OBJPROP_CORNER, InpAnchorCorner);
ObjectSetInteger(0, btn_name, OBJPROP_FONTSIZE, InpFontSize);
ObjectSetString(0, btn_name, OBJPROP_FONT, "Trebuchet MS");
ObjectSetInteger(0, btn_name, OBJPROP_BORDER_TYPE, BORDER_FLAT);
ObjectSetInteger(0, btn_name, OBJPROP_SELECTABLE, false);
}
ObjectSetInteger(0, btn_name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, btn_name, OBJPROP_YDISTANCE, y);
ObjectSetInteger(0, btn_name, OBJPROP_XSIZE, w);
ObjectSetInteger(0, btn_name, OBJPROP_YSIZE, h);
ObjectSetString(0, btn_name, OBJPROP_TEXT, sym);
ObjectSetInteger(0, btn_name, OBJPROP_BGCOLOR, bg_col);
ObjectSetInteger(0, btn_name, OBJPROP_COLOR, txt_col);
ObjectSetInteger(0, btn_name, OBJPROP_BACK, false); // Strict foreground
ObjectSetInteger(0, btn_name, OBJPROP_ZORDER, 1000); // Highest priority on entire chart
ObjectSetInteger(0, btn_name, OBJPROP_STATE, false);
}
//+------------------------------------------------------------------+
//| Render Grid Matrix |
//+------------------------------------------------------------------+
void RenderGrid()
{
if(g_symbols_count <= 0)
return;
int num_rows = MathMin(g_symbols_count, GRID_ROWS);
g_active_sym = _Symbol;
for(int k = 0; k < g_symbols_count; k++)
{
int col = k / GRID_ROWS;
int row = k % GRID_ROWS;
if(col >= GRID_COLUMNS)
break;
int x = InpTableX + col * (InpButtonWidth + InpGap);
int y = 0;
// Coordinate Geometry: Row 0 is visually at the top in both modes
if(InpAnchorCorner == CORNER_BOTTOM_LEFT)
{
y = InpTableY + (num_rows - 1 - row) * (InpButtonHeight + InpGap);
}
else // CORNER_TOP_LEFT
{
y = InpTableY + row * (InpButtonHeight + InpGap);
}
string sym = g_symbols[k];
bool is_current = (sym == g_active_sym);
CreateButtonWithShield(k, sym, x, y, InpButtonWidth, InpButtonHeight, is_current);
}
ChartRedraw(0);
}
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
g_prefix = StringFormat("SSW_%I64d_", ChartID());
g_last_check_ms = 0;
// Clean any prior artifacts from this chart instance
ObjectsDeleteAll(0, g_prefix);
// Load Asset Manifest
ParseSymbols();
// Draw Shielded Grid
RenderGrid();
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Wipe all graphical shields and buttons completely
ObjectsDeleteAll(0, g_prefix);
ChartRedraw(0);
}
//+------------------------------------------------------------------+
//| Custom indicator calculation iteration |
//+------------------------------------------------------------------+
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[])
{
// Forefront Layer Maintenance: Reassert state if external indicators modify objects
ulong current_ms = GetTickCount64();
if(current_ms - g_last_check_ms >= 500)
{
g_last_check_ms = current_ms;
// If active symbol changed externally or screen needs sync
if(g_active_sym != _Symbol)
{
RenderGrid();
}
}
return rates_total;
}
//+------------------------------------------------------------------+
//| Interactive Chart Event Handler (1-Click Chart Switching) |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
// Intercept User Clicks on either the Button or the Underlying Shield
if(id == CHARTEVENT_OBJECT_CLICK)
{
string target_symbol = "";
if(StringFind(sparam, g_prefix + "Btn_") == 0)
{
target_symbol = ObjectGetString(0, sparam, OBJPROP_TEXT);
ObjectSetInteger(0, sparam, OBJPROP_STATE, false);
}
else
if(StringFind(sparam, g_prefix + "Shld_") == 0)
{
// If click registered on shield, retrieve corresponding button symbol
string idx_str = StringSubstr(sparam, StringLen(g_prefix + "Shld_"));
string btn_name = g_prefix + "Btn_" + idx_str;
target_symbol = ObjectGetString(0, btn_name, OBJPROP_TEXT);
ObjectSetInteger(0, btn_name, OBJPROP_STATE, false);
}
if(target_symbol != "" && target_symbol != NULL)
{
// Switch Active Chart to Selected Symbol (Preserves Chart Timeframe & Indicators)
if(target_symbol != _Symbol)
{
ChartSetSymbolPeriod(0, target_symbol, _Period);
}
}
}
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+