Практическое применение нейросетей в трейдинге (Часть 2). Компьютерное зрение
Введение
Существенную проблему подготовки данных при обучении нейронных сетей для их использования в трейдинге представляет собой подготовка необходимой входной информации. Например, если мы будем применять десяток индикаторов. Если эти индикаторы, к тому же, будут представлять собой набор нескольких информативных графиков. Если будем рассчитывать эти индикаторы на определенную глубину, то в результате получим до сотни входов, а в некоторых случаях и более. Можем ли мы упростить обучение нейронной сети, используя компьютерное зрение? Для решения этой задачи воспользуемся сверточными нейронными сетями, которые сейчас применяются для решения задач классификации и распознавания.
Архитектура сверточной нейронной сети
В статье будет использована сверточная нейронная сеть, архитектура которой представлена на рисунке ниже. Впрочем, она характеризует общий принцип построения Convolutional Neural Network (CNN).
В данном варианте мы имеем:
- Вход CNN, который представляет изображение размером 449x449 пикселей.
- Первый сверточный слой из 96 карт признаков. Каждая карта представляет изображение размером 447x447. Ядро свертки 3x3.
- Слой подвыборки из 96 карт признаков размером 223x223 с ядром 2x2.
- Второй сверточный слой из 32 карты признаков. Каждая карта представляет изображение размером 221x221. Ядро свертки 3x3.
- Слой подвыборки из 32 карт признаков размером 110x110 с ядром 2x2.
- Третий сверточный слой из 16 карты признаков. Каждая карта представляет изображение размером 108x108. Ядро свертки 3x3.
- Слой подвыборки из 16 карт признаков размером 54x54 с ядром 2x2. На рисунке не показан.
- Полносвязный слой из 64 нейронов.
- Выходной слой из одного нейрона. Эти два слоя представляют собой блок классификации.
Забегая вперед, отмечу. Тем, кто впервые знакомится со сверточными нейронными сетями, не стоит опасаться кажущейся на первый взгляд громоздкости и сложности построения. Нейросеть заданной архитектуры строится автоматически. Нам необходимо задать только основные параметры.
Подготовка массива изображений для обучения и тестирования нейронной сети
Перед подготовкой массива изображений нам необходимо определиться — чего же мы хотим от нашей нейронной сети. В идеале было бы обучить сеть на точках разворота. И исходя из этого посыла выхватить изображения, на которых будет отрисован крайний экстремальный бар. Поскольку, как оказалось, прикладного значения данный эксперимент не имеет, мы остановимся на другом наборе изображений. Хотя, ознакомившись с материалом этой статьи, в дальнейшем вы можете поэкспериментировать и с таким массивом. Это будет даже полезно для того, чтобы удостовериться, в том, что нейросети отлично выполняют задачи классификации на законченных образах. Но отклики сети, полученные, на непрерывном временном ряде, требуют дополнительной оптимизации.
Наш эксперимент усложнять не будем и остановимся на двух категориях изображений:
- Buy - когда цена идет вверх или когда цена достигла дневного минимума
- Sell - когда цена идет вниз или когда цена достигла дневного максимума
Как мы видим, для обучения сети движение в каком либо направлении мы будем определять как достижение ценой новых последующих экстремальных значений в направлении тенденции и в этот момент времени делать снимок графика. Для обучения сети важен и момент разворота тенденции. Также будем делать снимок графика, когда цена достигнет дневных максимума или минимума.
Предварительно подготовим график для получения нужного образа. Для этого воспользуемся шаблоном CNN.tpl. Разместите его в папке \AppData\Roaming\MetaQuotes\Terminal\D0E8209F77C8CF37AD8BF550E51FF075\MQL5\Profiles\Templates.
В свойствах графика текст определим как "White".
Экспериментируя, вы можете накидывать другие индикаторы. Эти индикаторы мною были взяты произвольно. Размер графика выставляйте произвольно, но надо найти оптимальный в зависимости от возможностей "железа".
Воспользуемся скриптом для создания массива изображений.
//+------------------------------------------------------------------+ //| CNNet.mq5 | //| Copyright 2021, Andrey Dibrov. | //| https://www.mql5.com/ru/users/tomcat66 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, Andrey Dibrov." #property link "https://www.mql5.com/ru/users/tomcat66" #property version "1.00" #property strict #property script_show_inputs input string Date="2017.01.02 00:00"; input string DateOut="2018.12.13 23:00"; input string DateTest="2019.01.02 00:00"; input string Dataset="Train"; string Date1; int count,countB,countS; int day; double DibMin; double DibMax; int HandleDate; long WIDTH; long HEIGHT; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- MqlDateTime stm; ChartSetInteger(0,CHART_SHIFT,false); ChartSetInteger(0,CHART_AUTOSCROLL,false); ChartSetInteger(0,CHART_SHOW_OBJECT_DESCR,false); WIDTH=ChartGetInteger(0,CHART_WIDTH_IN_PIXELS); ChartSetInteger(0,CHART_SHOW_PRICE_SCALE,false); if(Dataset=="Test") { HandleDate=FileOpen(Symbol()+"Date.csv",FILE_CSV|FILE_READ|FILE_WRITE|FILE_ANSI,";"); ChartNavigate(0,CHART_END,-(iBarShift(NULL,PERIOD_H1,StringToTime(DateTest)))); Sleep(1000); for(int i=iBarShift(NULL,PERIOD_H1,StringToTime(DateTest)); i>0; i--) { Date1=TimeToString(iTime(NULL,PERIOD_H1,i)); if(DateTest<=Date1) { if(ChartNavigate(0,CHART_END,-i)) { Sleep(20); if(ChartScreenShot(0, (string)count + ".png", (int)WIDTH, (int)WIDTH, ALIGN_LEFT)) { FileWrite(HandleDate,TimeToString(iTime(NULL,PERIOD_H1,i))); count++; Sleep(20); } } } } } if(Dataset=="Train") { ChartNavigate(0,CHART_END,-iBarShift(NULL,PERIOD_H1,StringToTime(Date))); Sleep(1000); for(int i=iBarShift(NULL,PERIOD_H1,StringToTime(Date)); i>=iBarShift(NULL,PERIOD_H1,StringToTime(DateOut)); i--) { TimeToStruct(iTime(NULL,PERIOD_H1,i),stm); Date1=TimeToString(iTime(NULL,PERIOD_H1,i)); if(DateOut>=Date1 && Date<=Date1) { if(ChartNavigate(0,CHART_END,-i)) { Sleep(20); if(day != stm.day) { FileCopy("Sell" + (string)countS + ".png", 0, "Buy" + (string)(countB+1) + ".png", FILE_REWRITE); FileDelete("Sell" + (string)countS + ".png", 0); FileCopy("Buy" + (string)countB + ".png", 0, "Sell" + (string)(countS+1) + ".png", FILE_REWRITE); FileDelete("Buy" + (string)countB + ".png", 0); countB ++; countS ++; } day = stm.day; if(stm.hour == 0) { DibMin = iOpen(NULL, PERIOD_H1, i); DibMax = iOpen(NULL, PERIOD_H1, i); } if(iLow(NULL, PERIOD_H1, i+1) < DibMin) { DibMin = iLow(NULL, PERIOD_H1, i+1); countS ++; ChartScreenShot(0, "Sell" + (string)countS + ".png", (int)WIDTH, (int)WIDTH, ALIGN_LEFT); } if(iHigh(NULL, PERIOD_H1, i+1) > DibMax) { DibMax = iHigh(NULL, PERIOD_H1, i+1); countB ++; ChartScreenShot(0, "Buy" +(string)countB + ".png", (int)WIDTH, (int)WIDTH, ALIGN_LEFT); } Sleep(20); } } else break; } } }
Скрипт работает в двух режимах "Train" — создаем массив картинок для обучения, и "Test" — создаем массив изображений для получения откликов NN, на основе которых будет получен индикатор. Этот индикатор в последующем будем использовать для оптимизации стратегии торговли.
Запустим скрипт в режиме "Train".
Переменная "Date" — начальная дата выборки изображений для обучения. "DateOut' — конечная дата выборки изображений для обучения. "DateTest" — начальная дата выборки изображения для получения почасовых откликов нейронной сети. Конечной датой откликов будет час запуска скрипта.
В папке ...\MQL5\Files каталога данных мы получим набор изображений Buy... и Sell.... Всего 6125.
Далее мы подготовим каталоги для обучающего множества, валидационного и тестового. Для удобства создадим на "Рабочем столе" папку "CNN" и в ней три папки "Train", "Val", "Test".
В каталогах "Train" и "Val" создадим подкаталоги "Buy" и "Sell". В каталоге "Test" — подкаталог "Resp".
Из папки ...\MQL5\Files вырежем все файлы "Buy..." и вставим в папку ...\Train\Buy. Получим 3139 изображений. Такую же процедуру проведем с файлами "Sell..." и поместим их в каталог ...\Train\Sell. У нас получилось 2986 изображений. Из папки "Buy" и "Sell" вырежем по 30% последних (с наибольшими номерами) картинок и разместим их в соответствующих папках из каталога "Val".
Получилось
- ...\Train\Buy - 2198 изображений,
- ...\Val\Buy - 941 изображение,
- ...\Train\Sell - 2091 изображение,
- ...\Val\Sell - 895 изображений.
Таким образом мы подготовили набор изображений для обучения нашей сети. Изображения в данном случае у меня получились размером 449х449 пикселей.
Подготовим набор изображений для тестирования. Запустим скрипт в режиме "Test".
В папке ...\MQL5\Files каталога данных мы получим набор последовательных почасовых снимков. На данный момент — 12558. Мы их никак не разделяем, поскольку нейросеть должна сама проводить это разделение. Вернее, давать нам рекомендацию с какой вероятностью тот или иной снимок соответствует тем условиям на которых мы обучали сеть. Движение вверх и разворот внизу. Движение вниз и разворот вверху.
Вырежем эти файлы и вставим в папку ...CNN\Test\Resp.
Итак, набор изображений для дальнейшего тестирования откликов и оптимизации стратегии мы подготовили. Оставшийся в папке ...\MQL5\Files файл с датами и временем EURUSDDate перенесем в папку CNN.
В заключение хочется отметить одну особенности MetaTrader 5. Подготавливать набор изображений было бы удобнее и надежнее с помощью эксперта в тестере стратегий, однако, почему-то MetaTrader 5 в тестере стратегий графики не "скриншотит". Но в любом случае при реализации торгового робота эта особенность нам не помешает.
Обучение нейронной сети
Для работы со сверточными нейронными сетями мы воспользуемся средой Anaconda. Данную среду нам необходимо настроить для работы с CPU и GPU (если у вас есть видеокарта NVIDIA). Данная видеокарта нам необходима, чтобы ускорить процесс обучения. Хотя она налагает и некоторые ограничения при создании архитектуры NN. Это зависимость от объема оперативной памяти видеокарты. Но в скорости обучения мы выигрываем значительно. Например, в моем случае, если на CPU одна эпоха обучения длится 20 минут, то на GPU 1-2 минуты. Если надо обучать NN на 40 эпохах, то получится 13 часов и 1,5. А если все это на стадии исследований, то понятно, что использование GPU значительно ускоряет процесс поиска NN.
- Загрузим и установим последнюю версию Anaconda Navigator. Все делаем по умолчанию.
- Из меню "Пуск\Anaconda3" запустим "Anaconda Promt".
- Введем команду "pip install tensorflow". Установим программную библиотеку для машинного обучения, разработанную компанией Google.
- Введем команду "pip install keras". Установим нейросетевую библиотеку Keras.
- Создадим новую среду conda под GPU. Наберем команду conda create --name PythonGPU. И активируем среду - activate PythonGPU.
-
Для установки tensorflow gpu наберем команду - conda create -n PythonGPU python=3.6 tensorflow-gpu. Хочу обратить внимание, что tensorflow gpu необходимо устанавливать для python 3.6.
-
Для установки keras gpu наберем команду - conda install -c anaconda keras-gpu.
-
Установим интерфейс Jupyter для программирования в среде Python GPU. Для CPU Jupyter у нас был установлен при установке Anaconda. Наберем команду - conda install jupyter.
-
Установим еще две библиотеки Pandas и Pillow - conda install -c anaconda pandas. И потом - conda install pillow. Эти библиотеки также необходимо установить для CPU если вы не будете использовать видеокарту.
-
- Теперь мы можем приступить к обучению нашей сверточной нейронной сети. В ранее созданную папку CNN поместим два файла "Train.ipynb" и "Test.ipynb". Это файлы в формате Jupyter Notebook, с которыми мы и будем работать в дальнейшем. Запустим Jupyter Notebook (PythonGPU) и откроем файл Train.
Разберем поблочно программный код.
Загружаем необходимые модули нейросетевых библиотек.
from tensorflow.python.keras.preprocessing.image import ImageDataGenerator from tensorflow.python.keras.models import Sequential from tensorflow.python.keras.layers import Conv2D, MaxPooling2D from tensorflow.python.keras.layers import Activation, Dropout, Flatten, Dense from tensorflow.keras.callbacks import ModelCheckpoint from tensorflow.keras.models import load_model
Задаем необходимые гиперпараметры.
# Каталог с данными для обучения train_dir = 'train' # Каталог с данными для проверки val_dir = 'val' # Размеры изображения img_width, img_height = 449, 449 # Размерность тензора на основе изображения для входных данных в нейронную сеть # backend Tensorflow, channels_last input_shape = (img_width, img_height, 3) # Количество эпох epochs = 20 # Размер мини-выборки batch_size = 7 # Количество изображений для обучения nb_train_samples = 4289 # Количество изображений для проверки nb_validation_samples = 1836 # Количество изображений для тестирования #nb_test_samples = 3736
Создаем архитектуру сети.
- Задаем последовательную архитектуру CNN (convolutional neural network)
- Входное изображение 449х449 пикселей, трех канальное (красный, зеленый и синий). Мы используем цветное изображение. Можно будет поэкспериментировать с двухцветным
- Создаем первый слой свертки для работы с двухмерными данными - 96 карт признаков каждая с собственным ядром свертки размером 3х3. Каждый нейрон сверточного слоя подключен к квадратному участку изображения размером 3х3
- Слой активации, использующий функцию "relu" - поскольку она менее требовательна к вычислительным ресурсам
- Для уменьшения размерности добавляем слой подвыборки с ядром 2х2 с выбором максимального значения из этого квадрата
- Далее добавляем еще два слоя свертки с 32 и 16 ядрами и по два слоя активации и подвыборки
- Преобразуем данные из двумерного формата в одномерный
- Передаем преобразованные данные на полносвязный слой, у которого 64 нейрона
- С помощью функции слоя регуляризации Dropout(0.5) пытаемся избежать переобучения
- Добавляем полносвязный выходной слой с одним нейроном. Поскольку мы используем два класса изображений и будем получать отклик от NN в бинарном виде. Мы можем экспериментировать и с несколькими классами. Допустим - два тренда и флэт. Тогда бы в выходном слое было бы три нейрона. И естественно изображения нам надо было бы разделить на три класса.
- Функция активации "sigmoid". Подходит для классификации и лучше других зарекомендовала в наших экспериментах
Этот пример архитектуры мы можем легко модернизировать — увеличивать количество слоев, их размерность, менять местами в зависимости от последовательности размерности, размерность ядер свертки, функции активации, поработать с полно связными слоями. Однако здесь возникает дилемма — при использовании GPU необходимо увеличивать оперативную память видеокарты — если мы хотим наращивать архитектуру нейросети. Либо уменьшать размер изображений. Либо жертвовать своим временем и использовать CPU.
model = Sequential() model.add(Conv2D(96, (3, 3), input_shape=input_shape)) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Conv2D(32, (3, 3))) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Conv2D(16, (3, 3))) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Flatten()) model.add(Dense(64)) model.add(Activation('relu')) model.add(Dropout(0.5)) model.add(Dense(1)) model.add(Activation('sigmoid'))
Компилируем сеть. Используем функцию ошибки binary-crossentropy. В данном варианте мы будем использовать отклик, состоящий из двух классов, которые должен принимать значения 0 или 1 в идеале. Реально же наши значения будут распределяться от 0 до 1. Оптимизатор выбираем градиентного спуска. В данном случае исходя из экспериментов, он наиболее подходит для обучения NN. Выбираем метрику аккуратность (accuracy) — процентное соотношение правильных ответов.
model.compile(loss='binary_crossentropy', optimizer='sgd', metrics=['accuracy'])
Нормализуем данные о интенсивности пикселов изображений.
datagen = ImageDataGenerator(rescale=1. / 255)
Используем генераторы Keras для чтения данных с диска и создания обучающего и валидационного массивов для NN из наших изображений. Опять-таки, class_mode выбираем binary. Shuffle выбираем False. Перемешивать наши изображения не будем.
train_generator = datagen.flow_from_directory(
train_dir,
target_size=(img_width, img_height),
batch_size=batch_size,
class_mode='binary',
shuffle=False)
val_generator = datagen.flow_from_directory(
val_dir,
target_size=(img_width, img_height),
batch_size=batch_size,
class_mode='binary',
shuffle=False)
Функция callbacks сохраняет обученную NN после каждой эпохи. Это дает нам возможность выбрать наиболее подходящую сеть по значениям ошибки и процентам "попадания".
callbacks = [ModelCheckpoint('cnn_Open{epoch:1d}.hdf5')]
Приступим непосредственно к обучению сети.
model.fit( train_generator, steps_per_epoch=nb_train_samples // batch_size, epochs=epochs, validation_data=val_generator, validation_steps=nb_validation_samples // batch_size, callbacks=callbacks)
12. Запустим программу на выполнение, как показано на рисунке.
Если все было сделано правильно, то нейросеть начнет обучаться.
После окончания процесса обучения в папке CNN будет находится 20 обученных NN.
Посмотрим на результаты обучения и определимся, какую нейронную сеть нам выбрать для дальнейшего использования.
На первый взгляд на 18 й эпохе нейросеть обучилась с результатом ошибки 30% и 85% верных результатов. Однако при проверке NN на валидационном множестве мы видим, что ошибка растет, а процент положительных ответов падает. Значит, нам необходимо выбрать нейросеть обученную на 11 й эпохе. Как мы видим, здесь наиболее подходящие результаты проверки при валидации val_loss: 0.6607 и val_accuracy: 0.6129. В идеале, конечно, необходимо стремиться к получению значений ошибки, стремящейся к 0 (или хотя бы ниже примерно 35-40%), а аккуратность стремящейся к 1 (или хотя бы выше примерно 55-60%). Тогда оптимизацию можно было бы и не делать, либо проводить по минимальным параметрам для повышения качества торгов. Но и на данных результатах обучения можно создать положительную торговую систему.
Интерпретация отклика нейронной сети
Давайте теперь проверим, имеют ли все наши труды какое-либо практическое значение.
Запустим Jupyter Notebook без поддержки GPU и откроем файл Test.jpynb из каталога CNN.
Рассмотрим поблочно.
Загружаем необходимые модули нейросетевых библиотек.
from tensorflow.python.keras.preprocessing.image import ImageDataGenerator from tensorflow.python.keras.models import Sequential from tensorflow.python.keras.layers import Conv2D, MaxPooling2D from tensorflow.python.keras.layers import Activation, Dropout, Flatten, Dense from tensorflow.keras.callbacks import ModelCheckpoint from tensorflow.keras.models import load_model import pandas as pd
Задаем необходимые параметры.
predict_dir = 'Test' img_width, img_height = 449, 449 nb_predict_samples = 12558
Считываем из файла дату и время тестируемых изображений.
Date=pd.read_csv('EURUSDDate.csv', delimiter=';',header=None)
Загружаем сохраненную после 11 й эпохи NN.
model=load_model('cnn_Open11.hdf5')
Нормализуем изображения.
datagen = ImageDataGenerator(rescale=1. / 255)
С помощью генератора читаем данные с диска.
predict_generator = datagen.flow_from_directory( predict_dir, target_size=(img_width, img_height), shuffle=False)
Получаем отклики от NN.
indicator=model.predict(predict_generator, nb_predict_samples )
Покажем результат. Процесс получения откликов длительный и это нас оповестит, что он окончен.
print(indicator)
Полученный результат сохраним в файл.
Date=pd.DataFrame(Date) Date['0'] =indicator Date.to_csv('Indicator.csv',index=False, header=False,sep=';')
Запустим программу и после окончания ее работы в папке CNN получим файл Indicator.csv.
И перенесем его в папку C:\Users\...\AppData\Roaming\MetaQuotes\Terminal\Common\Files.
На график EURUSD H1 поместим индикатор NWI.
//+------------------------------------------------------------------+ //| NWI.mq5 | //| Copyright © 2019, Andrey Dibrov. | //+------------------------------------------------------------------+ #property copyright "Copyright © 2019, Andrey Dibrov." #property link "https://www.mql5.com/ru/users/tomcat66" #property version "1.00" #property indicator_separate_window #property indicator_buffers 2 #property indicator_plots 2 #property indicator_type1 DRAW_LINE #property indicator_type2 DRAW_LINE #property indicator_color1 Red #property indicator_color2 DodgerBlue int Handle; int i; int h; input int Период=5; double ExtBuffer[]; double SignBuffer[]; datetime Date1; datetime Date0; string File_Name="Indicator.csv"; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ void OnInit() { SetIndexBuffer(0,ExtBuffer,INDICATOR_DATA); SetIndexBuffer(1,SignBuffer,INDICATOR_DATA); IndicatorSetInteger(INDICATOR_DIGITS,5); Handle=FileOpen(File_Name,FILE_CSV|FILE_SHARE_READ|FILE_ANSI|FILE_COMMON,";"); //FileClose(Handle); } //+------------------------------------------------------------------+ //| Relative Strength Index | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { MqlDateTime stm; Date0=StringToTime(FileReadString(Handle)); i=iBarShift(NULL,PERIOD_H1,Date0,false); Handle=FileOpen(File_Name,FILE_CSV|FILE_SHARE_READ|FILE_ANSI|FILE_COMMON,";"); ArraySetAsSeries(ExtBuffer,true); ArraySetAsSeries(SignBuffer,true); while(!FileIsEnding(Handle) && !IsStopped()) { Date1=StringToTime(FileReadString(Handle)); ExtBuffer[i]=StringToDouble(FileReadString(Handle)); h=Период-1; if(i>=0) { while(h>=0) { SignBuffer[i]=SignBuffer[i]+ExtBuffer[i+h]; h--; } } SignBuffer[i]=SignBuffer[i]/Период; TimeToStruct(Date1,stm); i--; } FileClose(Handle); return(rates_total); } //+------------------------------------------------------------------+
Для упрощения интерпретировать отклики от CNN будем с помощью пересечения основной лини индикатора простой средней. Воспользуемся советником TestCNN.
//+------------------------------------------------------------------+ //| TestCNN.mq5 | //| Copyright © 2019, Andrey Dibrov. | //+------------------------------------------------------------------+ #property copyright " Copyright © 2019, Andrey Dibrov." #property link "https://www.mql5.com/ru/users/tomcat66" #property version "1.00" #property strict #include<Trade\Trade.mqh> CTrade trade; input int Период=5; input int H1; input int H2; input int H3; input int H4; input int LossBuy; input int ProfitBuy; input int LossSell; input int ProfitSell; ulong TicketBuy1; ulong TicketSell0; datetime Count; double Per; double Buf_0[]; double Buf_1[]; bool send1; bool send0; int h=4; int k; int K; int bars; int Handle; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Handle=FileOpen("Indicator.csv",FILE_CSV|FILE_SHARE_READ|FILE_ANSI|FILE_COMMON,";"); while(!FileIsEnding(Handle)&& !IsStopped()) { StringToTime(FileReadString(Handle)); bars++; } FileClose(Handle); ArrayResize(Buf_0,bars); ArrayResize(Buf_1,bars); Handle=FileOpen("Indicator.csv",FILE_CSV|FILE_SHARE_READ|FILE_ANSI|FILE_COMMON,";"); while(!FileIsEnding(Handle)&& !IsStopped()) { Count=StringToTime(FileReadString(Handle)); Buf_0[k]=StringToDouble(FileReadString(Handle)); h=Период-1; if(k>=h) { while(h>=0) { Buf_1[k]=Buf_1[k]+Buf_0[k-h]; h--; } Buf_1[k]=Buf_1[k]/Период; } k++; } FileClose(Handle); int deviation=10; trade.SetDeviationInPoints(deviation); trade.SetTypeFilling(ORDER_FILLING_RETURN); trade.SetAsyncMode(true); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- MqlDateTime stm; TimeToStruct(TimeCurrent(),stm); int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); double PriceAsk=SymbolInfoDouble(_Symbol,SYMBOL_ASK); double PriceBid=SymbolInfoDouble(_Symbol,SYMBOL_BID); double SL1=NormalizeDouble(PriceBid-LossBuy*point,digits); double TP1=NormalizeDouble(PriceAsk+ProfitBuy*point,digits); double SL0=NormalizeDouble(PriceAsk+LossSell*point,digits); double TP0=NormalizeDouble(PriceBid-ProfitSell*point,digits); if(LossBuy==0) SL1=0; if(ProfitBuy==0) TP1=0; if(LossSell==0) SL0=0; if(ProfitSell==0) TP0=0; //---------Buy1 if(send1==false && K>0 && Buf_0[K-1]>Buf_1[K-1] && Buf_0[K]<Buf_1[K] && iLow(NULL,PERIOD_H1,1)<iLow(NULL,PERIOD_H1,2) && stm.hour>H1 && stm.hour<H2 && H1<H2) { send1=trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,1,PriceAsk,SL1,TP1); TicketBuy1 = trade.ResultDeal(); } if(send1==true && K>0 && Buf_0[K-1]<Buf_1[K-1] && Buf_0[K]>Buf_1[K] && iHigh(NULL,PERIOD_H1,1)>iHigh(NULL,PERIOD_H1,2)) { trade.PositionClose(TicketBuy1); send1=false; } //---------Sell0 if(send0==false && K>0 && Buf_0[K-1]<Buf_1[K-1] && Buf_0[K]>Buf_1[K] && iHigh(NULL,PERIOD_H1,1)>iHigh(NULL,PERIOD_H1,2) && stm.hour>H3 && stm.hour<H4 && H3<H4) { send0=trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,1,PriceBid,SL0,TP0); TicketSell0 = trade.ResultDeal(); } if(send0==true && K>0 && Buf_0[K-1]>Buf_1[K-1] && Buf_0[K]<Buf_1[K] && iLow(NULL,PERIOD_H1,1)<iLow(NULL,PERIOD_H1,2)) { trade.PositionClose(TicketSell0); send0=false; } K++; } //+------------------------------------------------------------------+
Оптимизировать будем одновременно по периоду сигнальной линии, времени и стоп приказам по сделкам в обоих направлениях.
Нейронная сеть обучалась на временном периоде, находящемся до линии графика, и оптимизация проводилась до вертикальной красной линии. После — тест по оптимизированным откликам от NN. На графиках выше показаны два случайных положительных результата оптимизации, которые оптимизатор показал как результат с наибольшим приоритетом.
Визуализация слоев нейронной сети и повышение качества CNN
Нейронная сеть представляется как некий черный ящик. Однако это не совсем так, поскольку мы можем посмотреть, какие признаки CNN выделяет в картах признаков в слоях. И это дает нам еще информацию для аналитики и дальнейшего повышения качества CNN. Давайте посмотрим.
Запустим из папки CNN программу Visual.ipynb.
Загружаем необходимые модули нейросетевых библиотек.
from tensorflow.python.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.models import Model, load_model import pandas as pd from tensorflow.python.keras.preprocessing import image import matplotlib.pyplot as plt import numpy as np
Загрузим сохраненную модель.
model=load_model('cnn_Open11.hdf5')
Посмотрим на архитектуру сети.
model.summary()
Нас интересует, что же изучают вот эти сверточные слои.
Загрузим какое-либо изображение.
img_path='Train/Buy/Buy81.png' img=image.load_img(img_path,target_size=(449,449)) plt.figure(figsize=(8, 8)) plt.imshow(img) plt.show
Преобразуем изображение в массив numpy и нормализуем.
x=image.img_to_array(img) x=np.expand_dims(x,axis=0) x/=255
Обрежем модель на каком-либо сверточном слое. Номера сверточных слоев 0,3,6. Начнем с нулевого. По сути, мы создаем новую, но уже обученную, модель и будем получать от нее промежуточный результат, до классификации.
model=Model(inputs=model.input, outputs=model.layers[0].output)
После мы можем посмотреть на 3 слой и 6 й.
#model=Model(inputs=model.input, outputs=model.layers[3].output)
#model=Model(inputs=model.input, outputs=model.layers[6].output)
Выведем информацию о нашей урезанной модели.
model.summary()
То есть посмотрим на первый сверточный слой с номером 0.
Получим отклик от нейросети.
model=model.predict(x)
И распечатаем одну из карт признаков под номером 18.
print(model.shape) im=model[0,:,:,18]
plt.figure(figsize=(10, 10)) plt.imshow(im) plt.show()
Как мы видим в этой карте нейросеть выделила бычьи свечи. И сразу бросается в глаза, что на данном этапе CNN сложно разделять медвежьи свечи и точки Параболика. Поскольку они заданы нами одного цвета. Теперь мы можем сделать вывод, что все отдельные элементы на нашем графике следует подавать в разных цветах.
Посмотрим на все карты признаков
rows=12 filters=model.shape[-1] size=model.shape[1] cols=filters//rows display_grid=np.zeros((cols*size,rows*size)) for col in range(cols): for row in range(rows): channel_image=model[0,:,:,col*rows+row] channel_image-=channel_image.mean() channel_image/=channel_image.std() channel_image*=64 channel_image+=128 channel_image=np.clip(channel_image,0,255).astype('uint8') display_grid[col*size:(col+1)*size,row*size:(row+1)*size]=channel_image scale=1./size plt.figure(figsize=(scale*display_grid.shape[1],scale*display_grid.shape[1])) plt.grid(False) plt.imshow(display_grid,aspect='auto',cmap='viridis')
Давайте посмотрим на третью карту признаков под номером 2.
На этой карте CNN выделяет все свечи, но с разной тенью. И за счет этого картинка становится как бы объемной. Однако мы снова обращаем внимание на то, что тени у медвежьих свечей и у Параболика одного цвета.
Давайте посмотрим на карту номер 5 следующего сверточного слоя, который у нас имеет номер, как мы помним — 3.
Здесь точки Параболика накладываются друг на друга и CNN выделяет их как признак флэтовой модели. И если мы посмотрим на предыдущий рисунок, то увидим, что нейросеть этот участок так же отрисовала иначе. То есть можно сделать вывод о расширении категорий изображений для обучения. Для обучения нейросети необходимо ввести еще одну категорию — флэтовую.
Таким образом, визуальное изучение карт признаков CNN позволит нам более четко ставить задачи на обучение. А также позволит расширить категории признаков, выделяемых CNN, и тем самым уменьшить шумовые эффекты.
Заключение
Используя общедоступные инструменты и возможности сверточных нейронных сетей, мы можем использовать интересный и нетрадиционный подход к техническому анализу. И в то же время мы можем значительно упростить подготовку данных для обучения нейронных сетей. А визуализация процессов, проходящих внутри сети, помогает проанализировать — какие входные данные наиболее влияют на качество обучения.
В заключении хочу сказать про оптимизацию. Как я отмечал ранее, оптимизация проводилась довольно простая. Однако ее следовало бы привести в соответствие с задачами, которые мы ставили перед нашей сетью, и в соответствии с ними разделили обучающий массив на категории. И уже торговый робот создавать уже с этими условиям.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Опубликована статья Практическое применение нейросетей в трейдинге (Часть 2). Компьютерное зрение:
Автор: Andrey Dibrov
Опубликована статья Практическое применение нейросетей в трейдинге (Часть 2). Компьютерное зрение:
Автор: Andrey Dibrov
Вы меня простите, но при наличии цифровых данных обрабатывать графику - это извращение...
1. Надо все делать в png =)
2. Для целей нейротрендинга подходит обучение с подкреплением... Иначе придется объяснять нейронами разницу между двумя графическими изображениями (массивами), а при наличии цифровых данных это лишний наворот. Нейронки понимают все в цифре и исходные данные тоже в цифре =)
Вы меня простите, но при наличии цифровых данных обрабатывать
Вы меня простите, но при наличии цифровых данных обрабатывать графику - это извращение...
1. Надо все делать в png =)
2. Для целей нейротрендинга подходит обучение с подкреплением... Иначе придется объяснять нейронами разницу между двумя графическими изображениями (массивами), а при наличии цифровых данных это лишний наворот. Нейронки понимают все в цифре и исходные данные тоже в цифре =)
Ну.... Графику я использую для выявления подобностей и дальнейшего их использования как фильтра. Всего-то. Посоветовал бы, также использовать нейронки для анализа текстовых сообщений...
Смысл в том, что бы не формализовать что-то в "чиселки". Зигзаг, вообще проблемный индикатор... Конкретно запаздывающий в динамике и ни о чем не говорящий...
https://youtu.be/mcQH-OqC0Bs, https://youtu.be/XL5n4X0Jdd8
тут вы глубоко ошибаетесь. Перестаньте заниматься пипсовкой с обозрением одной свечи и все сразу будет явным.
тут вы глубоко ошибаетесь. Перестаньте заниматься пипсовкой с обозрением одной свечи и все сразу будет явным.
Если мы посмотрим лучшей нейросетью, которая находится у нас в голове, на скриншот графика, который обозревает наша искусственная нейронка... То мы увидим, что на нем не одна свеча
Вы меня простите, но при наличии цифровых данных обрабатывать
Вы меня простите, но при наличии цифровых данных обрабатывать графику - это извращение...
1. Надо все делать в png =)
2. Для целей нейротрендинга подходит обучение с подкреплением... Иначе придется объяснять нейронами разницу между двумя графическими изображениями (массивами), а при наличии цифровых данных это лишний наворот. Нейронки понимают все в цифре и исходные данные тоже в цифре =)
И да, на стадии обучения, ресурсы железа значительно надо поднимать. Но это все в пределах разумного.
Но на стадии анализа и получения отклика, нейросети необходимо только изображение без дополнительных оцифрованных данных. Например мои рабочие нейросети, которые анализируют временной ряд, выстроены в цепочку и у каждой более 50-входов.
Так вот и вопрос - где лучше по извращаться?... На стадии обучения или на стадии работы.