Discussing the article: "How to Obtain Synchronized Arrays for Use in Portfolio Trading Algorithms"
Обращение по начальной и конечной датам требуемого интервала времени
int CopyRates( string symbol_name, // symbol name ENUM_TIMEFRAMES timeframe, // period datetime start_time, // from what date datetime stop_time, // as of what date MqlRates rates_array[] // an array into which the data will be copied );
MQ decided not to implement a version of this reload where there are always 1,440 M1 bars in a day.
With this functionality, it seems there’s no need to come up with anything else.
Just for interest’s sake – here’s the result of some quick coding on the topic of merging time series by symbols.
matrix CopyMultipleSymbolRates(string &symbols[], ENUM_TIMEFRAMES period, datetime start_time, datetime stop_time, ulong flags = COPY_RATES_OHLC) { int sym_count = ArraySize(symbols); // Forcefully set the vertical orientation flag (rows = time) 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. Preloading data for(int i = 0; i < sym_count; i++) { // We use a vertical flag: rows = time, cols = prices 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 { // If there is no data, we initialise an empty structure for safety’s sake 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. Temporal grid merging while(true) { datetime min_time = D'2099.12.31'; bool any_left = false; // Find the minimum time amongst the current cursors for all characters 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 the character has data for this time if(data[i].cursor < data[i].rows && data[i].times[data[i].cursor] == min_time) { target_row_idx = data[i].cursor; data[i].cursor++; } // If there is no data, we use the previous known bar (Forward Fill) else if(data[i].cursor > 0) { target_row_idx = data[i].cursor - 1; } // If we have found an index (the current one or the previous one), we copy the entire price string 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 { // If there was no data for the character at the start of the story 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++; } // Adjust the matrix to the actual number of rows 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); }
It seems to be working correctly, after a few iterations of fixing logical errors.
Provided ‘as is’ – there may be errors in the AI’s responses! ;-)
Just for interest’s sake – here’s the result of some vibe-coding on the topic of merging time series by symbols.
It seems to be working correctly, after a few iterations of fixing logical errors.
Provided ‘as is’ – there may be errors in the AI’s responses! ;-)
It’s immediately clear that there’s no request for min_time.
I haven’t finished reading it yet, but I already have a question about the use of associative arrays. Since all the sequences are sorted from the outset, wouldn’t it be simpler (and more efficient) to perform the merge in a single pass, incrementing the iterators within each of the arrays? For read access – using an index – the data structure remains unchanged – it should be faster than an associative array.
You’re making a query for 18 February 2026 at 00:00 using M1 bars. EURUSD has a bar at the start of the interval, but GBPUSD does not. How should the GBPUSD bar at the start of the interval be filled?
As can be seen from the implementation, there will be NaNs – by design, so to speak; I’ve observed this in the test logs as well. In such cases, you can instruct the AI to either look for the previous data point or skip such an incomplete start.
Here is a version that the AI has optimised for speed by pre-building a general timeline and also added a follow-up query for an earlier bar where necessary.
if(data[i].rows > 0 && data[i].times[0] > global_timeline[0]) { // Request 1 bar immediately BEFORE start_time // Pre-loading: we take 1 bar immediately before start_time // Instead of NaN at the start of the fill loop (point 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. The AI gets confused with its own variants because it churns them out at the drop of a hat, and here in the comment we’re referring to the end of the filling cycle, not the start. Next time, we’ll need to agree with the AI to label each version with a unique number or name ;-) to simplify references.
PPS. And the SymbolData.cursor field – a leftover from the previous version – is superfluous and needs to be removed.
PPPS. By the way, it named the local variable `m_prev`, whilst the prefix is obviously used in some coding styles only for class members ;-), so that’s another minor formatting bug. Keep a close eye on it.
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
Check out the new article: How to Obtain Synchronized Arrays for Use in Portfolio Trading Algorithms.
As a trader gains experience, he or she almost inevitably comes to the point where it becomes necessary to analyze and use a portfolio of trading instruments. Let's list some examples of portfolio trading and portfolio analysis:
All of the portfolio trading and/or portfolio analysis methods listed above require that the data (bars) for the instruments being processed be time-synchronized with one another. Otherwise, the result may differ significantly from the real-world outcome and from what was intended in the mathematical data-processing algorithm. This is especially relevant on an exchange, where there are many illiquid instruments with numerous missing bars.
Author: Dmitriy Skub