Обсуждение статьи "Как получить синхронизированные массивы для использования в алгоритмах портфельной торговли"
Обращение по начальной и конечной датам требуемого интервала времени
int CopyRates( string symbol_name, // имя символа ENUM_TIMEFRAMES timeframe, // период datetime start_time, // с какой даты datetime stop_time, // по какую дату MqlRates rates_array[] // массив, куда будут скопированы данные );
MQ не стали делать вариант этой перегрузки, когда всегда в сутках 1440 M1-баров.
При таком функционале, вроде, ничего больше придумывать не нужно.
Для интереса - вот результат вайб-кодинга на тему объединения таймсерий по символам.
matrix CopyMultipleSymbolRates(string &symbols[], ENUM_TIMEFRAMES period, datetime start_time, datetime stop_time, ulong flags = COPY_RATES_OHLC) { int sym_count = ArraySize(symbols); // Принудительно добавляем флаг вертикальной ориентации (строки = время) ulong vertical_flags = flags | COPY_RATES_VERTICAL; struct SymbolData { matrix m; datetime times[]; int cursor; int rows; int cols; }; SymbolData data[]; ArrayResize(data, sym_count); int total_cols = 0; int max_possible_rows = 0; // 1. Предварительная загрузка данных for(int i = 0; i < sym_count; i++) { // Используем вертикальный флаг: rows = время, cols = цены if(data[i].m.CopyRates(symbols[i], period, vertical_flags, start_time, stop_time)) { CopyTime(symbols[i], period, start_time, stop_time, data[i].times); data[i].rows = (int)data[i].m.Rows(); data[i].cols = (int)data[i].m.Cols(); data[i].cursor = 0; total_cols += data[i].cols; max_possible_rows += data[i].rows; } else { // Если данных нет, инициализируем пустую структуру для безопасности data[i].rows = 0; data[i].cols = 0; data[i].cursor = 0; } } matrix res_matrix; if(total_cols == 0) { return res_matrix; } res_matrix.Init(max_possible_rows, total_cols); int current_row = 0; double nan_value = (double)"NaN"; // 2. Слияние по временной сетке while(true) { datetime min_time = D'2099.12.31'; bool any_left = false; // Ищем минимальное время среди текущих курсоров всех символов for(int i = 0; i < sym_count; i++) { if(data[i].cursor < data[i].rows) { if(data[i].times[data[i].cursor] < min_time) min_time = data[i].times[data[i].cursor]; any_left = true; } } if(!any_left) { break; } int col_offset = 0; for(int i = 0; i < sym_count; i++) { int target_row_idx = -1; // Если у символа есть данные на это время if(data[i].cursor < data[i].rows && data[i].times[data[i].cursor] == min_time) { target_row_idx = data[i].cursor; data[i].cursor++; } // Если данных нет, берем предыдущий известный бар (Forward Fill) else if(data[i].cursor > 0) { target_row_idx = data[i].cursor - 1; } // Если мы нашли индекс (текущий или предыдущий), копируем всю строку цен if(target_row_idx != -1) { for(int c = 0; c < data[i].cols; c++) { res_matrix[current_row][col_offset + c] = data[i].m[target_row_idx][c]; } } else { // Если данных по символу еще не было в начале истории for(int c = 0; c < data[i].cols; c++) { res_matrix[current_row][col_offset + c] = nan_value; } } col_offset += data[i].cols; } current_row++; } // Приводим матрицу к фактическому количеству строк res_matrix.Resize(current_row, total_cols); return res_matrix; } #define DAYLONG (60 * 60 * 24) void OnStart() { string symbols[] = {"EURUSD.c", "UK100", "XAUUSD.c"}; matrix x = CopyMultipleSymbolRates(symbols, _Period, TimeCurrent() / DAYLONG * DAYLONG - 1 * DAYLONG, TimeCurrent(), COPY_RATES_OHLC | COPY_RATES_VOLUME_TICK); Print(x); }
Вроде работает правильно, после нескольких итераций исправлений логических ошибок.
Предоставляется "как есть", в ответах ИИ могут быть ошибки! ;-)
Для интереса - вот результат вайб-кодинга на тему объединения таймсерий по символам.
Вроде работает правильно, после нескольких итераций исправлений логических ошибок.
Предоставляется "как есть", в ответах ИИ могут быть ошибки! ;-)
Ещё не дочитал до конца, но уже вопрос по поводу использования ассоциативных массивов. Поскольку все ряды изначально отсортированы, то не проще ли (эффективнее) делать слияние за один проход с продвижением интераторов внутри каждого из массивов? Для доступа на чтение - индекс - история не меняется - должно работать быстрее ассоциативного.
Делаете запрос с 18.02.2026 00:00 M1-бары. EURUSD имеет бар на начало интервала, GBPUSD - нет. Чем заполнять GBPUSD-бар начала интервала?
Как видно по реализации - там будут NaN-ы - так сказать by design, намеренно, я это видел и по тестовым логам. Можно попросить ИИ в таких случаях - либо искать предыдущий отсчет, либо пропускать такое неполное начало.
Вот вариант, который ИИ оптимизировал по скорости с помощью предварительного построения общего таймлайна и также добавил дозапрос более раннего бара при необходимости.
if(data[i].rows > 0 && data[i].times[0] > global_timeline[0]) { // Запрашиваем 1 бар непосредственно ПЕРЕД start_time // Дозагрузка: берем 1 бар непосредственно перед start_time // Вместо NaN в начале цикла заполнения (п. 3) matrix m_prev; if(m_prev.CopyRates(symbols[i], period, vertical_flags, start_time, 1)) { data[i].initial_row = m_prev.Row(0); } }
PS. ИИ путается в своих вариантах, потому что плодит их на каждый чих и здесь в комменте имеется в виду конец цикла заполнения, а не начало. Надо будет в следующий раз как-нибудь с ИИ условиться обзывать каждую версию уникальным номером или именем ;-) для упрощения референсов.
PPS. И поле SymbolData.cursor - осталось от прежнего варианта лишним, нужно удалить.
PPPS. Кстати, он назвал локальную переменную m_prev, а префикс очевидно используется некоторыми стилями только для членов класса ;-), так что - тоже мелкий оформительский баг. Глаз да глаз за ним.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Опубликована статья Как получить синхронизированные массивы для использования в алгоритмах портфельной торговли:
Описан практический подход к синхронизации баров между инструментами портфеля в MQL5. Предложены классы для загрузки, хранения и выравнивания OHLCV с опциями: пустой бар или перенос значений предыдущего бара, выбор символа синхронизации и обработка асинхронных новых баров. Показаны примеры использования в индикаторах мультиграфиков и корзины. Читатель получает готовый API для стабильных портфельных расчетов.
По мере эволюции трейдера в торговле, он почти неизбежно приходит к необходимости анализа и использования в торговле портфеля инструментов. Перечислим некоторые варианты портфельной торговли и портфельного анализа:
Все перечисленные выше варианты портфельной торговли и/или портфельного анализа требуют, чтобы обрабатываемые данные (бары) инструментов были синхронизированы между собой по времени. Иначе результат может сильно отличаться от реального и от задуманного в математическом алгоритме обработки данных. Это особенно актуально на бирже, где встречается большое количество малоликвидных инструментов с многочисленными отсутствующими барами.
Автор: Dmitriy Skub