Simulink: в помощь разработчику эксперта
Введение
На суд читателя уже было представлено несколько замечательных статей, раскрывающих возможности Матлаба. А именно то, как этот программный продукт способен расширить инструментарий программиста для создания торговых экспертов. В своей статье я попробую проиллюстрировать работу такого мощного матлабовского пакета как Simulink.
Я хочу предложить отчасти альтернативный способ разработки механической торговой системы для трейдера. Меня побудила обратиться к такому способу сложность стоящей перед трейдером задачи – создание, верификация и тестирование МТС. Я не являюсь профессиональным программистом. И поэтому принцип «от простого к сложному» имеет для меня первостепенное значение, когда я встречаюсь с таким понятием как МТС, а точнее создание МТС. Что есть для меня простое? Прежде всего это визуализация самого процесса создания системы и логики её функционирования. А также минимум рукописного кода. Такому моему требованию вполне соответствуют возможности пакета Simulink® известного программного продукта MATLAB, который является мировым лидером среди средств визуализации математических расчётов.
В данной статье я попробую создать и протестировать МТС на основе матлабовского пакета, а затем напишу эксперт для MetaTrader 5. Причём все исторические данные для бэктестинга будут использованы из МetaТrader 5.
Во избежание терминологической путаницы я буду называть торговую систему, функционирующую в Simulink, ёмким словом МТС, а ту, что трудится в MQL5, просто экспертом.
1. Основы Simulink и Stateflow
Прежде чем приступать к конкретным действиям ознакомимся с необходимым теоретическим минимумом.
С помощью пакета Simulink®, входящего в состав MATLAB, пользователь имеет возможность моделировать, симулировать и анализировать динамические системы. При этом можно поставить вопрос о сущности самой системы, смоделировать эту систему и затем наблюдать, что с ней произойдёт.
С помощью Simulink пользователь может построить модель с нуля или изменить уже существующую модель. Пакет поддерживает разработку линейных и нелинейных систем, созданных на основе дискретного, непрерывного и гибридного поведения.
Основные свойства пакета изложены на сайте разработчика:
- Обширные и расширяемые библиотеки предопределённых блоков
- Интерактивный графический редактор для сборки и управления интуитивными блоковыми диаграммами
- Способность управлять сложными проектами посредством разделения моделей на иерархии проектных элементов
- Модельный обозреватель для навигации, создания, конфигурации и поиска всех сигналов, параметров, свойств и сгенерированного кода, связанных с вашей моделью
- Интерфейсы программирования приложений (APIs) для связи с другими программами моделирования и использования рукописного кода
- Блоки функций пакета Embedded MATLAB™ для использования алгоритмов Матлаба в пакете Simulink и реализации встроенных систем
- Режимы симуляции (нормальный, акселератор и быстрый акселератор) для проведения объяснительного симулирования или на скоростях скомпилированного С-кода с применением решателей фиксированного или переменного шага
- Графический отладчик и профилировщик для изучения результатов симуляции и последующего диагностирования работы и неожиданного поведения системы
- Полный доступ к рабочему пространству Матлаба для анализа и визуализации результатов, настройки среды моделирования и определения сигналов, параметров и тестовых данных
- Средства модельного анализа и диагностики для обеспечения слаженности модели и идентификации ошибок модели
Итак приступим к непосредственному обзору среды Simulink. Она инициализируется из уже открытого окна Матлаба двумя способами:
- с помощью команды simulink в командном окне;
- с помощью пиктограммы Simulink на панели инструментов.
Рисунок 1. Инициализация Simulink
Когда команда выполнена, появляется окно обозревателя библиотек (Simulink Library Browser).
Рисунок 2. Обозреватель библиотек
В окне обозревателя содержится дерево компонентов библиотек Simulink. Для просмотра того или иного раздела библиотеки достаточно выделить его мышью – в правой части окна Simulink Browser Library появится набор пиктограмм компонентов активного раздела библиотеки. На Рис. 2 показан основной раздел библиотеки Simulink.
С помощью меню обозревателя или кнопок его панели инструментов можно открыть окно для создания новой модели или загрузить существующую. Отмечу, что работа с Simulink происходит на фоне открытого окна системы MATLAB, в котором нередко можно наблюдать за выполняемыми операциями , если, конечно, их вывод предусмотрен программой моделирования.
Рисунок 3. Окно Simulink
Прежде всего для нашей модели изменим некоторые параметры Simulation --> Configuration Parameters. Это окно имеет ряд вкладок с довольно большим числом параметров. Нас интересует вкладка по умолчанию Solver (Решатель), где можно установить параметры решающего устройства системы моделирования Simulink.
В области Simulation time задаётся время моделирования начальным временем Start time (обычно 0) и конечным временем Stop time.
Для нашей задачи установим для Start time значение 1. Stop time пока не будем изменять.
В опциях солвера я также изменил Type на Fixed-step, сам солвер на discrete и шаг (fixed-step size) на 1.
Рисунок 4. Окно Configuration Parameters
Среду Simulink успешно дополняет подсистема Stateflow, которая является пакетом событийного моделирования, основанным на теории конечных автоматов. Он позволяет представить функционирование системы на основе цепочки правил, которые задают соответствие событий и действий, выполняемых в ответ на эти события.
Графический интерфейс пользователя пакета Stateflow имеет следующие компоненты:
- графический редактор SF-диаграмм;
- обозреватель для анализа SF-диаграмм (Stateflow Explorer);
- навигатор (Stateflow Finder) для поиска в SF-диаграммах нужных объектов;
- отладчик SF-моделей;
- генератор кодов совместно с расширением для работы в реальном времени Real Time Workshop.
Чаще всего используется блок Диаграмма (Chart), который находится в разделе Stateflow. Давайте изучим его.
Перенесём блок из библиотеки и двойным кликом откроем диаграмму. Появляется пустое окно редактора SF диаграмм. Его можно использовать для создания SF-диаграмм и их отладки с целью получения от них нужных функций.
Слева вертикальное положение занимает панель инструментов. На ней размещены 9 кнопок. Они предназначены для определения (сверху вниз):
- состояния (State);
- узла памяти (History Junction);
- перехода по умолчанию (Default Transition);
- узел альтернативы (Connective Junction);
- таблицы истинности (Truth Table);
- функции (Function);
- Embedded MATLAB функции (Embedded MATLAB Function);
- ящика (Box);
- вызова Simulink функции (Simulink Function Call).
К сожалению, в рамках данной статье невозможно подробно рассмотреть каждый элемент. Поэтому ограничусь коротким описанием тех, которые нам понадобятся для нашей модели. Более подробную информацию можно получить в разделе Help Матлаба или на сайте разработчика.
Рисунок 5. Вид SF-диаграммы в редакторе
Ключевым объектом SF-диаграммы является состояние (state). Представлено графическим объектом в виде прямоугольника со скруглёнными углами.
Оно может быть исключительным или параллельным. Каждое состояние может быть родителем, то есть иметь своих потомков. Состояния могут быть активными или неактивными. Состояния могут выполнять определённые процедуры.
Переход (transition) в виде изогнутой линии со стрелкой связывает состояния и другие объекты. Создать переход можно путём щелчка левой кнопкой мыши на объекте-источнике и направлением курсора на объект-приёмник. Переход может иметь своё условие, которое записывается в квадратных скобках. Если при этом в фигурных скобках указана процедура перехода, то она будет выполнена при истинности условия. Слэшом обозначена процедура, выполняемая при подтверждении объекта-приёмника.
Узел альтернативы (connective junction) в виде кружка позволяет переходу проходить по различным путям, каждый из которых определён тем или иным условием. При этом выбирается тот переход, которые соответствует заданному условию.
Функция (function) представляет собой объект, который задан графически посредством потокового графа, включающего процедурный язык Stateflow. Потоковый граф – это графическая конструкция, которая отражает логическую структуру с помощью переходов и узлов альтернативы.
Событие (event) ещё один важный объект Stateflow, относящийся к группе неграфических объектов. Это тот объект, который может запускать процедуры в SF-диаграмме.
Процедура (action) также не является графическим объектом. Служит для вызова функции, задания определённого события, перехода и т.п.
Данные (data) в SF-модели являются числовыми значениями. Данные не являются графическими объектами. Они могут создаваться на любом уровне иерархии модели и имеют свойства.
2. Описание торговой стратегии
Теперь коротко о том, как мы будем торговать. В учебных целях эксперт будет очень простым, если не сказать примитивным.
МТС будет открывать позиции на основе перепада уровня (фронта) сигнала, в общем, после пересечения экспоненциальных мувингов с периодом 21 и 55 (числа Фибоначчи), усредняющих цены закрытия. Т.е. если EMA 21 пересекает EMA 55 снизу вверх, то открывается длинная позиция, иначе – короткая. Для некоторого фильтрования шума позиции будут открываться на K-м баре по цене открытия бара после возникновения креста 21/55. Торговля будет вестись на паре EURUSD на часовом масштабе. Позиция будет открываться только одна. Закрываться она будет по достижению уровня Take Profit или Stop Loss.
Сразу хочу оговориться, что при разработке МТС и бэктестинге допускаются некоторые упрощения торговой картинки. Например, система не будет проверять дошёл ли запрос на исполнение сделки до брокера. Позже на каркас системы мы нанижем в MQL5 торговые ограничения.
3. Моделирование торговой стратегии в Simulink
Для начала нам необходимо загрузить исторические ценовые данные в среду Матлаба. Сделаем мы это с помощью скрипта в MetaTrader 5, который сохранит их для последующего использования (testClose.mq5).
В Матлаб эти данные (open, high, low, close, spread) будут загружены также с помощью простого m-скрипта (priceTxt.m).
С помощью стандартной матлабовской функции movavg создадим массивы экспоненциальных мувингов:
[ema21, ema55] = movavg(close, 21, 55, 'e');
А также вспомогательный массив номеров баров:
num=1:length(close);
Создадим такие переменные:
K=3; sl=0.0065; tp=0.0295;
Итак, начнём процесс моделирования. Создадим чистое окно Simulink и
при сохранении назовём его mts.
Сразу оговорюсь, что все нижеописанные действия продублированы в видео формате.
Если что-то будет не совсем понятно или совсем непонятно, то можно в любом
случае проследить за моими действиями, посмотрев ролик.
При сохранении модели система может выдать примерно такую ошибку:
??? File "C:\Simulink\mts.mdl" contains characters which are incompatible with the current character encoding, windows-1251. To avoid this error, do one of
the following:
1) Use the slCharacterEncoding function to change the current character encoding to one of: Shift_JIS, windows-1252, ISO-8859-1.
2) Remove the unsupported characters. The first unsupported character is at line 23, byte offset 15.
Для её устранения нужно просто закрыть все окна моделей и поменять кодировку с помощью таких команд:
bdclose all
set_param(0, 'CharacterEncoding', 'windows-1252');
Укажем, откуда будет поступать информация для модели. Источником такой информации послужат исторические данные из MT5, содержащие цены открытия, максимумов, минимумов и закрытия. Кроме того, будем учитывать и спрэд, хотя плавающим он стал относительно недавно. Ну и наконец, запишем время открытия бара. Для целей моделирования некоторые массивы исходных данных будем трактовать как сигнал, т.е. как вектор значений некоторой функции времени в дискретные моменты времени.
Создадим подсистему FromWorkspace для получения данных из рабочего пространства Матлаба. В Обозревателе библиотек выберем раздел Simulink, Порты и Подсистемы (Ports & Subsystems). В окно Simulink модели перетащим мышью блок «Подсистема (Subsystem)». Переименуем его на FromWorkspace, кликнув на подписи Subsystem. Затем войдём в него, дважды кликнув по блоку левой кнопкой мыши, чтобы создать входные, выходные переменные и константы для системы.
Для создания источников сигналов в Обозревателе библиотек выберем раздел Блоки обработки сигналов (Signal Processing Blockset), источники (Signal Processing Sources). В окно подсистемы FromWorkspace модели перетащим мышью блок «Сигнал из рабочего пространства (Signal From Workspace)». Так как в модели будет 4 входных сигнала, то блок скопируем и создадим ещё 3 экземпляра. Сразу же укажем, какую переменную будет обрабатывать блок. Для этого кликаем 2 раза на блок и в свойствах прописываем имя переменной. Такими переменными станут: open, ema21, ema55, num. Блоки назовём так: open signal, ema21 signal, ema55 signal, num signal.
Теперь из раздела Simulink, «Часто используемы блоки (Commonly used blocks)» добавим блок создания канала (Bus Creator). Откроем блок и изменим число входов (Number of inputs) на 4. Соединим блоки open signal, ema21 signal, ema55 signal, num signal с входами блока Bus Creator.
Кроме того у нас будет ещё 5 входных констант. Блок константы (Constant) добавляется из раздела «Часто используемы блоки ( Commonly used blocks)». В качестве значения (Constant value) указываем имена переменных: spread, high, low, tp, sl. Где:
- spread – это массив значений спрэда;
- high – это массив значений ценовых максимумов;
- low – это массив значений ценовых минимумов;
- tp – величина Take Profit в абсолютном значении;
- sl - величина Stop Loss в абсолютном значении.
Блоки назовём так: spread array, high array,low array, Take Profit, Stop Loss.
В разделе Simulink, Порты и Подсистемы (Ports & Subsystems) выберем блок порта вывода (Out1), перенесём в окно подсистемы. Сделаем 5 копий порта вывода. Первый соединим с блоком Bus Creator, а другие - поочерёдно с блоками spread array, high array,low array, Take Profit, Stop Loss.
Первый порт переименуем на price, другие – по имени выводимой переменной.
Для создания торгового сигнала вставим блок сложения (Add) из раздела Simulink, «Математические операции (Math Operations)». Зададим ему имя emas differential. В блоке изменим список знаков (List of signs) с ++ на +-. Комбинацией клавиш Ctrl+К повернём блок на 90° по часовой. Соединим блок ema21 signal с входом “+”, а ema55 signal – с “-”.
Потом вставим блок задержки (Delay) из раздела Блоки обработки сигналов (Signal Processing Blockset), операции с сигналами (Signal Operations). Зададим ему имя K Delay. В поле Delay (samples) этого блока введём имя переменной K. Соединим его с предыдущим блоком.
Блоки emas differential и K Delay формируют фронт (перепад уровня) управляющего сигнала для вычисления только на том шаге моделирования, где произошло это изменение. Подсистема, которую мы создадим немного позже, активизируется, если хотя бы в одном элементе изменяется уровень сигнала.
Затем из раздела Simulink, «Часто используемы блоки (Commonly used blocks)» добавим блок мультиплекса (Mux). Тоже повернём блок на 90° по часовой. Разобьём сигнальную линию блока задержки на две и соединим с мультиплексом.
Из раздела Stateflow вставим блок диаграммы (Chart). Зайдём в диаграмму. Добавим 2 входящих события (Buy и Sell) и 2 исходящих события (OpenBuy и OpenSell). Значение триггера (Trigger) для события Buy установим в положение Falling (активизация подсистемы отрицательным фронтом), а для события Sell – на Rising (активизация подсистемы положительным фронтом). Значение триггера (Trigger) для событий OpenBuy и OpenSell установим в положение Function call (Вызов функции) (активизация подсистемы определяется логикой работы заданной S-функции).
Создадим там переход по умолчанию, 3 узла альтернативы. Первый узел свяжем переходом со вторым, задав условие и процедуру Buy{OpenBuy;}, и третьим, задав процедуру {OpenSell;}. Свяжем вход диаграммы с мультиплексом, а два выхода – с другим мультиплексом, который можно скопировать с первого. Последний соединим с блоком порта вывода, который скопируем с аналогичного, и назовём его Buy/Sell.
Чуть было не забыл. Чтобы модель нормально работала, нам нужно создать объект виртуального канала, который будет находится в Рабочем пространстве Матлаба. Для этого заходим в Редактор Канала (Bus Editor) через меню Tools. В Редакторе выбираем пункт Добавить Канал (Add Bus). Называем его InputBus. Вставляем элементы согласно именам входных переменных: open, ema21, ema55 и num. Открываем сам блок создания канала (Bus Creator) и устанавливаем галочку возле Specify properties via bus object (Задать свойства через объект канала). Т.е. мы связали наш блок с созданным объектом виртуального канала. Виртуальный канал означает, что сигналы объединяются только графически, никак не влияя на распределение памяти.
Сохраним сделанные изменения в окне подсистемы. На этом закончим работу с подсистемой FromWorkspace.
Теперь наступает время для создания «чёрного ящика». Это будет блок, который на основе входящих сигналов будет обрабатывать информацию и принимать торговые решения. Конечно создать его должны мы, а не какая-нибудь программа. Ведь только мы можем задавать те условия, на основании которых система должна торговать. Кроме того, блок должен будет выводить информацию в виде сигналов о совершённых сделках.
Искомый блок находится в разделе Stateflow и называется Диаграмма (Chart). Мы уже знакомы с ним, не так ли? Методом “тащи и бросай” переносим его на окно нашей модели.
Рисунок 6. Блоки входной подсистемы и SF диаграммы
Открываем Диаграмму и вносим свои данные. Прежде всего создадим объект канала, как это мы уже делали в подсистеме FromWorkspace. Но если первый поставлял нам сигналы из Рабочего Пространства, то этот будет возвращать полученный результат. Итак объект будет называться OutputBus. Его элементами станут: barOpen, OpenPrice, TakeProfit, StopLoss, ClosePrice, barClose, Comment, PositionDir, posN, AccountBalance.
Теперь будем конструировать. В окне диаграммы отобразим переход по умолчанию (#1). В качестве условия и процедуры укажем:
[Input.num>=56 && Input.num>Output.barClose] {Output.barOpen=Input.num;i = Input.num-1;Output.posN++;}
Условие означает, что данные будут обрабатываться, если количество входных баров будет не менее 56, а также, если входной бар будет по очерёдности старше бара закрытия предыдущей позиции. Тогда бару открытия (Output.barOpen) присваивается номер входящего бара, служебной индексной переменной i – индекс (начинается с 0), а номер открытой позиции увеличивается на 1.
Второй переход выполняется только тогда, когда открытая позиция не будет первой. В противном случае выполнится третий переход, который присвоит переменной баланса счёта Output.AccountBalance значение 100000.
Четвёртый переход осуществится тогда, когда диаграмма была инициирована событием OpenBuy. При этом направление позиции будет указано на покупку (Output.PositionDir=1), цена открытия станет равной цене открытия бара с учётом спрэда Output.OpenPrice=Input.open+spread[i]*1e-5). Также будут заданы значения выходных сигналов StopLoss и TakeProfit.
Если произойдёт событие OpenSell, то поток пойдёт по пятому переходу, устанавливая свои значения для выходных сигналов.
Шестой переход осуществится, если позиция длинная, иначе поток пойдёт по седьмому переходу.
Восьмой переход проверит не достиг ли максимум цены бара уровня Take Profit или минимум цены бара – уровня Stop Loss. Иначе значение индексной переменной i увеличится на единицу (девятый переход).
Десятый переход проверит условия, возникающие при Stop Loss: ценовой минимум бара пробил уровень Stop Loss. В случае подтверждения поток пойдёт по 11-му переходу, а затем по 12-му, где задаются значения разницы цены закрытия и открытия позиции, текущее значение баланса счёта и номер бара закрытия.
Если десятый переход не подтвердился, то позиция закроется по Take Profit (13-й переход). А затем от 14-го поток перейдёт к 12-му переходу.
Процедуры и условия переходов для короткой позиции зеркально противоположны.
Итак, мы создали в диаграмме новые переменные. Чтобы их интегрировать в нашу модель автоматически, нужно запустить модель прямо в окне диаграммы с помощью кнопки Start Simulation. По вид она схожа с кнопкой Play в проигрывателях. Тут появится Stateflow Symbol Wizard (SF мастер объектов) и предложит сохранить наши созданные объекты. Нажмём на кнопку CheckAll (Выбрать все) и кнопку Create (Создать). Объекты созданы. Зайдём в Обозреватель Модели. Слева в столбце иерархии (Model Hierarchy) кликнем на нашу диаграмму Chart. Упорядочим объекты по типу данных (DataType).
Добавим ещё данные с помощью команды меню Add и Data. Первую переменную назовём Input. Значение Области видимости (Scope) поменяем на Input, а тип (Type) – на “Bus: <bus object name>”. И прямо в это поле типа введём имя созданного ранее канала InputBus. Т.е. наша переменная Input будет иметь тип InputBus. Значение Порта (Port) установим на единицу.
Аналогичную операцию проделаем с переменной Output. Только она должна иметь Область видимости Output и тип Output Bus.
Изменим область видимости (Scope) для переменных high, low, sl, tp, spread на значение Input. Номера портов соответственно зададим в порядке таком: 3, 4, 6, 5, 2.
Также изменим Область видимости для переменной Lots на Constant и на вкладке Value Attributes (Параметры Значения) справа в поле Initial value (Начальное значение) запишем 1, событий OpenBuy и OpenSell – на Input. В событиях поменяем значение триггера на Function call(Вызов функции).
Создадим служебную переменную len с областью видимости Constant. На вкладке Value Attributes в поле Initial value запишем m-функцию length(close). Т.е. она будет равняться длине массива close, который находится в Рабочем Пространстве Матлаба.
И для переменных high и low укажем в поле Size (Размер) такое значение [len 1]. Т.е. мы зарезервировали в памяти массивы high и low размером [len 1].
Ещё укажем для переменной K на вкладке Value Attributes (Параметры Значения) справа в поле Initial value (Начальное значение) саму переменную K из Рабочего Пространства.
В итоге имеем подсистему Chart с 7-ью входными портами и одним выходным. Блок для удобства повернём так, чтобы порт input events() находился снизу. Блок переименуем на Position handling. В самой диаграмме также отобразим название блока. Объединим блоки подсистем FromWorkspace и Position handling по соответствующим портам. И изменим цвет блоков.
Нужно заметить, что подсистема Position handling будет функциониировать только тогда, когда её «разбудят» входящие события OpenBuy или OpenSell. Таким образом, мы оптимизируем работу подсистемы, избавляясь от лишних расчётов.
Рисунок 7. Подсистемы FromWorkspace и Position handling
Нам осталось только создать подсистему вывода результатов обработки исходных ценовых данных в Рабочее Пространство Матлаба и объединить её с подсистемой Position handling. Это будет самая простая задача.
Создадим подсистему ToWorkspace для получения результатов в Рабочее Пространство. Повторим действия, которые осуществлялись при создании подсистемы FromWorkspace. В Обозревателе библиотек выберем раздел Simulink, Порты и Подсистемы (Ports & Subsystems). В окно Simulink модели перетащим мышью блок «Подсистема (Subsystem)». Переименуем его на ToWorkspace, кликнув на подписи Subsystem. Соединим блок с подсистемой Position handling.
Затем войдём в него, дважды кликнув по блоку левой кнопкой мыши, чтобы создать переменные.
Так как в подсистему будут поступать данные из объекта OutputBus, который представляет собой невиртуальный канал (nonvirtual bus), то нам нужно выбрать сигналы из этого канала. Для этого мы в Обозревателе библиотек выберем раздел Simulink, «Часто используемы
блоки ( Commonly used blocks)» добавим блок выборки канала (Bus Selector). Блок будет иметь 1 входной и только 2 выходных сигнала, тогда как нам нужно иметь 10 таких сигналов.
Подключим блок к входному порту. Нажмём на кнопку “Start simulation” (это наша кнопка Play). Компилятор начнёт построение модели. Успешно она не построится, но создаст для блока выборки канала входные сигналы. Войдём в блок и увидим, что в левой части окна появились нужные сигналы, передаваемые по каналу OutputBus. Их все нужно выбрать с помощью кнопки Select (Выбрать) и перенести в правую часть – Selected signals (Выбранные сигналы).
Рисунок 8. Параметры блока Bus Selector
Снова обратимся к разделу Обозревателя библиотек Simulink, «Часто используемы блоки (Commonly used blocks)» добавим блок мультиплекса Mux. В нём укажет число входов (Number of inputs) равное 10.
Затем зайдём раздел Обозревателя библиотек Simulink, «Приёмники сигналов ( Sinks)» и перенесём в окно подсистемы блок To Workspace. В нём укажем новое имя переменной – AccountBalance, а формат вывода (Save format) поменяем со структуры (Structure) на массив (Array). Объединим блок с мультиплексом. Удалим порт вывода, так как он не понадобится. Отформатируем по цвету блоки. Сохраним окно. Подсистема готова.
Перед построением модели проверим наличие переменных в Рабочем Пространстве. Должны присутствовать такие переменные: InputBus, K, OutputBus, close, ema21, ema55, high, low, num, open, sl, spread, tp.
Величину Stop Time зададим в качестве параметра num(end). Т.е. будет обработан вектор такой длины, которая задана последним элементом массива num.
Прежде чем начать построение модели, нужно выбрать компилятор с помощью команды:
mex –setup
Please choose your compiler for building external interface (MEX) files:
Would you like mex to locate installed compilers [y]/n? y
Select a compiler:
[1] Lcc-win32 C 2.4.1 in C:\PROGRA~2\MATLAB\R2010a\sys\lcc
[2] Microsoft Visual C++ 2008 SP1 in C:\Program Files
(x86)\Microsoft Visual Studio 9.0
[0] None
Compiler: 2
Как видно, я выбрал компилятор Microsoft Visual C++ 2008 SP1.
Итак, начнём построение. Нажимаем на кнопку “Start simulation”. Есть ошибка: Stateflow Interface Error: Port width mismatch. Input "spread"(#139) expects a scalar. The signal is a one dimensional vector with 59739 elements.
Переменная “spread” должна иметь не тип double, а наследовать тип от сигнала из Simulink. В Обозревателе
Модели для этой переменной в поле Тип (Type) укажем Inherit: Same as Simulink, а в поле Размер (Size) укажем -1. Сохраним изменения. Снова запустим модель. Теперь компилятор заработал. Он покажет некоторые несущественные предупреждения. И менее чем за 40 сек. модель обработает данные почти 60 000 баров. Торговля ведётся с '2001.01.01 00:00 ' по '2010.08.16 11:00 '. Всего открывается 461 позиция. В следующем ролике вы можете увидеть, как работает модель.
4. Реализация стратегии в MQL5
Итак, наша МТС в Simulink собрана. Теперь нужно перенести торговую идею в среду MQL5. Мы имели дело с блоками и объектами Simulink, посредством которых отражали логику действия нашего торгового эксперта. Текущая задача – перенести логику МТС в тело эксперта MQL5.
При этом нужно отметить, что некоторые блоки не обязательно должны быть как-то обозначены в коде MQL5, так как их функции могут выполняться латентно. В самом коде я максимально подробно прокомментирую, какая строка к какому блоку имеет отношение. Иногда это отношение может быть и косвенным. А иногда она может отражать интерфейсную связь блоков или объектов.
Прежде чем начать этот раздел, позвольте обратить ваше внимание на статью «Пошаговое руководство по написанию MQL5-советников для начинающих». В ней доступно изложены основные идеи и элементарные правила написания эксперта в MQL5.
На них я останавливаться не буду. Но некоторые строки кода, присущие языку MQL5 и носящие служебный характер, будут мною заимствованы.
4.1 Подсистема “FromWorkspace”
Например, у нас есть блок “open signal” в подсистеме “FromWorkspace”. В Simulink он нужен для того, чтобы получать цену открытия бара при бэктестинге, и при возникновении торгового сигнала, открывать позицию по этой цене. В коде MQL5 такой блок не присутствует явно, потому что эксперт запрашивает цену сразу после получения торгового сигнала.
В эксперте нам понадобится обрабатывать данные, получаемые от скользящих средних. Поэтому для них будут созданы динамические массивы и соответствующие вспомогательные переменные, например хэндлы.
int ma1Handle; // хэндл индикатора Moving Average 1: блок "ema21 signal" int ma2Handle; // хэндл индикатора Moving Average 2: блок "ema55 signal" ma1Handle=iMA(_Symbol,_Period,MA1_Period,0,MODE_EMA,PRICE_CLOSE); // хэндл индикатора Moving Average 1: блок "ema21 signal" ma2Handle=iMA(_Symbol,_Period,MA2_Period,0,MODE_EMA,PRICE_CLOSE);// хэндл индикатора Moving Average 2: блок "ema55 signal" double ma1Val[]; // динамический массив для хранения значений индикатора Moving Average 1 для каждого бара: блок "ema21 signal" double ma2Val[]; // динамический массив для хранения значений индикатора Moving Average 2 для каждого бара: блок "ema55 signal" ArraySetAsSeries(ma1Val,true);// массив значений индикатора MA 1: блок "ema21 signal" ArraySetAsSeries(ma2Val,true);// массив значений индикатора MA 2: блок "ema55 signal"
Все прочие строки, как-то затрагивающие мувинги ema21 и ema55, можно считать вспомогательными.
Блоки “Take Profit” и “Stop Loss”определены как переменные ввода в пунктах:
input int TakeProfit=135; // Take Profit: блок Take Profit в подсистеме FromWorkspace input int StopLoss=60; // Stop Loss: блок Stop Loss в подсистеме FromWorkspaceУчитывая, что в MQL5 по паре eurusd есть 5 значащих цифр, то величину TakeProfit и StopLoss нужно будет обновить так:
int sl,tp; sl = StopLoss; tp = TakeProfit; if(_Digits==5) { sl = sl*10; tp = tp*10; }
Блоки “spread array”, “high array” и “low array” носят служебный характер, т.к. поставляют исторические данные в виде матриц соответствующих ценовых показателей для выявления торговых условий. В явном виде в коде не представлены. Однако можно утверждать, что например блок “spread array” нужен для формирования потока цен ask. А два других – для условия закрытия позиции, которые не задаются в коде, так как автоматически выполняются в MetaTrader 5 при достижении того или иного уровня цены.
Блок “num signal” является вспомогательным, в коде эксперта не отображается.
Блок “emas differential” проверяет условия для открытия коротких или длинных позиций путём нахождения разниц. Блок “K Delay” создаёт лаг для массивов средних на величину K. Таким образом, создаётся событие (event) Buy или Sell, являющееся входным событием для подсистемы Position opening. В коде всё это выраженно так:
// создание события Buy (активизация отрицательным фронтом) bool Buy=((ma2Val[1+K]-ma1Val[1+K])>=0 && (ma2Val[K]-ma1Val[K])<0) || ((ma2Val[1+K]-ma1Val[1+K])>0 && (ma2Val[K]-ma1Val[K])==0); // создание события Sell (активизация положительным фронтом) bool Sell=((ma2Val[1+K]-ma1Val[1+K])<=0 && (ma2Val[K]-ma1Val[K])>0)|| ((ma2Val[1+K]-ma1Val[1+K])<0 && (ma2Val[K]-ma1Val[K])==0);Подсистема Position opening сама создаёт события OpenBuy и OpenSell, которые обрабатываются в подсистеме Position handling с помощью условий и процедур.
4.2 Подсистема “ Position handling ”
Подсистема начинает работать, реагируя на события OpenBuy и OpenSell.
Первый переход подсистемы имеет одним из условий наличие не менее 56 баров, что выражено в коде проверкой такого условия:
if(Bars(_Symbol,_Period)<56) // 1-ый переход подсистемы «Position handling» : условие [Input.num>=56] { Alert("Недостаточно баров!"); return(-1); }Второе условие перехода – это проверка, что номер открытия бара больше номера закрытия (Input.num>Output.barClose), т.е. что позиция уже закрыта. В коде это представлено так:
//--- 1-ый переход подсистемы «Position handling»: условие [Input.num>Output.barClose] bool IsBought = false; // купили bool IsSold = false; // продали if(PositionSelect(_Symbol)==true) // есть открытая позиция { if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) { IsBought=true; //long } else if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL) { IsSold=true; // short } } if(IsTraded(IsBought,IsSold)){ // проверка на открытые позиции return;} //+------------------------------------------------------------------+ //| Функция проверки открытой позиции | //+------------------------------------------------------------------+ bool IsTraded(bool IsBought,bool IsSold) { if(IsSold || IsBought) { Alert("Уже совершили сделку"); return(true); } else return(false); }Четвёртый переход отвечает за открытие длинной позиции. Всё, что связано с этим в коде, представлено так:
// процедуры 4-го перехода подсистемы «Position handling»: открытие длинной позиции mrequest.action = TRADE_ACTION_DEAL; // покупаем по рынку mrequest.price = NormalizeDouble(latest_price.ask,_Digits); // последняя цена ask mrequest.sl = NormalizeDouble(latest_price.bid - STP*_Point,_Digits); // ставим Stop Loss mrequest.tp = NormalizeDouble(latest_price.bid + TKP*_Point,_Digits); // ставим Take Profit mrequest.symbol = _Symbol; // инструмент mrequest.volume = Lot; // всего лотов mrequest.magic = EA_Magic; // Magic Number mrequest.type = ORDER_TYPE_BUY; // ордер на покупку mrequest.type_filling = ORDER_FILLING_FOK; // в указанном объеме и по цене равной или лучше указанной в ордере mrequest.deviation=100; // величина проскальзывания OrderSend(mrequest,mresult); if(mresult.retcode==10009 || mresult.retcode==10008) // заявка выполнена или ордер размещен { Alert("Ордер на покупку размещен, тикет #:",mresult.order); } else { Alert("Ордер на покупку не размещен; ошибка:",GetLastError()); return; }Пятый переход отвечает за открытие короткой позиции. Всё, что связано с этим в коде, представлено так:
// процедуры 5-го перехода подсистемы «Position handling»: открытие короткой позиции mrequest.action = TRADE_ACTION_DEAL; // продаём по рынку mrequest.price = NormalizeDouble(latest_price.bid,_Digits); // последняя цена bid mrequest.sl = NormalizeDouble(latest_price.ask + STP*_Point,_Digits); // ставим Stop Loss mrequest.tp = NormalizeDouble(latest_price.ask - TKP*_Point,_Digits); // ставим Take Profit mrequest.symbol = _Symbol; // инструмент mrequest.volume = Lot; // всего лотов mrequest.magic = EA_Magic; // Magic Number mrequest.type= ORDER_TYPE_SELL; // ордер на продажу mrequest.type_filling = ORDER_FILLING_FOK; // в указанном объеме и по цене равной или лучше указанной в ордере mrequest.deviation=100; // величина проскальзывания OrderSend(mrequest,mresult); if(mresult.retcode==10009 || mresult.retcode==10008) // заявка выполнена или ордер размещен { Alert("Ордер на продажу размещен, тикет #:",mresult.order); } else { Alert("Ордер на продажу не размещен; ошибка:",GetLastError()); return; }
Остальные переходы подсистемы явно в эксперте не представлены, так как соответствующие процедуры (срабатывание стопов или достижение уровня Take Profit) в MQL5 производятся автоматически.
Подсистема ToWorkspace в коде MQL5 не представлена, так как её задача относится к функции вывода результата в Рабочее Пространство Матлаба.
Выводы
Итак, на примере простой торговой идеи я создал МТС в Simulink, в которой произвёл бэктестинг простой МТС. У меня самого на первых порах возникал такой вопрос: «Стоит ли городить огород, когда можно быстро реализовать торговую систему посредством MQL5 кода?» Конечно, можно обойтись и без визуализации процесса создания системы и логики её функционирования. Но чаще всего это удаётся опытным программистам или просто талантливым людям. Когда торговая система «обрастает» новыми условиями и функциями, то наличие блоковой схемы её работы явно облегчает участь трейдера.
Также хотел бы заметить, что я не противопоставлял возможности Simulink языку MQL5, а лишь проиллюстрировал, как с помощью некоторого блокового конструктора можно создать МТС. Возможно, что разработчики языка MQL в будущем создадут некоторый визуальный конструктор стратегий, что облегчит процесс написания эксперта.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Да я скачал советник и ещё там файлик один, запустил на истории и что-то ниодной сделки он не открыл, пробовал на разный таймфреймах, может я что-то упустил...А у вас всё работает?
ZahvatkiN, прочтите пож-ста снова разделы:
2. Описание торговой стратегии
4. Реализация стратегии в MQL5
Подгрузите историю... у нас всё работает...
Поменяйте входные параметры ради интереса...
Я скопировал только 2-а файла, сам советник Experts\mts.mq5 скомпилировал без ошибок и файл Scripts\testclose.mq5, который при компиляции выдал 8 предупреждений, параметры в свойствах менял, как уровни стопа и тейка, так и мувинги, всё равно на любом тайме пульс отсутствует)) Скан ошибок прилагаеться.
Я скопировал только 2-а файла, сам советник Experts\mts.mq5 скомпилировал без ошибок и файл Scripts\testclose.mq5, который при компиляции выдал 8 предупреждений, параметры в свойствах менял, как уровни стопа и тейка, так и мувинги, всё равно на любом тайме пульс отсутствует)) Скан ошибок прилагаеться.
Причина возникновения ошибки 4756
где можно видео посмотреть/скачать ?
Привет!
А как просто к советнику добавить открытие начального лота, чтоб самому постоянно не открывать?