Эксперименты с нейросетями (Часть 7): Передаем индикаторы
Введение
Добрый день, уважаемые пользователи сообщества MQL5. Сегодня мы более подробно поговорим о важности передачи осмысленных данных, так называемых временных рядов в нейросети. В своих прошлых статьях статьях посвященных экспериментам с нейросетями я неоднократно упоминал об этом. Сегодня мы откроем новую главу и перейдем непосредственно к передачи всеми любимыми индикаторами. Сделаем это осмысленно, вводя некоторые новые понятия, которые я использую в своей работе с нейросетями. Хотя, я думаю, это не предел, и у меня со временем появится новое виденье в понимании того, что именно нужно передавать и как. Но пока имеем то, что имеем и с ним и будем работать. И так приступим.
Предыстория и наблюдения
Читая большое количество статей, посвященных данной теме, я постоянно наблюдаю печальную картину прямых результатов систем торговли, основанных на нейросетях. Множество очень хороших идей и алгоритмов не приносят желаемого результата.
Наблюдается всегда одна и та же картина в передаче входных параметров. Например, передача значений индикаторов осцилляторов на прямую, которые, по моему мнению, с ценой актива не имеют нечего общего. Осцилляторы имеют всем известную проблему, вспомним так называемую дивергенцию. То значения цен открытия, закрытия, High и Low, что при прямой передаче не несут никакого смысла, а несут в систему непонятный шум, на котором пытаются обучить данные системы. Эти значения ни к чему не привязаны и имеют значительный разброс со стечением времени. Для примера откройте дневной график любой валютной пары и посмотрите на диапазон колебания цен закрытия.
Теория и понятия
В данном разделе я распишу некоторые понятия, которые я классифицировал для себя и использую в своих экспериментах.
- Дистанции – расстояния, какого-либо индикатора к другому индикатору или своему нулевому значению. Основным принципом, по моему мнению, является привязка текущей ситуации к стабильному значению на истории. Если мы передадим прямое значение индикатора MA 1 то получим непонятный разброс выборки так как значение цены меняется со временем и не «ходило» в определенном диапазоне что подразумевает классификацию или малейший намек на статистическое поведение. Иное дело, когда мы передадим разницу между двумя индикаторами MA 1 и MA 100 в пунктах, например. Или текущее расстояние на нулевой свече индикатора MACD с его значением 4 свечи назад. Таким образом, при помощи использования дистанций мы можем определить, далеко ли рынок ушел от своего среднего значения. Сильно ли он перекуплен, перепродан по сравнению с выборкой истории на текущий момент. Также при помощи дистанции мы определяем, куда движется рынок вверх или вниз, значение может принимать как положительное число, так и отрицательное.
- Накопление – подсчет суммарного значения индикатора или индикаторов относительно друг друга или своей нулевой точки. Или точки нулевого значения в некоторых индикаторах отличных от нуля. Таким образом, мы можем, определить при помощи накопления как долго происходит то или иное движение в определенную сторону. Если накопление маленькое возможно сейчас идет консолидация и наоборот, если накопление большое идет затяжной тренд. В зависимости от знака - или +, в какую сторону. То есть значения накоплений не имеют больших значений при «болтанке» - консолидации и имеют большие значения при трендах.
- Углы наклона – текущий угол наклона индикатора является очень хорошим средством сообщить нейросети об импульсном движении в данный момент или его отсутствия и затухании активности по инструменту. Возможность определения углов наклона индикаторов за определенную историю, например в 10 свечей неплохо себя зарекомендовало в анализе текущей ситуации по сравнению с результатами выборки на истории. Напомню, что углы наклона я измеряю в радианах, это единственный метод, который подошёл лично мне и не зависит от масштаба графика.
Примеры
Примеры использования дистанций:
- Передача дистанций на текущей нулевой свече. Индикатор MA 1 относительно MA 100. Индикатор MACD относительно своего текущего и нулевого значения. Индикатор CCI относительно своего текущего и нулевого значения.
#property copyright "2023, Roman Poshtar" #property link "https://www.mql5.com/ru/users/romanuch" #property strict #property version "1.0" input int x1 = 1; input int x2 = 1; input int x3 = 1; int handle_In1S1; int handle_In2S1; int handle_In3S1; int handle_In4S1; double ind_In1S1[]; double ind_In2S1[]; double ind_In3S1[]; double ind_In4S1[]; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int OnInit() { handle_In1S1=iMA(Symbol(),PERIOD_CURRENT,1,0,MODE_SMA,PRICE_CLOSE); //--- if the handle is not created if(handle_In1S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In2S1=iMA(Symbol(),PERIOD_CURRENT,100,0,MODE_SMA,PRICE_CLOSE); //--- if the handle is not created if(handle_In2S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In3S1=iMACD(Symbol(),PERIOD_CURRENT,12,26,9,PRICE_CLOSE); //--- if the handle is not created if(handle_In3S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In4S1=iCCI(Symbol(),PERIOD_CURRENT,14,PRICE_CLOSE); //--- if the handle is not created if(handle_In4S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In1S1,true); if(!iGetArray(handle_In1S1,0,0,1010,ind_In1S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In2S1,true); if(!iGetArray(handle_In2S1,0,0,1010,ind_In2S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In3S1,true); if(!iGetArray(handle_In3S1,0,0,1010,ind_In3S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In4S1,true); if(!iGetArray(handle_In4S1,0,0,1010,ind_In4S1)) { return; } //--- perceptron1(); } //+------------------------------------------------------------------+ //| The PERCEPRRON - a perceiving and recognizing function | //+------------------------------------------------------------------+ double perceptron1() { double w1 = x1 - 10.0; double w2 = x2 - 10.0; double w3 = x3 - 10.0; double a1 = ((ind_In1S1[0]-ind_In2S1[0])/Point()); double a2 = ind_In3S1[0]; double a3 = ind_In4S1[0]; Print("a1 = ", a1); Print("a2 = ", a2); Print("a3 = ", a3); Print("Perceptron = ", (w1 * a1 + w2 * a2 + w3 * a3)); Print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); return (w1 * a1 + w2 * a2 + w3 * a3); }
Выводимая информация в журнал:
a1 - разница в пунктах валютной пары между индикатором MA 1 и MA 100. Может принимать как положительное, так и отрицательное значение.
a2 - текущее значение индикатора MACD. Может принимать как положительное, так и отрицательное значение.
a3 - текущее значение индикатора CCI. Может принимать как положительное, так и отрицательное значение.
- Передача дистанций на N-количества свечей назад. Индикатор MA 1. Индикатор MA 100. Индикатор MACD. Индикатор CCI.
#property copyright "2023, Roman Poshtar" #property link "https://www.mql5.com/ru/users/romanuch" #property strict #property version "1.0" input int Candles= 10; input int x1 = 1; input int x2 = 1; input int x3 = 1; input int x4 = 1; int handle_In1S1; int handle_In2S1; int handle_In3S1; int handle_In4S1; double ind_In1S1[]; double ind_In2S1[]; double ind_In3S1[]; double ind_In4S1[]; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int OnInit() { handle_In1S1=iMA(Symbol(),PERIOD_CURRENT,1,0,MODE_SMA,PRICE_CLOSE); //--- if the handle is not created if(handle_In1S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In2S1=iMA(Symbol(),PERIOD_CURRENT,100,0,MODE_SMA,PRICE_CLOSE); //--- if the handle is not created if(handle_In2S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In3S1=iMACD(Symbol(),PERIOD_CURRENT,12,26,9,PRICE_CLOSE); //--- if the handle is not created if(handle_In3S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In4S1=iCCI(Symbol(),PERIOD_CURRENT,14,PRICE_CLOSE); //--- if the handle is not created if(handle_In4S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In1S1,true); if(!iGetArray(handle_In1S1,0,0,1010,ind_In1S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In2S1,true); if(!iGetArray(handle_In2S1,0,0,1010,ind_In2S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In3S1,true); if(!iGetArray(handle_In3S1,0,0,1010,ind_In3S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In4S1,true); if(!iGetArray(handle_In4S1,0,0,1010,ind_In4S1)) { return; } //--- perceptron1(); } //+------------------------------------------------------------------+ //| The PERCEPRRON - a perceiving and recognizing function | //+------------------------------------------------------------------+ double perceptron1() { double w1 = x1 - 10.0; double w2 = x2 - 10.0; double w3 = x3 - 10.0; double w4 = x4 - 10.0; double a1 = ((ind_In1S1[0]-ind_In1S1[Candles])/Point()); double a2 = ((ind_In2S1[0]-ind_In2S1[Candles])/Point()); double a3 = ind_In3S1[0]-ind_In3S1[Candles]; double a4 = ind_In4S1[0]-ind_In4S1[Candles]; Print("a1 = ", a1); Print("a2 = ", a2); Print("a3 = ", a3); Print("a3 = ", a4); Print("Perceptron = ", (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4)); Print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4); }
Выводимая информация в журнал:
a1 - разница в пунктах валютной пары индикатором MA 1 на свече 0 и свече 10. Может принимать как положительное, так и отрицательное значение.
a2 - разница в пунктах валютной пары индикатором MA 100 на свече 0 и свече 10. Может принимать как положительное, так и отрицательное значение.
a3 - разница в значении индикатора MACD на свече 0 и свече 10. Может принимать как положительное, так и отрицательное значение.
a4 - разница в значении индикатора CCI на свече 0 и свече 10. Может принимать как положительное, так и отрицательное значение.
- Передача дистанций от точки пересечения индикаторов MA1 и MA 100 относительно своего нулевого значения в прошлом. Индикатор MACD относительно своего нулевого значения в прошлом. Индикатор CCI относительно своего нулевого значения в прошлом.
#property copyright "2023, Roman Poshtar" #property link "https://www.mql5.com/ru/users/romanuch" #property strict #property version "1.0" input int Candles= 10; input int x1 = 1; input int x2 = 1; input int x3 = 1; input int x4 = 1; int handle_In1S1; int handle_In2S1; int handle_In3S1; int handle_In4S1; double ind_In1S1[]; double ind_In2S1[]; double ind_In3S1[]; double ind_In4S1[]; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int OnInit() { handle_In1S1=iMA(Symbol(),PERIOD_CURRENT,1,0,MODE_SMA,PRICE_CLOSE); //--- if the handle is not created if(handle_In1S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In2S1=iMA(Symbol(),PERIOD_CURRENT,100,0,MODE_SMA,PRICE_CLOSE); //--- if the handle is not created if(handle_In2S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In3S1=iMACD(Symbol(),PERIOD_CURRENT,12,26,9,PRICE_CLOSE); //--- if the handle is not created if(handle_In3S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In4S1=iCCI(Symbol(),PERIOD_CURRENT,14,PRICE_CLOSE); //--- if the handle is not created if(handle_In4S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In1S1,true); if(!iGetArray(handle_In1S1,0,0,1010,ind_In1S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In2S1,true); if(!iGetArray(handle_In2S1,0,0,1010,ind_In2S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In3S1,true); if(!iGetArray(handle_In3S1,0,0,1010,ind_In3S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In4S1,true); if(!iGetArray(handle_In4S1,0,0,1010,ind_In4S1)) { return; } //--- perceptron1(); } //+------------------------------------------------------------------+ //| The PERCEPRRON - a perceiving and recognizing function | //+------------------------------------------------------------------+ double perceptron1() { int c1=0; int c2=0; int c3=0; double w1 = x1 - 10.0; double w2 = x2 - 10.0; double w3 = x3 - 10.0; double w4 = x4 - 10.0; for(int i=0; i<=1000; i++){ if(ind_In1S1[0]>ind_In2S1[0]){if (ind_In1S1[i]<ind_In2S1[i]){c1=i; break;}} if(ind_In1S1[0]<ind_In2S1[0]){if (ind_In1S1[i]>ind_In2S1[i]){c1=i; break;}} } double a1 = ((ind_In1S1[0]-ind_In1S1[c1])/Point()); double a2 = ((ind_In2S1[0]-ind_In2S1[c1])/Point()); for(int i=0; i<=1000; i++){ if(ind_In3S1[0]>0){if (ind_In3S1[i]<0){c2=i; break;}} if(ind_In3S1[0]<0){if (ind_In3S1[i]>0){c2=i; break;}} } double a3 = ind_In3S1[0]-ind_In3S1[c2]; for(int i=0; i<=1000; i++){ if(ind_In4S1[0]>0){if (ind_In4S1[i]<0){c3=i; break;}} if(ind_In4S1[0]<0){if (ind_In4S1[i]>0){c3=i; break;}} } double a4 = ind_In4S1[0]-ind_In4S1[c3]; Print("a1 = ", a1); Print("a2 = ", a2); Print("a3 = ", a3); Print("a4 = ", a4); Print("Perceptron = ", (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4)); Print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4); }
Выводимая информация в журнал:
a1 - разница в пунктах валютной пары индикатором MA 1 на свече 0 и свече где было последнее пересечение индикаторов MA1 и MA 100. Может принимать как положительное, так и отрицательное значение.
a2 - разница в пунктах валютной пары индикатором MA 100 на свече 0 и свече где было последнее пересечение индикаторов MA1 и MA 100. Может принимать как положительное, так и отрицательное значение.
a3 - разница в значении индикатора MACD на свече 0 и свече где было последнее пересечение со значением 0. Может принимать как положительное, так и отрицательное значение.
a4 - разница в значении индикатора CCI на свече 0 и свече где было последнее пересечение со значением 0 . Может принимать как положительное, так и отрицательное значение.
Поиск значения пересечений предварительно осуществляется в цикле.
Примеры использования накопления для передачи в перцептрон:
- Передача накопления за N-количества свечей индикатора.
#property copyright "2023, Roman Poshtar" #property link "https://www.mql5.com/ru/users/romanuch" #property strict #property version "1.0" input int Candles= 10; input int x1 = 1; input int x2 = 1; input int x3 = 1; int handle_In1S1; int handle_In2S1; int handle_In3S1; int handle_In4S1; double ind_In1S1[]; double ind_In2S1[]; double ind_In3S1[]; double ind_In4S1[]; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int OnInit() { handle_In1S1=iMA(Symbol(),PERIOD_CURRENT,1,0,MODE_SMA,PRICE_CLOSE); //--- if the handle is not created if(handle_In1S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In2S1=iMA(Symbol(),PERIOD_CURRENT,100,0,MODE_SMA,PRICE_CLOSE); //--- if the handle is not created if(handle_In2S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In3S1=iMACD(Symbol(),PERIOD_CURRENT,12,26,9,PRICE_CLOSE); //--- if the handle is not created if(handle_In3S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In4S1=iCCI(Symbol(),PERIOD_CURRENT,14,PRICE_CLOSE); //--- if the handle is not created if(handle_In4S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In1S1,true); if(!iGetArray(handle_In1S1,0,0,1010,ind_In1S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In2S1,true); if(!iGetArray(handle_In2S1,0,0,1010,ind_In2S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In3S1,true); if(!iGetArray(handle_In3S1,0,0,1010,ind_In3S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In4S1,true); if(!iGetArray(handle_In4S1,0,0,1010,ind_In4S1)) { return; } //--- perceptron1(); } //+------------------------------------------------------------------+ //| The PERCEPRRON - a perceiving and recognizing function | //+------------------------------------------------------------------+ double perceptron1() { double sum1 = 0; double sum2 = 0; double sum3 = 0; double w1 = x1 - 10.0; double w2 = x2 - 10.0; double w3 = x3 - 10.0; for(int i=0; i<=Candles; i++) { sum1+=ind_In1S1[i]-ind_In2S1[i]; } double a1 = sum1; for(int i=0; i<=Candles; i++) { sum2+=ind_In3S1[i]; } double a2 = sum2; for(int i=0; i<=Candles; i++) { sum3+=ind_In4S1[i]; } double a3 = sum3; Print("a1 = ", a1); Print("a2 = ", a2); Print("a3 = ", a3); Print("Perceptron = ", (w1 * a1 + w2 * a2 + w3 * a3)); Print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); return (w1 * a1 + w2 * a2 + w3 * a3); }
Выводимая информация в журнал:
a1 - накопление разницы между индикаторами MA1 и MA 100 за количество свечей параметра Candles. Может принимать как положительное, так и отрицательное значение.
a2 - накопление индикатора MACD за количество свечей параметра Candles. Может принимать как положительное, так и отрицательное значение.
a3 - накопление индикатора CCI за количество свечей параметра Candles. Может принимать как положительное, так и отрицательное значение.
- Передача накопления от нулевой точки индикатора в прошлом или последнего пересечения индикаторов в прошлом.
#property copyright "2023, Roman Poshtar" #property link "https://www.mql5.com/ru/users/romanuch" #property strict #property version "1.0" input int Candles= 10; input int x1 = 1; input int x2 = 1; input int x3 = 1; int handle_In1S1; int handle_In2S1; int handle_In3S1; int handle_In4S1; double ind_In1S1[]; double ind_In2S1[]; double ind_In3S1[]; double ind_In4S1[]; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int OnInit() { handle_In1S1=iMA(Symbol(),PERIOD_CURRENT,1,0,MODE_SMA,PRICE_CLOSE); //--- if the handle is not created if(handle_In1S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In2S1=iMA(Symbol(),PERIOD_CURRENT,100,0,MODE_SMA,PRICE_CLOSE); //--- if the handle is not created if(handle_In2S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In3S1=iMACD(Symbol(),PERIOD_CURRENT,12,26,9,PRICE_CLOSE); //--- if the handle is not created if(handle_In3S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In4S1=iCCI(Symbol(),PERIOD_CURRENT,14,PRICE_CLOSE); //--- if the handle is not created if(handle_In4S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In1S1,true); if(!iGetArray(handle_In1S1,0,0,1010,ind_In1S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In2S1,true); if(!iGetArray(handle_In2S1,0,0,1010,ind_In2S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In3S1,true); if(!iGetArray(handle_In3S1,0,0,1010,ind_In3S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In4S1,true); if(!iGetArray(handle_In4S1,0,0,1010,ind_In4S1)) { return; } //--- perceptron1(); } //+------------------------------------------------------------------+ //| The PERCEPRRON - a perceiving and recognizing function | //+------------------------------------------------------------------+ double perceptron1() { double sum1 = 0; double sum2 = 0; double sum3 = 0; double w1 = x1 - 10.0; double w2 = x2 - 10.0; double w3 = x3 - 10.0; for(int i=0; i<=1000; i++) { if(ind_In1S1[0]>ind_In2S1[0]) { if(ind_In1S1[i]<ind_In2S1[i]) { break; }; sum1+=(ind_In1S1[i]-ind_In2S1[i]); } if(ind_In1S1[0]<ind_In2S1[0]) { if(ind_In1S1[i]>ind_In2S1[i]) { break; }; sum1+=(ind_In1S1[i]-ind_In2S1[i]); } } double a1 = sum1; for(int i=0; i<=1000; i++) { if(ind_In3S1[0]>0) { if(ind_In3S1[i]<0) { break; }; sum2+=ind_In3S1[i]; } if(ind_In3S1[0]<0) { if(ind_In3S1[i]>0) { break; }; sum2+=ind_In3S1[i]; } } double a2 = sum2; for(int i=0; i<=1000; i++) { if(ind_In4S1[0]>0) { if(ind_In4S1[i]<0) { break; }; sum3+=ind_In4S1[i]; } if(ind_In4S1[0]<0) { if(ind_In4S1[i]>0) { break; }; sum3+=ind_In4S1[i]; } } double a3 = sum3; Print("a1 = ", a1); Print("a2 = ", a2); Print("a3 = ", a3); Print("Perceptron = ", (w1 * a1 + w2 * a2 + w3 * a3)); Print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); return (w1 * a1 + w2 * a2 + w3 * a3); }
Выводимая информация в журнал:
a1 - накопление разницы между индикаторами MA1 и MA 100 с точки последнего пересечения. Может принимать как положительное, так и отрицательное значение.
a2 - накопление индикатора MACD с точки последнего пересечения значания 0. Может принимать как положительное, так и отрицательное значение.
a3 - накопление индикатора CCI с точки последнего пересечения значания 0. Может принимать как положительное, так и отрицательное значение.
Примеры использования углов наклона индикаторов:
- Передача углов наклона за N-количества свечей индикатора.
#property copyright "2023, Roman Poshtar" #property link "https://www.mql5.com/ru/users/romanuch" #property strict #property version "1.0" input int Candles= 10; input int x1 = 1; input int x2 = 1; input int x3 = 1; input int x4 = 1; int handle_In1S1; int handle_In2S1; int handle_In3S1; int handle_In4S1; double ind_In1S1[]; double ind_In2S1[]; double ind_In3S1[]; double ind_In4S1[]; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int OnInit() { handle_In1S1=iMA(Symbol(),PERIOD_CURRENT,1,0,MODE_SMA,PRICE_CLOSE); //--- if the handle is not created if(handle_In1S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In2S1=iMA(Symbol(),PERIOD_CURRENT,100,0,MODE_SMA,PRICE_CLOSE); //--- if the handle is not created if(handle_In2S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In3S1=iMACD(Symbol(),PERIOD_CURRENT,12,26,9,PRICE_CLOSE); //--- if the handle is not created if(handle_In3S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In4S1=iCCI(Symbol(),PERIOD_CURRENT,14,PRICE_CLOSE); //--- if the handle is not created if(handle_In4S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In1S1,true); if(!iGetArray(handle_In1S1,0,0,1010,ind_In1S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In2S1,true); if(!iGetArray(handle_In2S1,0,0,1010,ind_In2S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In3S1,true); if(!iGetArray(handle_In3S1,0,0,1010,ind_In3S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In4S1,true); if(!iGetArray(handle_In4S1,0,0,1010,ind_In4S1)) { return; } //--- perceptron1(); } //+------------------------------------------------------------------+ //| The PERCEPRRON - a perceiving and recognizing function | //+------------------------------------------------------------------+ double perceptron1() { double w1 = x1 - 10.0; double w2 = x2 - 10.0; double w3 = x3 - 10.0; double w4 = x4 - 10.0; double a1 = (((ind_In1S1[0]-ind_In1S1[Candles])/Point())/Candles); double a2 = (((ind_In2S1[0]-ind_In2S1[Candles])/Point())/Candles); double a3 = ((ind_In3S1[0]-ind_In3S1[Candles])/Candles); double a4 = ((ind_In4S1[0]-ind_In4S1[Candles])/Candles); Print("a1 = ", a1); Print("a2 = ", a2); Print("a3 = ", a3); Print("a4 = ", a4); Print("Perceptron = ", (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4)); Print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4); }
Выводимая информация в журнал:
a1 - угол наклона индикатора MA1 за количество свечей параметра Candles. Может принимать как положительное, так и отрицательное значение.
a2 - угол наклона индикатора MA100 за количество свечей параметра Candles. Может принимать как положительное, так и отрицательное значение.
a3 - угол наклона индикатора MACD за количество свечей параметра Candles. Может принимать как положительное, так и отрицательное значение.
a4 - угол наклона индикатора CCI за количество свечей параметра Candles. Может принимать как положительное, так и отрицательное значение.
- Передача углов наклона от нулевой точки индикатора в прошлом или последнего пересечения индикаторов в прошлом.
#property copyright "2023, Roman Poshtar" #property link "https://www.mql5.com/ru/users/romanuch" #property strict #property version "1.0" input int x1 = 1; input int x2 = 1; input int x3 = 1; input int x4 = 1; int handle_In1S1; int handle_In2S1; int handle_In3S1; int handle_In4S1; double ind_In1S1[]; double ind_In2S1[]; double ind_In3S1[]; double ind_In4S1[]; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int OnInit() { handle_In1S1=iMA(Symbol(),PERIOD_CURRENT,1,0,MODE_SMA,PRICE_CLOSE); //--- if the handle is not created if(handle_In1S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In2S1=iMA(Symbol(),PERIOD_CURRENT,100,0,MODE_SMA,PRICE_CLOSE); //--- if the handle is not created if(handle_In2S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In3S1=iMACD(Symbol(),PERIOD_CURRENT,12,26,9,PRICE_CLOSE); //--- if the handle is not created if(handle_In3S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In4S1=iCCI(Symbol(),PERIOD_CURRENT,14,PRICE_CLOSE); //--- if the handle is not created if(handle_In4S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In1S1,true); if(!iGetArray(handle_In1S1,0,0,1010,ind_In1S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In2S1,true); if(!iGetArray(handle_In2S1,0,0,1010,ind_In2S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In3S1,true); if(!iGetArray(handle_In3S1,0,0,1010,ind_In3S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In4S1,true); if(!iGetArray(handle_In4S1,0,0,1010,ind_In4S1)) { return; } //--- perceptron1(); } //+------------------------------------------------------------------+ //| The PERCEPRRON - a perceiving and recognizing function | //+------------------------------------------------------------------+ double perceptron1() { int c1=0; int c2=0; int c3=0; double w1 = x1 - 10.0; double w2 = x2 - 10.0; double w3 = x3 - 10.0; double w4 = x4 - 10.0; for(int i=0; i<=1000; i++) { if(ind_In1S1[0]>ind_In2S1[0]) { if(ind_In1S1[i]<ind_In2S1[i]) { c1=i; break; } } if(ind_In1S1[0]<ind_In2S1[0]) { if(ind_In1S1[i]>ind_In2S1[i]) { c1=i; break; } } } double a1 = (((ind_In1S1[0]-ind_In1S1[c1])/Point())/c1); double a2 = (((ind_In2S1[0]-ind_In2S1[c1])/Point())/c1); for(int i=0; i<=1000; i++) { if(ind_In3S1[0]>0) { if(ind_In3S1[i]<0) { c2=i; break; } } if(ind_In3S1[0]<0) { if(ind_In3S1[i]>0) { c2=i; break; } } } double a3 = ((ind_In3S1[0]-ind_In3S1[c2])/c2); for(int i=0; i<=1000; i++) { if(ind_In4S1[0]>0) { if(ind_In4S1[i]<0) { c3=i; break; } } if(ind_In4S1[0]<0) { if(ind_In4S1[i]>0) { c3=i; break; } } } double a4 = ((ind_In4S1[0]-ind_In4S1[c3])/c3); Print("a1 = ", a1); Print("a2 = ", a2); Print("a3 = ", a3); Print("a4 = ", a4); Print("Perceptron = ", (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4)); Print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4); }
Выводимая информация в журнал:
a1 - угол наклона индикатора MA1 с точки последнего пересечения индикаторов MA1 и MA100. Может принимать как положительное, так и отрицательное значение.
a2 - угол наклона индикатора MA100 с точки последнего пересечения индикаторов MA1 и MA100. Может принимать как положительное, так и отрицательное значение.
a3 - угол наклона индикатора MACD с точки последнего пересечения индикатором значения 0. Может принимать как положительное, так и отрицательное значение.
a4 - угол наклона индикатора CCI с точки последнего пересечения индикатором значения 0. Может принимать как положительное, так и отрицательное значение.
Пример использования комбинированных значений для передачи в перцептрон:
- Комбинированая передача значений двух индикаторов MA1 и MA100.
#property copyright "2023, Roman Poshtar" #property link "https://www.mql5.com/ru/users/romanuch" #property strict #property version "1.0" input int Candles= 10; input int x1 = 1; input int x2 = 1; input int x3 = 1; input int x4 = 1; input int x5 = 1; input int x6 = 1; input int x7 = 1; int handle_In1S1; int handle_In2S1; double ind_In1S1[]; double ind_In2S1[]; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int OnInit() { handle_In1S1=iMA(Symbol(),PERIOD_CURRENT,1,0,MODE_SMA,PRICE_CLOSE); //--- if the handle is not created if(handle_In1S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- handle_In2S1=iMA(Symbol(),PERIOD_CURRENT,100,0,MODE_SMA,PRICE_CLOSE); //--- if the handle is not created if(handle_In2S1==INVALID_HANDLE) { return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In1S1,true); if(!iGetArray(handle_In1S1,0,0,1010,ind_In1S1)) { return; } //--- //--- get data from the three buffers of the i-Regr indicator ArraySetAsSeries(ind_In2S1,true); if(!iGetArray(handle_In2S1,0,0,1010,ind_In2S1)) { return; } //--- perceptron1(); } //+------------------------------------------------------------------+ //| The PERCEPRRON - a perceiving and recognizing function | //+------------------------------------------------------------------+ double perceptron1() { int c1=0; double sum1 = 0; double sum2 = 0; double w1 = x1 - 10.0; double w2 = x2 - 10.0; double w3 = x3 - 10.0; double w4 = x4 - 10.0; double w5 = x5 - 10.0; double w6 = x6 - 10.0; double w7 = x7 - 10.0; double a1 = ((ind_In1S1[0]-ind_In2S1[0])/Point()); for(int i=0; i<=Candles; i++) { sum1+=ind_In1S1[i]-ind_In2S1[i]; } double a2 = sum1; for(int i=0; i<=1000; i++) { if(ind_In1S1[0]>ind_In2S1[0]) { if(ind_In1S1[i]<ind_In2S1[i]) { break; }; sum2+=(ind_In1S1[i]-ind_In2S1[i]); } if(ind_In1S1[0]<ind_In2S1[0]) { if(ind_In1S1[i]>ind_In2S1[i]) { break; }; sum2+=(ind_In1S1[i]-ind_In2S1[i]); } } double a3 = sum2; double a4 = (((ind_In1S1[0]-ind_In1S1[Candles])/Point())/Candles); double a5 = (((ind_In2S1[0]-ind_In2S1[Candles])/Point())/Candles); for(int i=0; i<=1000; i++) { if(ind_In1S1[0]>ind_In2S1[0]) { if(ind_In1S1[i]<ind_In2S1[i]) { c1=i; break; } } if(ind_In1S1[0]<ind_In2S1[0]) { if(ind_In1S1[i]>ind_In2S1[i]) { c1=i; break; } } } double a6 = (((ind_In1S1[0]-ind_In1S1[c1])/Point())/c1); double a7 = (((ind_In2S1[0]-ind_In2S1[c1])/Point())/c1); Print("a1 = ", a1); Print("a2 = ", a2); Print("a3 = ", a3); Print("a4 = ", a4); Print("a5 = ", a5); Print("a6 = ", a6); Print("a7 = ", a7); Print("Perceptron = ", (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4 + w5 * a5 + w6 * a6 + w7 * a7)); Print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4 + w5 * a5 + w6 * a6 + w7 * a7); }
Выводимая информация в журнал:
a1 - дистанция между индикаторами MA1 и MA100 на текущей свече. Может принимать как положительное, так и отрицательное значение.
a2 - накопление между индикаторами MA1 и MA100 за количество свечей параметра Candles. Может принимать как положительное, так и отрицательное значение.
a3 - накопление между индикаторами MA1 и MA100 с точки последнего пересечения индикаторов. Может принимать как положительное, так и отрицательное значение.
a4 - угол наклона индикатора MA1 за количество свечей параметра Candles. Может принимать как положительное, так и отрицательное значение.
a5 - угол наклона индикатора MA100 за количество свечей параметра Candles. Может принимать как положительное, так и отрицательное значение.
a6 - угол наклона индикатора MA1 с точки последнего пересечения индикаторов. Может принимать как положительное, так и отрицательное значение.
a7 - угол наклона индикатора MA100 с точки последнего пересечения индикаторов. Может принимать как положительное, так и отрицательное значение.
Советники
Сегодня мы разработаем 2 советника. В качестве примера будем использовать значения дистанций между индикаторами. Первый предназначен для проведения оптимизации и подбора параметров. Второй непосредственно для торговли полученными результатами первого. Почему так спросите Вы, все просто, мы уже рассматривали данный подход в моих предыдущих статьях. Данный метод помогает сделать перебор и использование в торговле всех доступных результатов оптимизации, а не один из них. Например, мы получили при оптимизации 50 хороших результатов. Согласитесь, что устанавливать советник на 50 графиков не очень удобно. Потому используем все результаты одновременно при помощи второго советника.
Более подробно про применяемый метод вы можете прочитать в моей статье Эксперименты с нейросетями (Часть 3): Практическое применение
Ниже приведу основной код первого советника для оптимизации и распишу, что мы передали в перцептрон:
Основной код на открытие позиций.
//SELL++++++++++++++++++++++++++++++++++++++++++++++++ if((perceptron1()<-Param) && (CalculatePositions(symbolS1.Name(), Magic, POSITION_TYPE_SELL, EAComment)==0) && (SpreadS1<=MaxSpread)) { OpenSell(symbolS1.Name(), LotsXSell, TP, SL, EAComment); } //BUY++++++++++++++++++++++++++++++++++++++++++++++++ if((perceptron1()>Param) && (CalculatePositions(symbolS1.Name(), Magic, POSITION_TYPE_BUY, EAComment)==0) && (SpreadS1<=MaxSpread)) { OpenBuy(symbolS1.Name(), LotsXBuy, TP, SL, EAComment); }
Код перцептрона.
- a1 - дистанция между индикаторoм MA1 на текущей свече и свече в параметре Candles.
- a2 - дистанция между индикаторoм MA100 на текущей свече и свече в параметре Candles.
- a3 - дистанция между индикаторoм CCI на текущей свече и свече в параметре Candles.
- a4 - дистанция между индикаторoм StdDev на текущей свече и свече в параметре Candles.
Параметр Candlesв данном случае не оптимизировался и имел значение 8.
//+------------------------------------------------------------------+ //| The PERCEPRRON - a perceiving and recognizing function | //+------------------------------------------------------------------+ double perceptron1() { double w1 = x1 - 10.0; double w2 = x2 - 10.0; double w3 = x3 - 10.0; double w4 = x4 - 10.0; double a1 = ((ind_In1S1[0]-ind_In1S1[Candles])/PointS1); double a2 = ((ind_In2S1[0]-ind_In2S1[Candles])/PointS1); double a3 = (ind_In3S1[0]-ind_In3S1[Candles]); double a4 = (ind_In4S1[0]-ind_In4S1[Candles]); return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4); }
Тут я предоставлю, все параметры для оптимизации и тестирования что бы дальше в тексте не повторятся:
- Рынок Forex;
- Валютная пара EURUSD;
- Период H1;
- Индикаторы: MA 1 SMA CLOSE, MA 200 SMA CLOSE, CCI 42, StdDev 60.
- СтопЛосс и ТейкПрофит, 400 и 830;
- Режим оптимизации и тестирования «1 Minute OHLC» и «Максимальная прибыльность». Наш советник изначально работает по ценам открытия М1. Сегодня в качестве эксперимента используем режим «Максимальная прибыльность»;
- Диапазон оптимизации 1 год. С 2021.10.10 по 2022.10.10. 1 год не является, каким-то критерием. Вы можете попробовать больше или меньше самостоятельно;
- Диапазон форвард тестирования 1 год. С 2022.10.10 по 2023.10.10;
- В форвард тестировании использовалось 50 первых лучших результатов оптимизации одновременно;
- Начальный депозит 10000;
- Плече 1:500.
Настройки оптимизации приведу на скриншоте ниже:
Результаты оптимизации на скриншотах ниже:
После завершения оптимизации формируем файл в формате CSV при помощи Excel. Напомню, что мы берем первые 50 лучших результатов. Вставляем полученный файл в код второго советника для тестирования. В коде это будет выглядеть так.
string EURUSD[][8]= { {"Profit","Trades","x1","x2","x3","x4","Param"}, {"266.45","239","2","1","9","8","5000"}, {"266.45","239","2","1","9","13","5000"}, {"266.45","239","2","1","9","11","5000"}, {"266.45","239","2","1","9","10","5000"}, {"266.45","239","2","1","9","8","5000"}, {"266.45","239","2","1","9","12","5000"}, {"266.45","239","2","1","9","20","5000"}, {"266.45","239","2","1","9","14","5000"}, {"266.45","239","2","1","9","2","5000"}, {"266.45","239","2","1","9","3","5000"}, {"259.69","239","0","0","12","17","5500"}, {"259.69","239","0","0","12","8","5500"}, {"259.69","239","0","0","12","1","5500"}, {"259.69","239","0","0","12","9","5500"}, {"259.69","239","0","0","12","16","5500"}, {"259.69","239","0","0","12","18","5500"}, {"259.69","239","0","0","12","11","5500"}, {"259.69","239","0","0","12","7","5500"}, {"259.69","239","0","0","12","15","5500"}, {"259.69","239","0","0","12","8","5500"}, {"259.69","239","0","0","12","9","5500"}, {"259.69","239","0","0","12","17","5500"}, {"259.69","239","0","0","12","6","5500"}, {"259.69","239","0","0","12","15","5500"}, {"259.69","239","0","0","12","4","5500"}, {"259.69","239","0","0","12","7","5500"}, {"259.69","239","0","0","12","11","5500"}, {"259.69","239","0","0","12","14","5500"}, {"259.69","239","0","0","12","1","5500"}, {"259.69","239","0","0","12","5","5500"}, {"259.69","239","0","0","12","3","5500"}, {"259.69","239","0","0","12","0","5500"}, {"259.69","239","0","0","12","12","5500"}, {"259.69","239","0","0","12","16","5500"}, {"259.69","239","0","0","12","8","5500"}, {"259.69","239","0","0","12","10","5500"}, {"259.69","239","0","0","12","16","5500"}, {"259.69","239","0","0","12","18","5500"}, {"259.69","239","0","0","12","13","5500"}, {"259.69","239","0","0","12","9","5500"}, {"259.69","239","0","0","12","12","5500"}, {"259.69","239","0","0","12","11","5500"}, {"259.69","239","0","0","12","7","5500"}, {"259.69","239","0","0","12","15","5500"}, {"259.69","239","0","0","12","14","5500"}, {"259.69","239","0","0","12","3","5500"}, {"259.69","239","0","0","12","19","5500"}, {"259.69","239","0","0","12","0","5500"}, {"259.69","239","0","0","12","17","5500"}, {"259.69","239","0","0","12","2","5500"} };
Также приведу код второго советника для торговли. Как видим, в цикле мы перебираем все значения, полученные при оптимизации, и сравниваем их с текущими результатами выполнения персептрона. Если значения совпадают, открывается соответствующая позиция на покупку или продажу. Ограничение на количество одновременно открытых позиций контролируется параметром MaxSeries и функцией CalculateSeries которая подсчитывает позиции по комментариям.
for(int i=1; i<=(ArraySize(EURUSD)/8)-1; i++) { comm=(EURUSD[i][0]+EURUSD[i][1]); x1=(int)StringToInteger(EURUSD[i][2]); x2=(int)StringToInteger(EURUSD[i][3]); x3=(int)StringToInteger(EURUSD[i][4]); x4=(int)StringToInteger(EURUSD[i][5]); Param=(int)StringToInteger(EURUSD[i][6]); //SELL++++++++++++++++++++++++++++++++++++++++++++++++ if((NewOpen==true) && (CalculateSeries(Magic)<MaxSeries) && (perceptron1()<-Param) && (CalculatePositions(symbolS1.Name(), Magic, POSITION_TYPE_SELL, EAComment+" En_"+comm)==0) && (SpreadS1<=MaxSpread)) { OpenSell(symbolS1.Name(), LotsXSell, TP, SL, EAComment+" En_"+comm); } //BUY++++++++++++++++++++++++++++++++++++++++++++++++ if((NewOpen==true) && (CalculateSeries(Magic)<MaxSeries) && (perceptron1()>Param) && (CalculatePositions(symbolS1.Name(), Magic, POSITION_TYPE_BUY, EAComment+" En_"+comm)==0) && (SpreadS1<=MaxSpread)) { OpenBuy(symbolS1.Name(), LotsXBuy, TP, SL, EAComment+" En_"+comm); } }
//+------------------------------------------------------------------+ //| Calculate Positions | //+------------------------------------------------------------------+ int CalculateSeries(ulong mag) { int total=0; string com=""; for(int i=PositionsTotal()-1; i>=0; i--) { if(position.SelectByIndex(i)) { if(position.Magic()==mag) { if(com!=position.Comment()) { com=position.Comment(); total++; } } } } //--- return(total); }
Результат бэквард-тестирования:
Результат форвард-тестирования:
Заключение
Список файлов во вложении:
- Distance 1, Distance 2, Distance 3, - советники с примерами передачи дистанций в перцептрон;
- Accumulation 1, Accumulation 2 - советники с примерами передачи накопления в перцептрон;
- Angle 1, Angle 2 - советники с примерами передачи углов наклона индикаторов в перцептрон;
- Combo 1 - советник с примером передачи комбинированных данных индикаторов в перцептрон;
- Perceptron – opt - советник для оптимизации;
- Perceptron – trade – советник для торговли оптимизированными параметрами.
Как видно из результатов форвард тестирования наш подход к передаче индикаторов неплохо работает. Первые полгода советник довольно уверенно толкает баланс вверх. Выше в статье я привел всевозможные примеры передачи индикаторов, которые я думаю, моим читателям будет интересно протестировать. Работы в этот раз проделано не мало, но всегда есть к чему стремится.
По всем вопросам вы можете обратиться на форуме или в личные сообщения. Я всегда буду рад Вам помочь.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Новый термин в трейдинге : Canbles :-)
Исправь опечатки Candles! от слова Candle, через d
Новый термин в трейдинге : Canbles :-)
Исправь опечатки Candles! от слова Candle, через d
Исправили. Спасибо за наблюдательность )
Исправили.
Нифига не исправил. Так и осталось Canbles.
Теперь точно все места в статье исправили.
Спасибо за сообщение.
Теперь точно все места в статье исправили.
Спасибо за сообщение.
Спасибо.