EMA125Handle=iMA (NULL, PERIOD_D1,125,0,MODE_EMA, PRICE_CLOSE);
При этом запаздывание, конечно, сократится, но количество ложных сигналов увеличится:
Видимо для улучшения данной стратегии, нужно привлекать дополнительные индикаторы.
Попробуем сделать этот же самый индикатор, но не с помощью графических построений, а помещая графические объекты на график символа.
В свойствах индикатора теперь не нужно объявлять буфера данных и цвета индикатора и графические серии для них. Оставим только буфера индикатора для промежуточных расчетов и хэндлы используемых индикаторов:
#property indicator_chart_window
#property indicator_buffers 4
double EMA34HBuffer [];
double EMA34LBuffer [];
double EMA125Buffer [];
double PSARBuffer [];
int EMA34HHandle;
int EMA34LHandle;
int EMA125Handle;
int PSARHandle;
int bars_calculated=0;
В функции OnInit () соответственно оставим только привязку массивов к буферам промежуточных расчетов и получение хэндлов используемых индикаторов:
int OnInit ()
{
// – - indicator buffers mapping
SetIndexBuffer (0,EMA34HBuffer, INDICATOR_CALCULATIONS);
SetIndexBuffer (1,EMA34LBuffer, INDICATOR_CALCULATIONS);
SetIndexBuffer (2,EMA125Buffer, INDICATOR_CALCULATIONS);
SetIndexBuffer (3,PSARBuffer, INDICATOR_CALCULATIONS);
EMA34HHandle=iMA (NULL,0,34,0,MODE_EMA, PRICE_HIGH);
EMA34LHandle=iMA (NULL,0,34,0,MODE_EMA, PRICE_LOW);
EMA125Handle=iMA (NULL,0,125,0,MODE_EMA, PRICE_CLOSE);
PSARHandle=iSAR (NULL,0,0.02, 0.2);
// – —
return (INIT_SUCCEEDED);
}
В функции OnCalculate () определим создание объектов на графике символа:
int OnCalculate (const int rates_total,
const int prev_calculated,
const datetime &time [],
const double &open [],
const double &high [],
const double &low [],
const double &close [],
const long &tick_volume [],
const long &volume [],
const int &spread [])
{
// – —
int values_to_copy;
int start;
int calculated=BarsCalculated (EMA34HHandle);
if (calculated <=0)
{
return (0);
}
if (prev_calculated==0 || calculated!=bars_calculated)
{
start=1;
if (calculated> rates_total) values_to_copy=rates_total;
else values_to_copy=calculated;
}
else
{
start=rates_total-1;
values_to_copy=1;
} if (!FillArrayFromMABuffer (EMA34HBuffer,0,EMA34HHandle, values_to_copy)) return (0); if (!FillArrayFromMABuffer (EMA34LBuffer,0,EMA34LHandle, values_to_copy)) return (0); if (!FillArrayFromMABuffer (EMA125Buffer,0,EMA125Handle, values_to_copy)) return (0);
if (!FillArrayFromPSARBuffer (PSARBuffer, PSARHandle, values_to_copy)) return (0);
for (int i=start; i { if (close [i-1]> open [i-1] &&close [i-1]> EMA34HBuffer [i-1] &&close [i-1]> EMA34LBuffer [i-1] &&low [i-1]> EMA125Buffer [i-1] &&low [i-1]> PSARBuffer [i-1] &&EMA125Buffer [i-1] if (!ObjectCreate (0,«Buy»+i, OBJ_ARROW,0,time [i-1],high [i-1])) { return (false); } ObjectSetInteger (0,«Buy»+i, OBJPROP_COLOR, clrGreen); ObjectSetInteger (0,«Buy»+i, OBJPROP_ARROWCODE,233); ObjectSetInteger (0,«Buy»+i, OBJPROP_WIDTH,2); ObjectSetInteger (0,«Buy»+i, OBJPROP_ANCHOR, ANCHOR_UPPER); ObjectSetInteger (0,«Buy»+i, OBJPROP_HIDDEN, true); ObjectSetString (0,«Buy»+i, OBJPROP_TOOLTIP,»»+close [i-1]); } if (close [i-1] if (!ObjectCreate (0,«Sell»+i, OBJ_ARROW,0,time [i-1],low [i-1])) { return (false); } ObjectSetInteger (0,«Sell»+i, OBJPROP_COLOR, clrRed); ObjectSetInteger (0,«Sell»+i, OBJPROP_ARROWCODE,234); ObjectSetInteger (0,«Sell»+i, OBJPROP_WIDTH,2); ObjectSetInteger (0,«Sell»+i, OBJPROP_ANCHOR, ANCHOR_LOWER); ObjectSetInteger (0,«Sell»+i, OBJPROP_HIDDEN, true); ObjectSetString (0,«Sell»+i, OBJPROP_TOOLTIP,»»+close [i-1]); } } bars_calculated=calculated; // – - return value of prev_calculated for next call return (rates_total); } Здесь функцией ObjectCreate создаются объекты стрелка, привязанные ко времени и максимальной или минимальной цене. Функцией ObjectSetInteger со свойством OBJPROP_COLOR определяется цвет стрелки. Функцией ObjectSetInteger со свойством OBJPROP_ARROWCODE определяется направление стрелки вверх или вниз. Функцией ObjectSetInteger со свойством OBJPROP_WIDTH определяется размер объекта. Функцией ObjectSetInteger со свойством OBJPROP_ANCHOR определяется привязка к цене сверху или снизу по центру. Функцией ObjectSetInteger со свойством OBJPROP_HIDDEN – true определяется отсутствие созданных объектов в списке объектов графика символа. Функцией ObjectSetString со свойством OBJPROP_TOOLTIP определяется содержание всплывающей подсказки при наведении указателя на объект. В функции OnDeinit () уберем все добавленные графические объекты: void OnDeinit (const int reason) { ObjectsDeleteAll (0, -1, -1); } Более подробно о создании объектов на графике символа мы поговорим далее. Графические объекты Как уже было показано ранее, мы можем рисовать на графике символа не только диаграммы индикатора, но и добавлять различные графические объекты с помощью функции ObjectCreate: bool ObjectCreate ( long chart_id, // идентификатор графика string name, // имя объекта ENUM_OBJECT type, // тип объекта int sub_window, // индекс окна datetime time1, // время первой точки привязки double price1, // цена первой точки привязки datetime timeN=0, // время N-ой точки привязки double priceN=0, // цена N-ой точки привязки datetime time30=0, // время 30-й точки привязки double price30=0 // цена 30-точки привязки ); Здесь параметр sub_window это индекс главного окна графика символа со значением 0 или индекс подокна другого индикатора, присоединенного к графику символа. Например, если в предыдущем примере мы изменим код, и присоединим к графику символа, скажем, индикатор ADX, мы увидим следующее: for (int i=start; i { if (close [i-1]> open [i-1] &&close [i-1]> EMA34HBuffer [i-1] &&close [i-1]> EMA34LBuffer [i-1] &&low [i-1]> EMA125Buffer [i-1] &&low [i-1]> PSARBuffer [i-1] &&EMA125Buffer [i-1] if (!ObjectCreate (0,«Buy»+i, OBJ_ARROW,0,time [i-1],high [i-1])) { return (false); } if (!ObjectCreate (0,«Buy1»+i, OBJ_ARROW,1,time [i-1],high [i-1])) { return (false); } ObjectSetInteger (0,«Buy»+i, OBJPROP_COLOR, clrGreen); ObjectSetInteger (0,«Buy»+i, OBJPROP_ARROWCODE,233); ObjectSetInteger (0,«Buy»+i, OBJPROP_WIDTH,2); ObjectSetInteger (0,«Buy»+i, OBJPROP_ANCHOR, ANCHOR_UPPER); ObjectSetInteger (0,«Buy»+i, OBJPROP_HIDDEN, true); ObjectSetString (0,«Buy»+i, OBJPROP_TOOLTIP, close [i-1]); ObjectSetInteger (0,«Buy1»+i, OBJPROP_COLOR, clrGreen); ObjectSetInteger (0,«Buy1»+i, OBJPROP_ARROWCODE,233); ObjectSetInteger (0,«Buy1»+i, OBJPROP_WIDTH,2); ObjectSetInteger (0,«Buy1»+i, OBJPROP_ANCHOR, ANCHOR_UPPER); ObjectSetInteger (0,«Buy1»+i, OBJPROP_HIDDEN, true); ObjectSetString (0,«Buy1»+i, OBJPROP_TOOLTIP, close [i-1]); } if (close [i-1] if (!ObjectCreate (0,«Sell»+i, OBJ_ARROW,0,time [i-1],low [i-1])) { return (false); } if (!ObjectCreate (0,«Sell1»+i, OBJ_ARROW,1,time [i-1],low [i-1])) { return (false); } ObjectSetInteger (0,«Sell»+i, OBJPROP_COLOR, clrRed); ObjectSetInteger (0,«Sell»+i, OBJPROP_ARROWCODE,234); ObjectSetInteger (0,«Sell»+i, OBJPROP_WIDTH,2); ObjectSetInteger (0,«Sell»+i, OBJPROP_ANCHOR, ANCHOR_LOWER); ObjectSetInteger (0,«Sell»+i, OBJPROP_HIDDEN, true); ObjectSetString (0,«Sell»+i, OBJPROP_TOOLTIP, close [i-1]); ObjectSetInteger (0,«Sell1»+i, OBJPROP_COLOR, clrRed); ObjectSetInteger (0,«Sell1»+i, OBJPROP_ARROWCODE,234); ObjectSetInteger (0,«Sell1»+i, OBJPROP_WIDTH,2); ObjectSetInteger (0,«Sell1»+i, OBJPROP_ANCHOR, ANCHOR_LOWER) ObjectSetInteger (0,«Sell1»+i, OBJPROP_HIDDEN, true); ObjectSetString (0,«Sell1»+i, OBJPROP_TOOLTIP, close [i-1]); } } Нумерация подокон идет сверху вниз в порядке отображения. Тип отображаемого объекта задается перечислением ENUM_OBJECT, которое можно посмотреть в справочнике. После добавления графических объектов, не забываем их удалять в функции обратного вызова OnDeinit (), используя функцию ObjectDelete: bool ObjectDelete ( long chart_id, // chart identifier string name // object name ); Или используя функцию ObjectsDeleteAll: int ObjectsDeleteAll ( long chart_id, // chart identifier int sub_window=-1, // window index int type=-1 // object type ); Помимо вышеупомянутых функций ObjectCreate, ObjectDelete и ObjectsDeleteAll, MQL5 предлагает набор функций для работы с графическими объектами: ObjectName, ObjectFind, ObjectGetTimeByValue, ObjectGetValueByTime, ObjectMove, ObjectsTotal, ObjectGetDouble, ObjectGetInteger, ObjectGetString, ObjectSetDouble, ObjectSetInteger, ObjectSetString, TextSetFont, TextOut, TextGetSize. Функции ObjectName, ObjectFind, ObjectGetTimeByValue, ObjectGetValueByTime, ObjectsTotal, ObjectGetDouble, ObjectGetInteger, ObjectGetString, TextGetSize – это функции возвращающие информацию. Функции ObjectSetDouble, ObjectSetInteger, ObjectSetString, TextSetFont – это функции устанавливающие свойства объекта. Функция ObjectMove перемещает объект в окне. Функция TextOut выводит текст в пиксельный массив для отображения объектом OBJ_BITMAP_LABEL или OBJ_BITMAP. После добавления графических объектов рекомендуется принудительно перерисовать график символа с помощью функции ChartRedraw: void ChartRedraw ( long chart_id=0 // идентификатор графика ); Функция ObjectCreate позволяет создавать программным способом те графические объекты, которые вы можете вручную нарисовать на графике символа, пользуясь панелью инструментов клиентского терминала. С помощью функции ObjectSetDouble устанавливаются такие свойства графического объекта, как OBJPROP_PRICE – изменение параметра price функции ObjectCreate, OBJPROP_LEVELVALUE – определение уровней для таких объектов, как инструменты Фиббоначи и Вилы Эндрюса, OBJPROP_SCALE – определение масштаба для таких объектов, как инструменты Ганна и Дуги Фибоначчи, OBJPROP_ANGLE – определение угла объекта, т.е. возможность повернуть объект, который изначально не имеет жесткой привязки, например, повернуть текст, OBJPROP_DEVIATION – определение отклонения для объекта Канал стандартного отклонения. Пример использования OBJPROP_PRICE: int OnCalculate (const int rates_total, const int prev_calculated, const datetime &time [], const double &open [], const double &high [], const double &low [], const double &close [], const long &tick_volume [], const long &volume [], const int &spread []) { // – — ArraySetAsSeries (time, true); ArraySetAsSeries (high, true); ArraySetAsSeries (low, true); ArraySetAsSeries (close, true); ObjectDelete (0,«Price»); if (!ObjectCreate (0,«Price», OBJ_HLINE,0,time [1],close [1])) { return (false); } ObjectSetInteger (0,«Price», OBJPROP_COLOR, clrGreen); ObjectSetInteger (0,«Price», OBJPROP_WIDTH,1); ObjectSetString (0,«Price», OBJPROP_TOOLTIP, close [1]); if (open [1]> close [1]) ObjectSetDouble (0,«Price», OBJPROP_PRICE, low [1]); if (open [1] ObjectSetDouble (0,«Price», OBJPROP_PRICE, high [1]); // – - return value of prev_calculated for next call return (rates_total); } //+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – + void OnDeinit (const int reason) { ObjectsDeleteAll (0, -1, -1); } В этом коде создается горизонтальный уровень, показывающий минимальную или максимальную цену предыдущего бара, в зависимости от того, является ли этот бар бычьим или медвежьим. Пример использования OBJPROP_ANGLE: int OnCalculate (const int rates_total, const int prev_calculated, const datetime &time [], const double &open [], const double &high [], const double &low [], const double &close [], const long &tick_volume [], const long &volume [], const int &spread []) { // – — ArraySetAsSeries (time, true); ArraySetAsSeries (high, true); ArraySetAsSeries (low, true); ArraySetAsSeries (close, true); ObjectDelete (0,«Line»); ObjectDelete (0,«Price»); if (!ObjectCreate (0,«Line», OBJ_VLINE,0,time [1],close [1])) { return (false); } ObjectSetInteger (0,«Line», OBJPROP_COLOR, clrBlue); ObjectSetInteger (0,«Line», OBJPROP_WIDTH,1); ObjectSetString (0,«Line», OBJPROP_TOOLTIP, close [1]); if (!ObjectCreate (0,«Price», OBJ_TEXT,0,time [3],high [1])) { return (false); } ObjectSetString (0,«Price», OBJPROP_TEXT, close [1]); ObjectSetInteger (0,«Price», OBJPROP_COLOR, clrBlack); ObjectSetDouble (0,«Price», OBJPROP_ANGLE,90); ObjectSetString (0,«Price», OBJPROP_TOOLTIP, close [1]); // – - return value of prev_calculated for next call return (rates_total); } //+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – + void OnDeinit (const int reason) { ObjectsDeleteAll (0, -1, -1); } Этот код создает вертикальную линию с подписью цены закрытия предыдущего бара. С помощью функции ObjectSetInteger устанавливаются такие свойства графического объекта, как цвет, стиль, размер и др. С помощью функции ObjectSetString можно изменить имя объекта, при этом объект со старым именем будет удален и будет создан объект с новым именем, установить текст для таких объектов, как текст, кнопка, метка, поле ввода, событие, установить текст всплывающей подсказки для объекта, описание уровня для объектов, имеющих уровни, шрифт, имя BMP-файла для объекта «Графическая метка» и «Рисунок», символ для объекта «График». Функция TextSetFont позволяет установить тип шрифта текста, его размер, стиль и угол наклона для объектов, содержащих текст. Как уже было сказано, функция TextOut позволяет скомбинировать текст и изображение. Например, следующий код выводит текст в изображение, залитое одним цветом: uint ExtImg [10000]; //+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – + //| Custom indicator initialization function | //+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – + int OnInit () { ObjectCreate (0,«Image», OBJ_BITMAP_LABEL,0,0,0); ObjectSetString (0,«Image», OBJPROP_BMPFILE,»:: IMG»); ArrayFill (ExtImg,0,10000,0xffffff); TextOut («Text», 10,10,TA_LEFT|TA_TOP, ExtImg,100,100,0x000000,COLOR_FORMAT_XRGB_NOALPHA); ResourceCreate (»:: IMG», ExtImg,100,100,0,0,0,COLOR_FORMAT_XRGB_NOALPHA); ChartRedraw (); // – — return (INIT_SUCCEEDED); } Здесь ExtImg это пиксельный массив, представляющий изображение 100х100 пикселей. Функция ObjectCreate создает объект «Графическая метка», а функция ObjectSetString устанавливает для этого объекта файл изображения с именем::IMG. По поводу знака «::» справочник говорит следующее: Для использования своего ресурса в коде нужно перед именем ресурса добавлять специальный признак "::». Функция ArrayFill заполняет пиксельный массив пикселями белого цвета. Функция TextOut выводит в пиксельный массив слово «Text». Функция ResourceCreate создает из пиксельного массива ресурс с именем::IMG. В итоге на белом фоне отображается надпись «Text». Также можно вывести текст на готовое изображение: #resource "\\Images\\image.bmp» uint ExtImg [10000]; //+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – + //| Custom indicator initialization function | //+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – + int OnInit () { ObjectCreate (0,«Image», OBJ_BITMAP_LABEL,0,0,0); ObjectSetString (0,«Image», OBJPROP_BMPFILE,»:: IMG»); uint width=100; uint height=100; ResourceReadImage("::Images\\image.bmp», ExtImg, width, height); TextOut («Text», 10,10,TA_LEFT|TA_TOP, ExtImg,100,100,0xffffff, COLOR_FORMAT_XRGB_NOALPHA); ResourceCreate (»:: IMG», ExtImg,100,100,0,0,0,COLOR_FORMAT_XRGB_NOALPHA); ChartRedraw (); // – — return (INIT_SUCCEEDED); } Здесь функция ResourceReadImage считывает существующее изображение из папки Images окна Navigator редактора MQL5 в пиксельный массив::IMG, связанный с объектом «Графическая метка», а функция TextOut выводит в пиксельный массив слово «Text». То же самое можно проделать и с объектом «Рисунок»: #resource "\\Images\\image.bmp» uint ExtImg [10000]; //+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – + //| Custom indicator initialization function | //+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – + int OnInit () { // – — return (INIT_SUCCEEDED); } //+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – + //| Custom indicator iteration function | //+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – + int OnCalculate (const int rates_total, const int prev_calculated, const datetime &time [], const double &open [], const double &high [], const double &low [], const double &close [], const long &tick_volume [], const long &volume [], const int &spread []) { // – — ArraySetAsSeries (time, true); ArraySetAsSeries (high, true); ArraySetAsSeries (low, true); ArraySetAsSeries (close, true); ObjectDelete (0,«Image»); ObjectCreate (0,«Image», OBJ_BITMAP,0,time [1],close [1]); ObjectSetString (0,«Image», OBJPROP_BMPFILE,»:: IMG»); uint width=100; uint height=100; ResourceReadImage("::Images\\image.bmp», ExtImg, width, height); TextOut («Text», 10,10,TA_LEFT|TA_TOP, ExtImg,100,100,0xffffff, COLOR_FORMAT_XRGB_NOALPHA); ResourceCreate (»:: IMG», ExtImg,100,100,0,0,0,COLOR_FORMAT_XRGB_NOALPHA); ChartRedraw (); // – - return value of prev_calculated for next call return (rates_total); } //+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – + void OnDeinit (const int reason) { ObjectsDeleteAll (0, -1, -1); } В качестве примера использования графических объектов, рассмотрим создание индикатора, который выводит в небольшое окно на графике символа тот же график, но с другим временным периодом. Для этого используем графический объект OBJ_CHART. В качестве входных параметров индикатора используем символ графика и его период: #property indicator_chart_window input string InpSymbol=«EURUSD»; // Символ input ENUM_TIMEFRAMES InpPeriod=PERIOD_CURRENT; // Период В функции OnInit () создадим графический объект График: int OnInit () { if (!ObjectCreate (0,«Chart», OBJ_CHART,0,0,0)) { return (false); } По умолчанию точка привязки этого объекта – левый верхний угол графика. Определим отступ точки привязки объекта, его размеры, символ и период графика, отображение шкалы времени, размер точки привязки, с помощью которой можно перемещать объект, отображение ценовой шкалы, режим перемещения мышкой, цвет рамки графика: ObjectSetInteger (0,«Chart», OBJPROP_XDISTANCE,10); ObjectSetInteger (0,«Chart», OBJPROP_YDISTANCE,20); ObjectSetInteger (0,«Chart», OBJPROP_XSIZE,300); ObjectSetInteger (0,«Chart», OBJPROP_YSIZE,200); ObjectSetString (0,«Chart», OBJPROP_SYMBOL, InpSymbol); ObjectSetInteger (0,«Chart», OBJPROP_PERIOD, InpPeriod); ObjectSetInteger (0,«Chart», OBJPROP_DATE_SCALE, true); ObjectSetInteger (0,«Chart», OBJPROP_WIDTH,1); ObjectSetInteger (0,«Chart», OBJPROP_PRICE_SCALE, true); ObjectSetInteger (0,«Chart», OBJPROP_SELECTABLE, true); ObjectSetInteger (0,«Chart», OBJPROP_SELECTED, true); ObjectSetInteger (0,«Chart», OBJPROP_COLOR, clrBlue); С помощью свойства объектов OBJPROP_CHART_ID функции ObjectGetInteger получим идентификатор графика, используя который мы теперь можем применять функции работы с графиками (https://www.mql5.com/ru/docs/chart_operations) и свойства графиков (https://www.mql5.com/ru/docs/constants/chartconstants/enum_chart_property): long chartId=ObjectGetInteger (0,«Chart», OBJPROP_CHART_ID); Откроем наш график символа, к которому мы хотим присоединить индикатор, и нажав правой кнопкой мышки, выберем пункт в контекстном меню Шаблоны и Сохранить шаблон. Теперь мы можем перенести на наш графический объект все настройки и индикаторы графика символа: ChartApplyTemplate(chartId,"my.tpl»); ChartRedraw (chartId); // – — return (INIT_SUCCEEDED); } Присоединив индикатор к графику символа, мы можем нажать на нем правой кнопкой мышки и изменить его свойства, включая его период, размеры и др. Функция PlaySound Функция PlaySound воспроизводит звуковой файл. Например, это можно делать при появлении сигнала индикатора для напоминания: bool PlaySound ( string filename // имя WAV-файла ); В качестве примера добавим звуковой сигнал в наш индикатор Impulse keeper при появлении первого сигнала на покупку или продажу. Скачаем какой-нибудь WAV-сигнал из Интернета и поместим его файл в папку Sounds терминала. Добавим код в индикатор Impulse keeper: #property indicator_chart_window #property indicator_buffers 4 double EMA34HBuffer []; double EMA34LBuffer []; double EMA125Buffer []; double PSARBuffer []; int EMA34HHandle; int EMA34LHandle; int EMA125Handle; int PSARHandle; int bars_calculated=0; int countBuy=0 int countSell=0; //+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – + //| Custom indicator initialization function | //+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – + int OnInit () { // – - indicator buffers mapping SetIndexBuffer (0,EMA34HBuffer, INDICATOR_CALCULATIONS); SetIndexBuffer (1,EMA34LBuffer, INDICATOR_CALCULATIONS); SetIndexBuffer (2,EMA125Buffer, INDICATOR_CALCULATIONS); SetIndexBuffer (3,PSARBuffer, INDICATOR_CALCULATIONS); EMA34HHandle=iMA (NULL,0,34,0,MODE_EMA, PRICE_HIGH); EMA34LHandle=iMA (NULL,0,34,0,MODE_EMA, PRICE_LOW); EMA125Handle=iMA (NULL,0,125,0,MODE_EMA, PRICE_CLOSE); PSARHandle=iSAR (NULL,0,0.02, 0.2); // – — return (INIT_SUCCEEDED); } //+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – + //| Custom indicator iteration function | //+ – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – + int OnCalculate (const int rates_total,