Generate code in 3 minutes
Enter your idea and AI generates runnable code.
To create a multi-currency scanner in MQL4, the best approach is to build a Dashboard Indicator. This allows you to attach the tool to a single chart (e.g., EURUSD) while it monitors specific logic (MA Crossover) across many other pairs in the background.
EURUSD,GBPUSD,USDJPY).Save this code as MA_Cross_Scanner.mq4 in your MQL4/Indicators folder.
//+------------------------------------------------------------------+
//| MA_Cross_Scanner.mq4 |
//| Copyright 2023, Quantitative |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative"
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
#property indicator_chart_window
//--- INPUT PARAMETERS
input string InpSymbols = "EURUSD,GBPUSD,USDJPY,AUDUSD,USDCAD"; // Comma-separated symbols
input ENUM_TIMEFRAMES InpTF = PERIOD_H1; // Timeframe to scan
input int InpFastPeriod = 12; // Fast MA Period
input int InpSlowPeriod = 26; // Slow MA Period
input ENUM_MA_METHOD InpMethod = MODE_EMA; // MA Method
input ENUM_APPLIED_PRICE InpPrice = PRICE_CLOSE;// Applied Price
input bool InpAlertPopup = true; // Popup Alert
input bool InpAlertPush = false; // Push Notification
input bool InpAlertEmail = false; // Email Alert
//--- GLOBAL VARIABLES
string g_symbols[];
datetime g_last_alert_time[];
int g_total_symbols = 0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// 1. Split the input string into an array of symbols
string sep = ",";
ushort u_sep = StringGetCharacter(sep, 0);
g_total_symbols = StringSplit(InpSymbols, u_sep, g_symbols);
// 2. Resize the alert tracking array
ArrayResize(g_last_alert_time, g_total_symbols);
ArrayInitialize(g_last_alert_time, 0);
// 3. Create the visual dashboard
for(int i = 0; i < g_total_symbols; i++)
{
// Trim whitespace
StringTrimLeft(g_symbols[i]);
StringTrimRight(g_symbols[i]);
CreateLabel("Lbl_Sym_" + string(i), g_symbols[i], 20, 30 + (i * 20), clrWhite);
CreateLabel("Lbl_Stat_" + string(i), "Scanning...", 100, 30 + (i * 20), clrGray);
}
CreateLabel("Lbl_Header", "MA CROSS SCANNER (" + EnumToString(InpTF) + ")", 20, 10, clrGold);
// 4. Use a Timer to scan symbols not on the current chart
EventSetTimer(1); // Scan every 1 second
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
EventKillTimer();
ObjectsDeleteAll(0, "Lbl_");
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
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[])
{
// We use OnTimer for multi-currency scanning to ensure updates
// even if the current chart doesn't receive ticks.
return(rates_total);
}
//+------------------------------------------------------------------+
//| Timer function |
//+------------------------------------------------------------------+
void OnTimer()
{
for(int i = 0; i < g_total_symbols; i++)
{
string sym = g_symbols[i];
// Ensure symbol is available in Market Watch
if(MarketInfo(sym, MODE_BID) <= 0)
{
UpdateLabel("Lbl_Stat_" + string(i), "Invalid Symbol", clrDimGray);
continue;
}
// Get MA values for Candle 1 (Previous closed) and Candle 2
double fast1 = iMA(sym, InpTF, InpFastPeriod, 0, InpMethod, InpPrice, 1);
double slow1 = iMA(sym, InpTF, InpSlowPeriod, 0, InpMethod, InpPrice, 1);
double fast2 = iMA(sym, InpTF, InpFastPeriod, 0, InpMethod, InpPrice, 2);
double slow2 = iMA(sym, InpTF, InpSlowPeriod, 0, InpMethod, InpPrice, 2);
// Get time of the last closed bar
datetime barTime = iTime(sym, InpTF, 1);
// Logic Check
bool isBullish = (fast1 > slow1) && (fast2 <= slow2);
bool isBearish = (fast1 < slow1) && (fast2 >= slow2);
// Update Dashboard & Alert
if(isBullish)
{
UpdateLabel("Lbl_Stat_" + string(i), "BUY CROSS", clrLime);
ProcessAlert(i, sym, "BUY", barTime);
}
else if(isBearish)
{
UpdateLabel("Lbl_Stat_" + string(i), "SELL CROSS", clrRed);
ProcessAlert(i, sym, "SELL", barTime);
}
else
{
// Determine current state without crossover
if(fast1 > slow1) UpdateLabel("Lbl_Stat_" + string(i), "Bullish Trend", clrGreen);
else UpdateLabel("Lbl_Stat_" + string(i), "Bearish Trend", clrMaroon);
}
}
}
//+------------------------------------------------------------------+
//| Helper: Process Alerts |
//+------------------------------------------------------------------+
void ProcessAlert(int index, string symbol, string type, datetime barTime)
{
// Check if we already alerted for this specific bar
if(g_last_alert_time[index] != barTime)
{
string msg = symbol + " (" + EnumToString(InpTF) + ") MA Crossover: " + type;
if(InpAlertPopup) Alert(msg);
if(InpAlertPush) SendNotification(msg);
if(InpAlertEmail) SendMail("MA Scanner Alert", msg);
// Update last alert time to prevent spam
g_last_alert_time[index] = barTime;
}
}
//+------------------------------------------------------------------+
//| Helper: Create Dashboard Label |
//+------------------------------------------------------------------+
void CreateLabel(string name, string text, int x, int y, color clr)
{
if(ObjectFind(0, name) < 0)
{
ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 10);
ObjectSetString(0, name, OBJPROP_FONT, "Arial");
}
ObjectSetString(0, name, OBJPROP_TEXT, text);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
}
//+------------------------------------------------------------------+
//| Helper: Update Dashboard Label |
//+------------------------------------------------------------------+
void UpdateLabel(string name, string text, color clr)
{
ObjectSetString(0, name, OBJPROP_TEXT, text);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
}
//+------------------------------------------------------------------+
Installation:
F4 to open the MetaEditor.Indicators folder in the Navigator and select "New File".MA_Cross_Scanner, select "Custom Indicator", and click Finish.F7 to Compile.Setup:
MA_Cross_Scanner from the Navigator onto the chart.Configuration (Inputs Tab):
EURUSD,GBPUSD,XAUUSD). Note: These symbols must be visible in your "Market Watch" window, or the scanner won't be able to retrieve data.InpAlertPush to true if you want notifications on your phone (requires MT4 mobile setup).1 (the previously closed candle) and index 2 (the candle before that).OnTimer (running every 1 second) instead of OnTick. This is crucial because if you put the scanner on a quiet pair (like a minor cross), OnTick wouldn't fire often enough to alert you about a move on a busy pair (like Gold).Q: Why does the dashboard say "Invalid Symbol"?
A: The symbol name in the input list must match exactly what is in your Market Watch window. If your broker uses suffixes (e.g., EURUSD.pro), you must type EURUSD.pro in the input list.
Q: Can I scan multiple timeframes at once?
A: This specific code scans one timeframe (selected in inputs) across multiple pairs. To scan multiple timeframes, simply drag the indicator onto the chart a second time and select a different timeframe in the inputs.
Q: Why aren't alerts triggering immediately when lines cross?
A: This tool waits for the candle to close (confirmed cross). This prevents "repainting" where the lines cross during a volatile candle but un-cross before the candle closes, leading to false signals.