Разработка пользовательского канала Дончиана с помощью MQL5
Введение
В этой статье мы поговорим об очень важной части торговли - идентификации тренда, а также узнаем о техническом инструменте, который может быть полезен в этом деле, а именно - о канала Дончиана (Дончяна). Это индикатор, следующий за трендом.
Все эти вопросы будут рассмотрены в следующих разделах:
Определение канала Дончиана
В этом разделе мы дадим определение каналу Дончиана и рассмотрим принципы его применения. Канал Дончиана был разработан трейдером Ричардом Дончианом. Его основная цель - обнаружить тренд. Это индикатор, следующий за трендом и запаздывающий, поскольку он следует направлениям тренда и движениям цен. Он состоит из трех линий, образующих канал, в котором находится цена. Верхняя линия канала представляет собой самую высокую цену, зафиксированную в течение определенного времени, нижняя линия канала представляет собой самую низкую цену за определенный период времени, а средняя линия представляет собой половину расстояния между верхней и нижней линиями.
Ниже представлен пример канала:
На графике мы видим линию над ценами, еще одну под ценами и третью между ними. Индикатор содержит или окружает цены верхней и нижней линиями в дополнение к еще одной средней линии, которая также может быть полезна. Индикатор может быть основой для разных стратегий поиска тренда, в частности для поиска прорывов и линий поддержки/сопротивления.
Основной принцип использования индикатора заключается в наблюдении максимумов и минимумов цен в течение определенного времени, чтобы определить тренды или преобладающее направление. Если цена находится выше самого высокого максимума определенного времени, это может быть сигналом на покупку. Если цена находится ниже самого низкого минимума определенного времени, это может быть сигналом на продажу. Итак, после указания определенного периода времени и определения ценовых максимумов и минимумов, мы наблюдаем за ценой до тех пор, пока цены не начнут двигаться в определенном направлении вверх или вниз, и сигналом здесь является пробитие предопределенного максимума или минимума.
Индикатор можно использовать для определения уровней стоп-лосса и тейк-профита. Это очень важно в торговле, поскольку позволяет снизить вероятность ошибок в установке уровней при использовании правильных параметров. Например, минимум канала можно использовать в качестве уровня стоп-лосса для позиции на покупку или уровня тейк-профита для позиции на продажу. Максимум канала можно использовать в качестве стоп-лосса для позиции на продажу или тейк-профита для позиции на покупку.
Рассмотрим способ расчета индикатора:
- Верхняя линия (CH)= Самый высокий максимум последних N периодов
- Нижняя линия (CL)= Самый низкий минимум последних N периодов
- Средняя линия (ML)= (CH+CL)/2
Итак, нам нужно определить желаемый период времени, в течение которого нам необходимо определить направление, обнаружить ценовые максимум и минимум, построить линии для наблюдения и получить среднюю линию, рассчитав половину расстояния между ценовыми экстремумами. В то время как канал Дончиана отображает самый высокий максимум и самый низкий минимум в течение определенного периода времени, полосы Боллинджера отображают среднее значение за период времени после сложения и вычитания двух стандартных отклонений. Чтобы подробнее узнать о полосах Боллинджера и о создании торговой системы на их основе, прочтите одну из моих предыдущих статей.
Канал рекомендуется использовать совместно с другими техническими индикаторами для получения полноценной картины рынка и лучших результатов.
Пользовательский канал Дончиана
В этом разделе мы рассмотрим метод, который можно использовать для создания собственного канала Дончиана с помощью MQL5. Создадим индикатор с верхней, нижней и средней линиями.
Создадим дополнительные параметры со следующими значениями идентификатора:
- indicator_chart_window - отображать индикатор в окне графика.
- indicator_buffers - количество буферов расчета индикатора. Используемое значение (3).
- indicator_plots - количество графических серий в индикаторе. Используемое значение (3).
#property indicator_chart_window #property indicator_buffers 3 #property indicator_plots 3
Создать два входных параметра: один для периода, а другой для цвета линий индикатора, как показано ниже:
- Создадим целочисленную переменную (indPeriod) и установим 20 в качестве значения по умолчанию. Вы можете использовать другое значение.
- Создадим переменную цвета (indColor) и установим синий цвет в качестве значения по умолчанию. Вы также можете использовать другое значение.
input int indPeriod=20; //Period input color indColor=clrBlue; //Color
Создайте глобальные переменные, как показано ниже:
- массивы типа double - upperBuff, lowerBuff, middleBuff
- переменные типа double - upperLine, lowerLine, middleLine
- целочисленный тип переменных - start и bar
double upperBuff[]; double lowerBuff[]; double middleBuff[]; double upperLine,lowerLine,middleLine; int start, bar;
Создайте пользовательскую функцию индикатора, используя void, чтобы ничего не возвращать, и создайте переменную indInit с тремя параметрами (индекс, буфер в виде динамического массива и метка в виде строки для каждой строки индикатора). В теле функции сделаем следующее:
- Используем функции SetIndexBuffer, которая связывает указанный индикатор с одномерным динамическим массивом. Его параметры:
- index - указать номер индикаторного буфера. Мы будем использовать индексную переменную.
- buffer[] - определить созданный динамический массив buffer[].
- data_type - определить значения по умолчанию, которые нам нужно сохранить (INDICATOR_DATA).
- Используем функцию PlotIndexSetInteger пять раз с разными параметрами prop-id и prop_value, как мы увидим в коде. Функция устанавливает значение соответствующей линии индикатора. Свойство индикатора должно быть целым числом. Параметры:
- plot_index - определить индекс графического построения. Мы будем использовать индексную переменную.
- prop_id - определить значение идентификатора свойства, которое может быть одним из ENUM_PLOT_PROPERT_INTEGER.
- prop_value - определить значение определенного свойства в prop_id.
- Используем функцию PlotIndexSetString, которая устанавливает значение соответствующего индикатора свойства строки. Ее параметры такие же, как у функции PlotIndexSetInteger, но свойство индикатора здесь должно быть строкой.
- Используем функцию PlotIndexSetDouble для установки значения соответствующего индикатора свойства double. Параметры те же, но свойство индикатора должно быть типа double.
void indInit(int index, double &buffer[],string label) { SetIndexBuffer(index,buffer,INDICATOR_DATA); PlotIndexSetInteger(index,PLOT_DRAW_TYPE,DRAW_LINE); PlotIndexSetInteger(index,PLOT_LINE_WIDTH,2); PlotIndexSetInteger(index,PLOT_DRAW_BEGIN,indPeriod-1); PlotIndexSetInteger(index,PLOT_SHIFT,1); PlotIndexSetInteger(index,PLOT_LINE_COLOR,indColor); PlotIndexSetString(index,PLOT_LABEL,label); PlotIndexSetDouble(index,PLOT_EMPTY_VALUE,EMPTY_VALUE); }
После этого в теле OnInit() мы трижды используем нашу пользовательскую функцию индикатора для трех строк индикатора, и она будет выглядеть так:
indInit(0,upperBuff,"Donchian Channel"); indInit(1,lowerBuff,"Donchian Channel"); indInit(2,middleBuff,"Middle Donchian");
Используем функцию IndicatorSetString для установки текстовой метки индикатора.
IndicatorSetString(INDICATOR_SHORTNAME,"Donchian ("+IntegerToString(indPeriod)+")");
В части OnCalculate мы выполним следующие шаги для расчета индикатора:
Проверяем, меньше ли times_total, чем введенный пользователем период +1, если да, то нам нужно, чтобы программа возвращала ноль.
if(rates_total<indPeriod+1) { return 0; }
Присвойте значение переменной start с помощью тернарного оператора ?: - если start=prev_calculated==0 равен true, оператор будет установлен indPeriod. При false оператор будет установлен prev_calculated-1.
start=prev_calculated==0? indPeriod: prev_calculated-1;
Используем функцию for для создания цикла расчета индикатора, выражение 1 будет (bar=start), выражение 2 будет (bar < rate_total), а выражение 3 будет (bar ++) для приращения бара на единицу. Оператор цикла for будет таким же, как следующий:
- Вычисление upperLine путем определения максимального значения high с помощью функции ArrayMaximum, которая ищет наибольшее значение в массиве.
- Вычисление lowerLine путем определения минимального значения low с помощью функции ArrayMinimum, которая ищет наименьшее значение в массиве.
- Вычисление middleLine путем деления суммы upperLine и lowerLine на 2
- Присвоение значений upperBuff[bar], lowerBuff[bar] и middleBuff[bar}
for(bar=start;bar<rates_total;bar++) { upperLine=high[ArrayMaximum(high,bar-indPeriod+1,indPeriod)]; lowerLine=low[ArrayMinimum(low,bar-indPeriod+1,indPeriod)]; middleLine=(upperLine+lowerLine)/2; upperBuff[bar]=upperLine-(upperLine-lowerLine); lowerBuff[bar]=lowerLine+(upperLine-lowerLine); middleBuff[bar]=middleLine; }
Полный код выглядит так:
#property indicator_chart_window #property indicator_buffers 3 #property indicator_plots 3 input int indPeriod=20; //Period input color indColor=clrBlue; //Color double upperBuff[]; double lowerBuff[]; double middleBuff[]; double upperLine,lowerLine,middleLine; int start, bar; void indInit(int index, double &buffer[],string label) { SetIndexBuffer(index,buffer,INDICATOR_DATA); PlotIndexSetInteger(index,PLOT_DRAW_TYPE,DRAW_LINE); PlotIndexSetInteger(index,PLOT_LINE_WIDTH,2); PlotIndexSetInteger(index,PLOT_DRAW_BEGIN,indPeriod-1); PlotIndexSetInteger(index,PLOT_SHIFT,1); PlotIndexSetInteger(index,PLOT_LINE_COLOR,indColor); PlotIndexSetString(index,PLOT_LABEL,label); PlotIndexSetDouble(index,PLOT_EMPTY_VALUE,EMPTY_VALUE); } int OnInit() { indInit(0,upperBuff,"Donchian Channel"); indInit(1,lowerBuff,"Donchian Channel"); indInit(2,middleBuff,"Middle Donchian"); IndicatorSetString(INDICATOR_SHORTNAME,"Donchian ("+IntegerToString(indPeriod)+")"); return(INIT_SUCCEEDED); } 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[]) { if(rates_total<indPeriod+1) { return 0; } start=prev_calculated==0? indPeriod: prev_calculated-1; for(bar=start;bar<rates_total;bar++) { upperLine=high[ArrayMaximum(high,bar-indPeriod+1,indPeriod)]; lowerLine=low[ArrayMinimum(low,bar-indPeriod+1,indPeriod)]; middleLine=(upperLine+lowerLine)/2; upperBuff[bar]=upperLine-(upperLine-lowerLine); lowerBuff[bar]=lowerLine+(upperLine-lowerLine); middleBuff[bar]=middleLine; } return(rates_total); }
Код должен быть скомпилирован без ошибок и предупреждений, тогда мы найдем индикатор в окне "Навигатор" торгового терминала в папке "Индикаторы" и, выполнив его, откроем окно параметров и введем следующее:
Как мы видим, у нас есть два входных параметра:
- Period - определить продолжительность времени для расчета индикатора. Значение по умолчанию - 20.
- Color - определить цвет линий индикатора. Значение по умолчанию - синий.
После настройки параметров и нажатия ОК индикатор прикрепляется к графику:
Как видим, всё выглядит как надо.
Советник Donchian Channel
Используем пользовательский канал Дончиана в торговой системе, создав советник для генерации сигналов на основе движения или поведения индикатора. Мы можем сделать это, используя два разных метода: первый — написать код индикатора в советнике, второй — использовать функцию iCustom для прикрепления созданного индикатора к советнику. Здесь мы разработаем очень простые системы, чтобы понять идею и то, как мы можем улучшить эти системы на основе второго метода, рассмотренного при создании советника.
Советник Donchian Channel Simple
Создадим первую систему, которая отображает значения индикатора (верхний, средний и нижний каналы) на графике. Программа будет постоянно отслеживать эти значения и выводить их на график в качестве комментария.
Ниже приведены шаги для создания советника:
Создадим входную переменную периода индикатора со значением по умолчанию (20). Пользователь может обновить значение из входных параметров советника.
input int indPeriod=20; //Period
Создайте целочисленную глобальную переменную donChianChannel.
int donchianChannel;
В части OnInit() обновим donchianChannel, назначив ему функцию iCustom, которая возвращает дескриптор созданного пользовательского индикатора. Параметры такие:
- symbol — имя символа; у нас это _Symbol, то есть рассчитываем индикатор по символу текущего графика.
- period - таймфрейм для расчета; значение PERIOD_CURRENT означает, что индикатор будет рассчитываться на текущем таймфрейме
- name - указать строковое имя индикатора и путь к нему.
- После этого укажем входные данные индикатора, то есть его период.
donchianChannel=iCustom(_Symbol,PERIOD_CURRENT,"My Files\\Donchian_Channel\\Donchian_Channel",indPeriod);
В части OnDeinit() используем функцию Print для возврата сообщения Donchian Channel EA Removed при удалении советника.
Print("Donchian Channel EA Removed");
В части OnTick() создадим три массива ChannelBuff, ChannelBuff1 и MiddleBuff.
double channelBuff[],channelBuff1[], middleBuff[];
Используем функцию CopyBuffer для получения данных каждого буфера пользовательского канала Дончиана. Его параметры:
- indicator_handle - чтобы указать хендл индикатора, мы будем использовать созданный хендл donchianChannel для всех трех буферов.
- buffer_num - номер буфера: 0 - channelBuff, 1 - channelBuff1 и 2 - middleBuff.
- start_pos - позиция первого копируемого элемента. Используем 0 для всех трех буферов.
- count - количество копируемых данных. Используем 3 для всех трех буферов.
- buffer[] - чтобы указать целевой массив для копирования, мы укажем три буфера (channelBuff, channelBuff1 и middleBuff).
CopyBuffer(donchianChannel,0,0,3,channelBuff); CopyBuffer(donchianChannel,1,0,3,channelBuff1); CopyBuffer(donchianChannel,2,0,3,middleBuff);
Определим текущие значения каждой строки после создания переменной double для всех.
double channelHigh=channelBuff1[0]; double channelMiddle=middleBuff[0]; double channelLow=channelBuff[0];
Используйте функцию Comment, чтобы вернуть комментарий к графику с тремя значениями, каждое из которых находится в отдельной строке.
Comment("Channel High: ",channelHigh,"\nChannel Middle: ",channelMiddle,"\nChannel Low: ",channelLow);
Полный код выглядит так:
input int indPeriod=20; //Period int donchianChannel; int OnInit() { donchianChannel=iCustom(_Symbol,PERIOD_CURRENT,"My Files\\Donchian_Channel\\Donchian_Channel",indPeriod); return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { Print("Donchian Channel EA Removed"); } void OnTick() { double channelBuff[],channelBuff1[], middleBuff[]; CopyBuffer(donchianChannel,0,0,3,channelBuff); CopyBuffer(donchianChannel,1,0,3,channelBuff1); CopyBuffer(donchianChannel,2,0,3,middleBuff); double channelHigh=channelBuff1[0]; double channelMiddle=middleBuff[0]; double channelLow=channelBuff[0]; Comment("Channel High: ",channelHigh,"\nChannel Middle: ",channelMiddle,"\nChannel Low: ",channelLow); }
После успешной компиляции код появится в окне Навигатора в папке "Советники". При запуске советника на графике окно параметров выглядит так:
После запуска советника на графике мы увидим значения канала Дончиана в виде комментария:
На графике имеется искомый сигнал в виде комментария с тремя значениями индикатора (Channel High, Channel Middle и Channel Low). Каждое значение - в отдельной строке.
Если мы сравним значения сигналов советника и значения индикатора, мы увидим, что значения в окне данных такие же, как и значения сигналов советника:
Если мы хотим, чтобы советник находил сигналы на основе движений и уровней индикатора, его можно улучшить, установив необходимые условия для получения сигналов на покупку и продажу.
Советник Donchian Channel Breakout:
В этой версии советника нам нужно, чтобы программа постоянно проверяла все три значения индикаторов, и если цена (ask) пробьет максимум канала, нам нужно получить сигнал на покупку в качестве комментария на графике. Соответственно, если цена (bid) пробьет минимум канала, нам необходимо получить сигнал на продажу. Во всех остальных случаях ничего получать не надо.
Ниже приведен полный код для создания этого типа торговой системы в виде советника:
input int indPeriod=20; //Period int donchianChannel; int OnInit() { donchianChannel=iCustom(_Symbol,PERIOD_CURRENT,"My Files\\Donchian_Channel\\Donchian_Channel",indPeriod); return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { Print("Donchian Channel EA Removed"); } void OnTick() { double channelBuff[],channelBuff1[], middleBuff[]; CopyBuffer(donchianChannel,0,0,3,channelBuff); CopyBuffer(donchianChannel,1,0,3,channelBuff1); CopyBuffer(donchianChannel,2,0,3,middleBuff); double channelHigh=channelBuff1[0]; double channelMiddle=middleBuff[0]; double channelLow=channelBuff[0]; double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK); double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID); if(ask>channelHigh) { Comment("Buy Signal"); } else if(bid<channelLow) { Comment("Sell Signal"); } else Comment(" "); }
Какие отличия появились в этом коде:
Определение ask и bid с помощью функции SymbolInfoDouble для возврата значений свойств (ask, bid) после создания для них переменных double.
double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK); double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);
Условия стратегии:
При покупке
Нам нужна, чтобы программа проверила ask и максимум канала для определения их позиций. Если ask превышает максимум канала, это условие для покупки, и нам нужно, чтобы советник возвращал сигнал на покупку в качестве комментария на графике, как только это условие будет выполнено.
if(ask>channelHigh) { Comment("Buy Signal"); }
При продаже
Нам нужна, чтобы программа проверила bid и минимум канала для определения их позиций. Если bid ниже минимума канала, это условие для продажи, и нам нужно, чтобы советник возвращал сигнал на продажу в качестве комментария на графике, как только это условие будет выполнено.
else if(bid<channelLow) { Comment("Sell Signal"); }
В остальных случаях
Нам нужно, чтобы советник ничего не возвращал, если есть что-то кроме условий покупки или продажи.
else Comment(" ");
После успешной компиляции перетащим советник на нужный график для получения сигналов:
В случае сигнала на покупку
В верхнем левом углу графика мы видим сигнал на покупку после пробития максимума канала вверх.
В случае сигнала на продажу
В верхнем левом углу графика мы видим сигнал на продажу после пробития минимума канала вниз.
В остальных случаях
Как мы видим, сигнала нет, так как цена движется внутри канала - ниже максимума канала и выше минимума.
Канал Дончиана и прорыв скользящей средней:
Теперь нам нужно немного улучшить советник, фильтруя сигналы путем добавления в условия стратегии скользящей средней. Итак, нам нужно получить сигнал на покупку, когда цена ask прорвется выше максимума канала в случае, если 200-периодная EMA (экспоненциальная скользящая средняя) находится ниже цены ask. В случае сигнала на продажу нам необходимо убедиться, что цена bid пробилась ниже минимума канала и в то же время 200-периодная EMA находится выше цены bid. Во всех остальных случаях мы не должны получать ничего.
Ниже приведен полный код для создания этого типа торговой системы:
input int indPeriod=20; //Period input int maPeriod=200; //Moving Average Period int donchianChannel; int EMA; double emaArray[]; int OnInit() { donchianChannel=iCustom(_Symbol,PERIOD_CURRENT,"My Files\\Donchian_Channel\\Donchian_Channel",indPeriod); EMA = iMA(_Symbol,_Period,maPeriod,0,MODE_EMA,PRICE_CLOSE); return(INIT_SUCCEEDED); } void OnDeinit(const int reason) { Print("Donchian Channel EA Removed"); } void OnTick() { double channelBuff[],channelBuff1[], middleBuff[]; CopyBuffer(donchianChannel,0,0,3,channelBuff); CopyBuffer(donchianChannel,1,0,3,channelBuff1); CopyBuffer(donchianChannel,2,0,3,middleBuff); ArraySetAsSeries(emaArray,true); CopyBuffer(EMA,0,0,3,emaArray); double channelHigh=channelBuff1[0]; double channelMiddle=middleBuff[0]; double channelLow=channelBuff[0]; double EMAValue=NormalizeDouble(emaArray[0],_Digits); double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK); double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID); if(ask>channelHigh&&ask>EMAValue) { Comment("Buy Signal","\nAsk above Channel High","\nAsk above (",maPeriod,") EMA"); } else if(bid<channelLow&&bid<EMAValue) { Comment("Sell Signal","\nBid below Channel Low","\nBid Below (",maPeriod,") EMA"); } else Comment(" "); }
Какие отличия появились в этом коде:
Создадим еще один входной параметр целочисленной переменной maPeriod со значением по умолчанию (200).
input int maPeriod=200; //Moving Average Period
Создадим глобальную целочисленную переменную EMA.
int EMA;
Создадим массив emaArray[]
double emaArray[];
Обновим переменную EMA, используя функцию iMA, чтобы вернуть дескриптор индикатора скользящей средней, и его параметры:
- symbol - символ; у нас это (_Symbol), то есть рассчитываем индикатор по символу текущего графика.
- period - таймфрейм для расчета; значение (_period) означает, что индикатор будет рассчитываться на текущем таймфрейме.
- ma_period - период усреднения, мы будем использовать пользовательский входной параметр (maPeriod).
- ma_shift - укажем (0), поскольку сдвиг не требуется.
- ma_method - тип скользящей средней, мы будем использовать (MODE_EMA), поскольку нам нужно экспоненциальное скользящее среднее.
- applied_price - тип цены; используем (PRICE_CLOSE).
EMA = iMA(_Symbol,_Period,maPeriod,0,MODE_EMA,PRICE_CLOSE);
Используем функцию ArraySetAsSeries для установки AS_SERIES. Ее параметры:
- array[] - чтобы указать массив, используем emaArray.
- flag - направление индексирования в массиве, true.
ArraySetAsSeries(emaArray,true);
Используем функцию для получения данных из буфера скользящей средней
CopyBuffer(EMA,0,0,3,emaArray);
Определим значение EMA и нормализуем его
double EMAValue=NormalizeDouble(emaArray[0],_Digits);
Условия стратегии:
При покупке
Если цена > максимум канала, отображается следующий комментарий на графике
- Сигнал на покупку
- Ask выше максимума канала
- Ask выше периода EMA
if(ask>channelHigh&&ask>EMAValue) { Comment("Buy Signal","\nAsk above Channel High","\nAsk above (",maPeriod,") EMA"); }
При продаже
Если цена < минимум канала, отображается следующий комментарий на графике
- Сигнал на продажу
- Bid ниже минимума канала
- Bid ниже периода EMA
else if(bid<channelLow&&bid<EMAValue) { Comment("Sell Signal","\nBid below Channel Low","\nBid Below (",maPeriod,") EMA"); }
В случае отсутствия сигнала
else Comment(" ");
После успешной компиляции и запуске на графике, мы увидим следующие торговые сигналы
В случае сигнала на покупку
Как мы видим, у нас есть сигнал на покупку (цена выше максимума канала и 200 EMA).
В случае сигнала на продажу
У нас есть сигнал на продажу (цена ниже минимума канала и 200 EMA).
В случае отсутствия сигнала
Сигнала нет. Цена ниже 200 EMA, но при этом выше минимума и ниже максимума канала.
Заключение
В статье мы убедились в том, что канал Дончиана может быть полезным и ценным инструментом, особенно в пользовательском исполнении и в составе торговых систем. Вы сможете создавать свои каналы Дончиана в соответствии с вашими предпочтениями. Также вы сможете создать торговую систему, используя функцию iCustom для торговли или получения сигналов на основе индикатора. Вы можете улучшить торговую систему (советника), добавляя определенные условия и используя другие технические инструменты.
Надеюсь, статья будет для вас полезной и поможет улучшить ваши торговые результаты. Не применяйте содержимое этой статьи без надлежащего тестирования. Не существует инструмента, подходящего для всех трейдеров.
По ссылкам ниже вы можете ознакомиться с другими моими статьями. В частности, вы можете найти серию статей о создании торговых систем на основе самых популярных технических индикаторов. Я надеюсь, вы найдете их полезными.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/12711
- Бесплатные приложения для трейдинга
- Форексный VPS бесплатно на 24 часа
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования