Execute code only once a bar (although changing timeframes!)

 

Hi coders,

I made an indicator which should show an alert every minute. That works fine but when I change timeframes, it immediately shows the alert without waiting for a new m1-bar. I know this happens because the static datetime variable is initialized with a change of a timeframe. But I want to change timeframes whenever I want and the alert should only be shown as programmed.

My current solution is to use an object and I store the current bar's time in the object's text property. That works fine but I guess there is a "more elegant" solution. Is it possible to use/define variables which are not initialized whenever the timeframe is changed?

This one is not working:

//+------------------------------------------------------------------+
//| 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[])
  {
//---
   static datetime m1Time0;
   if (m1Time0!=iTime(_Symbol, 1, 0)) {
      m1Time0=iTime(_Symbol, 1, 0);
      Alert(_Symbol+" "+TimeToString(iTime(_Symbol, 1, 0), TIME_SECONDS));
   }
   
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

This object-based version works fine:

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   if (ObjectFind(0, "test")!=0) {
      ObjectCreate(0, "test", OBJ_HLINE, 0, 0, 0);
      ObjectSetString(0, "test", OBJPROP_TEXT, TimeToString(iTime(_Symbol, 1, 0), TIME_SECONDS));
   }
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| 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[])
  {
//---
   datetime m1Time0 = StringToTime(ObjectGetString(0, "test", OBJPROP_TEXT));
   if (m1Time0!=iTime(_Symbol, 1, 0)) {
      ObjectSetString(0, "test", OBJPROP_TEXT, TimeToString(iTime(_Symbol, 1, 0), TIME_SECONDS));
      Alert(_Symbol+" "+TimeToString(iTime(_Symbol, 1, 0), TIME_SECONDS));
   }
   
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
 
Marcus Riemenschneider:

Hi coders,

I made an indicator which should show an alert every minute. That works fine but when I change timeframes, it immediately shows the alert without waiting for a new m1-bar. I know this happens because the static datetime variable is initialized with a change of a timeframe. But I want to change timeframes whenever I want and the alert should only be shown as programmed.

My current solution is to use an object and I store the current bar's time in the object's text property. That works fine but I guess there is a "more elegant" solution. Is it possible to use/define variables which are not initialized whenever the timeframe is changed?

This one is not working:

This object-based version works fine:


Use the terminal Global variables instead.

https://www.mql5.com/en/docs/globals

Documentation on MQL5: Global Variables of the Terminal
Documentation on MQL5: Global Variables of the Terminal
  • www.mql5.com
Global variables are kept in the client terminal for 4 weeks since the last access, then they will be deleted automatically. An access to a global variable is not only setting of a new value, but reading of the global variable value, as well.
 

And just for the sake of completeness (although I suspect not useful in your circumstances) - static variables and variables with a global-scope "survive" a chart timeframe change if they are in an EA.

So if you aren't using an EA on the chart already, and the logic for the alert would work in an EA, you can take that approach instead of terminal GVs.

 
honest_knave:

And just for the sake of completeness (although I suspect not useful in your circumstances) - static variables and variables with a global-scope "survive" a chart timeframe change if they are in an EA.

So if you aren't using an EA on the chart already, and the logic for the alert would work in an EA, you can take that approach instead of terminal GVs.


Thanks guys. I never used GVs and after reading the docs I think I should stay with my object-based-version. The GVs can only be double-values but I need a datetime or at least a string which I convert into a datetime. So in my case I don't see any advantages of using GVs.

 
  1. Marcus Riemenschneider: My current solution is to use an object and I store the current bar's time in the object's text property. That works fine but I guess there is a "more elegant" solution. Is it possible to use/define variables which are not initialized whenever the timeframe is changed?
    Your problem occurs because unlike EAs, indicators are reloaded on chart changes. You can't stop the initialization, so use it.
       static bool     isFirstTick = true;
       static datetime m1Time0     = 0; 
       datetime now                = iTime(_Symbol, 1, 0);
       if (m1Time0!=now) {
         m1Time0=now; if(isFirstTick){ isFirstTick=false; return prev_calculated; }
         Alert(...
    

  2. nicholishen: Use the terminal Global variables instead.
    1. Terminal variables are good for interprocess communication and persistent storage. OPs problem doesn't have either. What are pattern would you use for this? PrefixSymbol doesn't work if there are multiple charts open with different timeframes. PrefixSymbolTF doesn't work because OP is changing TFs.
    2. OPs object use is better because it's chart specific.
 
whroeder1:
  1. Terminal variables are good for interprocess communication and persistent storage. OPs problem doesn't have either. 

So you're saying that saving a value that can survive a re-initialization is not persistent storage?

OPs object use is better because it's chart specific.

OP's object use comes with a higher overhead. 

 What are pattern would you use for this?

void OnStart()
{
   LastAlert(Time[0]);
   if(LastAlert() != Time[0])
      Print("ERROR!");
}

datetime LastAlert()
{
   string name = string(ChartID());
   name = _Symbol+StringSubstr(name,StringLen(name)-5);
   return (datetime)GlobalVariableGet(name);
}

void LastAlert(datetime time)
{
   string name = string(ChartID());
   name = _Symbol+StringSubstr(name,StringLen(name)-5);
   GlobalVariableSet(name,time);
}
 
whroeder1:
  1. Your problem occurs because unlike EAs, indicators are reloaded on chart changes. You can't stop the initialization, so use it.

    1. Terminal variables are good for interprocess communication and persistent storage. OPs problem doesn't have either. What are pattern would you use for this? PrefixSymbol doesn't work if there are multiple charts open with different timeframes. PrefixSymbolTF doesn't work because OP is changing TFs.
    2. OPs object use is better because it's chart specific.

I never thought about checking additionally the first tick of a bar. That seems to be the optimal solution for my problem. I use it now in my indicator and it works perfect.

Thank you!!

 
Marcus Riemenschneider:

I never thought about checking additionally the first tick of a bar. That seems to be the optimal solution for my problem. I use it now in my indicator and it works perfect.

Thank you!!


Just initialize the static variable with the actual time. That's the easiest way.

static datetime m1Time0  = iTime(_Symbol, 1, 0);
 
Laszlo Tormasi: Just initialize the static variable with the actual time. That's the easiest way.
Don't try to use any price or server related functions in OnInit (or on load,) as there may be no connection/chart yet:
  1. Terminal starts.
  2. Indicators/EAs are loaded. Static and globally declared variables are initialized. (Do not depend on a specific order.)
  3. OnInit is called.
  4. For indicators OnCalculate is called with any existing history.
  5. Human may have to enter password, connection to server begins.
  6. New history is received, OnCalculate called again.
  7. New tick is received, OnCalculate/OnTick is called. Now TickValue, TimeCurrent and prices are valid.
Reason: