Циклы-в-ТСЛаб

Статья от коллеги по алготрейдингу: Александра Ганова
Решил разобраться для себя в разделе «Циклы», а заодно и написать пояснение по каждому из имеющихся в TSLab блоков из вышеуказанного раздела. Документация TSLab по циклам есть, в документации есть довольно подробные пояснения и даже примеры, но, судя по количеству вопросов, которые возникают по данным блокам, они далеко не всем понятны и не такие интуитивные как большинство других блоков, стало быть, требуют дополнительного исследования. Итак, начнем.

Блок «Цикл»

Чтобы понимать, что такое итерация цикла необходимо ознакомиться с краткой информацией по тому, что такое цикл. Фактически, цикл TSLab представляет собой конструкцию цикла For:
Code:

for ([действия_до_выполнения_цикла]; [условие]; [действия_после_выполнения])
{
    // действия
}

В теле цикла конструкция производит какое-либо полезное для программы действие, которое повторяется энное количество раз, пока не будут окончены все итерации, либо пока не будет осуществлен выход из тела цикла.

У блока «Цикл» в TSLab имеется только один вход. Как следует из документации, на данный вход подается какое-либо целое положительное число, которое управляет количеством итераций на каждой свече инструмента, то есть данный вход задает количество «запусков» кода из тела цикла на каждой свече инструмента. Дополнительно в цикле имеется настройка «Макс.Количество», которая также работает с количеством итераций и задает их максимальное значение. В совокупности получаем следующий результат — мы либо задаем количество итераций через вход кубика, либо непосредственно указываем это количество в настройке кубика если данное количество не предполагает изменяться в зависимости от каких-либо условий в скрипте.

Составим самую простую конструкцию и посмотрим какой именно код под нее создаст TSLab. На входе зададим значение 5 (кубик «Iterations»), в настройках кубиках поставим максимальное значение 10. В случае если максимальное значение будет меньше того, что указано на входе, то будет использовано максимально значение.
Пример-Цикла1

В данный момент не будем разбираться почему конструкция именно такова, вернемся к этому моменту позже. Сейчас посмотрим что представляет из себя цикл и где именно в коде будут указанные 5 итераций на входе. Открываем сгенерированный файл и видим необходимую конструкцию.

cycle_1.2

В коде видим следующие инструкции:

Code:

int CycleCount = Cycle_h.GetCount(Iterations[i]);

Инструкция задает количество итераций цикла, при этом Iterations[i] это и есть наш кубик «Iterations», который фактически представляет собой список констант по количеству загруженных в скрипт баров инструмента. ТСЛаб автоматически «разворачивает» все константы в списки значений, что можно видеть по следующему фрагменту кода.

Code:

public TSLab.Script.Optimization.OptimProperty Iterations_Value = new TSLab.Script.Optimization.OptimProperty(5D, false, 1D, 10D, 1D, 1);
this.Iterations_h.Value = ((double)(this.Iterations_Value.Value));
System.Collections.Generic.IList Iterations = context.GetData("Iterations", new string[]
{
   this.Iterations_h.Value.ToString(),
   ТоргуемИнструм"
},
delegate {return this.Iterations_h.Execute(context);});

Метод Cycle_h.GetCount(Iterations[i]) фактически представляет собой выбор минимального значения из двух возможных, то есть из значения, переданного на вход кубика и значения максимального количества итераций, указанного в настройках кубика. Далее это значение присваивается переменной CycleCount и участвует непосредственно в установлении количества итераций сформированного далее цикла.

Цикл сформирован конструкцией «For Next Step», описанной выше. На скрине синей рамкой обведен сам цикл, зеленой рамкой — тело цикла. Видим, что счетчик итераций j начинается с нуля и будет увеличиваться на каждой итерации на 1, максимальное значение будет равно CycleCount-1 (в данном случае 4).

Итого получим 5 итераций на каждой свече инструмента, то есть j будет увеличиваться от 0 до 4: 0, 1, 2, 3, 4.

У кубика как и большинства других кубиков снизу имеется контрольный выход, который может быть соединен с контрольной панелью для управления параметром «кол-во итераций». В информационных целях, думаю, что не лишним будет упомянуть, что не только контрольный выход с кубика может быть соединен с контрольной панелью, но и основной выход, в этом случае на контрольную панель будет выведено последнее известное значение данного кубика на последнем баре скрипта. Это значение отображается в контрольной панели как поле, которое нельзя изменить (предназначено только для чтения).
Пример-Цикла2

Теперь рассмотрим верхний выход кубика. Согласно документации выход во-первых называется «служебный соединитель», а во-вторых имеет конкретное предназначение — соединиться с блоками открытия позиций или обновляемыми значениями (простое и обновляемое значение цикла) для целей определения входа в цикл. Пока не очень понятно что это значит, посмотрим генерируемый код.

Используем известный всем кубик «Обновляемое значение», т.к. функционал «обновляемого значения цикла» пока не совсем понятен, разберем его позже.

Для исследования подключения кубика «Обновляемое значение» (ОЗ) создадим в редакторе следующую конструкцию (для упрощения на вход «ОЗ» подадим значение количества итераций, а в качестве условия обновления укажем True с логической константы; в данном случае нам не важны поступающие на «ОЗ» значения, важен принцип работы данной конструкции).
Пример-цикла3

Получим сгенерированный код, который расскажет для чего необходимо соединить «служебный соединитель» цикла с таким же выходом «ОЗ». Фактически по коду мы видим, что все, что в данной конструкции позволило нам соединение — это фактически создать цикл с пустым телом, то есть никакой полезной работы такая конструкция не выполняет — все итерации не будут делать ничего. Пока не совсем понятно для чего это нужно, разбираемся дальше.
cycle_1.5

Чтобы понять дальнейшую логику несколько модернизируем скрипт, а именно прикрутим к циклу кубики «Формула» и «Результат цикла».
Пример-циклы4

Кубик «Формула» и так всем знаком, а по кубику «Результат цикла» стоит дать пояснения. Согласно документации данный кубик считается окончанием цикла и содержит значение тела цикла, то есть результаты вычисления цикла на указанной итерации. Чтобы было понятно о чем речь, к примеру, у нас имеется счетчик, который на первой итерации каждого бара выдает 1, на второй 2, на третьей 3 и т.д. Задавая нужное значение на входе кубика «результат цикла» мы как раз и будем управлять тем значение, которое мы хотим получить. Есть один нюанс — нумерация итераций, как видно выше из конструкции цикла начинается с нуля, а не с единицы, соответственно, чтобы задать нужный номер итерации надо также начинать с нуля, то есть нужна итерация номер 1 — указываем на входе ноль, нужна итерация 2 — указываем на входе 1 и т.д.

кубик-Результат-Цикла
В дополнение кубик имеет настройки «Индекс» и «Использовать последний индекс».
Название настроек говорит само за себя:
«Индекс» — задает номер итерации с которой необходимо получить значение;
настройка «Использовать последний индекс» указывает на необходимость использовать последнюю итерацию, причем настройка «Использовать последний индекс» будет иметь приоритет над «Индекс», то есть, к примеру, если в цикле всего 5 итераций, в настройке «Индекс» задано значение 2, и активирована настройка «Использовать последний индекс», то будет выдано значение с пятой итерации несмотря на то, что в настройке «Индекс» указано значение 2. Происходит это потому что в коде условие «Использовать последний индекс» идет раньше, чем условие, заданное в настройке «Индекс» и если оно равно True, то дальнейшая проверка условия выполняться не будет, то есть будет выдано значение с последней итерации.

Code:

if ((CycleResult_h.UseLastIndex || (CycleResult_h.Index == j)))

Также есть еще один дополнительный нюанс — если на вход управления номеров итерации подключен кубик с номером как в примере ниже (кубик «Константа»), то будет использоваться только условие с этого кубика, условия заданные в кубике «Результат цикла» будут проигнорированы.

Code:

if ((((int)(IterNumber[i])) == j))

Проверим по коду изменилось ли что-либо в части обработки кубика «Обновляемое Значение», ведь он подключен к циклу и его значения фактически используются в расчетах, то есть данные выведены на панель графика и изменяются в формуле, которая, в свою очередь, также выведен на панель.
cycle_1.7

Как видно из кода — ничего не поменялось, то есть код обработки кубика «Обновляемое Значение» располагается после цикла и никак в нем не задействован. Хм.. также не может быть, чтобы возможность подключения есть, но она ни на что не влияет.

Пробуем еще изменить ранее собранную конструкцию — добавим выходящее из кубика «Обновляемое Значение» значение в тело обработки цикла и посмотрим что из этого получится. Тут надо сделать оговорку относительно фразы «ни на что не влияет», как было описано выше (и описано в документации) данный вход используется для обозначения входа в цикл, то есть фактически данное соединение используется для того, чтобы создать саму конструкцию цикла и разместить ее в коде. Но можно ли как-то использовать само значение «ОЗ» в данном коде. «Да» — можно, причем для этого есть 2 варианта:
первый вариант — подать данное значение от «ОЗ» в цепочку связей между циклом и его кубиком «Результат цикла»;
второй вариант — разорвать цепочку и подать значение сразу в результат цикла, а обращение к итерации цикла подать из кубика «Цикл» на вход №2 кубика «Результат цикла», причем сделать это надо через формула, т.к. если подать напрямую, то связь соединится, но редактором будет выдана ошибка о том, что соединять кубик «цикл» с другими кубиками, кроме формул нельзя (Элемент ‘Cycle’ содержит ошибку: Выход обработчика «Цикл» может быть соединён только с формулами). Примеры и сгенерированный код ниже.

Способ №1

Пример-циклы5

У наблюдательного читателя может возникнуть разумный вопрос: что за мутная инструкция UpdatableValue*(Cycle+1)/(Cycle+1) в формуле после цикла. Дело в том, что номер итерации цикла должен быть каким-либо образом использован в последующей формуле, иначе цикл не «заведется» и будет пытаться присвоить значение текущей итерации кубика цикла за пределами самого цикла, от чего редактор будет при запуске скрипта выбрасывать ошибку «CS0103: Имя «j» не существует в текущем контексте.». Но что если нам не нужен номер итерации в наших вычислениях, тогда разумно умножить значение в формуле на номер итерации и тут же разделить, получим значение формулы без изменений, то есть умноженное на 1, однако, т.к. итерации начинаются с нуля, то если просто указать Cycle/Cycle, то получим деление на ноль, т.к. на первой же итерации запись превратится в 0/0, чтобы не получить такой результат прибавляем по единице, в этом случае не первой итерации получим значение (0+1)/(0+1), то есть 1.
cycle_1.9

Способ №2

Пример-циклы6

ИТОГ. С блоком «Цикл» в его самом простом использовании появилась некоторая ясность, в дальнейших постах рассмотрим все остальные блоки. В качестве еще одного нюанса (он тоже описан в документации) необходимо отметить, что если блоки задействованы в обработке цикла, то им присваивается индекс, где номер до разделительной точки равен номеру кубика «Цикл», что говорит о последовательности выполнения собранной конструкции и о фактически задействованных в ней кубиках помимо самого кубика «Цикл».

Скачать конечный вариант скрипта для TSLab можно тут: https://cloud.mail.ru/public/mcUy/a4vgCTYNz


ПРИМЕР ПО ЦИКЛАМ В ТСЛАБ. ЧАСТЬ №2

 
 
 


Вы уже сейчас можете начать изучать Видео курс- роботы в TSLab и научиться самому делать любых роботов!
 
Можно записаться на следующий поток ОнЛайн курса «Создание роботов в TSLab без программирования», информацию по которому можно посмотреть тут->
 
Также можете научиться программировать роботов на нашем Видео курсе «Роботы для QUIK на языке Lua»
 
Если же вам не хочется тратить время на обучение, то вы просто можете выбрать уже готовые роботы из тех, что представлены у нас ДЛЯ TSLab, ДЛЯ QUIK, ДЛЯ MT5, ДЛЯ КРИПТОВАЛЮТЫ!
 
Также можете посмотреть совершенно бесплатные наработки для МТ4, Квика, МТ5. Данный раздел также постоянно пополняется.
 
Не откладывайте свой шанс заработать на бирже уже сегодня!