mirror of
https://github.com/softwaredevelop/mql5.git
synced 2026-11-02 11:16:15 +00:00
299 lines
11 KiB
Plaintext
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//+------------------------------------------------------------------+
|