Как работает AFL.

Введение

Одним из самых важных аспектов языка AFL является то, что он является языком обработки и вычислений массивов (array processing language). Он оперирует с массивами (векторами) данных. Этот способ вычислений очень похож на то, как работают популярные пакеты электронных таблиц (типа Microsoft Excel). Каждый, кто знаком с MS Excel, не должен испытать трудностей с пониманием AFL. Фактически все примеры в этой статье были подготовлены с использованием MS Excel.

Что такое массив (array)?

Массив это просто список (или строка) значений. В некоторых книжках он может называться вектором. Каждая пронумерованная строка в последующих примерах представляет собой отдельный массив. Для каждого символа Амиброкер сохраняет в своей базе данных 6 массивов. Один для цены открытия, один для цены лоу, один для цены хай, один для цены закрытия и один для объёма (см. строки с номерами 1-5 в таблицах далее по тексту) и один для открытого интереса. К ним можно обратиться в AFL по именам: open, low, high, close, volume, openint или O, L, H, C, V, OI.

Bar 1 2 3 4 5 6 7 8 9 10
1 Open 1.23 1.24 1.21 1.26 1.24 1.29 1.33 1.32 1.35 1.31
Массив цен открытия.

Любые другие массивы вычисляются из этих шести массивов с использованием встроенных функций AFL. Эти массивы не хранятся в базе данных, а вычисляются, когда они необходимы.

Каждое индивидуальное значение в массиве имеет ассоциированную с ним дату. Если у вас включена опция показывать подсказки (Preferences -> Miscellaneous Tab - > Price data tool tips), то когда вы перемещаете курсор над свечками дневного графика, появляется небольшой жёлтенький прямоугольник. AFL берёт значения open, low, high, close, volume из соответствующих массивов и показывает их в подсказке.

Обработка массивов – почему в AFL так быстро?

Давайте рассмотрим, как производится обработка следующего выражения:

MyVariable = ( High + Low )/2;

Когда AFL вычисляет такой оператор как ( High + Low )/2 он не повторяет этот код для каждого бара. Вместо этого он берёт МАССИВ High и МАССИВ Low и суммирует соответственные элементы массивов за один этап. Другими словами оператор «+» (и другие операторы тоже) выполняется над целыми массивами сразу и производится на скорости исполнения полностью откомпилированного кода, затем результирующий массив (каждый его элемент) делится на 2 также за один раз.

Посмотрим всё это в деталях. Когда движок AFL разбирает выражение ( High + Low )/2 он сначала берёт массивы High (1) и Low (2) и производит (за один откомпилированный шаг) временный массив (3). Затем он создаёт финальный массив (4) деля значение каждого элемента временного массива (3) на два. Этот результат присваивается переменной myVariable

Bar 1 2 3 4 5 6 7 8 9 10
1 High (исходный массив) 1.24 1.27 1.25 1.29 1.25 1.29 1.35 1.35 1.37 1.29
2 Low (исходный массив) 1.20 1.21 1.19 1.20 1.21 1.24 1.30 1.28 1.31 1.27
3 High+Low (временный массив
создаваемый в процессе вычислений)
2.44 2.48 2.44 2.49 2.46 2.53 2.65 2.63 2.68 2.46
4 ( High+Low ) /2
(назначается в MyVariable)
1.22 1.24 1.22 1.245 1.23 1.265 1.325 1.315 1.34 1.23
Шаги AFL при вычислении ( High + Low ) /2.

Скользящие средние, условные выражения

Теперь рассмотрим следующий код:

Cond1 = Close > MA( Close, 3 );
Cond2 = Volume > Ref( Volume, -1 );
Buy = Cond1 AND Cond2;
Sell = High > 1.30;

Этот код генерирует сигнал на покупку когда сегодняшние закрытие происходит выше чем 3-х дневное скользящее среднее и сегодняшний объём превосходит вчерашний. Он также генерирует сигнал на продажу, когда цена хай дня пробьёт 1.30.

Когда в своём коде вам скажем нужно посмотреть не превзошла ли цена закрытия например 3-х дневную простую скользящую среднюю, AFL сначала пробегает по массиву close создавая новый массив который можно условно назвать как MA(close,3) для того символа который анализируется. Каждая ячейка нового массива затем может быть сравнена одна к одной с соответственной ячейкой массива close. В нашем примере таким образом создаётся массив называемый Cond1. Для каждой ячейки, где цена закрытия выше чем значение в соответствующей ячейке MA(close,3), значение элемента в новом массиве 'Cond1' устанавливается в '1'. В противном случае значение в 'Cond1' устанавливается в '0'.

AFL также может заглядывать на заданное число ячеек массива вперёд или назад используя функцию Ref (см. строчку 6 в таблице далее по тексту в которой показан созданный временный массив содержащий значения объёма за предыдущий день)

В строке 9 показан массив Cond2 созданный как результат сравнения значения каждого элемента массива volume с предыдущим ему элементом массива, которое устанавливает в ячейку массива Cond2 значение '1' (истина) или '0' (ложь).

Строка 10 показывает массив 'Buy' создающийся как результат сравнения значений ячеек Cond1 с ячейками массива Cond2. Если элемент массива Cond1 равен '1' и соответствующий ему элемент из массива Cond2 также равен '1', то в ячейку массива 'Buy' устанавливается значение '1'.

Строка 11 показывает массив 'Sell' создаваемый во всех случаях когда значение в ячейке массива close превосходит $1.30.

День 1 2 3 4 5 6 7 8 9 10
1 Open 1.23 1.24 1.21 1.26 1.24 1.29 1.33 1.32 1.35 1.37
2 High 1.24 1.27 1.25 1.29 1.25 1.29 1.35 1.35 1.37 1.29
3 Low 2.44 2.48 2.44 2.49 2.46 2.53 2.65 2.63 2.68 2.46
4 Close 1.23 1.26 1.24 1.28 1.25 1.25 1.31 1.30 1.32 1.28
5 Volume 8310 3021 5325 2834 1432 5666 7847 555 6749 3456
6 Ref( Volume, -1 ) (временный массив
создаваемый в процессе вычислений)
Null 8310 3021 5325 2834 1432 5666 7847 555 6749
7 MA( Close, 3 ) (временный массив
создаваемый в процессе вычислений)
Null Null 1.243 1.260 1.357 1.260 1.270 1.287 1.310 1.300
8 Cond1 = Close < MA(close, 3) ( 1 (или True) если
выражение выполняется в противном случае ноль )
Null Null 1 0 1 1 0 0 0 1
9 Cond2 = Volume > Ref(volume, -1) Null 0 1 0 0 1 1 0 1 0
10 Buy = Cond1 AND Cond2 Null Null 1 0 0 1 0 0 0 0
11 Sell = High > 1.30 0 0 0 0 0 0 1 1 1 0

Buy и Sell являются специальными массивами чьи результирующие значения могут быть отображены в окне Analyser или в виде красных или зелёных отметок на экране при необходимости.

Чуть посложнее

Приведённые выше примеры очень простые. Здесь будет дано объяснение следующим трём вещам которые по-видимому порождают некоторую путаницу у пользователей:

Как описано в обучающем руководстве по базовым графическим средствам (Tutorial: Basic charting guide), вы можете выбрать и отметить любую котировку на графике и выделить временной интервал графика (From-To range). Бар, отмеченный вертикальной линией, называется “выбранным” баром ("selected" bar), также первый и последний бары интервала называются “начальным” и ”конечным” барами ("begin" and "end" bars). В AFL есть специальные функции, которые позволяют обращаться к значениям массива на выбранном, начальном и конечном баре соответственно. Это функции SelectedValue, BeginValue и EndValue. Также есть ещё одна функция – LastValue, которая позволяет получить значение массива на самом последнем баре графика. Эти четыре функции берут из массива элемент на заданном баре и возвращают ЕДИНСТВЕННОЕ ЧИСЛО представляющее собой значение массива в данной точке. Это позволяет вычислять различную статистику относительно выбранных точек графика. Например:

EndValue( Close ) - BeginValue( Close )
даст вам величину долларового изменения цены закрытия в выбранном временном промежутке.

Когда число, полученное с помощью одной из этих функций, сравнивается с массивом или выполняется какая-либо другая арифметическая операция, в которой участвует число и массив, она осуществляется так, как если бы число распространялось на все элементы массива. Это иллюстрируется в приведённой ниже таблице (строки 2, 6, 7). Зелёным цветом отмечен “начальный” бар, а красным цветом - “конечный” бар. “Выбранный” бар закрашен синим.

День 1 2 3 4 5 6 7 8 9 10
1 Open 1.23 1.24 1.21 1.26 1.24 1.29 1.33 1.32 1.35 1.37
2 BeginValue( Open ) 1.24 1.24 1.24 1.24 1.24 1.24 1.24 1.24 1.24 1.24
3 EndValue( Open ) 1.32 1.32 1.32 1.32 1.32 1.32 1.32 1.32 1.32 1.32
4 SelectedValue( Open ) 1.21 1.21 1.21 1.21 1.21 1.21 1.21 1.21 1.21 1.21
5 LastValue( Open ) 1,37 1,37 1,37 1,37 1,37 1,37 1,37 1,37 1,37 1,37
6 Close 1,22 1,26 1,23 1,28 1,25 1,25 1,31 1,30 1,32 1,28
7 Close <= BeginValue( Open ) 1 0 1 0 0 0 0 0 0 0
8 result = IIF( Close <= BeginValue( Open ), Close, Open ); 1,22 1,24 1,23 1,26 1,24 1,29 1,33 1,32 1,35 1,37
9 Period 2 3 4 2 3 5 2 3 4 2
10 Factor = 2/(Period+1) 0,667 0,500 0,400 0,667 0,500 0,333 0,667 0,500 0,600 0,667
11 1 – Factor 0,333 0,500 0,600 0,333 0,500 0,667 0,333 0,500 0,600 0,333
12 AMA( Close, Factor ) 0,8125 1,0363 1,1138 1,2234 1,2367 1,2399 1,2853 1,2927 1,3036 1,2866

Теперь по функции

IIF(condition, truepart, falsepart)
Она работает таким образом, что возвращает значение второго (truepart) или третьего (falsepart) аргумента в зависимости от условия (condition). Как вы можете видеть в вышеприведённой таблице, значения в строку 8 поступают из массива Close (truepart) для баров, когда условие выполняется (1), и приходит из массива Open (falsepart) для всех остальных баров. Таким образом, массив возвращаемый функцией IIF составляется как из значений элементов из массива Close, так и из значений некоторых элементов из массива Open. Следует отметить что оба аргумента как truepart так и falsepart являются массивами и они вычисляются независимо от условия, не обращая на него внимание (так что всё это не представляет собой обыкновенный IF-THEN-ELSE оператор, а является функцией возвращающей массив).

Функция

AMA(array, factor)
похоже, вызывает наибольшие проблемы с пониманием. Но в действительности она очень проста. Она работает рекурсивным методом. Это означает, что она использует предыдущие свои значения для вычисления текущего результата. Она обрабатывает массив бар за баром, на каждом шаге она умножает текущую ячейку массива из первого аргумента (array) на текущую ячейку второго аргумента (factor) и прибавляет к этому предыдущее значение AMA умноженное на (1-Factor). Давайте рассмотрим колонку 3. Значение AMA в колонке 3 получено как умножение цены закрытия из колонки 3 (1,23) на множитель Factor (0,4). Затем мы прибавляем предыдущее значение AMA (1,0363) умноженное на (1-Factor = 0,6). Результат (округлённый до 4 знаков после запятой) получается 1,23 * 0,4 + 1,0363 * 0,6 = 1,1138.

Если вы посмотрите строку 12 этой таблицы, вы можете отметить, что эти значения выглядят как скользящее среднее массива close. Это так и есть. Фактически мы описали, как вычисляется функция AMA экспоненциального скользящего среднего с переменным периодом.

Циклы

С версией Амиброкера 4.40 появилась возможность организовывать циклы для итерации массивов с помощью операторов for и while и добавлен оператор ветвления if-else. Эти усовершенствования дали возможность работать обоими способами: как с использованием вычислений над массивами (описанными выше) для быстрых и простых расчётов, так и используя циклы для того чтобы делать сложную обработку. Следующий код приведён в качестве примера, как реализовать экспоненциальное скользящее усреднение с переменным периодом (описанное выше) с использованием циклов:

Period = ... некие вычисления

vaexp[ 0 ] = Close[ 0 ]; // инициализация первого значения

for( i = 1; i < BarCount; i++ )
{
    // вычисление значения параметра сглаживания
    Factor = 2/(Period[ i ] + 1 );

    // вычисление значения i-го элемента массива
    // используя close на текущем баре ( close[ i ] )
    // и предыдущее значение вычисляемого среднего ( vaexp[ i - 1 ] )

    vaexp[ i ] = Factor * Close[ i ] + ( 1 - Factor ) * vaexp[ i - 1 ];
}

Как вы можете видеть, код получается длиннее, но с другой стороны он очень похож на код любого другого языка программирования как C/Pascal/Basic. Так что пользователи с некоторым опытом в программировании могут легко суметь его понять.


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


Если вы испытываете затруднения с программированием на AFL, то сначала можно создать массивы в виде примера в таблице Excel. Если это представляет проблему, обратитесь за помощью к друзьям, особенно если они бухгалтеры. (Только прежде спросите их, не заняты ли они сдачей фондов или квартальной отчетности.)


Когда вы освоитесь и набьёте руку, вы сможете запрограммировать любую систему из книжек по трейдингу или написать свою.


--- Special thanks to Geoff Mulhall for original article in the newsletter that was the basis of this tutorial ---

--- Перевод michaelus ---


Удачи.