Lesson: Global vs Local Scope
By the end of this lesson you will understand:
void functions help you reuse logicOnTick() clean and readableScope means:
Where in your code something exists, and who can use it.
MQL4 has two main scopes you’ll use constantly:
Before we write any real trading logic, we need to understand where variables live and who is allowed to use them. This is called scope.
A global variable is a variable that is declared outside of any function.
Because it is outside all functions, it can be accessed from:
OnInit()OnTick()Think of global variables as shared memory for your EA. All parts of the EA can see and use them.
Global variables are placed at the top of the EA file,
before OnInit() or OnTick().
// ===== GLOBAL SCOPE (top of file) =====
double RiskPercent = 1.0;
int MagicNumber = 12345;
bool TradingEnabled = true;
Once declared here, these variables exist for the entire lifetime of the EA. They do not reset on every tick.
Use global variables only for values that must be:
Common examples include:
Do NOT make everything global.
If you make every variable global, your EA becomes:
Temporary values — like calculations, loop counters, or one-time checks — should be local variables inside functions.
A simple rule to remember:
If a value is only needed inside one function, it should NOT be global.
In the next section, we will look at local scope and see how it keeps your EA clean, safe, and easy to understand.
Local variables are declared inside a function.
They exist only while the function runs, then disappear.
void CheckSpread()
{
double spreadPoints = MarketInfo(Symbol(), MODE_SPREAD); // LOCAL
Print("Spread: ", spreadPoints);
}
Think of local variables as a “workbench” for the current function only.
Many beginners put everything in OnTick():
void OnTick()
{
// Spread check
double spread = MarketInfo(Symbol(), MODE_SPREAD);
if(spread > 20) return;
// Lot size logic
double lotSize = 0.10; // pretend
// Entry logic
if(OrdersTotal() == 0)
{
// OrderSend...
}
// Trade management logic
if(OrdersTotal() > 0)
{
// OrderModify, trailing, etc...
}
}
Problem: This works at first, but quickly becomes messy, hard to debug, and easy to break.
A void function means:
Do this task — it performs actions but returns nothing.
// Updates a global flag based on spread
void UpdateTradingEnabledBySpread()
{
double spreadPoints = MarketInfo(Symbol(), MODE_SPREAD); // LOCAL
if(spreadPoints > 20)
TradingEnabled = false; // GLOBAL
else
TradingEnabled = true; // GLOBAL
}
Now you can call this from anywhere without repeating logic.
You would simply use UpdateTradingEnabledBySpread(); to call this function.
This complete EA skeleton shows the order of everything, from top to bottom.
Note: This is a teaching structure (not a full strategy). It shows clean structure, global vs local scope, and reusable void logic.
//+------------------------------------------------------------------+
//| EA_Structure_Demo.mq4 |
//| Demonstrates Global vs Local Scope + Reusable void functions |
//+------------------------------------------------------------------+
#property strict
//==============================
// 1) INPUTS (settings user can change)
//==============================
input double InpRiskPercent = 1.0;
input int InpMagicNumber = 12345;
input int InpMaxSpreadPts = 20;
//==============================
// 2) GLOBAL VARIABLES (EA "state")
// - Used across multiple functions
// - Persist between ticks
//==============================
bool gTradingEnabled = true;
double gTodayProfit = 0.0; // example state (pretend this is percent)
datetime gLastBarTime = 0; // used for New Bar detection
//==============================
// 3) FUNCTION DECLARATIONS (optional, but helps readability)
//==============================
void UpdateTradingEnabledBySpread();
void UpdateDailyStopRules();
void ManageOpenTrades();
void LookForEntries();
bool IsNewBar();
int CountOpenTrades(string sym, int magic);
//+------------------------------------------------------------------+
//| OnInit() - runs once when EA starts |
//+------------------------------------------------------------------+
int OnInit()
{
Print("EA started. Magic=", InpMagicNumber, " Risk%=", InpRiskPercent);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| OnDeinit() - runs once when EA is removed |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("EA stopped.");
}
//+------------------------------------------------------------------+
//| OnTick() - runs every tick |
//| IMPORTANT: keep it short! It's a traffic controller. |
//+------------------------------------------------------------------+
void OnTick()
{
// 1) Update rules / environment checks
UpdateTradingEnabledBySpread();
UpdateDailyStopRules();
// If trading is disabled, stop here.
if(!gTradingEnabled) return;
// Optional: run heavy logic once per bar instead of every tick
if(IsNewBar())
{
// 2) Manage open trades
ManageOpenTrades();
// 3) Look for new entries
LookForEntries();
}
}
//==============================
// 4) REUSABLE VOID FUNCTIONS (Action blocks)
//==============================
// Checks spread and updates a global flag
void UpdateTradingEnabledBySpread()
{
// LOCAL variable: only needed inside this function
double spreadPts = MarketInfo(Symbol(), MODE_SPREAD);
if(spreadPts > InpMaxSpreadPts)
gTradingEnabled = false;
else
gTradingEnabled = true;
}
// Example: daily stop rules (profit target / max loss)
void UpdateDailyStopRules()
{
// In a real EA, you’d calculate today’s closed profit/loss.
// Here we just demonstrate structure using a pretend % variable.
if(gTodayProfit >= 6.0) gTradingEnabled = false; // +6% done
if(gTodayProfit <= -6.0) gTradingEnabled = false; // -6% done
}
// Example open-trade management block
void ManageOpenTrades()
{
int total = OrdersTotal(); // LOCAL
for(int i = total - 1; i >= 0; i--)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
if(OrderSymbol() != Symbol()) continue;
if(OrderMagicNumber() != InpMagicNumber) continue;
// Manage trades here:
// trailing stop, breakeven, exit rules, etc.
}
}
// Example entry block
void LookForEntries()
{
// Only enter if no open trades for this symbol/magic
if(CountOpenTrades(Symbol(), InpMagicNumber) > 0)
return;
// LOCAL variables for setup logic
double bid = Bid;
double ask = Ask;
// Entry conditions would go here (strategy-specific)
// OrderSend would go here
// OrderSend(Symbol(), OP_BUY, lots, ask, slippage, sl, tp, "Demo", InpMagicNumber, 0, clrBlue);
}
//==============================
// 5) SMALL HELPER FUNCTIONS (return values)
//==============================
// Detect a new bar (common structure tool)
bool IsNewBar()
{
datetime currentBarTime = Time[0]; // LOCAL
if(currentBarTime != gLastBarTime)
{
gLastBarTime = currentBarTime; // GLOBAL updated
return(true);
}
return(false);
}
// Helper: count open trades for a given symbol/magic
int CountOpenTrades(string sym, int magic)
{
int count = 0; // LOCAL
int total = OrdersTotal(); // LOCAL
for(int i = total - 1; i >= 0; i--)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
if(OrderSymbol() != sym) continue;
if(OrderMagicNumber() != magic) continue;
count++;
}
return(count);
}
void OnTick()
{
UpdateRules();
if(!Allowed) return;
ManageTrades();
FindEntries();
}
Using void functions for reusable logic
Next we’ll build a reusable “EA toolbox”: spread filter, time filter, risk lot sizing, trade counting, and safe OrderSend wrappers.