Что такое переменные в MQL и как их использовать
Программирование торгового робота на MQL5 начинается не с торговых функций и не со сложных индикаторов, а с простого вопроса: где робот будет хранить данные? Цена последнего тика, количество сделок, флаг «в позиции или нет», имя инструмента — все это нужно где-то запоминать между вызовами OnTick(). Именованные ячейки памяти, в которых MQL5 хранит данные, называются переменными.
Тема кажется элементарной, но именно здесь новички совершают большинство ошибок: путают типы данных, неправильно пишут имена, не понимают разницу между локальной и глобальной областью видимости. В этой статье я системно разберу переменные в MQL5: что это, как объявлять, какие типы существуют, чем отличаются локальные и глобальные переменные, зачем нужны input-параметры и каких ошибок стоит избегать. Каждый тезис подкреплен корректным кодом, который можно открыть в MetaEditor, скомпилировать и проверить в тестере стратегий. Никакой магии и обещаний легкой прибыли — только честная техническая база.
Что такое переменнаяПеременная — это именованная область памяти, в которую программа записывает значение и из которой может прочитать его в любой момент. Представьте коробку с этикеткой: на этикетке — имя переменной, внутри — её значение. Пока программа работает, коробка существует. Положили новое значение — старое исчезло, на его месте лежит новое.
В MQL5 каждая переменная строго типизирована. При объявлении вы обязаны указать тип, и компилятор проследит, чтобы в переменную не попало значение другого типа. Строгая типизация — это защита от ошибок: цена не подставится туда, где ожидается количество сделок, а строка не превратится в число.
Зачем роботу переменныеБез переменных робот был бы слепым: каждый новый тик начинался бы с чистого листа. Судьба переменной зависит от того, где она объявлена. Локальная переменная, объявленная внутри функции, уничтожается, когда функция завершает работу. Поэтому «помнить» что-то между тиками могут только глобальные переменные, объявленные вне функций.
Практический пример: чтобы не открывать вторую позицию поверх первой, роботу нужно помнить факт наличия позиции. Для этого объявляется глобальная переменная bool inPosition. Пришла сделка — записали true. Сделка закрылась — записали false. Точно так же хранятся объем последней сделки, цена входа, время срабатывания условия. Переменные — это память робота, и от того, как вы ей распорядитесь, зависит качество всей логики.
Основные типы данных в MQL5В повседневной практике вам понадобятся четыре базовых типа. int — целые числа в диапазоне примерно от минус двух до плюс двух миллиардов; используется для счетчиков, индексов, номеров сделок. double — вещественные числа с плавающей точкой и высокой точностью; именно в double принято хранить цены, объемы, коэффициенты и результаты деления. bool — логический тип, принимает только true или false; идеален для флагов и условий. string — строка текста в двойных кавычках; используется для имен, комментариев, путей к файлам.
Помимо них в MQL5 есть и другие типы — long, uint, char, datetime, color, enum, — но начинающему достаточно уверенно работать с четырьмя базовыми. Ошибочно думать, что целочисленный тип подходит для цен: цена 1.2345 в int превратится в 1, и робот начнет работать с неточными данными. Дробные торговые величины — только double.
// ==== Объявление переменных разных типов ====int dealsCount = 0; // целое числоdouble entryPrice = 0.0; // дробное числоbool inPosition = false; // логический флагstring tradeSymbol = Symbol(); // текст
Обратите внимание на соглашение об именах: в этой статье и в большинстве примеров используется стиль camelCase — первое слово с маленькой буквы, каждое следующее с большой (dealsCount, entryPrice). MQL5 не настаивает на этом стиле, но единообразие сильно облегчает чтение кода.
Объявление и инициализацияОбъявление переменной состоит из типа и имени. Инициализация — это присваивание начального значения прямо при объявлении. Хорошая привычка — всегда инициализировать переменную, чтобы в ней не оказалось случайного значения.
// ==== Объявление, затем инициализация ====int deals;deals = 0;
// ==== Объявление сразу с инициализацией ====double lotSize = 0.1;
Если значение при объявлении не задано, переменная получает значение по умолчанию для своего типа: для int — 0, для double — 0.0, для bool — false, для string — пустая строка. Тем не менее явная инициализация делает намерение программиста очевидным, а код — более предсказуемым.
Присваивание значенийПрисваивание выполняется оператором «=». В MQL5, как и в языках семейства C, это операция «взять значение справа и записать в переменную слева». Поэтому a = b; читается как «записать в a значение из b», а не «a равно b».
// ==== Примеры присваивания ====int a = 5; // инициализацияint b; // объявлениеb = a; // теперь b равно 5a = 10; // a стало 10, b осталось 5
// ==== Присваивание с вычислением ====double price = Ask; // цена продажи из терминалаprice = price + 0.01; // увеличили цену на 0.01
Важное следствие: когда в переменную записывается значение другой переменной базового типа, копируется значение, а не ссылка. Тот факт, что a позже изменено на 10, никак не влияет на b. Понимание этого избавляет от десятка «необъяснимых» ошибок в логике.
При переносе данных между типами работают правила преобразования. Целое число без потерь записывается в double: double x = 10; даст 10.0. Обратная операция — из double в int — отбрасывает дробную часть: int y = 3.7; даст 3. Такие преобразования лучше делать явными, чтобы не полагаться на поведение компилятора:
double d = 3.7;int whole = (int)d; // явное преобразование, whole = 3
Как называть переменныеЕсть правила, которые MQL5 не простит нарушить. Имя должно начинаться с латинской буквы или символа подчеркивания; далее могут идти буквы, цифры и подчеркивания. Пробелы запрещены. Имя не должно совпадать с зарезервированными словами: int, double, bool, string, if, else, for, while и другими. Регистр важен: Price и price — две разные переменные.
// ==== Корректные имена ====int dealsCount; // okdouble last_price; // ok, подчеркивание допустимоbool _private; // ok, но лучше избегать
// ==== Некорректные имена ====// int 1deal; // начинается с цифры — ошибка// int deal count; // пробел — ошибка// int if; // зарезервированное слово — ошибка
Гораздо важнее правил — здравый смысл. Имя должно описывать назначение переменной: lastPrice, dealsCount, isBlocked говорят сами за себя. Имена вида q, x1, tmp2 понятны только в момент написания, а через месяц превращаются в загадку. Осмысленные имена — это дешевый способ документировать код без единого комментария.
Локальные и глобальные переменныеГлобальная переменная объявляется вне функций, как правило, в начале программы, и доступна в любой функции: OnInit, OnTick, OnDeinit. Её значение сохраняется между вызовами функций — именно поэтому глобальные переменные используют для хранения состояния робота.
Локальная переменная объявляется внутри функции и существует только во время её работы. Каждый раз, когда функция вызывается заново, локальные переменные создаются заново. Обратиться к локальной переменной из другой функции невозможно — компилятор выдаст ошибку «undeclared identifier».
// ==== Глобальные переменные: доступны во всех функциях ====int dealsCount;double totalProfit;
// =============================================================================================== //// = Для чего: обработчик тика — использует глобальные переменные и локальные данные = //// =============================================================================================== //void OnTick() { // === Локальная переменная видна только внутри OnTick === double currentPrice = Bid;
// === Работаем с глобальными переменными === dealsCount++; totalProfit = 0.0;
// === Выводим информацию на график === if(dealsCount > 0) Comment("Сделок: ", dealsCount, ", цена: ", currentPrice); }
Если внутри функции объявить локальную переменную с тем же именем, что и глобальная, она «затенит» глобальную: внутри функции будет работать локальная, а глобальная окажется недоступной. Затенение — частый источник путаницы, поэтому старайтесь давать всем переменным уникальные имена.
Правило простое: состояние, которое должно жить дольше одного вызова функции, — глобальное; всё остальное — локальное. Локальные переменные экономят память и снижают риск случайного изменения данных другой функцией.
input-параметры: настройка без правки кодаИногда значение должно быть доступно пользователю в окне свойств эксперта. Для этого в MQL5 существует модификатор input. Переменные, объявленные с input, становятся параметрами эксперта: их видно в настройках, и их можно менять без перекомпиляции программы.
// ==== input-параметры: настраиваются пользователем ====input double LotSize = 0.1; // объём сделкиinput int MaxDeals = 5; // максимум сделокinput bool UseSL = true; // использовать стоп-лоссinput string Comment = "MQL Master";
В коде input-переменные используются как обычные, но их значения приходят из настроек. В тестере стратегий именно по таким параметрам выполняется оптимизация: тестер перебирает значения LotSize, MaxDeals и других input-переменных в поисках лучшей комбинации.
Важный нюанс: input-переменные глобальны по своей природе и доступны во всех функциях, однако перезаписывать их во время работы — плохая практика. Пользователь задал настройку — пусть робот ею пользуется, а не меняет её по своему усмотрению.
Демонстрация: состояние робота между тикамиСоберем всё вместе в мини-примере: глобальные переменные хранят статистику, локальные — текущие данные тика, input-параметр управляет уведомлениями.
// ==== Глобальные переменные: состояние робота ====int ticksCount;double lastBarOpen;
// ==== input-параметр: настраивается пользователем ====input bool EnableLog = true;
// =============================================================================================== //// = Для чего: обработчик тика — считает тики и замечает появление новой свечи = //// =============================================================================================== //void OnTick() { // === Каждый новый тик увеличиваем счётчик === ticksCount++;
// === Запоминаем цену открытия текущего бара === double currentOpen = iOpen(_Symbol, _Period, 0);
// === Если открылся новый бар — логируем событие === if(lastBarOpen != currentOpen) { lastBarOpen = currentOpen; if(EnableLog) Print("Новый бар: ", currentOpen); }
// === Выводим статистику на график === Comment("Тиков: ", ticksCount); }
На этом примере видно, как именно переменные связывают между собой вызовы функции: поскольку ticksCount и lastBarOpen — глобальные, их значения сохраняются, и каждый следующий тик «помнит», что было раньше. Уберите глобальность — и счётчик будет сбрасываться на каждом тике.
Типичные ошибки начинающихПервая: использование переменной до инициализации. Здесь важно помнить, что локальные переменные без явного значения могут содержать «мусор». Решение простое — всегда задавайте значение при объявлении.
Вторая: путаница между = и ==. Один знак — присваивание, два — сравнение. Классическая ошибка if(lot = 0.1) вместо if(lot == 0.1): первое запишет 0.1 в lot и вернет истину, и условие будет срабатывать всегда. Компилятор такое пропустит, а логика сломается.
Третья: неправильный тип для цены. Объявление int price = Ask; отбросит дробную часть, и робот будет оперировать «округленными» ценами. Торговые величины — только double.
Четвертая: деление на ноль без проверки. При вычислении, например, процента по количеству сделок, сначала проверьте, что делитель не равен нулю.
Пятая: затенение глобальной переменной локальной с тем же именем. Симптом — «почему-то моя переменная не меняется». Лечится уникальными именами и внимательным чтением предупреждений компилятора.
Шестая: опечатки в именах. Опечатка в имени переменной — это не предупреждение, а ошибка компиляции. Читайте сообщения MetaEditor внимательно: в них указан путь к файлу и номер строки.
// ==== Ошибка: int вместо double для цены ====double price = 1.2345;// int wrong = price; // потеряет дробную часть
// ==== Ошибка: «=» вместо «==» в условии ====// if(price = 0.5) // присваивание, а не сравнениеif(price == 0.5) // корректное сравнение Comment("Половина!");
Как практиковатьсяТеория без практики забывается за три дня. Откройте MetaEditor, создайте новый эксперт и напишите первый код: объявите глобальные переменные, добавьте пару input-параметров, а в OnTick посчитайте что-нибудь простое — например, количество вызовов функции. Затем прогоните робота в тестере стратегий на любом символе: так вы увидите, как input-параметры влияют на поведение и как глобальные переменные работают между тиками.
Компилируйте код как можно чаще и учитесь читать диагностику. Не бойтесь экспериментировать — песочница тестера для того и существует. Обучение подобным темам есть на https://max.ru/channel_mqlmasters, а в Телеграм-канале https://t.me/mqlmasters мы разбираем примеры, отвечаем на вопросы и публикуем практические материалы для начинающих разработчиков торговых роботов.
ЗаключениеПеременные — первый и самый важный строительный блок любого кода на MQL5. Понимание типов, правил именования и области видимости позволяет писать код, который читается, компилируется с первого раза и не подкидывает сюрпризов на живом рынке. Не обещаю, что после этой статьи вы напишете прибыльного робота, — обещаю, что вы перестанете спотыкаться о базу и сможете двигаться дальше: к условиям, циклам, функциям, торговым операциям и созданию полноценных стратегий. Успехов в освоении MQL5!
Владимир, опубликовал запись .
С момента публикации зафиксировано 43 просмотра. Сейчас эту запись просматривают 2 незарегистрированных пользователя.
|
|