- Типы матриц и векторов
- Создание и инициализация матриц и векторов
- Копирование матриц, векторов и массивов
- Копирование таймсерий в матрицу или вектор
- Копирование истории тиков в матрицу или вектор
- Вычисление выражений с матрицами и векторами
- Манипуляции над матрицами и векторами
- Произведения матриц и векторов
- Преобразования (разложение) матриц
- Получение статистики
- Характеристики матриц и векторов
- Решение уравнений
- Методы машинного обучения
Решение уравнений
В методах машинного обучения и задачах оптимизации часто требуется найти решение системы линейных уравнений. MQL5 содержит четыре метода, которые позволяют решать такие уравнения в зависимости от типа матрицы.
- Solve — решает линейное матричное уравнение или систему линейных алгебраических уравнений;
- LstSq — решает систему линейных алгебраических уравнений приблизительно (для неквадратных или вырожденных матриц);
- Inv — вычисляет мультипликативную обратную матрицу по отношению к квадратной невырожденной матрице методом Жордана-Гаусса;
- PInv — вычисляет псевдообратную матрицу методом Мура-Пенроузаю
Ниже приведены прототипы методов.
vector<T> matrix<T>::Solve(const vector<T> b)
vector<T> matrix<T>::LstSq(const vector<T> b)
matrix<T> matrix<T>::Inv()
matrix<T> matrix<T>::PInv()
Методы Solve и LstSq подразумевают решение системы уравнений вида A * X = B, где A — матрица, B — переданный через параметр вектор со значениями функции (или "зависимой переменной").
Попробуем применить метод LstSq для решения системы уравнений, которая представляет собой модель идеальной торговли корзиной инструментов (в нашем случае будем анализировать основные валюты Forex). Для этого на заданном количестве "исторических" баров нужно найти такие размеры лотов для каждой валюты, чтобы линия баланса стремилась к постоянно растущей прямой.
Обозначим i-ую валютную пару Si. Её котировка на баре с индексом k равна Si[k]. Нумерация баров будет идти из прошлого в будущее, как в матрицах и векторах, заполненных методом CopyRates. Таким образом, начало собранных котировок для "обучения" модели соответствует бару, помеченному у нас номером 0, но на шкале времени это будет самый старый исторический бар (из тех, что мы обрабатываем, согласно настройкам алгоритма). Бары, идущие вправо (в будущее) от него имеют номера 1, 2 и так далее, вплоть до общего количества баров, на которых пользователь закажет расчет.
Изменение цены символа между 0-м баром и N-м баром обуславливает прибыль (или убыток) к моменту N-го бара.
Учитывая множество валют, получим, например, для 1-го бара такое уравнение прибыли:
(S1[1] - S1[0]) * X1 + (S2[1] - S2[0]) * X2 + ... + (Sm[1] - Sm[0]) * Xm = B |
Здесь m — общее количество символов, Xi — размер лота каждого символа, B — плавающая прибыль (условный баланс, если зафиксировать прибыль).
Для простоты сократим запись: перейдем от абсолютных значений к приращениям цен (Ai [k] = Si [k] - Si [0]). С учетом движения по барам, получим несколько выражений для виртуальной кривой баланса:
A1[1] * X1 + A2[1] * X2 + ... + Am[1] * Xm = B[1]
|
Успешная торговля характеризуется постоянной прибылью на каждом баре, то есть модель для правостороннего вектора B — это монотонно возрастающая линия, в идеале прямая.
Реализуем эту модель и подберем для неё коэффициенты X на базе котировок. Поскольку мы еще не знаем прикладных API, то не станем кодировать полноценную торговую стратегию. Просто построим виртуальный график баланса с помощью функции GraphPlot из стандартного заголовочного файла Graphic.mqh (мы его уже использовали для демонстрации математических функций).
Полный исходный код нового примера находится в скрипте MatrixForexBasket.mq5.
Во входных параметрах позволим пользователю выбирать общее количество баров для выборки данных (BarCount), а также номер бара внутри этой выборки (BarOffset), где заканчивается условное прошлое и начинается условное будущее.
На условном прошлом будет строиться модель (решаться вышеприведенная система линейных уравнений), а на условном будущем мы выполним форвард-тест.
input int BarCount = 20; // BarCount (известная "история" и "будущее")
|
Для заполнения вектора идеальным балансом напишем функцию ConstantGrow: она будет использоваться далее при инициализации.
void ConstantGrow(vector &v)
|
Список торгуемых инструментов (основные Forex-пары) задан "жестко" в начале функции OnStart — отредактируйте его под свои требования и торговое окружение.
void OnStart()
|
Создадим матрицу rates, куда будут складываться котировки символов, вектор model с желаемой кривой баланса и вспомогательный вектор close для по-символьного запроса цен закрытия баров (данные из него будут копироваться в столбцы матрицы rates).
matrix rates(BarCount, size);
|
В цикле по символам копируем цены закрытия в вектор close, вычисляем приращения цен и записываем их в соответствующий столбец матрицы rates.
for(int i = 0; i < size; i++)
|
Особенности расчета стоимости одного пункта цены (в валюте депозита) мы рассмотрим в Главе 5.
Также стоит отметить, что на разных финансовых инструментах бары с одинаковыми индексами могут иметь разные временные метки, например, если в одной из стран был праздничный день и рынок был закрыт (вне Forex инструменты могут в принципе иметь разные расписания торговых сессий). Для решения этой проблемы, строго говоря, требуется более глубокий анализ котировок с учетом времен баров и их синхронизация перед вставкой в матрицу rates. Мы здесь это не делаем для простоты, а также поскольку рынок Forex большую часть времени функционирует по единым правилам.
Разделяем матрицу на две части: начальная часть будет использоваться для нахождения решения (это эмулирует оптимизацию на истории), а последующая часть — для форвард-теста (расчета последующих изменений баланса).
matrix split[];
|
Теперь, имея решение, построим кривую баланса на всех барах выборки (в начале будет идти идеальная "историческая" часть, а затем начнется "будущая", не использовавшаяся для подстройки модели).
vector balance = vector::Zeros(BarCount);
|
Оценим качество решения по критерию R2.
if(BarOffset > 0)
|
Для отображения кривой баланса на графике необходимо перенести данные из вектора в массив.
double array[];
|
Вот пример журнала, полученного при запуске скрипта на EURUSD,H1.
Solution (lots per symbol):
|
А вот как выглядит кривая виртуального баланса.

Виртуальный баланс торговли корзиной валют лотами согласно решению СЛАУ
Левая половина имеет более ровную форму и более высокий показатель R2, что не удивительно, потому что модель (переменные X) подстраивались именно под неё.
Ради интереса увеличим в 10 раз глубину обучения и проверки, то есть зададим в параметрах BarCount = 200 и BarOffset = 100. Получим новую картинку.

Виртуальный баланс торговли корзиной валют лотами согласно решению СЛАУ
Очевидно, что "будущая" половина выглядит более изрезанно, и можно даже сказать, что нам повезло, что она продолжает расти, несмотря на столь простую модель. Как правило, на форвард-тесте кривая виртуального баланса существенно деградирует и начинает идти вниз.
Важно отметить, что для проверки модели мы брали найденные значения X из решения системы "как есть", в то время как на практике нам потребуется их нормализовать под минимальные лоты и шаг лотов, что отрицательно скажется на результатах и приблизит их к реальности.