Общие вопросы по разработке > Как в индикаторах и стратегиях получать данные вышестоящих таймфреймов?!

Общие вопросы по разработке в Альфа-Директ 4. Обсуждение разработки пользовательских индикаторов, стратегий.
Аватара пользователя
evge
Администратор
Сообщения: 1807
Зарегистрирован: 04 фев 2016, 09:46
Откуда: Млечный путь, планета Земля
Благодарил (а): 83 раза
Поблагодарили: 361 раз
Контактная информация:

Как в индикаторах и стратегиях получать данные вышестоящих таймфреймов?!

Непрочитанное сообщение evge » 10 дек 2016, 20:57

Пока не реализован в АД4 доступ к более чем одному входным рядам, приходится "извращаться" в получении данных вышестоящих таймфреймов.

Приведу один метод (использованный в SOH), как получить данные баров вышестоящего таймфрейма H1 в "любом" нижестоящем.

Для хранения OHLC бров будем использовать глобальные переменные типа DoubleList

Доступ из кода индикатора к часовым данным будет очень прост:

O[0..x] - Цена открытия последнего часа [0] и предшествующих
H[0..x] - Цена максимальная последнего часа [0] и предшествующих
L[0..x] - Цена минимальная последнего часа [0] и предшествующих
C[0..x] - Цена закрытия последнего часа [0] и предшествующих

Под последним часом понимается не текущий (формирующийся, незаконченный) бар, а предыдущий полностью законченный бар.

Open - Цена открытия текущего часа
Min - Цена минимальная текущего часа (может изменятся, т.к. текущий час формируется)
Max - Цена максимальная текущего часа (может изменятся, т.к. текущий час формируется)

Как видно выше, Close текущего часа нет, т.к. он ещё не определен в текущем часе, но доступен в качестве Input.Close[0] входного ряда.
Вообщем-то и Min, Max могут изменится к окончанию часа. Но если вдруг потребуется динамически отрисовывать индикатор по текущему часу то это тоже доступно через данные глобальные переменные.

В функции Initialize() добавляем глобальные переменные:

Код: Выделить всё

   // для сохранения списков значений OHLC часовых баров
   AddGlobalVariable("O", Types.DoubleList);
   AddGlobalVariable("H", Types.DoubleList);
   AddGlobalVariable("L", Types.DoubleList);
   AddGlobalVariable("C", Types.DoubleList);
   // для хранения минимальной и максимальной цены текущего часа
   AddGlobalVariable("Min", Types.Double, 999999999);
   AddGlobalVariable("Max", Types.Double, 0.0);
   // для хранения цены открытия текущего часа
   AddGlobalVariable("Open", Types.Double, 0.0);


в самом начале функции Evaluate() добавляем следующий код:

Код: Выделить всё

// закончился часовой бар и начался новый
// сохраняем часовые данные в списках
if (BarTime(0).Hours != BarTime(1).Hours)
{
   // сохраняем данные часового бара
   O.Insert(0, Open);
   H.Insert(0, Max);
   L.Insert(0, Min);
   C.Insert(0, Input.Close[1]);
   // лишнее удаляем
   var CN = O.Count;
   if (CN > Period)
   {
      O.RemoveAt(CN - 1);
      H.RemoveAt(CN - 1);
      L.RemoveAt(CN - 1);
      C.RemoveAt(CN - 1);
   }
   // новый бар
   Min = Double.MaxValue;
   Max = 0.0;
   Open = Input.Open[0];
}
if (Input.Low[0] < Min) Min = Input.Low[0];
if (Input.High[0] > Max) Max = Input.High[0];


Здесь неописанная переменная (или чаще параметр) "Period", которая ограничивает наши DoubleList по размерности, например это может быть период для H1 заданный в параметрах индикатора. Вообщем, он определяет максимально доступную длину истории для хранения наших данных от H1, при превышении которой происходит удаление последнего элемента.

После данного кода Evaluate() можно использовать ряды OHLC и переменные для текущего часа.

Ещё возможно изменить условие, которое описывает момент начала нового часового бара.

Код: Выделить всё

if (BarTime(0).Hours != BarTime(1).Hours)

см. TimeSpan (MSDN)

Например, для получения данных дневного таймфрема условие можно изменить на такое:

Код: Выделить всё

if (BarDate(0).Day != BarDate(1).Day)

см. DateTime (MSDN)

В данном примере объёмы не сохраняются, но ничего мешает завести ещё 3 глобальные переменные и сохранять Volume, VolumeBid, VolumeAsk.

Надеюсь, что кому-то поможет данный пример...

Если у кого-то есть собственные наработки или замечания буду рад увидеть :)
никогда такого не было и вот опять

Аватара пользователя
evge
Администратор
Сообщения: 1807
Зарегистрирован: 04 фев 2016, 09:46
Откуда: Млечный путь, планета Земля
Благодарил (а): 83 раза
Поблагодарили: 361 раз
Контактная информация:

Re: Как в индикаторах получать данные вышестоящих таймфреймов?!

Непрочитанное сообщение evge » 11 дек 2016, 08:38

Ещё модифицированный выше код и более упрощенный вариант хранения OHLC
в данном варианте в O,H,L,C списках в [0] индексе будут сохраняться значения текущего часа, в [1] предыдущего и так далее.
Упрощение в более понятном и простом хранении значений, т.к. здесь в [0] - текущий бар, в [1] предыдущий и т.д. и в отсутствии глоб. переменных для хранения OHLC текущего часа: Min, Max, Open

В Initialize() Добавляем только 4 глоб переменные типом DoubleList

Код: Выделить всё

// для сохранения списков значений OHLC часовых баров
AddGlobalVariable("O", Types.DoubleList);
AddGlobalVariable("H", Types.DoubleList);
AddGlobalVariable("L", Types.DoubleList);
AddGlobalVariable("C", Types.DoubleList);


в Evaluate() следующий код

Код: Выделить всё

// закончился часовой бар и начался новый
// сохраняем часовые данные в списках
if (BarTime(0).Hours != BarTime(1).Hours)
{
   // новый бар
   O.Insert(0, Input.Open[0]);
   H.Insert(0, Input.High[0]);
   L.Insert(0, Input.Low[0]);
   C.Insert(0, Input.Close[0]);
   // лишнее удаляем
   var CN = O.Count;
   if (CN > Period)
   {
      O.RemoveAt(CN - 1);
      H.RemoveAt(CN - 1);
      L.RemoveAt(CN - 1);
      C.RemoveAt(CN - 1);
   }
}
if (O.Count > 0)
{
   if (Input.Low[0] < L[0]) L[0] = Input.Low[0];
   if (Input.High[0] > H[0]) H[0] = Input.High[0];
   C[0] = Input.Close[0];
}


Здесь "Period" - максимальная длина хранимой истории, задавайте параметром или вручную свою максимальную размерность списков.

В индикаторе перед использованием данных этих списков необходимо проверять их размерность. Достаточно проверки размерности одного из списков для использования и всех остальных.

Код: Выделить всё

if (O.Count >= CNT)
{
   //... O[0..x] H[0..x] L[0..x] C[0..x], где x = Period - 1;
}


Здесь "CNT" - минимально требуемая история в барах. Минимальная длина списков для дальнейшего анализа.
никогда такого не было и вот опять

Gemin
Сообщения: 23
Зарегистрирован: 06 сен 2016, 18:02

Re: Как в индикаторах получать данные вышестоящих таймфреймов?!

Непрочитанное сообщение Gemin » 14 дек 2016, 12:55

Добрый день, Evge!
Сложно не признать гениальность Вашего решения, также хотел отметить, что аналогов не видел!
Спасибо, но довести до динамического точного отрисовывания индикатора получилось только на М30 во второй половине часа…
Так понимаю, что вопрос в sD[1] и Signal[1] - они меняютcя в течении часа, что естественно влияет на sD[0] и Signal[0].

Код: Выделить всё

   var a = (eMax - eMin);
   if (a > 0.0) sK = (C[0] - eMin) / (eMax - eMin) * 100.0;

   sKH.Insert(0, sK);
   if (sKH.Count > nK) sKH.RemoveAt(sKH.Count - 1);

   sD = sD[1] - (sKH[0 + nD] - sKH[0]) / nD;

   sDH.Insert(0, sD[0]);
   if (sDH.Count > nK) sDH.RemoveAt(sDH.Count - 1);

   Signal = Signal[1] - (sDH[0 + nSignal] - sDH[0]) / nSignal;


Следующие глоб переменные типом DoubleList В Initialize() надо оставить?

Код: Выделить всё

   AddGlobalVariable("sDH", Types.DoubleList);
   AddGlobalVariable("sKH", Types.DoubleList);

Если возможно, спасите!
Спасибо!

Аватара пользователя
evge
Администратор
Сообщения: 1807
Зарегистрирован: 04 фев 2016, 09:46
Откуда: Млечный путь, планета Земля
Благодарил (а): 83 раза
Поблагодарили: 361 раз
Контактная информация:

Re: Как в индикаторах получать данные вышестоящих таймфреймов?!

Непрочитанное сообщение evge » 14 дек 2016, 13:20

если речь о SOH, то там описано что на текущем часе нижестоящего таймфрейма рисуется значение предыдущего часа таймфрейма H1,
т.е. если сравнивали, то сравнивать надо поставив 2 графика рядом на одном индикатор SOH на другом графике SO с одинаковыми параметрами, но на графике SO ТФ=H1,

при этом надо на 1 свечу сдвинуть назад график и эти значения будут на графике SOH последними, для любой свечки последнего часа.

В описании SOH это указал, что для текущего часа ничего не рассчитывается в SOH, на текущем часе рисуются значения SO для ТФ=H1 предыдущего часа, который мы уже можем рассчитать.

У меня значения при тестировании индикатора совпадали даже на M1. Приведите скриншот - пример несовпадения значений.
никогда такого не было и вот опять

Gemin
Сообщения: 23
Зарегистрирован: 06 сен 2016, 18:02

Re: Как в индикаторах получать данные вышестоящих таймфреймов?!

Непрочитанное сообщение Gemin » 14 дек 2016, 13:52

То что SOH отражает одни данные с ТФ 1 час на разных ТФ(меньше часа) – это читал и это факт, но данные прошлого часа...
Просто решил, что:

Код: Выделить всё

  O.Insert(0, Input.Open[0]);
   H.Insert(0, Input.High[0]);
   L.Insert(0, Input.Low[0]);
   C.Insert(0, Input.Close[0]);
позволит получать динамические данные, поскольку «в данном варианте в O,H,L,C списках в [0] индексе будут сохраняться значения текущего часа, в [1] предыдущего и так далее».
Это и получается, но только на ТФ30 мин и во второй половине часа, а первую половину часа данные статичные…
Спасибо!

Аватара пользователя
evge
Администратор
Сообщения: 1807
Зарегистрирован: 04 фев 2016, 09:46
Откуда: Млечный путь, планета Земля
Благодарил (а): 83 раза
Поблагодарили: 361 раз
Контактная информация:

Re: Как в индикаторах получать данные вышестоящих таймфреймов?!

Непрочитанное сообщение evge » 14 дек 2016, 13:57

Gemin писал(а):получать динамические данные


Я уже думал об этом, но проблема в том, что в текущий час динамически меняются и Min и Max и необходимо будет перерисовывать все значения индикатора от текущего бара назад до начала часа, т.к. если мы считаем индикатор для часового ТФ то и входящими значениями для него будут данные от часового ТФ и на каждом баре внутри часа они должны быть одинаковыми, т.е. они не должны быть динамическими.

Хотя конечно можно попробовать рисовать динамически внутри часа по данным предыдущих часов и текущего, а на следующий бар брать только реальный максимум и минимум по часам + текущие динамические значения.

Попробую такое реализовать
никогда такого не было и вот опять

Gemin
Сообщения: 23
Зарегистрирован: 06 сен 2016, 18:02

Re: Как в индикаторах получать данные вышестоящих таймфреймов?!

Непрочитанное сообщение Gemin » 14 дек 2016, 14:30

Где мастер есть, там говорят лучше не … мешать, но как видится, Min и Max отрабатывают как положено, поскольку и у ТФ 1 час они также меняются и будут одинаковыми, а вот вопрос именно в sD[1] и Signal[1], они перестают быть статичны, а должны быть такими весь час.
Т.е. с каждым баром меньшего ТФ sD и Signal пересчитываются и пересчитываются....
Если их заменить на

Код: Выделить всё

(sKH[1 + nD] - sKH[1]) / nD
и

Код: Выделить всё

(sDH[1 + nSignal] - sDH[1]) / nSignal
соответственно, то возникает первоначальный некорректный расчет индикатора, если только обнулять где-то…

Аватара пользователя
evge
Администратор
Сообщения: 1807
Зарегистрирован: 04 фев 2016, 09:46
Откуда: Млечный путь, планета Земля
Благодарил (а): 83 раза
Поблагодарили: 361 раз
Контактная информация:

Re: Как в индикаторах получать данные вышестоящих таймфреймов?!

Непрочитанное сообщение evge » 14 дек 2016, 14:52

Думается мне от лесенки всё равно не уйти будет.
Даже если текущий час динамически рисоваться будет, то с переходом на следующий час будет резкий перепад в расчете, т.к. данные предыдущих часов меняются в разрезе часа.

получится нечто подобное:

MACDSignal-02.png
MACDSignal-02.png (46.58 КБ) 39331 просмотр
никогда такого не было и вот опять

Gemin
Сообщения: 23
Зарегистрирован: 06 сен 2016, 18:02

Re: Как в индикаторах и стратегиях получать данные вышестоящих таймфреймов?!

Непрочитанное сообщение Gemin » 12 янв 2017, 13:07

Добрый день, Evge!
Если возможно, подскажите пример условия для получения данных недельного таймфрема на нижестоящих.
Такое условие не работает…

Код: Выделить всё

if (BarDate(0).DayOfWeek != BarDate(1).DayOfWeek)

Так понимаю, что понедельник нужен...)
Спасибо!

Аватара пользователя
evge
Администратор
Сообщения: 1807
Зарегистрирован: 04 фев 2016, 09:46
Откуда: Млечный путь, планета Земля
Благодарил (а): 83 раза
Поблагодарили: 361 раз
Контактная информация:

Re: Как в индикаторах и стратегиях получать данные вышестоящих таймфреймов?!

Непрочитанное сообщение evge » 12 янв 2017, 14:25

Gemin писал(а):Так понимаю, что понедельник нужен...)


а если это не понедельник? например это праздник?

DayOfWeek возвращает день недели
значит надо сравнить день недели текущего бара с предществующим, причем текущий должен быть МЕНЬШЕ предыдущего. Не имеет разницы что текущий день бара это понедельник, вторник или другой день недели.

Код: Выделить всё

if (BarDate(0).DayOfWeek < BarDate(1).DayOfWeek)
{
//здесь первая свечка новой недели любого ТФ до W
}


из MSDN System.DayOfWeek

DayOfWeek Перечисление представляет день недели в календарях, семь дней в неделю. Значение константы в этом перечислении лежит в диапазоне от DayOfWeek.Sunday к DayOfWeek.Saturday. Если приводится к целому типу, его значение лежит в диапазоне от нуля (указывает, что DayOfWeek.Sunday) до 6 (что означает DayOfWeek.Saturday).
никогда такого не было и вот опять


Вернуться в «Общие вопросы по разработке»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 21 гость