I'm currently developing several multitimeframe (MTF) indicators for MT5, and I have been dealing with the well-known problem of working
with MTF when the market is closed, as I have recently posted here.
I think I have successfully developed a new solution I haven't found in the forum, which consists of automatically switching to a different
timeframe, and return back to the original one, using global variables of the client terminal.
I will try to summarize the problem and my solution, and I hope that it can be useful to some of you working on the same problem. I'm sorry if I
make some wrong assumptions, or my explanations are not perfect, since I'm new to MT5 and I have not as much expertise as some other members of
the forum. I have been reading and understanding the documentation as much as I could.
Description of the problem:
Let's say we are in M1 (TF1) but we need to access bars from D1 (TF2). We call iBarShift in TF2, but the market is closed (let's say we're on weekend)
so we get an error (WRONG_VALUE or -1). That error means that TF2 bars are still not loaded in the client terminal, and the indicator won't
load either, since we need bars from TF2 but we can't get them. Since it is weekend, there aren't onCalculate events as they would be
when the market is open. Thus, the final result is the indicator won't load.
Some known solutions:
To manually switch to a different timeframe than TF1, let's say to M5 (TF3) and then we switch back to TF1. Sure enough, the
terminal will have had time to load TF2 bars, and the indicator will fully reload. Since TF2 bars are ready, iBarShift won't fail
again.
To use ChartSetSymbolPeriod(0, NULL, 0) as
Alain Verleyen has suggested in several posts, like
this one for example. This is similar to refresh the chart, but as
I explained here, this can lead to some really strange behaviour: iBarShift alternates between failing and not, which doesn't make
a lot of sense, really. Another problem with this method is that it's
like using a hammer to kill a fly, as Alain cleverly stated, because it will lead to reload all indicators for all charts using the
considered symbol.
My solution: automatically switch to TF3 and back to TF1 using global terminal variables
In order to do this switch automatically we set a timer that would generate a OnTimer event. Before the timer goes by, the client
terminal will have some time to load bars in TF2 (and even sometimes it will have to load TF1 bars too). When OnTimer gets called, we will
use ChartSetSymbolPeriod to move to TF3.
Given that the indicator is fully re-initialized after moving to a different TF, we have to use a global
variable of the client terminal to be able to do the trick, because they don't get deleted after the indicator is re-initialized. In that
global variable we will store the current Timeframe (TF1), so that we can return to it later. Normal global variables, or
static variables are deleted after an indicator gets re-initialized, so there would be no way to know to which TF to return to.
After we have moved automatically to TF3, we check whether our global variable exists. If it exist, we don't calculate the indicator, and we just
switch back to our original timeframe TF1. When we are again in TF1, we just delete the global terminal variable.
I have tried this system with a very simple multitimeframe indicator that paints the closing value of the chosen higher timeframe candles.
By switching back and forth between timeframes, the problem stated in point 2 above by Alain in the previous section dissapears. Now only the
current indicator will be reloaded. I have tried to make the code clear enough so that it can be reusable:
#property copyright"Copyright 2020, Carlos"#property indicator_chart_window#property indicator_buffers1// buffers used#property indicator_plots1// buffers displayed//--- Indicator Properties#property indicator_label1"Close Value in Other TF"#property indicator_type1DRAW_LINE#property indicator_color1 Blue
#property indicator_style1STYLE_SOLID#property indicator_width12#ifndef _VERBOSE_
#define _VERBOSE_
#endif
#define SECONDS_TO_WAIT 1//--- input parametersinputENUM_TIMEFRAMES other_TimeFrame = PERIOD_D1;
//--- Indicator bufferdouble otherTF_Buffer[];
int offset;
string gvar_magic_name;
//+------------------------------------------------------------------+//| |//+------------------------------------------------------------------+intOnInit()
{
//--- indicator buffers mappingSetIndexBuffer(0, otherTF_Buffer, INDICATOR_DATA);
IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
double tf1 = PeriodSeconds(_Period);
double tf2 = PeriodSeconds(other_TimeFrame);
offset = (int)ceil(tf2/tf1);
// Create a kind of unique variable name for this indicator: (chart ID number + indicator file name)
gvar_magic_name = IntegerToString(ChartID())+ "_" + __FILE__;
// Check if we have to change timeframes
check_auto_timeframe_travel(gvar_magic_name);
return(INIT_SUCCEEDED);
}
//+---------------------------------------------------------------------------------+//| After SECONDS_TO_WAIT seconds have gone by, the Timer event is generated, and |//| we move to a different TF, from which we will be back to the original TF. |//+---------------------------------------------------------------------------------+voidOnTimer(void)
{
move_to_a_different_TF(gvar_magic_name);
}
//+------------------------------------------------------------------+//| |//+------------------------------------------------------------------+intOnCalculate(constint rates_total,
constint prev_calculated,
constdatetime &time[],
constdouble &open[],
constdouble &high[],
constdouble &low[],
constdouble &close[],
constlong &tick_volume[],
constlong &volume[],
constint &spread[])
{
int start;
int shift;
double c; // close value in the other timeframe#ifdef _VERBOSE_
Print("On Calculate ==> Rates total = ", rates_total, "; Prev_calculated = ", prev_calculated);
#endif
//--- Detect start positionif(prev_calculated == 0)
{
// If our global variable of the client terminal exists, it means that we have switched// temporarily to a different timeframe, so we don't need to calculate the indicator now. if(GlobalVariableCheck(gvar_magic_name)) return0;
// Check if we can start reading bars in the TF we want to. If we can't, auto TF switch// will be initiated. if(check_ibarshift_in_Other_TF(time[rates_total-1], other_TimeFrame, SECONDS_TO_WAIT) == false) return0;
start = 1;
ArrayInitialize(otherTF_Buffer,EMPTY_VALUE);
}
else
{
start = prev_calculated - offset;
}
//--- Main loop#ifdef _VERBOSE_
Print(" Before main loop: start = ", start);
#endif
for(int i = start; i < rates_total && !IsStopped(); i++)
{
shift = iBarShift(NULL, other_TimeFrame, time[i], false);
if(shift < 0)
{
#ifdef _VERBOSE_
Print("iBarShift error #", GetLastError());
#endif
return i;
}
c = iClose(NULL, other_TimeFrame, shift);
if(c < 0)
{
#ifdef _VERBOSE_
Print("iClose error #", GetLastError());
#endif
return i;
}
// Save the close value from the other timeframe
otherTF_Buffer[i] = c;
}
//--- return value of prev_calculated for next call#ifdef _VERBOSE_
Print(" Loop end, returning rates_total = ", rates_total);
#endif
return(rates_total);
}
//+------------------------------------------------------------------------------------------------------------+//| Let's say we are in M1 (TF1) and we want to access bars from D1 (TF2). We try iBarShift in TF2, but we get | //| an error. That means TF2 bars are still not loaded in the client terminal, so the indicator doesn't load |//| either. What we would do manually, would be to switch to M5 (TF3) and back to TF1 (it could be TF3 = TF2, |//| but not necessarily). |//| |//| In order to do this switch automatically we set a timer that would generate a OnTimer event. Before the |//| timer goes by, the client terminal will have some time to load bars in TF2 (and even sometimes it will |//| have to load TF1 bars too). When OnTimer gets called, we will use ChartSetSymbolPeriod to move to TF3. | //+------------------------------------------------------------------------------------------------------------+bool check_ibarshift_in_Other_TF(datetime t, ENUM_TIMEFRAMES tf, int seconds)
{
int res;
staticbool is_timer_already_set = false;
// Test iBarShift for date 't' and get the result. If it doesn't work, it will return WRONG_VALUE
res = iBarShift(NULL, tf, t);
#ifdef _VERBOSE_
Print(" check_ibarshift(date = ", t, ") ==> iBarShift = ", res);
#endif
// If iBarShift is not ready and returns an error, then set a timer for making an automatic change// to another timeframe so that the candles have time to load. if(res == WRONG_VALUE)
{
// This function can be entered several times before the timer// is called, so we just make sure to set the timer ONCE. if(is_timer_already_set == false)
{
#ifdef _VERBOSE_
Print(" Setting a ", SECONDS_TO_WAIT, " seconds Timer");
#endif
EventSetTimer(seconds);
is_timer_already_set = true;
}
returnfalse;
}
returntrue;
}
//+------------------------------------------------------------------------------------------------------------+//| This function let us move to another TF, using ChartSetSymbolPeriod. If we are in TF1, we need to move to |//| TF2 (being TF1 != TF2, of course). |//| Given that the indicator is fully re-initialized after moving to a different TF, we have to use a global |//| variable of the client terminal to be able to do the trick, because they can stay up even for 4 weeks. |//| In that global variable , we store the current Timeframe, so that we can return to it later. |//+------------------------------------------------------------------------------------------------------------+void move_to_a_different_TF(string gvar_name)
{
ENUM_TIMEFRAMES TF_to_move_to = get_higher_tf(_Period);
#ifdef _VERBOSE_
Print("=> Moving to ", EnumToString(TF_to_move_to), " using ChartSetSymbolPeriod.");
#endif
GlobalVariableSet(gvar_name, _Period);
ChartSetSymbolPeriod(0, NULL, TF_to_move_to);
}
//+------------------------------------------------------------------------------------------------------------+//| Return the timeframe to the right of a given timeframe, taking into account just the classic set of TFs: |//| M1, M5, M15, M30, H1, H4, D1, W1, MN |//+------------------------------------------------------------------------------------------------------------+ENUM_TIMEFRAMES get_higher_tf(ENUM_TIMEFRAMES tf)
{
if (tf < PERIOD_M5) returnPERIOD_M5;
elseif(tf < PERIOD_M15) returnPERIOD_M15;
elseif(tf < PERIOD_M30) returnPERIOD_M30;
elseif(tf < PERIOD_H1) returnPERIOD_H1;
elseif(tf < PERIOD_H4) returnPERIOD_H4;
elseif(tf < PERIOD_D1) returnPERIOD_D1;
elseif(tf < PERIOD_W1) returnPERIOD_W1;
elseif(tf < PERIOD_MN1) returnPERIOD_MN1;
elsereturnPERIOD_W1; //If tf is MN1, we can't switch to the immediately higher timeframe// so we switch to the immediately lower timeframe instead.
}
//+------------------------------------------------------------------------------------------------------------+//| If we have set a global variable of the client terminal, it means we are forcing a re-initialization of |//| the indicator by switching automatically back and forth to another TimeFrame. |//| |//| If a global variable already exists, get its value, which is the original timeframe (TF1) where we have | //| to return to. Here two different scenarios may arise: |//| |//| 1) The current timeframe (TF2) is different from the original timeframe. That means we have moved to |//| TF2 from TF1, and we have now to return to TF1. |//| |//| 2) TF2 is the same as TF1. That means we are already back from the TF2. This means that trick of |//| moving back and forth between timeframe is done. We only have to delete the global variable. |//+------------------------------------------------------------------------------------------------------------+void check_auto_timeframe_travel(string gname)
{
// if(GlobalVariableCheck(gname))
{
// Recover the value we stored, which is the timeframe where we have to return to. ENUM_TIMEFRAMES orig_tf = (ENUM_TIMEFRAMES) GlobalVariableGet(gname);
if(_Period != orig_tf)
{
// If the current TF is different from the original TF, that means we have temporarily// auto-moved to another TF. We have to send the order to move back to the original TF.ChartSetSymbolPeriod(0, NULL, orig_tf);
}
else
{
GlobalVariableDel(gname);
#ifdef _VERBOSE_
Print("OnInit: we're back to ", EnumToString(orig_tf), ". Deleting global terminal variable.");
#endif
}
}
}
//+------------------------------------------------------------------------------------------------------------+
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.
Hello everyone,
I'm currently developing several multitimeframe (MTF) indicators for MT5, and I have been dealing with the well-known problem of working with MTF when the market is closed, as I have recently posted here.
I think I have successfully developed a new solution I haven't found in the forum, which consists of automatically switching to a different timeframe, and return back to the original one, using global variables of the client terminal.
I will try to summarize the problem and my solution, and I hope that it can be useful to some of you working on the same problem. I'm sorry if I make some wrong assumptions, or my explanations are not perfect, since I'm new to MT5 and I have not as much expertise as some other members of the forum. I have been reading and understanding the documentation as much as I could.
Description of the problem:
Let's say we are in M1 (TF1) but we need to access bars from D1 (TF2). We call iBarShift in TF2, but the market is closed (let's say we're on weekend) so we get an error (WRONG_VALUE or -1). That error means that TF2 bars are still not loaded in the client terminal, and the indicator won't load either, since we need bars from TF2 but we can't get them. Since it is weekend, there aren't onCalculate events as they would be when the market is open. Thus, the final result is the indicator won't load.
Some known solutions:
To manually switch to a different timeframe than TF1, let's say to M5 (TF3) and then we switch back to TF1. Sure enough, the terminal will have had time to load TF2 bars, and the indicator will fully reload. Since TF2 bars are ready, iBarShift won't fail again.
To use ChartSetSymbolPeriod(0, NULL, 0) as Alain Verleyen has suggested in several posts, like this one for example. This is similar to refresh the chart, but as I explained here, this can lead to some really strange behaviour: iBarShift alternates between failing and not, which doesn't make a lot of sense, really.
Another problem with this method is that it's like using a hammer to kill a fly, as Alain cleverly stated, because it will lead to reload all indicators for all charts using the considered symbol.
My solution: automatically switch to TF3 and back to TF1 using global terminal variables
In order to do this switch automatically we set a timer that would generate a OnTimer event. Before the timer goes by, the client terminal will have some time to load bars in TF2 (and even sometimes it will have to load TF1 bars too). When OnTimer gets called, we will use ChartSetSymbolPeriod to move to TF3.
Given that the indicator is fully re-initialized after moving to a different TF, we have to use a global variable of the client terminal to be able to do the trick, because they don't get deleted after the indicator is re-initialized. In that global variable we will store the current Timeframe (TF1), so that we can return to it later. Normal global variables, or static variables are deleted after an indicator gets re-initialized, so there would be no way to know to which TF to return to.
After we have moved automatically to TF3, we check whether our global variable exists. If it exist, we don't calculate the indicator, and we just switch back to our original timeframe TF1. When we are again in TF1, we just delete the global terminal variable.
I have tried this system with a very simple multitimeframe indicator that paints the closing value of the chosen higher timeframe candles. By switching back and forth between timeframes, the problem stated in point 2 above by Alain in the previous section dissapears. Now only the current indicator will be reloaded. I have tried to make the code clear enough so that it can be reusable: