Generate code in 3 minutes
Enter your idea and AI generates runnable code.
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.
IsNewCandle): This function stores the time of the last processed bar in a static variable.Time[0]: It compares the current bar's open time with the stored time.true. Otherwise, it returns false.//+------------------------------------------------------------------+
//| 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
}
}
static datetime last_time:
static keyword is crucial. It ensures that the last_time variable is not reset to 0 every time the function is called. It remembers the value from the previous tick.Time[0]:
Time[0] changes to the new time (e.g., from 12:00 to 13:00 on an H1 chart).Time[0] is 13:00. last_time is 12:00. They are different. last_time updates to 13:00. Function returns true.Time[0] is 13:00. last_time is 13:00. They are equal. Function returns false.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: 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.