Ещё раз о IndicatorCounted() - страница 3

 
Так и есть. Для оптимизации вычислений. Чтобы не пересчитывать всю историю, а только несколько баров в конце.

Информация о количестве уже посчитанных баров используется во всех наших встроенных индикаторах. И считается стандартным для всех индикаторов способом. Функция IndicatorCounted просто отдаёт эту уже посчитанную информацию, чтобы индикаторописатели тоже могли вкусить от благ.

Не нравится - не пользуйтесь. Учитывайте самостоятельно.


Просто есть два типа индикаторов:
1. Рассчитываемый относительно текущего бара (по сути как в Омеге с их Ref) и просчет истории таких индикаторов можно поручить терминалу
2. Требующие перерассчета на истории
Тут не смысла в кол-ве неподсчитанных баров, можно пропустить все неподсчитанные и рассчитать текущий бар (0й). Но и тут может оказаться выгодней иметь обратную нумерацию относительно сегодняшней. Когда 0й бар это самый старый, тогда нет смысла на каждом новом баре пересчитывать массивы, достаточно пересчитать новый бар сохраняя значения имеющегося массива. Плюс добавить функцию или сигнал что 0й бар сместился вглубь при докачке истории , старее имеющейся.
 
2. Требующие перерассчета на истории
Тут не смысла в кол-ве неподсчитанных баров, можно пропустить все неподсчитанные и рассчитать текущий бар (0й). Но и тут может оказаться выгодней иметь обратную нумерацию относительно сегодняшней. Когда 0й бар это самый старый, тогда нет смысла на каждом новом баре пересчитывать массивы, достаточно пересчитать новый бар сохраняя значения имеющегося массива. Плюс добавить функцию или сигнал что 0й бар сместился вглубь при докачке истории , старее имеющейся.

Здесь есть проблема при запуске терминала. Происходит следующее:
-Индикатор рассчитывается на имеющейся истории
-Приходит первый тик, т.е. начинается новый бар. Индикатор рассчитывется для истории+новый бар(то есть для истории с дырой)
-Докачивается история. Индикатор рассчитывается для истории без дыры.
Пусть у нас вначале было Bars0 баров. При получении первого тика их становится Bars0+1, то есть нулевой бар при нумерации от начала получает номер Bars0+1. Затем при докачке между ними втискивается ещё энное количества баров и номер Bars0+1 начинает соответствовать первому докачанному бару. Если мы для нулевого бара успели что-нибудь посчитать и запомнить по номеру от начала графика получаем ошибку - величина, рассчитанная для нулевого бара будет отнесена к первому докачанному.
Пример возникающего из-за этого бага есть в "Что возвращают функции Lowest и Highest"
 
Rosh:
Предложите свой изящный и 100%-ный способ добиться экономного и стабильного расчета индикаторов

Slawa:
Не нравится - не пользуйтесь. Учитывайте самостоятельно.

На самом деле проблема не в написании кода, а в тестировании. Функция ответственная, разнообразие возможных ситуаций велико, отсюда неслабые требования к времени тестирования (или к количеству тестирующих).
Тем не менее, раз я вроде бы заварил эту небольшую кашу, предложу проект решения.
Независимо от результата спасибо Славе за замечания по существу.
//+------------------------------------------------------------------+
//| Вспомогательная функция для CalculateFirstBar                    |
//+------------------------------------------------------------------+
// MinBars - минимально допустимое количество баров на графике
int ResetIndicator(int MinBars) {
//--
// Здесь нужно уничтожить объекты(если индикатор их создаёт)
// и инициализировать те переменные, которые при запуске индикатора 
// должны иметь определённое значение (если есть такая необходимость).
// А также выполнить другие действия, необходимые для правильного
// пересчета индикатора (если вышеперечисленных недостаточно)
//--
// Возвращаем индекс бара, с которого начнётся перерасчёт индикатора
  return(Bars-MinBars+1);
}
//+------------------------------------------------------------------+
//| Одна из альтернатив IndicatorCounted() (проект :)                |
//+------------------------------------------------------------------+
// Эта функция должна использоваться вместе с вспомогательной функцией ResetIndicator
// EveryTick - определяет тип работы: 
//             true - обсчитывается каждый тик
//             false - при открытии нового бара обсчитывется закончившийся
// MinBars - минимально допустимое количество баров на графике
// Mode - тип реакции на докачку истории:
//        0 - обсчитывем только докачанные бары
//        1 - пересчитывем всю историю
int CalculateFirstBar(bool EveryTick, int MinBars, int Mode) {
  static int LastBarTime = Time[Bars-1];
  static int preBars = 0;
  static int CurPeriod = 0;
  static string CurSymbol = "AAABBB";
  static int BarTime = 0;
  bool ReCalc;
  int FBar;
// Если нет необходимости обрабатывать каждый тик, работаем только при изменении числа баров
  if (!EveryTick && Bars == preBars) return(-1);
// Число баров должно превышать MinBars-1
  if (Bars < MinBars) return(-1);
// Если не в тестере, нужно проверить период и символ
  if (IsTesting()==false) {   
// Если изменился период или символ или была докачка в начало истории,
// будем пересчитывать всю историю
    if (CurPeriod != Period() || CurSymbol != Symbol() || LastBarTime != Time[Bars-1]) ReCalc = true;
    else ReCalc = false;
  }
  else ReCalc = false;
// Период и символ те же, проверим число баров и время предпоследнего.
// Если ситуация штатная, расчёт индикатора начнём с первого бара
  if (!ReCalc && ((Bars-preBars==1 && BarTime==Time[1]) || (Mode==0 && Bars==preBars && BarTime==Time[0]))) FBar = 1;
// Иначе ситуация нештатная, будем разбираться 
  else {
// Если изменился символ или период или задан режим пересчёта истории, 
// или была докачка в начало истории, делаем reset индикатора
    if (ReCalc || Mode == 1) FBar = ResetIndicator(MinBars); 
// Если символ и период те же и не задан режим пересчёта истории, 
// расчёт индикатора начнём с последнего обсчитанного бара
    if (!ReCalc && Mode == 0) FBar = Bars-preBars; 
  } 
// Модифицируем контрольные переменные
  preBars = Bars;  
  BarTime=Time[0];
  if (ReCalc) {
    LastBarTime = Time[Bars-1];
    CurPeriod = Period();
    CurSymbol = Symbol();
  }
// Уф-ф  
  return(FBar);
}


Применение:

int start() {
  int FBar = CalculateFirstBar(EveryTick, MinBars, Mode);

  for (int pos=FBar;pos>=0;pos--) {  // это для работы на каждом тике
//  for (int pos=FBar;pos>0;pos--) {  // это для работы на закончившемся баре
...


Если есть желающие тестировать, готов это дело сопровождать.


 
Сразу кое-что добавил в код (вспомнил, что бары могут и в начало истории докачиваться)
 
Lite версия :). Убраны комментарии - слишком хорошо тоже нехорошо :)
int ResetIndicator(int MinBars) {

  return(Bars-MinBars+1);
}
//--------------------------------------------------------------------
int CalculateFirstBar(bool EveryTick, int MinBars, int Mode) {
  static int LastBarTime = Time[Bars-1];
  static int preBars = 0;
  static int CurPeriod = 0;
  static string CurSymbol = "AAABBB";
  static int BarTime = 0;
  bool ReCalc;
  int FBar;
  if (!EveryTick && Bars == preBars) return(-1);
  if (Bars < MinBars) return(-1);
  if (IsTesting()==false) {   
    if (CurPeriod != Period() || CurSymbol != Symbol() || LastBarTime != Time[Bars-1]) ReCalc = true;
    else ReCalc = false;
  }
  else ReCalc = false;
  if (!ReCalc && ((Bars-preBars==1 && BarTime==Time[1]) || (Mode==0 && Bars==preBars && BarTime==Time[0]))) FBar = 1;
  else {
    if (ReCalc || Mode == 1) FBar = ResetIndicator(MinBars); 
    if (!ReCalc && Mode == 0) FBar = Bars-preBars; 
  } 
  preBars = Bars;  
  BarTime=Time[0];
  if (ReCalc) {
    LastBarTime = Time[Bars-1];
    CurPeriod = Period();
    CurSymbol = Symbol();
  }
  return(FBar);
}
//--------------------------------------------------------------------


Причина обращения: