Problem Description
Solution
To execute a function only once per candle in MQL4, you need to detect the exact moment the current bar's opening time changes. Since OnTick() triggers with every price movement, you must compare the current bar's time (Time[0]) against a stored variable representing the previous bar's time.
Here is the most robust and reusable way to implement this using a boolean check function.
Implementation Strategy
- Create a helper function (
IsNewCandle): This function stores the time of the last processed bar in astaticvariable. - Check
Time[0]: It compares the current bar's open time with the stored time. - Update and Return: If the times differ, it updates the stored time and returns
true. Otherwise, it returnsfalse.
MQL4 Code Example
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Initialization logic here
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Check if a new candle has just opened
if(IsNewCandle())
{
// Code inside this block runs only ONCE per bar
Print("New candle opened at: ", TimeToString(TimeCurrent()));
// Call your specific trading or analysis function here
MyTradingLogic();
}
}
//+------------------------------------------------------------------+
//| Custom Function: Detects New Candle |
//+------------------------------------------------------------------+
bool IsNewCandle()
{
// 'static' ensures the variable retains its value between OnTick calls
static datetime last_time = 0;
// Time[0] is the open time of the current bar on the current chart
if(Time[0] != last_time)
{
// Update the stored time to the current bar's time
last_time = Time[0];
return(true); // New candle detected
}
return(false); // Same candle, no new bar
}
//+------------------------------------------------------------------+
//| Your Custom Logic Function |
//+------------------------------------------------------------------+
void MyTradingLogic()
{
// Example: Check Moving Average Crossover
double maFast = iMA(NULL, 0, 12, 0, MODE_EMA, PRICE_CLOSE, 1);
double maSlow = iMA(NULL, 0, 26, 0, MODE_EMA, PRICE_CLOSE, 1);
if(maFast > maSlow)
{
Print("Bullish condition on new bar.");
// OrderSend logic would go here
}
}
Key Concepts Explained
static datetime last_time:- The
statickeyword is crucial. It ensures that thelast_timevariable is not reset to 0 every time the function is called. It remembers the value from the previous tick.
- The
Time[0]:- This is a predefined MQL4 array that holds the open time of the current bar. When a new bar forms,
Time[0]changes to the new time (e.g., from 12:00 to 13:00 on an H1 chart).
- This is a predefined MQL4 array that holds the open time of the current bar. When a new bar forms,
- Execution Flow:
- Tick 1 (New Bar):
Time[0]is 13:00.last_timeis 12:00. They are different.last_timeupdates to 13:00. Function returnstrue. - Tick 2 (Same Bar):
Time[0]is 13:00.last_timeis 13:00. They are equal. Function returnsfalse.
- Tick 1 (New Bar):
Handling Specific Timeframes or Symbols
If you need to check for a new candle on a specific timeframe (e.g., H4) while the EA is running on a different chart (e.g., M15), replace Time[0] with iTime():
bool IsNewCandle_H4()
{
static datetime last_time_h4 = 0;
// Check H4 time specifically
datetime current_h4_time = iTime(NULL, PERIOD_H4, 0);
if(current_h4_time != last_time_h4)
{
last_time_h4 = current_h4_time;
return(true);
}
return(false);
}
Q&A: MQL4 New Candle Detection
Q: Why shouldn't I just use Volume[0] == 1 to detect a new candle?
A: While Volume[0] == 1 theoretically indicates the first tick of a new bar, it is unreliable. In fast-moving markets, the EA might miss the very first tick due to network latency or processing delays. If the EA receives the data when the volume is already 2 or 3, the condition fails, and the logic never executes for that bar. Comparing time is always accurate regardless of how many ticks have passed.
Q: Will IsNewCandle() return true when I first attach the EA to the chart?
A: Yes. When the EA initializes, the static variable last_time is 0. The current Time[0] will be a valid date (e.g., 2023.10.27 10:00). Since 0 does not equal the current time, it returns true on the very first tick. If you want to skip the first execution, you can initialize last_time with Time[0] inside OnInit().
Q: Can I use this logic for backtesting?
A: Yes, this logic works perfectly in the Strategy Tester. It ensures that your strategy only calculates signals at the open of bars, which significantly speeds up backtesting compared to calculating on every tick.
Q: Does this work for Renko or non-time-based charts?
A: Standard MQL4 Time[] arrays rely on time-based bars. For offline charts (like Renko generated by indicators), the Time[0] usually still updates when a new brick is formed, so this logic generally works, provided the offline chart generator updates the history data correctly.