English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Нейронные сети - от теории к практике

Нейронные сети - от теории к практике

MetaTrader 5Примеры | 6 октября 2012, 09:45
63 745 79
Dmitriy Parfenovich
Dmitriy Parfenovich

Введение

В наше время, наверное, каждый трейдер слышал о нейронных сетях и знает, как это круто. В представлении большинства те, которые в них разбираются, это какие-то чуть ли не сверхчеловеки. В этой статье я постараюсь рассказать, как устроена нейросеть, что с ней можно делать и покажу практические примеры её использования.


Понятие о нейронных сетях

Нейронные сети – это одно из направлений исследований в области искусственного интеллекта, основанное на попытках воспроизвести нервную систему человека, а именно: способность нервной системы обучаться и исправлять ошибки, что должно позволить смоделировать, хотя и достаточно грубо, работу человеческого мозга.

Нейронная сеть состоит, как это ни странно, из нейронов.

Рис. 1. Структурная схема нейрона
Рис. 1. Структурная схема нейрона

Структуру нейрона можно представить из следующих блоков:

  1. Входные сигналы Входные сигналы;
  2. Весовые коэффициенты Весовые коэффициенты;
  3. Сумматор Сумматор и его выход Результат вычислений нейрона;
  4. Функция активации нейрона Функция активации нейрона;
  5. Выходной сигнал Выходной сигнал.

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

Формула вычисления значения нейрона

Результат вычислений нейрона в данном случае и есть результат вычислений нейрона.

Формула функции активации

Результаты вычислений предаются на выход не напрямую, а через функцию активации, о которой мы поговорим позже. Одним словом, нейронную сеть можно представить в виде "черного ящика", на вход которого мы подаем сигналы, а на выходе получаем результат.

Рис. 2. Структурная схема многослойной нейронной сети
Рис. 2. Структурная схема многослойной нейронной сети

А так будет выглядеть многослойная нейронная сеть. Она включает в себя:

  • Входной слой - служит для распределения данных по сети и не производит никаких вычислений. Выходы этого слоя передают сигналы на входы следующего слоя (скрытого или выходного);
  • Выходной слой - обычно содержит один нейрон (может и больше), который выдает результат расчетов всей нейронной сети. На основании это сигнала строится дальнейшая логика управления советника;
  • Скрытые слои - слои обычных нейронов, которые передают сигналы от входа к выходу. Их входом служит выход предыдущего слоя, а выход - входом следующего слоя.

В данном примере была рассмотрена нейронная сеть с двумя скрытыми слоями, хотя возможны варианты и с большим количеством слоев.


Нормализация входных данных

Нормализация входных данных - это процесс, при котором все входные данные проходят процесс "выравнивания", т.е. приведения к интервалу [0,1] или [-1,1]. Если не провести нормализацию, то входные данные будут оказывать дополнительное влияние на нейрон, что приведет к неверным решениям. Другими словами, как можно сравнивать величины разных порядков?

В общем виде формула нормализации выглядит так:

Формула нормализации

где:

  • Нормализуемое значение - значение, подлежащее нормализации;
  • Интервал значений х - интервал значений х;
  • Интервал, к которому будет приведено значение x - интервал, к которому будет приведено значение x.

Поясню сказанное на примере:

Пусть есть n входных данных из интервала [0,10], тогда Минимальное значение x = 0, а Максимальное значение x = 10. Данные будем приводить к интервалу [0,1], тогда d1 = 0, а d2 = 1. Теперь, подставив все значения в формулу, можно вычислить нормализованные значения для любого x из n входных данных.

На языке MQL5 это выглядит следующим образом:

double d1=0.0;
double d2=1.0;
double x_min=iMA_buf[ArrayMinimum(iMA_buf)];
double x_max=iMA_buf[ArrayMaximum(iMA_buf)];
for(int i=0;i<ArraySize(iMA_buf);i++)
  {
   inputs[i]=(((iMA_buf[i]-x_min)*(d2-d1))/(x_max-x_min))+d1;
  }

Сначала мы указываем крайние значения для выходного значения, после получаем минимальное и максимальное значения индикатора (копирование данных с индикатора пропущено, но к примеру там может быть 10 последних значений). Последним шагом произведем в цикле нормализацию каждого входного элемента (значения индикатора на различных барах) и сохраним в массив для дальнейшей работы с ним.


Функции активации нейронов

Функция активации нейрона - это функция, которая вычисляет выходной сигнал нейрона. На вход этой функции подается сумма всех произведений сигналов и весов этих сигналов (далее, средневзвешенная сумма):

Рис. 3. Структурная схема нейрона с выделенной функцией активации
Рис. 3. Структурная схема нейрона с выделенной функцией активации

Функция активации в общем виде будет выглядеть так:

Формула функции активации

где:

  • Функция активации нейрона - сама функции активации;
  • Результат вычислений нейрона - средневзвешенная сумма, полученная на первом этапе вычисления выходного значения нейрона;
  • Пороговое значение срабатывания функции активации - пороговое значение срабатывания функции активации. Используется только для жесткой пороговой функции, в других функциях оно равно нулю.

Основные виды функций активации:

  1. Единичный скачок или жесткая пороговая функция.
    График функции единичный скачок или жесткой пороговой функции
    Функция описывается следующей формулой:
    Формула функции
    Пока средневзвешенная сумма меньше определенного значения, функция активации возвращает ноль, а когда становится больше - единицу.

  2. Сигмоидальная функция или сигмоид.
    График сигмоидальной функции или сигмоиды
    Формула, описывающая сигмоид:
    Формула описывающая сигмоиду
    Часто применяется в многослойных нейронных и других сетях с непрерывными сигналами. Гладкость и непрерывность функции - важные положительные качества.

  3. Гиперболический тангенс.
    График функции гиперболического тангенса
    Формула:
    Формула описывающая функцию гипербалического тангенсаили Формула описывающая функцию гипербалического тангенса
    Также часто применяется в сетях с непрерывными сигналами. Ее особенность в том, что она может возвращать отрицательные значения результата.


Изменение формы функции активации нейрона

В предыдущем разделе мы рассмотрели функции активации. Но есть еще очень важный момент - крутизна функции (кроме жесткой пороговой функции). Рассмотрим более подробно сигмоидальную функцию.

Если обратиться к графику функции, легко заметить, что функция имеет гладкую форму на отрезке [-5,5]. Допустим, у нас есть сеть, состоящая из одного нейрона с 10 входами и одним выходом. Теперь попробуем рассчитать "крайние" значения переменной Результат вычислений нейрона. На каждый вход будет подаваться нормализованное значение (как я уже говорил в разделе Нормализация входных данных), допустим из интервала [-1,1].

Возьмем отрицательные входные значения, так как функция дифференцируема и при отрицательном аргументе. Весовые коэффициенты будем выбирать тоже из этого интервала. При всех возможных вариантах сочетаний входных данных и весовых коэффициентов получим крайние значения Результат вычислений нейрона в интервале [-10,10], так как:

Формула вычисления значения нейрона

На языке MQL5 эта формула будет выглядеть следующим образом:

for(int n=0; n<10; n++) 
  {
   NET+=Xn*Wn;
  }

Теперь нужно построить график функции активации в найденном интервале. Для примера возьмем сигмоидальную функцию. Проще всего это сделать в Excel.

Рис. 4. График сигмоидальной функции в Excel
Рис. 4. График сигмоидальной функции в Excel

Тут мы видим наглядно, что значения аргумента за пределами интервала [-5,5] никак не влияют на результат. Значит, область значений неполная. Попробуем это исправить. Добавим к аргументу некий добавочный коэффициент d, который расширит область значений.

Рис. 5. График сигмоидальной функции в Excel с добавочным коэффициентом
Рис. 5. График сигмоидальной функции в Excel с добавочным коэффициентом

Теперь снова посмотрим графики. Мы добавили добавочный коэффициент d=0.4, который исправил форму функции. Сравнив значения в таблице мы видим, что распределение стало более равномерным. Значит, результат запишем следующим образом:

for(int n=0; n<10; n++) 
  {
   NET+=Xn*Wn;
  }
NET*=0.4;

Теперь рассмотрим функцию активации гиперболический тангенс. Опуская всю теорию, рассмотренную в предыдущей функции, перейдем сразу к практике. Единственное, что в ней по другому - выход может принадлежать интервалу [-1,1]. Средневзвешенная сумма может принимать значения также из интервала [-10,10].

Рис. 6. График функции гиперболического тангенса в Excel с добавочным коэффициентом
Рис. 6. График функции гиперболического тангенса в Excel с добавочным коэффициентом

Из рисунка видно, что добавочный коэффициент d=0.2 исправил форму этой функции. Значит, результат запишем следующим образом:

for(int n=0;n<10;n++) 
  {
   NET+=Xn*Wn;
  }
NET*=0.2;

Таким образом можно править форму любой функции активации.


Практическое занятие

Теперь перейдем к практике. Для начала попробуем описать программно вычисление нейрона, а затем подключим к нему функцию активации. Вспомним, как вычисляется значение нейрона:

Формула вычисления значения нейрона

double NET;
double x[3];
double w[3];
int OnInit()
  {
   x[0]=0.1; // устанавливаем значение входа х1
   x[1]=0.8; // устанавливаем значение входа х2
   x[2]=0.5; // устанавливаем значение входа х3

   w[0]=0.5; // устанавливаем значение весового коэффициента w1
   w[1]=0.6; // устанавливаем значение весового коэффициента w2
   w[2]=0.3; // устанавливаем значение весового коэффициента w3

   for(int n=0;n<3;n++)
     {
      NET+=x[n]*w[n]; // суммируем средневзвешенные значения входов
     }
  }

Разберемся:

  1. Для начала мы объявили переменную для хранения решения нейрона Результат вычислений нейрона и два массива: входные данные Входные сигналы и весовые коэффициенты Весовые коэффициенты;
  2. Расположили мы эти переменные в самом начале, ни в одном из тел функций, для того чтобы сделать глобальными (доступными из любого места программы);
  3. В функции инициализации OnInit() (вообще можно в любой) мы заполняем массивы входных данных и весовых коэффициентов;
  4. Далее следует цикл суммирования, n<3 так как у нас всего три входа и соответственно три весовых коэффициента к ним;
  5. Суммируем средневзвешенные значения входов и записываем все это в переменную Результат вычислений нейрона.

С первой задачей разобрались - сумму получили. Теперь очередь функции активации. Приведу код расчета функций активации, описанных в разделе Функции активации нейронов.

Единичный скачок или жесткая пороговая функция

double Out;
if(NET>=x) Out=1;
else Out=0;

Сигмоидальная функция или сигмоид

double Out = 1/(1+exp(-NET));

Гиперболический тангенс

double Out = (exp(NET)-exp(-NET))/(exp(NET)+exp(-NET));


Соберем все вместе

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

Мы воспользуемся уже готовым советником, собранным в статье Быстрый старт или краткий курс для начинающих, но немного его изменив. К примеру, заменим трендовый индикатор Moving Average на осциллятор Relative Strength Index. Параметры индикатора и порядок их следования можно посмотреть во встроенной справке.

//+------------------------------------------------------------------+
//|                                                neuro-example.mq5 |
//|                        Copyright 2012, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2012, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>        //подключаем библиотеку для совершения торговых операций
#include <Trade\PositionInfo.mqh> //подключаем библиотеку для получения информации о позициях

//--- значения весовых коэффициентов
input double w0=0.5;
input double w1=0.5;
input double w2=0.5;
input double w3=0.5;
input double w4=0.5;
input double w5=0.5;
input double w6=0.5;
input double w7=0.5;
input double w8=0.5;
input double w9=0.5;

int               iRSI_handle;  // переменная для хранения хендла индикатора
double            iRSI_buf[];   // динамический массив для хранения значений индикатора

double            inputs[10];   // массив для хранения входных сигналов
double            weight[10];   // массив для хранения весовых коэффициентов

double            out;          // переменная для хранения выходного значения нейрона

string            my_symbol;    // переменная для хранения символа
ENUM_TIMEFRAMES   my_timeframe; // переменная для хранения таймфрейма
double            lot_size;     // переменная для хранения минимального объема совершаемой сделки

CTrade            m_Trade;      // объект для выполнения торговых операций
CPositionInfo     m_Position;   // объект для получения информации о позициях
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- сохраним текущий символ графика для дальнейшей работы советника именно на этом символе
   my_symbol=Symbol();
//--- сохраним текущий период графика для дальнейшей работы советника именно на этом периоде
   my_timeframe=PERIOD_CURRENT;
//--- сохраним минимальный объем совершаемой сделки
   lot_size=SymbolInfoDouble(my_symbol,SYMBOL_VOLUME_MIN);
//--- подключаем индикатор и получаем его хендл
   iRSI_handle=iRSI(my_symbol,my_timeframe,14,PRICE_CLOSE);
//--- проверяем наличие хендла индикатора
   if(iRSI_handle==INVALID_HANDLE)
     {
      //--- хендл не получен, выводим сообщение в лог об ошибке, завершаем работу с ошибкой
      Print("Не удалось получить хендл индикатора");
      return(-1);
     }
//--- добавляем индикатор на ценовой график
   ChartIndicatorAdd(ChartID(),0,iRSI_handle);
//--- устанавливаем индексацию для массива iRSI_buf как в таймсерии
   ArraySetAsSeries(iRSI_buf,true);
//--- переносим весовые коэффициенты в массив
   weight[0]=w0;
   weight[1]=w1;
   weight[2]=w2;
   weight[3]=w3;
   weight[4]=w4;
   weight[5]=w5;
   weight[6]=w6;
   weight[7]=w7;
   weight[8]=w8;
   weight[9]=w9;
//--- возвращаем 0, инициализация завершена
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- удаляем хэндл индикатора и освобождаем занимаемую им память
   IndicatorRelease(iRSI_handle);
//--- освобождаем динамический массив iRSI_buf от данных
   ArrayFree(iRSI_buf);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- переменная для хранения результатов работы с буфером индикатора
   int err1=0;
//--- копируем данные из индикаторного массива в динамический массив iRSI_buf для дальнейшей работы с ними
   err1=CopyBuffer(iRSI_handle,0,1,10,iRSI_buf);
//--- если есть ошибки, то выводим сообщение в лог об ошибке и выходим из функции
   if(err1<0)
     {
      Print("Не удалось скопировать данные из индикаторного буфера");
      return;
     }
//---
   double d1=0.0;                                 //нижняя граница интервала для нормализации значений
   double d2=1.0;                                 //верхняя граница интервала для нормализации значений
   double x_min=iRSI_buf[ArrayMinimum(iRSI_buf)]; //минимальное значение на интервале
   double x_max=iRSI_buf[ArrayMaximum(iRSI_buf)]; //максимальное значение на интервале

//--- В цикле заполняем массив входов значениями индикатора с предварительной нормализацией
   for(int i=0;i<ArraySize(inputs);i++)
     {
      inputs[i]=(((iRSI_buf[i]-x_min)*(d2-d1))/(x_max-x_min))+d1;
     }
//--- записываем результат вычисления нейрона в переменную out
   out=CalculateNeuron(inputs,weight);
//--- если значение выхода нейрона меньше 0.5
   if(out<0.5)
     {
      //--- если уже существует позиция по этому символу
      if(m_Position.Select(my_symbol))
        {
         //--- и тип этой позиции Sell, то закрываем ее
         if(m_Position.PositionType()==POSITION_TYPE_SELL) m_Trade.PositionClose(my_symbol);
         //--- а если тип этой позиции Buy, то выходим
         if(m_Position.PositionType()==POSITION_TYPE_BUY) return;
        }
      //--- если дошли сюда, значит позиции нет, открываем ее
      m_Trade.Buy(lot_size,my_symbol);
     }
//--- если значение выхода нейрона больше или равно 0.5
   if(out>=0.5)
     {
      //--- если уже существует позиция по этому символу
      if(m_Position.Select(my_symbol))
        {
         //--- и тип этой позиции Buy, то закрываем ее
         if(m_Position.PositionType()==POSITION_TYPE_BUY) m_Trade.PositionClose(my_symbol);
         //--- а если тип этой позиции Sell, то выходим
         if(m_Position.PositionType()==POSITION_TYPE_SELL) return;
        }
      //--- если дошли сюда, значит позиции нет, открываем ее
      m_Trade.Sell(lot_size,my_symbol);
     }
  }
//+------------------------------------------------------------------+
//|   Функция вычисления нейрона                                     |
//+------------------------------------------------------------------+
double CalculateNeuron(double &x[],double &w[])
  {
//--- переменная для хранения средневзвешенной суммы входных сигналов
   double NET=0.0;
//--- В цикле по количеству входов получаем средневзвешенную сумму входов
   for(int n=0;n<ArraySize(x);n++)
     {
      NET+=x[n]*w[n];
     }
//--- умножаем средневзвешенную сумму входов на добавочный коэффициент
   NET*=0.4;
//--- передаем средневзвешенную сумму входов в функцию активации и возвращаем ее значение
   return(ActivateNeuron(NET));
  }
//+------------------------------------------------------------------+
//|   Функция активации нейрона                                      |
//+------------------------------------------------------------------+
double ActivateNeuron(double x)
  {
//--- переменная для хранения результата функции активации
   double Out;
//--- сигмоид
   Out=1/(1+exp(-x));
//--- возвращаем значение функции активации
   return(Out);
  }
//+------------------------------------------------------------------+

Первое, что нам понадобится, так это обучить нашу сеть. Проведем оптимизация весовых коэффициентов

 Рис. 7. Тестер стратегий с установленными параметрами

Рис. 7. Тестер стратегий с установленными параметрами

Оптимизацию будем проводить со следующими параметрами:

  • Интервал - ну, к примеру, с начала года. Чем больше период, тем меньше будет подгонки под историю и тем качественнее будет результат.
  • Режим торговли - обычный, только цены открытия. Нет смысла тестировать на всех тиках, так как наш советник берет 10 последних значений с индикатора, кроме текущего.
  • Оптимизация - можно поставить полный перебор, хотя я для быстрого результата воспользовался генетическим алгоритмом. Удобно для оценки алгоритма. Если результат удовлетворяет, можно протестировать полным перебором для более точного результата.
  • Форвард-период - 1/2 и больше, позволит оценить, как долго советник сможет работать с полученными результатами до следующей переоптимизации.
  • Таймфрейм и Валютная пара - по желанию.

Рис. 8. Указываем параметры для оптимизации и их пределы

Рис. 8. Указываем параметры для оптимизации и их пределы

Укажем, что будут оптимизироваться все весовые коэффициенты и интервалы для них. Запустим оптимизацию, вернувшись на вкладку "Настройки" и нажав кнопку "Старт".

 Рис. 9. Полученные данные после оптимизации

Рис. 9. Полученные данные после оптимизации

После оптимизации на вкладке "Результаты оптимизации" выбираем тот проход, у которого максимальная прибыль (для сортировки по одному из параметров необходимо кликнуть на названию колонки). После можно оценить остальные параметры и при необходимости выбрать желаемый проход.

После делаем двойной клик на нужном проходе - проходит тестирование, с результатами которого можно ознакомиться на вкладках "Результаты" и "График".

Рис. 10. Отчет тестирования

Рис. 10. Отчет тестирования

 Рис. 11. График баланса

Рис. 11. График баланса

Рис. 12. Пример торговли советника

Рис. 12. Пример торговли советника

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


Преимущества нейронных сетей

Теперь попробуем сравнить советник на обычной логике с советником на основе нейронной сети. Для примера сравним результаты оптимизации и тестирования советника MACD Sample, идущего в стандартной поставке терминала, с нейронным советником на основе MACD.

Параметры тейк-профита и трейлинг-стопа оптимизировать не будем, так как в нейросетевом советнике они отсутствуют. Попытаемся протестировать советники с одинаковыми показателями: в обоих советниках используется MACD со следующими параметрами:

  • Период быстрой средней: 12;
  • Период медленной средней: 26;
  • Период усреднения разности: 9;
  • Тип цены: цена закрытия.

Валютную пару и тайм-фрейм можно задать при тестировании, оставим без изменения EURUSD, H1. Период тестирования одинаковый: по ценам открытия, с начала года.

MACD Sample macd-neuro-examle
Тестер Стратегий с установленными параметрами для MACD Sample
Тестер Стратегий с установленными параметрами для macd-neuro-example
Указываем параметры для оптимизации и их пределы Указываем параметры для оптимизации и их пределы
Полученные данные после оптимизации Полученные данные после оптимизации
Отчет тестирования Отчет тестирования
График баланса График баланса


Теперь сравним основные параметры протестированных советников:

Параметр MACD Sample macd-neuro-examle
Чистая прибыль 733,56 2 658,29
Абсолютная просадка по балансу 0,00 534,36
Максимальная просадка по средствам 339,50 (3,29%) 625,36 (6,23%)
Прибыльность 4,72 1,55
Фактор восстановления 2,16 4,25
Матожидание выигрыша 30,57 8,08
Коэффициент Шарпа 0,79 0,15
Всего трейдов 24 329
Всего сделок 48 658
Прибыльные трейды (% от всех) 21 (87,50%) 187 (56,84%)
Средний прибыльный трейд 44,33 39,95
Средний непрерывный выигрыш 5 2


Рис. 13. Сравнение основных параметров

Рис. 13. Сравнение основных параметров


Заключение

В данной статье мы рассмотрели основные моменты, которые необходимо знать при проектировании советников с использование нейронных сетей. Узнали о структуре нейрона и нейронной сети, видах функций активации и процессе оптимизации, методах исправления формы функции активации и процессе нормализации входных данных. Провели сравнение советников на стандартной логике и на основе нейронных сетей.

Прикрепленные файлы |
neuro-example.mq5 (7.48 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (79)
Boris Egorov
Boris Egorov | 26 февр. 2017 в 10:19

Статья супер, наверно пока единственная с более менее подробным и понятным изложением,

хотелось бы попросить автора исправить картинку, все таки в этом примере рассматривается не сеть, а перцептрон,

и очень ждем  примера именно нейронной сети, например: 2 нейрона на входе, 3 в скрытом слое, 1 на выходе

большое спасибо за статью!

Forester
Forester | 24 янв. 2018 в 17:19

Изменение крутизны функции активации совершенно не нужно делать!

Смотрим формулу:

for(int n=0; n<10; n++) 
  {
   NET+=Xn*Wn;
  }
NET*=0.4;

При обучении сеть должна подбирать множители Wn. Если для сети выгоднее чтобы итог был *0,4, то она просто подберет все веса Wn, каждый из которых уже будет * 0.4. Т.е. просто внесем общий множитель в скобки, который сам и определится по минимуму ошибки.

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

Evgeniy Scherbina
Evgeniy Scherbina | 5 мар. 2019 в 17:28

Нормализация проводится неверно, а потом еще коэффициент 0.4 зачем-то...

Предположим, есть ряд значений: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

Значения из этого ряда нужно привести к последовательности [0,1]. Логично предположить, что это будет ряд: 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.

Однако по вашей методике получаются просто случайные числа. Предположим, мы получаем значения из индикатора: 6, 7, 8, 9, 10. Упрощая вашу формулу:

Мы получаем:

6 >> 0

7 >> 0.25

8 >> 0.5

9 >> 0.75

10 >> 1

В этом ряду, нормализованном согласно указаниям из вашей статьи, верно только последнее значение.

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

Но признаю, я использовал именно эту публикацию в качестве отправной точки. Распечатал, внимательно перечитывал, делал пометки ручкой. Потом пошел в Дом книги и купил Осовского "Нейронные сети для обработки информации". Прочитал, стал очень умным, и вот пишу...

Boris Egorov
Boris Egorov | 2 нояб. 2019 в 13:40
Evgeniy Scherbina:

я суть ваших претензий не понял, а формула нормализации вообще классическая

Игорь Медведев
Игорь Медведев | 4 апр. 2022 в 17:37

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

В MetaTrader 5 появились торговые сигналы - это лучше, чем ПАММ-счета! В MetaTrader 5 появились торговые сигналы - это лучше, чем ПАММ-счета!
Мы рады объявить, что в торговой платформе MetaTrader 5 появились Торговые Сигналы - лучшее средство для инвесторов и управляющих. Теперь вы можете следить за торговыми операциями успешного трейдера, а терминал - будет автоматически повторять их на вашем счете!
Интервью с Рожериу Фигурелли (ATC 2012) Интервью с Рожериу Фигурелли (ATC 2012)
Сегодня мы поговорим о постоянном участнике из Бразилии Рожериу Фигурелли (figurelli), который с 2007 года не пропустил ни один Чемпионат. В этом году он также выставил своего конкурсного советника на продажу в Маркете наряду с другими своими продуктами. Рожериу считает, что сертификация платформы MetaTrader 5 на крупнейшей бразильской бирже BM&FBOVESPA приведет к появлению новых профессиональных разработчиков и трейдеров, которые пока не знают весь потенциал роботов-инвесторов.
Интервью с Андреем Бариновым (ATC 2012) Интервью с Андреем Бариновым (ATC 2012)
Еще в пятницу в первую неделю соревнований торговый робот Андрея Баринова (Wahoo) занимал пятое место в TOP-10. Андрей в первый раз участвует в Чемпионате, но успел уже выполнить более 100 заказов в Работе, а также выставил десяток продуктов в Маркете. Мы пообщались с ним и узнали, что создать "простой мультивалютный советник" не просто, а достаточно просто.
Как подготовить котировки MetaTrader 5 для других программ Как подготовить котировки MetaTrader 5 для других программ
В статье приводятся примеры создания каталогов, копирования данных и записи в файл, работы с инструментами из окна Обзор рынка или общего списка, примеры обработки ошибок и многое другое. В итоге всё будет собрано в один скрипт, с помощью которого можно записать в файлы данные в формате, указанном пользователем.