Инженерия признаков с Python и MQL5 (Часть III): Угол наклона цены (2) Полярные координаты
Интерес к трансформации изменений уровня цен в изменения углов не ослабевает. Как мы уже обсуждали в предыдущей статье этой серии, необходимо преодолеть множество трудностей, чтобы успешно преобразовать изменения уровней цен в угол, представляющий эти изменения.
Одним из наиболее часто упоминаемых ограничений в обсуждениях сообщества и сообщениях на форумах является отсутствие интерпретируемого смысла таких расчетов. Опытные члены сообщества часто объясняют, что между двумя линиями существует угол, поэтому попытка вычислить угол, образованный изменением цены, не имеет физического смысла в реальном мире.
Отсутствие реальной интерпретации — лишь одна из многих проблем, которую приходится преодолевать трейдерам, заинтересованным в расчете угла, создаваемого изменениями уровней цен. В нашей предыдущей статье мы попытались решить эту проблему, заменив время на ось x, чтобы образовавшийся угол представлял собой соотношение уровней цен и имел какое-то интерпретируемое значение. During our exploration, we observed that it is effortless to find our dataset riddled with "infinity" values after performing this transformation. Читатели, желающие быстро освежить в памяти то, что мы наблюдали ранее, могут быстро перейти по ссылке на статью, здесь.
Учитывая многочисленные проблемы, возникающие при попытке преобразовать изменения уровня цен в соответствующее изменение угла, а также отсутствие определенного реального значения, имеется лишь ограниченное количество структурированной информации по этому вопросу.
Мы рассмотрим проблему преобразования цены в угол с совершенно новой точки зрения. На этот раз мы будем использовать более математически сложный и надежный подход по сравнению с инструментами, которые мы создали в первой попытке. Читатели, уже знакомые с полярными координатами, могут сразу перейти к разделу "Начало работы в MQL5", чтобы увидеть, как эти математические инструменты реализованы в MQL5.
В противном случае мы теперь перейдем к пониманию того, что такое полярные координаты, и попробуем интуитивно понять, как их применять для расчета угла, образованного изменениями цен на нашем терминале MetaTrader 5, и использовать эти сигналы для торговли. То есть:
- Предложенное нами решение имеет реальный физический смысл.
- Мы также решим проблему, с которой сталкивались ранее, связанную с бесконечными или неопределенными значениями.
Что такое полярные координаты и чем они могут быть полезны?
Всякий раз, когда мы используем технологию GPS или даже простые приложения электронных таблиц, мы применяем технологию, известную как декартовы координаты. Это математическая система для представления точек на плоскости, обычно с двумя перпендикулярными осями.
Любая точка в декартовой системе представлена парой координат (x, y). x - горизонтальное расстояние от начала координат, а y - вертикальное.
Если мы хотим изучать процессы, имеющие периодические составляющие или некоторую форму кругового движения, полярные координаты подходят для этого лучше, чем декартовы. Полярные координаты — это совершенно другая система, используемая для представления точек на плоскости с использованием расстояния от точки отсчета и угла от направления отсчета, который увеличивается против часовой стрелки.
Финансовые рынки, как правило, демонстрируют закономерности, которые повторяются почти периодически. Поэтому их можно представить в виде полярных координат. Углы для расчета изменений в уровнях цен могут быть естественным образом получены путем представления уровней цен в виде полярных пар.
Полярные координаты представлены в виде пары (r, theta), где:
- R - радиальное расстояние от точки отсчета (начала координат)
- Theta - угол, измеренный от опорного направления.
Используя тригонометрические функции, реализованные в MQL5 Matrix и Vector API, мы можем легко преобразовать изменения цены в угол, представляющий изменение цены.
Чтобы достичь нашей цели, нам необходимо сначала ознакомиться с терминологией, которую мы будем использовать в сегодняшнем обсуждении. Во-первых, нам необходимо определить входные данные x и y, которые будут преобразованы. Для нашего обсуждения мы установим x как цену открытия символа, а y будет представлять цену закрытия.

Рис. 1. Определение наших декартовых точек, которые будут преобразованы в полярные точки
Теперь, когда мы определили входные данные x и y, нам нужно вычислить первый элемент полярной пары — радиальное расстояние от начала координат, r.

Рис. 2. Замкнутая формула для вычисления r по (x, y)
Геометрически полярные координаты можно представить себе как описание окружности. Угол, образованный между r и x, называется тета (theta). Таким образом, полярные координаты просто предполагают, что использование r и theta столь же информативно, как и непосредственное использование x и y. Если представить себе x и y так, как показано на рис. 3, то радиальное расстояние r рассчитывается путем применения теоремы Пифагора к сторонам x и y.

Рис. 3. Полярные координаты можно визуализировать как описание точки на окружности
Угол между r и x, тета, удовлетворяет наше стремление к реальному значению. В этом простом примере тета коррелирует с направлением тренда, формируемого изменениями цены открытия и закрытия. Тета получается путем вычисления арктангенса цены закрытия, деленного на цену открытия, как показано на рисунке ниже.

Рис. 4. Расчет theta по цене открытия (x) и закрытия (y)
Учитывая любые полярные координаты (r, theta), мы можем легко преобразовать их обратно в исходные уровни цен, используя две формулы ниже:

Рис. 5. Как преобразовать полярные координаты обратно в декартовы
До сих пор мы обсудили 4 формулы, но только последние 3 формулы содержат theta. Первая формула, которую мы используем для расчета r, не имеет никакого отношения к theta. Последние три формулы, которые мы обсудили, содержат theta и могут быть легко дифференцированы. Производные этих тригонометрических функций — хорошо известные результаты, которые можно легко найти в Интернете или в любом учебнике по элементарному анализу.
Мы будем использовать эти 3 производные в качестве дополнительных входных данных для обучения нашего компьютера изучению взаимосвязи между изменениями углов и соответствующими им изменениями уровней цен.
Начало работы в MQL5
Сначала нам нужно создать скрипт на языке MQL5, который будет извлекать исторические рыночные данные из нашего терминала MetaTrader 5, а также выполнять преобразования, чтобы предоставить нам создаваемые углы.
Сначала нам нужно определить имя CSV-файла, который мы создаем, а затем указать, сколько строк данных нужно извлечь. Поскольку количество извлекаемых баров может различаться в зависимости от вашего брокера, мы установили этот параметр в качестве входных данных для скрипта.
#property copyright "Gamuchirai Zororo Ndawana" #property link "https://www.mql5.com" #property version "1.00" #property script_show_inputs //---File name string file_name = _Symbol + " " + " Polar Coordinates.csv"; //---Amount of data requested input int size = 100; int size_fetch = size + 100;
При выполнении нашего скрипта мы создадим обработчик файлов для записи уровней цен и соответствующих им изменений угла.
void OnStart() { //---Write to file int file_handle=FileOpen(file_name,FILE_WRITE|FILE_ANSI|FILE_CSV,","); for(int i=size;i>0;i--){ if(i == size){ FileWrite(file_handle,"Time","Open","High","Low","Close","R","Theta","X Derivatie","Y Derivative","Theta Derivative"); } else{
Перейдем к расчету r, используя формулу, которую мы обсуждали на рис. 2.
double r = MathSqrt(MathPow(iOpen(_Symbol,PERIOD_CURRENT,i),2) + MathPow(iClose(_Symbol,PERIOD_CURRENT,i),2));
Theta рассчитывается через обратный тангенс отношения y к x. Это уже реализовано в API MQL5.
double theta = MathArctan2(iClose(_Symbol,PERIOD_CURRENT,i),iOpen(_Symbol,PERIOD_CURRENT,i));
Напомним, что формула для расчета x (цены открытия) приведена на рис. 5 выше. Мы можем дифференцировать эту формулу относительно theta, чтобы вычислить первую производную цены открытия. Производная cos(), как вы уже знаете, равна -sin().
double derivative_x = r * (-(MathSin(theta)));
Мы также можем вычислить производную y, поскольку нам известна производная тригонометрических функций.
double derivative_y = r * MathCos(theta);
Наконец, мы знаем первую производную theta. Однако тригонометрическая функция напрямую не реализована в API MQL5, вместо этого мы будем использовать математическое тождество для ее замены с использованием соответствующих функций MQL5.
double derivative_theta = (1/MathPow(MathCos(theta),2));
Теперь, когда мы рассчитали наши углы, мы можем приступить к записи наших данных.
FileWrite(file_handle,iTime(_Symbol,PERIOD_CURRENT,i), iOpen(_Symbol,PERIOD_CURRENT,i), iHigh(_Symbol,PERIOD_CURRENT,i), iLow(_Symbol,PERIOD_CURRENT,i), iClose(_Symbol,PERIOD_CURRENT,i), r, theta, derivative_x, derivative_y, derivative_y ); } } FileClose(file_handle); } //+---------
Анализ данных
Теперь, когда наши данные записаны в формате CSV, давайте используем их для обучения нашего компьютера торговле по образовавшимся углам. Мы будем использовать несколько библиотек Python для ускорения разработки.
import pandas as pd import numpy as np import seaborn as sns import matplotlib.pyplot as plt
Пометьте данные, чтобы отметить, выросли ли уровни цен на следующий день или упали.
data = pd.read_csv("EURUSD Polar Coordinates.csv") data["UP DOWN"] = 0 data.loc[data["Close"] < data["Close"].shift(-1),"UP DOWN"] = 1 data
Вот торговый сигнал, который мы ищем. Обратите внимание, что всякий раз, когда R и Theta увеличиваются в нашем исходном наборе данных, уровень цен никогда не падает. Этот вызов pandas ничего не возвращает, и это показывает реальный смысл поддержки полярных пар. Знание будущих значений R и Theta так же полезно, как и знание будущего уровня цен.
data.loc[(data["R"] < data["R"].shift(-1)) & (data['Theta'] < data['Theta'].shift(-1)) & (data['Close'] > data['Close'].shift(-1))
Аналогично, если мы выполним тот же запрос, но в противоположном направлении, то есть, будем искать случаи, когда R и Theta увеличились, но будущая цена упала, мы снова обнаружим, что pandas возвращает 0 случаев.
data.loc[(data["R"] > data["R"].shift(-1)) & (data['Theta'] > data['Theta'].shift(-1)) & (data['Close'] < data['Close'].shift(-1))]
Следовательно, наши торговые сигналы будут формироваться всякий раз, когда наш компьютер ожидает, что будущие значения R и Theta будут больше текущих. Двигаясь дальше, теперь мы можем визуализировать наши ценовые данные в виде точек на полярном круге. Как мы можем видеть на рис. 6, данные по-прежнему сложно эффективно разделить.
data['Theta_rescaled'] = (data['Theta'] - data['Theta'].min()) / (data['Theta'].max() - data['Theta'].min()) * (2 * np.pi) data['R_rescaled'] = (data['R'] - data['R'].min()) / (data['R'].max() - data['R'].min()) # Create the polar plot fig, ax = plt.subplots(subplot_kw={'projection': 'polar'}) # Plot data points on the polar axis ax.scatter(data['Theta_rescaled'], data['R_rescaled'],c=data["UP DOWN"], cmap='viridis', edgecolor='black', s=100) # Add plot labels ax.set_title("Polar Plot of OHLC Points") plt.colorbar(plt.cm.ScalarMappable(cmap='viridis'), ax=ax, label='1(UP) | O(DOWN)') plt.show()

Рис. 6. Визуализация наших ценовых данных в виде полярных точек на полярном круге
Давайте быстро проверим, являются ли какие-либо из наших значений нулевыми.
data.isna().any()

Рис. 7. Проверяем, являются ли какие-либо из наших значений нулевыми
Моделирование данных
Все наши значения определены. Нам также необходимо маркировать данные. Напомним, что нашей целью являются будущие значения theta и r.
LOOK_AHEAD = 1 data['R Target'] = data['R'].shift(-LOOK_AHEAD) data['Theta Target'] = data['Theta'].shift(-LOOK_AHEAD) data.dropna(inplace=True) data.reset_index(drop=True,inplace=True)
Имейте в виду, что мы хотим удалить данные за последние 2 года, чтобы использовать их для проверки нашего приложения.
#Let's entirely drop off the last 2 years of data _ = data.iloc[-((365 * 2) + 230):,:] data = data.iloc[:-((365 * 2) + 230),:] data

Рис. 8. Наш набор данных после удаления данных за последние 2 года
Давайте теперь проведем обучение, используя имеющиеся данные. В качестве модели мы будем использовать дерево с градиентным бустингом, поскольку оно особенно хорошо подходит для изучения эффектов взаимодействия.
from sklearn.ensemble import GradientBoostingRegressor from sklearn.model_selection import train_test_split,TimeSeriesSplit,cross_val_score
Теперь определим наш объект разделения временного ряда.
tscv = TimeSeriesSplit(n_splits=5,gap=LOOK_AHEAD) Определим входные данные и цели.
X = data.columns[1:-5] y = data.columns[-2:]
Разделим данные на обучающую и тестовую части.
train , test = train_test_split(data,test_size=0.5,shuffle=False)
Теперь подготовим разделение на обучающую и тестовую части.
train_X = train.loc[:,X] train_y = train.loc[:,y] test_X = test.loc[:,X] test_y = test.loc[:,y]
Разделение обучения и тестирования должно быть стандартизировано.
mean_scores = train_X.mean() std_scores = train_X.std()
Масштабируем данные.
train_X = ((train_X - mean_scores) / std_scores) test_X = ((test_X - mean_scores) / std_scores)
Инициализируем модель.
model = GradientBoostingRegressor()
Подготовим таблицу для хранения результатов.
results = pd.DataFrame(index=["Train","Test"],columns=["GBR"])
Подгоним модель под прогноз R.
results.iloc[0,0] = np.mean(np.abs(cross_val_score(model,train_X,train_y["R Target"],cv=tscv))) results.iloc[1,0] = np.mean(np.abs(cross_val_score(model,test_X,test_y["R Target"],cv=tscv))) results
| GBR | |
|---|---|
| Обучение | 0.76686 |
| Тестирование | 0.89129 |
Подгоним модель для прогнозирования Theta.
results.iloc[0,0] = np.mean(np.abs(cross_val_score(model,train_X,train_y["Theta Target"],cv=tscv))) results.iloc[1,0] = np.mean(np.abs(cross_val_score(model,test_X,test_y["Theta Target"],cv=tscv))) results
| GBR | |
|---|---|
| Обучение | 0.368166 |
| Тестирование | 0.110126 |
Экспорт в ONNX
Загружаем нужные нам библиотеки.
import onnx import skl2onnx from skl2onnx.common.data_types import FloatTensorType
Инициализируем модели.
r_model = GradientBoostingRegressor() theta_model = GradientBoostingRegressor()
Сохраним глобальные баллы стандартизации для всего набора данных в формате CSV.
mean_scores = data.loc[:,X].mean() std_scores = data.loc[:,X].std() mean_scores.to_csv("EURUSD Polar Coordinates Mean.csv") std_scores.to_csv("EURUSD Polar Coordinates Std.csv")
Нормализуем весь набор данных.
data[X] = ((data.loc[:,X] - mean_scores) / std_scores)
Подгоним модели под масштабированные данные.
r_model.fit(data.loc[:,X],data.loc[:,'R Target']) theta_model.fit(data.loc[:,X],data.loc[:,'Theta Target'])
Определим форму входных данных.
initial_types = [("float_input",FloatTensorType([1,len(X)]))]
Подготовим прототипы ONNX к сохранению.
r_model_proto = skl2onnx.convert_sklearn(r_model,initial_types=initial_types,target_opset=12) theta_model_proto = skl2onnx.convert_sklearn(theta_model,initial_types=initial_types,target_opset=12)
Сохраним файлы ONNX.
onnx.save(r_model_proto,"EURUSD D1 R Model.onnx") onnx.save(theta_model_proto,"EURUSD D1 Theta Model.onnx")
Начало работы в MQL5
Теперь мы готовы создать наше торговое приложение.
//+------------------------------------------------------------------+ //| EURUSD Polar EA.mq5 | //| Gamuchirai Ndawana | //| https://www.mql5.com/en/users/gamuchiraindawa | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Ndawana" #property link "https://www.mql5.com/en/users/gamuchiraindawa" #property version "1.00" //+------------------------------------------------------------------+ //| System Constants | //+------------------------------------------------------------------+ #define ONNX_INPUTS 9 //The total number of inputs for our onnx model #define ONNX_OUTPUTS 1 //The total number of outputs for our onnx model #define TF_1 PERIOD_D1 //The system's primary time frame #define TRADING_VOLUME 0.1 //The system's trading volume
Загрузим модели ONNX в качестве системных ресурсов.
//+------------------------------------------------------------------+ //| System Resources | //+------------------------------------------------------------------+ #resource "\\Files\\EURUSD D1 R Model.onnx" as uchar r_model_buffer[]; #resource "\\Files\\EURUSD D1 Theta Model.onnx" as uchar theta_model_buffer[];
Определим наши глобальные переменные. Мы будем использовать некоторые из этих переменных для стандартизации наших данных, хранения прогнозов нашей модели ONNX и многого другого.
//+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ double mean_values[] = {1.1884188643844635,1.1920754015799868,1.1847545720868993,1.1883860236998025,1.6806588395310122,0.7853854898794739,-1.1883860236998025,1.1884188643844635,1.1884188643844635}; double std_values[] = {0.09123896995032886,0.09116171300874902,0.0912656190371797,0.09120265318308786,0.1289537623737421,0.0021932437785043796,0.09120265318308786,0.09123896995032886,0.09123896995032886}; double current_r,current_theta; long r_model,theta_model; vectorf r_model_output = vectorf::Zeros(ONNX_OUTPUTS); vectorf theta_model_output = vectorf::Zeros(ONNX_OUTPUTS); double bid,ask; int ma_o_handler,ma_c_handler,state; double ma_o_buffer[],ma_c_buffer[];
Загрузим торговую библиотеку.
//+------------------------------------------------------------------+ //| Library | //+------------------------------------------------------------------+ #include <Trade/Trade.mqh> CTrade Trade;
Наше торговое приложение в основном состоит из обработчиков событий. На каждом этапе жизненного цикла приложения мы будем вызывать специальные функции для выполнения задач, которые соответствуют нашей цели на данный момент. Поэтому во время инициализации мы настроим наши технические индикаторы, и когда появятся новые цены, мы обновим их показания.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- if(!setup()) { Comment("Failed To Load Corretly"); return(INIT_FAILED); } Comment("Started"); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- OnnxRelease(r_model); OnnxRelease(theta_model); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- update(); } //+------------------------------------------------------------------+
Получение прогноза от нашей модели ONNX. Обратите внимание, что мы приводим все входные данные модели к типу float, чтобы гарантировать, что модель получает данные правильного формата и размера с учетом ее ожиданий.
//+------------------------------------------------------------------+ //| Get a prediction from our models | //+------------------------------------------------------------------+ void get_model_prediction(void) { //Define theta and r double o = iOpen(_Symbol,PERIOD_CURRENT,1); double h = iHigh(_Symbol,PERIOD_CURRENT,1); double l = iLow(_Symbol,PERIOD_CURRENT,1); double c = iClose(_Symbol,PERIOD_CURRENT,1); current_r = MathSqrt(MathPow(o,2) + MathPow(c,2)); current_theta = MathArctan2(c,o); vectorf model_inputs = { (float) o, (float) h, (float) l, (float) c, (float) current_r, (float) current_theta, (float)(current_r * (-(MathSin(current_theta)))), (float)(current_r * MathCos(current_theta)), (float)(1/MathPow(MathCos(current_theta),2)) }; //Standardize the model inputs for(int i = 0; i < ONNX_INPUTS;i++) { model_inputs[i] = (float)((model_inputs[i] - mean_values[i]) / std_values[i]); } //Get a prediction from our model OnnxRun(r_model,ONNX_DATA_TYPE_FLOAT,model_inputs,r_model_output); OnnxRun(theta_model,ONNX_DATA_TYPE_FLOAT,model_inputs,theta_model_output); //Give our prediction Comment(StringFormat("R: %f \nTheta: %f\nR Forecast: %f\nTheta Forecast: %f",current_r,current_theta,r_model_output[0],theta_model_output[0])); }
Обновим систему по мере поступления новых цен.
//+------------------------------------------------------------------+ //| Update system state | //+------------------------------------------------------------------+ void update(void) { static datetime time_stamp; datetime current_time = iTime(_Symbol,TF_1,0); bid = SymbolInfoDouble(_Symbol,SYMBOL_BID); ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK); if(current_time != time_stamp) { CopyBuffer(ma_o_handler,0,0,1,ma_o_buffer); CopyBuffer(ma_c_handler,0,0,1,ma_c_buffer); time_stamp = current_time; get_model_prediction(); manage_account(); if(PositionsTotal() == 0) get_signal(); } }
Перейдем к управлению аккаунтом. Если мы теряем деньги в сделке, закроем ее как можно скорее. В противном случае, если мы вошли в сделку, но скользящие средние пересекаются против нее, мы немедленно ее закрываем.
//+------------------------------------------------------------------+ //| Manage the open positions we have in the market | //+------------------------------------------------------------------+ void manage_account() { if(AccountInfoDouble(ACCOUNT_BALANCE) < AccountInfoDouble(ACCOUNT_EQUITY)) { while(PositionsTotal() > 0) Trade.PositionClose(Symbol()); } if(state == 1) { if(ma_c_buffer[0] < ma_o_buffer[0]) Trade.PositionClose(Symbol()); } if(state == -1) { if(ma_c_buffer[0] > ma_o_buffer[0]) Trade.PositionClose(Symbol()); } }
Настроим системные переменные, такие как технические индикаторы и модели ONNX.
//+------------------------------------------------------------------+ //| Setup system variables | //+------------------------------------------------------------------+ bool setup(void) { ma_o_handler = iMA(Symbol(),TF_1,50,0,MODE_SMA,PRICE_CLOSE); ma_c_handler = iMA(Symbol(),TF_1,10,0,MODE_SMA,PRICE_CLOSE); r_model = OnnxCreateFromBuffer(r_model_buffer,ONNX_DEFAULT); theta_model = OnnxCreateFromBuffer(theta_model_buffer,ONNX_DEFAULT); if(r_model == INVALID_HANDLE) return(false); if(theta_model == INVALID_HANDLE) return(false); ulong input_shape[] = {1,ONNX_INPUTS}; ulong output_shape[] = {1,ONNX_OUTPUTS}; if(!OnnxSetInputShape(r_model,0,input_shape)) return(false); if(!OnnxSetInputShape(theta_model,0,input_shape)) return(false); if(!OnnxSetOutputShape(r_model,0,output_shape)) return(false); if(!OnnxSetOutputShape(theta_model,0,output_shape)) return(false); return(true); }
Проверяем, есть ли у нас торговый сигнал. В первую очередь мы проверим ориентацию нашей стратегии пересечения скользящих средних. После этого проверим наши ожидания, полученные с помощью моделей ONNX. Таким образом, если пересечение скользящих средних дает нам медвежьи настроения, а наши модели ONNX r и theta дают нам бычьи настроения, мы не будем открывать позиции до тех пор, пока две системы не придут в согласие.
//+------------------------------------------------------------------+ //| Check if we have a trading signal | //+------------------------------------------------------------------+ void get_signal(void) { if(ma_c_buffer[0] > ma_o_buffer[0]) { if((r_model_output[0] < current_r) && (theta_model_output[0] < current_theta)) { return; } if((r_model_output[0] > current_r) && (theta_model_output[0] > current_theta)) { Trade.Buy(TRADING_VOLUME * 2,Symbol(),ask,0,0); Trade.Buy(TRADING_VOLUME * 2,Symbol(),ask,0,0); state = 1; return; } Trade.Buy(TRADING_VOLUME,Symbol(),ask,0,0); state = 1; return; } if(ma_c_buffer[0] < ma_o_buffer[0]) { if((r_model_output[0] > current_r) && (theta_model_output[0] > current_theta)) { return; } if((r_model_output[0] < current_r) && (theta_model_output[0] < current_theta)) { Trade.Sell(TRADING_VOLUME * 2,Symbol(),bid,0,0); Trade.Sell(TRADING_VOLUME * 2,Symbol(),bid,0,0); state = -1; return; } Trade.Sell(TRADING_VOLUME,Symbol(),bid,0,0); state = -1; return; } }
Мы не используем отмену определения системных констант.
//+------------------------------------------------------------------+ //| Undefine system variables we don't need | //+------------------------------------------------------------------+ #undef ONNX_INPUTS #undef ONNX_OUTPUTS #undef TF_1 //+------------------------------------------------------------------+
Тестирование системы
Давайте теперь начнем тестировать нашу систему. Напомним, что на этапе подготовки данных мы исключили данные с 1 января 2022 года, чтобы наш тест на истории отражал, насколько хорошо наша стратегия может работать на данных, которых она никогда не видела.

Рис. 9. Настройки нашего тестирования на истории
Теперь укажем начальные настройки аккаунта.

Рис. 10. Второй набор настроек для нашего критического тестирования на истории на данных вне выборки
Мы можем наблюдать кривую изменения эквити торговых сигналов, генерируемых нашей новой системой. Наша стратегия началась с баланса в USD 5000 и закончилась с балансом около USD 7000. Это хорошие результаты, которые побуждают нас продолжать изучать и пересматривать нашу стратегию.

Рис. 11. Результаты тестирования на истории, полученные нами при торговле сигналами, сгенерированными нашими угловыми преобразованиями
Давайте подробно проанализируем результаты. Точность нашей стратегии на данных вне выборки составила 88%. Это обнадеживающая информация, которая может стать для читателя хорошей отправной точкой для создания собственных приложений путем расширения функциональности и возможностей терминала MetaTrader 5, продемонстрированных нами в этом приложении. Или читатель может рассмотреть возможность использования нашей структуры в качестве руководства и полной замены нашей торговой стратегии своей собственной.

Рис. 12. Подробный анализ результатов нашего тестирования на истории
Заключение
Представленное здесь решение продемонстрировало, как реализовать потенциальное преимущество, полученное путем осмысленного преобразования изменений в уровнях цен в изменения в углах наклона. Наша процедура предоставляет вам простую и элегантную структуру для решения этой проблемы, позволяя вам наилучшим образом сочетать как торговую, так и математическую логику. Более того, в отличие от рядовых участников рынка, которые пытаются напрямую предсказать цену, теперь у вас есть альтернативные показатели, которые так же полезны, как знание самой цены, но при этом их легче прогнозировать последовательно.
| Прикрепленный файл | Описание |
|---|---|
| Polar Fetch Data | Наш настраиваемый скрипт для извлечения данных о ценах и преобразования их в полярные координаты. |
| EURUSD Polar EA | Советник, торгующий на основе сигналов, полученных при обнаружении изменений угла наклона. |
| EURUSD D1 R Model | Модель ONNX, отвечающая за прогнозирование наших будущих значений R |
| EURUSD D1 Theta Model | Модель ONNX, отвечающая за прогнозирование наших будущих значений Theta |
| EURUSD Polar Coordinates | Jupyter Notebook, который мы использовали для анализа данных, полученных с помощью скрипта MQL5 |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/17085
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Моделирование рынка (Часть 13): Сокеты (VII)
Нейросети в трейдинге: Рекуррентное моделирование микродвижений рынка (Окончание)
Алгоритм эволюции элитных кристаллов — Elite Crystal Evolution Algorithm (CEO-inspired): Практика
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
В вашем предложении есть один серьезный недостаток.
Формула r=(x^2+y^2)^0.5 работает только в том случае, если x и y соизмеримы. То есть по обеим осям у нас одинаковые единицы измерения.
В нашем случае, по оси x у нас время, а по оси y -пункты. Они несоизмеримы, секунды нельзя перевести в пункты.
Именно поэтому у вас получились несуразные 180 градусов. То есть, цена пошла в противоположном направлении - от настоящего к прошлому. Хотите углы, то стройте линейную регрессию y = a*x+b. И из значения a выводите угол. Далее сравните результат с круговым нормальным распределением. Будет интересно.