CustomSymbolCreate и CustomRatesReplace. Это необходимо для бэктестинга криптовалютных пар, которых нет у вашего брокера.Исходный код
В сфере количественного трейдинга (Quantitative Trading) часто возникает задача протестировать стратегию на альтернативных данных, например, на котировках криптовалют с бирж Binance, Coinbase или Kraken, которых может не быть в стандартном терминале MT5 вашего брокера. MetaTrader 5 предоставляет мощный функционал для создания пользовательских символов (Custom Symbols). Представленный ниже скрипт на MQL5 считывает исторические данные из CSV-файла, автоматически создает кастомный символ и импортирует в него историю баров.
При разработке торговых роботов на основе импортированных данных часто возникает необходимость скрыть свои намерения от брокера. В таких случаях полезно изучить, как реализовать Виртуальный Stop Loss и Take Profit в MQL4: Невидимые для брокера.
//+------------------------------------------------------------------+
//| ImportCryptoToCustom.mq5 |
//| Copyright 2024, Quant Developer |
//| https://finfluct.com |
//+------------------------------------------------------------------+
#property copyright "Quant Developer"
#property link "https://finfluct.com"
#property version "1.00"
#property script_show_inputs
// Входные параметры скрипта
input string InpCustomSymbolName = "BTCUSD_Custom"; // Имя кастомного символа
input string InpFileName = "crypto_data.csv"; // Имя CSV файла (в папке MQL5/Files)
input string InpCustomGroup = "CryptoCustom"; // Группа символов
input string InpBaseSymbol = "BTCUSD"; // Базовый символ для копирования свойств
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
// 1. Создаем кастомный символ
ResetLastError();
if(!CustomSymbolCreate(InpCustomSymbolName, InpCustomGroup, InpBaseSymbol))
{
int err = GetLastError();
if(err != ERR_MARKET_SYMBOL_EXIST)
{
PrintFormat("Ошибка создания символа %s. Код ошибки: %d", InpCustomSymbolName, err);
return;
}
else
{
PrintFormat("Символ %s уже существует. Продолжаем импорт.", InpCustomSymbolName);
}
}
// Включаем отображение символа в Market Watch
if(!SymbolSelect(InpCustomSymbolName, true))
{
PrintFormat("Не удалось выбрать символ %s в Market Watch.", InpCustomSymbolName);
return;
}
// 2. Открываем CSV файл для чтения
int file_handle = FileOpen(InpFileName, FILE_READ|FILE_CSV|FILE_ANSI, ',');
if(file_handle == INVALID_HANDLE)
{
PrintFormat("Ошибка открытия файла %s. Убедитесь, что он находится в папке MQL5/Files. Код ошибки: %d", InpFileName, GetLastError());
return;
}
// Динамический массив для хранения баров
MqlRates rates[];
ArrayResize(rates, 0);
// Пропускаем заголовок, если он присутствует в файле
if(!FileIsEnding(file_handle))
{
string header = FileReadString(file_handle);
string header_lower = header;
StringToLower(header_lower);
if(StringFind(header_lower, "time") == -1 && StringFind(header_lower, "date") == -1)
{
// Если первая строка не заголовок, возвращаем указатель в начало
FileSeek(file_handle, 0, SEEK_SET);
}
}
Print("Начало чтения файла...");
// Читаем данные построчно
while(!FileIsEnding(file_handle))
{
string time_str = FileReadString(file_handle);
if(time_str == "") continue;
double open_val = FileReadNumber(file_handle);
double high_val = FileReadNumber(file_handle);
double low_val = FileReadNumber(file_handle);
double close_val = FileReadNumber(file_handle);
long volume_val = (long)FileReadNumber(file_handle);
MqlRates rate;
rate.time = StringToTime(time_str);
rate.open = open_val;
rate.high = high_val;
rate.low = low_val;
rate.close = close_val;
rate.tick_volume = volume_val;
rate.real_volume = volume_val;
rate.spread = 0;
if(rate.time == 0)
{
PrintFormat("Предупреждение: Неверный формат даты/времени '%s'. Пропуск строки.", time_str);
continue;
}
int size = ArraySize(rates);
ArrayResize(rates, size + 1);
rates[size] = rate;
}
FileClose(file_handle);
int total_rates = ArraySize(rates);
if(total_rates == 0)
{
Print("Нет данных для импорта.");
return;
}
PrintFormat("Успешно прочитано %d баров. Запись в кастомный символ...", total_rates);
// 3. Записываем котировки в кастомный символ
ResetLastError();
int imported = CustomRatesReplace(InpCustomSymbolName, rates[0].time, rates[total_rates - 1].time, rates);
if(imported < 0)
{
PrintFormat("Ошибка импорта котировок. Код ошибки: %d", GetLastError());
}
else
{
PrintFormat("Импорт завершен! Успешно добавлено/обновлено %d баров для символа %s.", imported, InpCustomSymbolName);
}
} Разбор параметров
InpCustomSymbolName: Строковое имя создаваемого кастомного символа (например,BTCUSD_Custom).InpFileName: Имя CSV-файла с котировками, который должен находиться в директорииMQL5/Filesтерминала.InpCustomGroup: Название группы в окне "Обзор рынка" (Market Watch), куда будет помещен новый символ.InpBaseSymbol: Имя существующего базового символа (например,BTCUSD), свойства которого (размер контракта, маржинальные требования, шаг цены) будут скопированы для нового кастомного инструмента.
Как запустить
Для успешного запуска скрипта выполните следующие шаги:
1. Подготовьте CSV-файл с историческими данными. Файл должен иметь формат: YYYY.MM.DD HH:MM:SS,Open,High,Low,Close,Volume. Например: 2023.10.27 12:00:00,34100.5,34250.0,34050.2,34200.0,150. Обратите внимание, что разделителем должна быть запятая.
2. Поместите подготовленный файл в папку данных терминала. Для этого в MT5 выберите Файл -> Открыть каталог данных, затем перейдите по пути MQL5/Files и скопируйте файл туда.
3. Откройте MetaEditor, создайте новый скрипт, скопируйте в него представленный выше код и скомпилируйте его.
4. Перетащите скомпилированный скрипт из навигатора MT5 на любой график. В появившемся окне параметров укажите имя вашего CSV-файла и желаемое имя кастомного символа.
5. После завершения работы скрипта откройте окно Символы (Ctrl+U), найдите созданный инструмент в группе CryptoCustom и убедитесь, что бары успешно импортированы. Теперь вы можете открыть график этого символа и запускать на нем любые советники или индикаторы.
Если вы работаете с мультитаймфреймовым анализом на кастомных символах, вам также может быть интересно узнать, Как считывать данные индикатора с другого таймфрейма (iCustom MTF) без перерисовки в MQL4.
Для построения сложных сеточных стратегий на основе импортированных данных обратите внимание на Динамический Мартингейл с учетом корреляции пар в MQL4.




