Знакомство с языком MQL5 (Часть 29): Освоение API и функции WebRequest в языке MQL5 (III)
Введение
Приветствуем и снова рады видеть вас в Части 29 серии "Знакомство с языком MQL5"! В предыдущей статье мы рассмотрели элементы URL и узнали, как использовать API MQL5, чтобы получать самые свежие котировки с внешних платформ. Кроме того, вы научились читать JSON-ответ и извлекать именно ту информацию, которая вам нужна.
В этой статье мы пойдем дальше и займемся более практичным проектом. Мы свяжемся с внешней платформой и получим не только текущую цену, но полные свечные данные, включая время, цены открытия, максимума, минимума и закрытия для нескольких свечей. В этой статье мы также извлечем из этих данных каждый элемент и разложим их по отдельным массивам. Например, все цены открытия сохраним в одном массиве, все максимумы – в другом и т.д. Благодаря этому проекту вы лучше разберетесь в работе со структурированными JSON-ответами, обработке массивов в MQL5 и грамотной организации входящих рыночных данных.
Получение свечных данных с помощью функции WebRequest
На первом этапе проекта мы используем функцию WebRequest, чтобы получить свечные данные. На этот раз мы будем запрашивать полную информацию о свечах, а не только текущую цену, как в предыдущей статье. Сюда входят время, а также цены открытия, закрытия, максимума и минимума.
В этой статье мы запросим у внешней платформы данные по пяти последним дневным свечам. После получения данных мы разобьем их и сохраним каждую ценовую категорию в отдельные массивы: цены открытия, цены максимума и т.д.
Параметр method – первый аргумент функции WebRequest. Параметру method нужно присвоить значение GET, поскольку мы лишь запрашиваем данные у сервера, а не отправляем и не обновляем их. Это сообщает серверу, что нам нужны только данные.
Пример:string method = "GET";
В предыдущей статье мы разобрали различные компоненты URL. Это протокол, домен, путь и строка запроса. Понимание компонентов позволит вам точно знать, куда уйдет запрос, и какой ресурс сервер должен вернуть в ответ.
Пример:
string method = "GET"; string url = "https://api.binance.com/api/v3/klines?symbol=BTCUSDT&interval=1d&limit=5";
Все указания для сервера Binance задаются прямо в URL. Первым идет протокол https:// – он указывает на защищенное соединение. Далее идет домен api.binance.com – он сообщает MetaTrader, к какому серверу подключаться. Затем путь /api/v3/klines сообщает серверу, что нам нужны данные по свечам.
Строка запроса – это все, что идет после пути. Строка запроса начинается со знака вопроса, который показывает, что далее идут дополнительные параметры. Первый параметр (symbol=BTCUSDT) задает торговую пару, по которой мы запрашиваем данные. Символ ? используется, чтобы перейти к параметрам запроса. Например, limit=5 просит сервер вернуть последние пять свечей, а interval=1d задает дневной таймфрейм.
Аналогия:
Представьте офисный комплекс, куда вы пришли за определенными данными. Протокол https:// – это главная дорога к зданию (защищенное шифруемое соединение). Следующий элемент – api.binance.com: основной сервер, на котором Binance предоставляет доступ к своим данным.
"Маршрут" начинается, как только вы входите в здание. Представьте, что вам нужно пройти через несколько "комнат", чтобы попасть в нужный отдел Сначала вы попадаете в "комнату" /api – это своего рода центр всех API‑сервисов. Далее через дверь /v3 вы переходите к версии API v3. Наконец, "дверь" /klines ведет в раздел со свечными данными. С каждым шагом вы приближаетесь к данным, которые хотите получить.
Когда мы дошли до нужной "комнаты", строка запроса начинается со знака вопроса. Именно здесь вы впервые задаете серверу параметры запроса. Затем вы указываете symbol=BTCUSDT – это означает, что нужны данные по BTC относительно доллара США.
Символ & разделяет команды внутри запроса друг от друга. Например, мы используем символ & после первой команды для указания параметра symbol для добавления дополнительных команд. Параметр interval=1d означает, что мы запрашиваем дневные свечи. Параметр limit=5 означает, что нужны последние пять свечей.
Чем точнее вы сформируете строку запроса, тем точнее сервер поймет, какие данные нужно вернуть. Важно помнить: форматы ответов у разных платформ отличаются, поэтому всегда читайте документацию. Теперь можно настроить остальные аргументы функции WebRequest: headers (заголовки), timeout (таймаут), data[] (массив данных), result[] (массив результатов) и result_headers (заголовки ответа). Когда все настроено, мы можем вызвать функцию WebRequest, отправить запрос и получить свечные данные напрямую из Binance API.
Пример:
string method = "GET"; string url = "https://api.binance.com/api/v3/klines?symbol=BTCUSDT&interval=1d&limit=5"; string headers = ""; int time_out = 5000; char data[]; char result[]; string result_headers; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- WebRequest(method, url, headers, time_out, data, result, result_headers); string server_result = CharArrayToString(result); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- }
Когда программа отправляет запрос на сервер, это работает примерно так же, как отправка письма в компанию и ожидание ответа. Содержимое "конверта" (например адрес, тип запроса и данные, на основе которых сервер должен сформировать ответ) – это method, URL, headers, timeout и data. Как только "конверт" подготовлен, он отправляется на сервер.
Представьте, что вы входите в большое офисное здание, где вся входящая почта сначала попадает в центральную "почтовую" комнату. Там все свалено в почтовые мешки, ничего не разобрать, и все перемешано. Прежде чем кто-либо сможет понять, что именно было отправлено, письма нужно отсортировать. Первый шаг похож на то, как вы получаете от курьера запечатанный почтовый мешок. Мешок уже у вас в руках, но вы все еще не можете прочитать, что внутри. Это всего лишь неструктурированный контейнер с "сырыми" данными.
Это как будто высыпать содержимое мешка на стол и разложить письма так, чтобы их можно было прочитать. Как только вы все разложили правильно, неразборчивая "каша" превращается в ясную и хорошо организованную информацию. Теперь, когда каждое письмо стало понятным, вы можете использовать его содержимое. Здесь действует тот же принцип. Сначала вы получаете "сырой" ответ, причем в формате, который неудобно читать и разбирать. Затем вы приводите его к понятному, читабельному виду, чтобы наконец разобраться, что именно прислал сервер.
Вывод:

Результат:
[ [ 1763337600000, "94261.45000000", "96043.00000000", "91220.00000000", "92215.14000000", "39218.59806000", 1763423999999, "3674562070.23860600", 8134322, "18690.19245000", "1750979467.78626070", "0" ], [ 1763424000000, "92215.14000000", "93836.01000000", "89253.78000000", "92960.83000000", "39835.14769000", 1763510399999, "3641033186.30045840", 8786593, "20130.95957000", "1841176605.14182350", "0" ], [ 1763510400000, "92960.83000000", "92980.22000000", "88608.00000000", "91554.96000000", "32286.63760000", 1763596799999, "2925773651.25595790"
Мы не смогли вывести весь ответ сервера из-за ограничений логов советника MetaTrader 5, но программа все равно может прочитать данные целиком.
Понимание формата возвращаемых свечных данных
Следующий шаг после получения свечных данных – понять JSON-структуру, которую использует сервер. Это очень важно, потому что разные системы отправляют данные по-разному. Если сначала не разобраться со структурой, вы не поймете, как извлечь нужные значения. Перед тем как писать логику извлечения данных, всегда полезно сначала посмотреть на структуру ответа.
По небольшому фрагменту ответа, который MetaTrader 5 смог вывести в журнал, мы можем четко увидеть структуру, используемую Binance. Каждая свеча представлена массивом, и у каждого элемента этого массива свое значение. Эта структура показана в примере ниже:
[ [ 1763337600000, "94261.45000000", "96043.00000000", "91220.00000000", "92215.14000000", "39218.59806000", 1763423999999, "3674562070.23860600", 8134322, "18690.19245000", "1750979467.78626070", "0" ], [ 1763424000000, "92215.14000000", "93836.01000000", "89253.78000000", "92960.83000000", "39835.14769000", 1763510399999, "3641033186.30045840", 8786593, "20130.95957000", "1841176605.14182350", "0" ], [ 1763510400000, "92960.83000000", "92980.22000000", "88608.00000000", "91554.96000000", "32286.63760000", 1763596799999, "2925773651.25595790"
Каждый внутренний массив соответствует одной свече, и порядок значений в нем всегда один и тот же. Сначала идет время открытия свечи, затем цена открытия, цена максимума, цена минимума, цена закрытия и объем. Далее идут другие значения: время закрытия, объем в котируемом активе, количество сделок и т.д. MetaTrader 5 не смог вывести весь ответ целиком из-за ограничения по числу символов, но напечатанного фрагмента нам достаточно, чтобы понять формат.
Формат:
[ [array 1], [array 2], [array 3], [array 4], [array 5] ]
Эти внутренние массивы разделены запятыми, и каждый из них представляет одну полную свечу. Каждый массив содержит фактические данные по свече, и они идут в фиксированном порядке, принятом у Binance.
Например, один из таких массивов выглядит так:
[ 1763337600000, "94261.45000000", "96043.00000000", "91220.00000000", "92215.14000000", "39218.59806000", 1763423999999, "3674562070.23860600", 8134322, "18690.19245000", "1750979467.78626070", "0" ]
Весь этот массив целиком представляет одну свечу. Первое число – это время открытия свечи в миллисекундах, второе – цена открытия; далее идут цена максимума, цена минимума и цена закрытия. Время закрытия свечи в этом примере – 1763423999999 (также в миллисекундах). Далее идет объем в котируемом активе – "3674562070.23860600", а количество сделок задается числом 8134322. Затем указаны объем покупки базового актива "18690.19245000" и объем покупки котируемого актива "1750979467.78626070". Наконец, "0" - это неиспользуемое поле, оставленное для совместимости.
Когда структура понятна, становится легко извлекать нужные значения (open, high, low, close) и сохранять каждое из них в отдельном массиве.
Разделение значений свечи на отдельные массивы
Теперь, когда мы понимаем, какой ответ приходит с сервера, и в какой последовательности в нем расположены данные по каждой свече, следующий шаг – разделить эти значения на отдельные массивы. Это упростит дальнейший анализ и использование времени открытия, цены открытия, цены максимума, цены минимума, цены закрытия и объема в нашем индикаторе или советнике, так как мы сможем хранить их отдельно.
Поскольку каждый внутренний массив содержит полный набор данных по одной свече, мы можем считать, что индекс 0 – это самая свежая свеча, индекс 1 – предыдущая, а остальные идут далее по тому же принципу. Благодаря такой структуре легко обращаться к каждой свече по отдельности и извлекать только те значения, которые нам нужны.
Первый шаг в преобразовании "сырого" ответа сервера в отдельные массивы – выбрать символ, который позволит нам отделять одну полную свечу от другой. Поскольку запятые встречаются и внутри значений каждой свечи, попытка разделять свечи по запятой никогда не увенчается успехом. В этом случае все "разъедется", и собрать значения обратно в правильном порядке будет невозможно. Нам нужен символ, который не встречается в самих значениях, а появляется только в конце каждого набора свечных данных.
Результат:
[ [ 1763337600000, "94261.45000000", "96043.00000000", "91220.00000000", "92215.14000000", "39218.59806000", 1763423999999, "3674562070.23860600", 8134322, "18690.19245000", "1750979467.78626070", "0" ], [ 1763424000000, "92215.14000000", "93836.01000000", "89253.78000000", "92960.83000000", "39835.14769000", 1763510399999, "3641033186.30045840", 8786593, "20130.95957000", "1841176605.14182350", "0" ], [ 1763510400000, "92960.83000000", "92980.22000000", "88608.00000000", "91554.96000000", "32286.63760000", 1763596799999, "2925773651.25595790"
Мы не можем использовать запятую для разделения элементов массива, потому что хотим сохранить все дневные данные по каждой свече как единый блок. Многие значения внутри одной свечи – время открытия, цены открытия, максимума, минимума и закрытия, а также объем – уже разделены запятыми. Если попытаться отделять свечи запятыми, функция начнет дробить значения внутри свечи, вместо того чтобы воспринимать свечу как единое целое. В результате будет довольно сложно понять, какие числа относятся к какой свече.
Если использовать запятые как разделители, данные "разлетятся", а отдельные значения разных свечей фактически перемешаются. Собирать для каждой свечи правильную последовательность значений стало бы сложно и легко приводило бы к ошибкам. Тогда сама идея структурировать свечные данные для анализа потеряла бы смысл, потому что целостность каждой свечи как отдельного набора данных была бы нарушена.
Чтобы решить эту проблему, нам нужно использовать символ, который не встречается среди фактических значений свечи. Для каждой свечи этот символ должен встречаться только один раз – в идеале ближе к концу блока данных по свече. Выбирая такой символ, мы гарантируем, что каждая свеча будет четко отделяться от других, при этом сами значения останутся неизменными.
Поскольку закрывающая квадратная скобка всегда стоит в конце каждого блока со свечой, она идеально подходит на роль разделителя. Мы можем разделить данные так, чтобы каждая свеча попала в свой массив, используя закрывающую квадратную скобку как разделитель. Так мы избегаем путаницы из-за запятых внутри данных, сохраняем структуру свечей и упрощаем доступ, обработку и анализ каждой свечи по отдельности.
Пример:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- WebRequest(method, url, headers, time_out, data, result, result_headers); string server_result = CharArrayToString(result); // Print(server_result); string candle_data[]; int array_count = StringSplit(server_result,']', candle_data); Print(array_count); //--- return(INIT_SUCCEEDED); }
Пояснение:
Сначала мы объявили динамический массив строк candle_data, чтобы после разбиения большого ответа Binance API на части можно было хранить каждую свечу отдельно и независимо.
Затем мы указали MQL5 разрезать строку server_result каждый раз, когда в ней встречается символ ']'. Мы выбрали этот символ, поскольку каждая свеча в возвращаемой структуре данных заканчивается на ']'. Это единственный отличительный символ, который всегда указывает на конец каждого блока со свечой. Запятые уже используются внутри значений свечи – например, они разделяют цены открытия, максимума, минимума и закрытия, – поэтому в качестве разделителя свечей они не подходят. В то же время, символ ']' является самым безопасным вариантом, потому что он появляется только после массива каждой свечи.
Функция StringSplit требует три параметра. Первый параметр – строка, которую нужно разделить; в нашем случае это server_result. Второй параметр – символ-разделитель '\]', по которому мы делим строку. Третий параметр – это массив candle_data, в который будут записаны все части после разделения. Функция возвращает общее количество получившихся после разделения текста частей, и это число мы сохраняем в array_count.
Чтобы понять, как функция считает элементы, рассмотрим простую строку: "MQL5, ALGO, TRADING". Если использовать запятую в качестве разделителя, функция увидит две запятые, но вернет число 3. Это связано с тем, что StringSplit всегда ожидает, что после каждого разделителя должен идти еще один элемент. Поэтому функция учитывает последний элемент даже в том случае, если он окажется пустым. Наши свечные данные разделяются по той же логике. Каждый раз, когда функция встречает ']', она воспринимает это как конец предыдущей свечи и добавляет в подсчет следующую.
Вывод:

В этом примере функция StringSplit возвращает значение 7. Чтобы понять почему, достаточно посмотреть, сколько раз символ ']' встречается в ответе сервера. Поскольку каждая из пяти свечей, по которым мы запрашивали данные, заканчивается на ']', у нас уже есть пять закрывающих скобок. В начале всего ответа (перед первой свечой) уже стоит один '[', а в самом конце всей структуры стоит еще один. Значит, всего в ответе шесть закрывающих скобок.
Поскольку StringSplit всегда считает, что после разделителя должен быть еще один фрагмент текста, она добавит один лишний элемент после последнего символа ']': функция разбивает строку каждый раз, когда встречает этот символ. Поэтому метод возвращает 7, хотя в ответе всего 6 закрывающих квадратных скобок: это 6 разбиений плюс один дополнительный пустой сегмент в конце. Вот почему array_count показывает 7, хотя мы запрашивали данные только по 5 свечам.
После успешного разделения ответа сервера на части с помощью функции StringSplit следующим шагом будет вывод каждого элемента массива по отдельности. Это позволит проверить, что разделение сработало так, как задумано, и четко увидеть, что именно содержит каждая часть. Просматривая каждый элемент отдельно, мы сможем понять, какие части текста относятся к свечам, а какие – это просто пустые "хвосты", появившиеся из-за механики разбиения.
Пример:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- WebRequest(method, url, headers, time_out, data, result, result_headers); string server_result = CharArrayToString(result); // Print(server_result); string candle_data[]; int array_count = StringSplit(server_result,']', candle_data); Print(candle_data[0]); //--- return(INIT_SUCCEEDED); }
candle_data[0]:
[[1763424000000, "92215.14000000", "93836.01000000", "89253.78000000", "92960.83000000", "39835.14769000", 1763510399999, "3641033186.30045840", 8786593, "20130.95957000", "1841176605.14182350", "0"
candle_data[1]:
,[ 1763510400000, "92960.83000000", "92980.22000000", "88608.00000000", "91554.96000000", "32286.63760000", 1763596799999, "2925773651.25595790", 6822174, "15060.08451000", "1365200809.17455710", "0"
candle_data[2]:
[ 1763596800000, "91554.96000000", "93160.00000000", "86100.00000000", "86637.23000000", "39733.19073000", 1763683199999, "3548950335.09842180", 7841395, "18283.84047000", "1634256490.95743210", "0"
Извлечение времени и значений OHLC из данных первой свечи
После разделения ответа сервера у нас в массиве появляется несколько записей, и каждая из них представляет собой часть исходных данных. На этом этапе критически важно определить, какой элемент соответствует какой свече. В каждой свече есть конкретные значения (время открытия, цена открытия, цена максимума, цена минимума и цена закрытия), и они всегда расположены в предсказуемом порядке. Было бы сложно определить, какое число относится к той или иной характеристике свечи, если заранее точно не перечислить, какие значения и в каком порядке идут в массиве.
Первый шаг в этом процессе – посмотреть на шаблон первого элемента массива. Изучая структуру этого элемента (часто это самая свежая свеча), мы можем увидеть, как именно расположены разные значения. Зная порядок значений, мы можем сопоставить каждое число с соответствующей характеристикой свечи и тем самым обеспечить точную интерпретацию данных. По аналогии, как только мы разберем шаблон первой свечи, можно применить ту же логику и к остальным элементам массива. Каждый следующий элемент в массиве будет устроен аналогично, потому что ответ сервера имеет единообразную структуру для каждой свечи. Это единообразие гарантирует корректное сопоставление данных каждой свечи и упрощает автоматизацию процесса идентификации.
Наконец, нужно идентифицировать каждый компонент, чтобы перейти к дальнейшей обработке и анализу. После точной привязки каждого значения к своей свече мы сможем проводить расчеты, объединять связанные значения для сравнения и преобразовывать строковые представления в числа. Этот этап критически важен при работе с ответами API в MQL5, потому что он служит основой для всех дальнейших операций со свечными данными.
candle_data[0]:
[[ 1763424000000, // opening time "92215.14000000", // open price "93836.01000000", // high price "89253.78000000", // low price "92960.83000000", // close price "39835.14769000", // volume 1763510399999, // closing time "3641033186.30045840", // quote asset volume 8786593, // number of trades "20130.95957000", // taker buy base asset volume "1841176605.14182350", // taker buy quote asset volume "0" // placeholder
По индексу 0 можно довольно легко извлечь время открытия и значения OHLC: достаточно удалить лишние символы и затем разделить строку по запятым.
Пример://+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- WebRequest(method, url, headers, time_out, data, result, result_headers); string server_result = CharArrayToString(result); // Print(server_result); string candle_data[]; int array_count = StringSplit(server_result,']', candle_data); string day1_data = candle_data[0]; StringReplace(day1_data,"[[",""); StringReplace(day1_data,"\"",""); Print(day1_data); //--- return(INIT_SUCCEEDED); }
Пояснение:
Чтобы сохранить информацию из первого элемента массива свечных данных, сначала создадим строковую переменную. Это упрощает работу с одной свечой и при этом не затрагивает остальные данные. Прежде чем извлекать полезные значения, нужно очистить содержимое этой переменной: в нем все еще есть лишние символы, например, двойные квадратные скобки и кавычки.
Для удаления этих лишних символов используется функция StringReplace. Для работы этой функции нужны три параметра. Первым параметром идет текст, который нужно очистить. Второй параметр – это точный символ или группа символов, которые нужно удалить. Третий параметр задает, на что именно нужно заменить удаляемый фрагмент. Если в качестве параметра замены указать пустую строку, лишние символы просто будут удалены. Так мы уберем кавычки и двойные квадратные скобки из текста с помощью этой функции. После этой очистки свечные данные становится проще обрабатывать: их уже можно разделить на отдельные части, такие как время открытия, цена открытия, цена максимума, цена минимума и цена закрытия.
Аналогия:
Представьте, что вы пришли в библиотеку и достали древнюю книгу, у которой несколько страниц оказались склеены. Вам нужно будет осторожно снять слои, покрывающие фактическое содержимое, прежде чем вы сможете четко увидеть первую страницу. Запись string day1_data = candle_data[0]; похожа на то, как вы выбираете первую страницу из большой, многостраничной книги. Большая книга – это полный ответ API от сервера, где каждой свече соответствует отдельная "страница". Мы просто извлекаем первую "страницу", выбирая candle_data[0], чтобы затем работать с ней независимо от остальных данных.
Далее вызов StringReplace(day1_data, "[[", ""); выполняет похожую роль – он снимает лишнюю "обертку", которая как будто приклеена к верхней части страницы. В старых книгах на первой странице иногда бывают наклейки, ярлыки или полоски скотча. Пока вы не уберете их, страницу не получится прочитать корректно. Примерно так же в данных мешают лишние символы [[. Мы аккуратно удаляем их, потому что они не являются частью нужной нам информации.
Затем вызов StringReplace(day1_data,"\"", ""); напоминает, будто вы стираете мелкую пыль и следы, разбросанные по бумаге. Когда нам нужно корректно обрабатывать числа, двойные кавычки вокруг значений только мешают и не несут пользы. Мы удаляем эти кавычки – и страница становится чистой и простой для чтения. Наконец, мы как будто поднимаем очищенную страницу на яркий свет и проверяем результат, выводя ее через Print(day1_data);. Мы убеждаемся, что "пыль" удалена, "обертка" снята, и в итоге остается читаемый текст, который мы уже можем понять и использовать для дальнейшей обработки.
Вывод:
1763510400000, 92960.83000000, 92980.22000000, 88608.00000000, 91554.96000000, 32286.63760000, 1763596799999, 2925773651.25595790, 6822174, 15060.08451000, 1365200809.17455710, 0
Сырые свечные данные мы восстановили корректно, однако пока что они по–прежнему "склеены" в одну длинную строку. Дальше нам нужно работать с каждым значением отдельно: временем открытия, ценами открытия, максимума, минимума и закрытия. Поэтому оставлять все в одной строке нельзя. Затем эту очищенную строку нужно разделить и преобразовать в массив, чтобы к каждому компоненту можно было обращаться отдельно и использовать его в наших вычислениях.
Пример:WebRequest(method, url, headers, time_out, data, result, result_headers); string server_result = CharArrayToString(result); // Print(server_result); string candle_data[]; int array_count = StringSplit(server_result,']', candle_data); string day1_data = candle_data[0]; StringReplace(day1_data,"[[",""); StringReplace(day1_data,"\"",""); string day1_data_array[]; StringSplit(day1_data,',',day1_data_array); ArrayPrint(day1_data_array);
Вывод:

Пояснение:
Сначала мы объявили динамический массив, чтобы после удаления лишних символов хранить получившуюся длинную строку. Эта строка должна стать массивом, потому что ее содержимое будет преобразовано в отдельные элементы. Поскольку значения в строке разделены запятыми, логично использовать запятую как разделитель, чтобы получить отдельные элементы массива.
Свечные данные – как вагоны поезда, сцепленные друг с другом (time, open, high, low, close и т.д.). Чтобы работать с отдельными значениями, нужно сначала их разъединить. Первая строка, string day1_data_array[];, похожа на подготовку большой пустой парковки: после разделения у каждого "вагона" (значения) будет свое отдельное место.
Вторая строка, StringSplit(day1_data, ',', day1_data_array);, напоминает устройство, которое "расцепляет" состав на каждой запятой. Состав "разрезается" по запятым. После разделения каждый "вагон" (то есть отдельное значение: время открытия, цена открытия, максимум, минимум или закрытие) получает свое место на парковке – отдельную ячейку массива. Теперь вся информация легко доступна и уже разделена. Последняя строка, ArrayPrint(day1_data_array);, напоминает ситуацию, будто вы стоите перед парковкой и по списку проверяете каждый "вагон", чтобы убедиться, что значения корректно разделились и встали на свои места.
Извлечение времени и значений OHLC из данных второй свечи
После обработки первой свечи следующей будет вторая. Процесс остается таким же, как и для первой свечи – это нужно, чтобы сохранить точность и придерживаться той же структуры. Обработка каждой свечи по отдельности снижает риск путаницы и гарантирует точность анализа.
Перед тем как разделять данные второй свечи, важно внимательно изучить ее "сырой" шаблон. Выводя необработанный ответ, мы можем выявить любые лишние символы, которые мешают разделению: запятые, скобки или кавычки. Выполнив этот шаг, мы не дадим лишним символам случайно попасть в массив и тем самым исказить извлечение числовых значений.
Если мы понимаем точный формат "сырых" данных второй свечи, мы можем удостовериться, что ответ сервера сохраняет последовательную структуру. Аналогичным образом, посредством сравнения со структурой первой свечи мы можем убедиться, что порядок значений (время открытия, цена открытия, цена максимума, цена минимума и цена закрытия) остается неизменным. Благодаря этой согласованности мы сможем использовать ту же процедуру очистки и разделения, не подстраивая код под возможные вариации формата.
Пример:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- WebRequest(method, url, headers, time_out, data, result, result_headers); string server_result = CharArrayToString(result); // Print(server_result); string candle_data[]; int array_count = StringSplit(server_result,']', candle_data); //DAY 1 string day1_data = candle_data[0]; StringReplace(day1_data,"[[",""); StringReplace(day1_data,"\"",""); string day1_data_array[]; StringSplit(day1_data,',',day1_data_array); //DAY 2 Print(candle_data[1]); //--- return(INIT_SUCCEEDED); }
Вывод:
, [ 1763596800000, // Opening time "91554.96000000", // Open price "93160.00000000", // High price "86100.00000000", // Low price "86637.23000000", // Close price "39733.19073000", 1763683199999, "3548950335.09842180", 7841395, "18283.84047000", "1634256490.95743210", "0"
После проверки формата мы выделяем символы, которые нужно удалить: в частности запятую, за которой идет открывающая квадратная скобка, а также двойные кавычки. После удаления лишних символов мы можем объявить массив строк, чтобы хранить данные за второй день.
Пример:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- WebRequest(method, url, headers, time_out, data, result, result_headers); string server_result = CharArrayToString(result); // Print(server_result); string candle_data[]; int array_count = StringSplit(server_result,']', candle_data); //DAY 1 string day1_data = candle_data[0]; StringReplace(day1_data,"[[",""); StringReplace(day1_data,"\"",""); string day1_data_array[]; StringSplit(day1_data,',',day1_data_array); //DAY 2 string day2_data = candle_data[1]; StringReplace(day2_data,",[",""); StringReplace(day2_data,"\"",""); Print(day2_data); string day2_data_array[]; StringSplit(day2_data,',',day2_data_array); //--- return(INIT_SUCCEEDED); }
Вывод:
1763596800000, 91554.96000000, 93160.00000000, 86100.00000000, 86637.23000000, 39733.19073000, 1763683199999, 3548950335.09842180, 7841395, 18283.84047000, 1634256490.95743210, 0
По этому выводу видно, что все лишние символы действительно были удалены. После этого мы использовали функцию StringSplit, указав запятую в качестве разделителя, чтобы разбить данные на независимые элементы одного массива. Это упростило работу с каждым значением по отдельности.
Извлечение времени и значений OHLC из данных третьей свечи
Данные по третьей свече мы обработаем так же, как и данные по первым двум. Сначала мы выведем данные в журнал, разберемся с форматом и удалим все лишние символы. После удаления лишних символов мы объявим массив строк для хранения данных третьей свечи. Затем данные делятся на отдельные элементы с помощью функции StringSplit, где запятая используется как разделитель. Как и в случае с предыдущими свечами, это позволяет обращаться независимо к каждому значению – времени открытия, цене открытия, цене максимума, цене минимума и цене закрытия. Таким образом, все свечные данные оказываются надежно организованы и подготовлены для дальнейшей обработки.
Print(candle_data[2]);
Вывод:
,[ 1763683200000, "86637.22000000", "87498.94000000", "80600.00000000", "85129.43000000", "72256.12679000", 1763769599999, "6061348756.34156410", 11826480, "34071.85828000", "2859133223.22405230", "0"Пример:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- WebRequest(method, url, headers, time_out, data, result, result_headers); string server_result = CharArrayToString(result); // Print(server_result); string candle_data[]; int array_count = StringSplit(server_result,']', candle_data); //DAY 1 string day1_data = candle_data[0]; StringReplace(day1_data,"[[",""); StringReplace(day1_data,"\"",""); string day1_data_array[]; StringSplit(day1_data,',',day1_data_array); //DAY 2 string day2_data = candle_data[1]; StringReplace(day2_data,",[",""); StringReplace(day2_data,"\"",""); string day2_data_array[]; StringSplit(day2_data,',',day2_data_array); //DAY 3 string day3_data = candle_data[2]; StringReplace(day3_data,",[",""); StringReplace(day3_data,"\"",""); string day3_data_array[]; StringSplit(day3_data,',',day3_data_array); //--- return(INIT_SUCCEEDED); }
Благодаря единообразному формату свечей мы можем легко убрать из чисел кавычки и ведущие символы. Затем очищенные значения можно разделить и сохранить в массиве – так числовую обработку станет проще выполнять.
Пример:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- WebRequest(method, url, headers, time_out, data, result, result_headers); string server_result = CharArrayToString(result); // Print(server_result); string candle_data[]; int array_count = StringSplit(server_result,']', candle_data); //DAY 1 string day1_data = candle_data[0]; StringReplace(day1_data,"[[",""); StringReplace(day1_data,"\"",""); string day1_data_array[]; StringSplit(day1_data,',',day1_data_array); //DAY 2 string day2_data = candle_data[1]; StringReplace(day2_data,",[",""); StringReplace(day2_data,"\"",""); string day2_data_array[]; StringSplit(day2_data,',',day2_data_array); //DAY 3 string day3_data = candle_data[2]; StringReplace(day3_data,",[",""); StringReplace(day3_data,"\"",""); string day3_data_array[]; StringSplit(day3_data,',',day3_data_array); //DAY 4 string day4_data = candle_data[3]; StringReplace(day4_data,",[",""); StringReplace(day4_data,"\"",""); string day4_data_array[]; StringSplit(day4_data,',',day4_data_array); //DAY 5 string day5_data = candle_data[4]; StringReplace(day5_data,",[",""); StringReplace(day5_data,"\"",""); string day5_data_array[]; StringSplit(day5_data,',',day5_data_array); //--- return(INIT_SUCCEEDED); }
Чтобы не перегружать вас, на этом месте мы сделаем паузу и прервемся в рамках этой статьи. В следующей статье мы продолжим и объединим сопоставимые данные из дневных свечей в один массив. Например, все времена открытия будут объединены в один массив, и то же самое мы сделаем с ценами максимума, минимума и закрытия. В результате работа со свечными данными станет более удобной и структурированной.
Заключение
В этой статье показано, как использовать функцию WebRequest в языке MQL5 для получения свечных данных от API внешней платформы, и как начать организовывать эти данные. Мы рассмотрели, как разделить данные на отдельные массивы для каждой свечи, устранить лишние символы и проанализировать ответ сервера, чтобы выявить его шаблон.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/20375
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Алгоритм оптимизации бабочек — Butterfly Optimization Algorithm (BOA)
Разрабатываем мультивалютный советник (Часть 30): От торговой стратегии — к запуску мультивалютного советника
Машинное обучение и Data Science (Часть 36): Работа с несбалансированными финансовыми рынками
Как создать и адаптировать RL-агент с LLM и квантовым кодированием в алгоритмическом трейдинге на MQL5
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования