Screenshot Customizer

собирает торговые данные с графиков в виде скриншотов в отдельную папку, вы можете сами настроить какая информация о счете будет отображаться

Советник Trader

Работает по модернизированной методике Мартингейла.

Dmitriy

Эта долгосрочная торговая система работает на 12 парах и 1 таймфрейме. Она использует различные стратегии по тренду.

Логин: Пароль:
Забыли пароль?Зарегистрироваться
Блоги Рейтинг брокеров Журнал Магазин Прогнозы Новости Каталог
 
Блоги
Добавить запись

(LAST DAY) Челлендж EraTrade на 15% дешевле и доступ в VIP ForexTimes — БЕСПЛАТНО!

LAST DAY Оплата картой и СБП, выплаты без KYC обновление в Era Trade! Теперь в Era Trade доступна оплата картой и по СБП, а выплаты осуществляются без прохождения KYC. Платежная система поддерживает 190+ стран и любые виды валют, которые...

Прогнозы и торговые сигналы, 20 минут назад

PRO ForexTimesКоличество подписчиков1427

Отчет по торговому сигналу NG за 27.07.2026

Уважаемые коллеги, пример работы торгового сигнала от 27.07.2026 из VIP комьюнити ForexTimes: NG: Close order +2947.65$ +450.0 pips Не упусти свою прибыль. Присоединяйся к ForexTimes!!! Торговые сигналы в VIP комьюнити ForexTimes генерируются на...

Прогнозы и торговые сигналы, 1 час назад

PRO ForexTimesКоличество подписчиков1427

Торговая идея по AUDUSD за 15.10.2026

Коллеги, торговая идея по AUDUSD - рост торгового инструмента в направлении уровня скопления 3028 месячных опционных контрактов PUT (0.7210) и далее, вероятно, в направлении уровня "Космос", по-прежнему актуальна. Не пропустите торговые сигналы....

Прогнозы и торговые сигналы, 3 часа назад

PRO ForexTimesКоличество подписчиков1427

Торговый сигнал (отложенный ордер) по WTI за 15.10.2026

Уважаемые коллеги, в VIP комьюнити ForexTimes был опубликован новый ТОРГОВЫЙ СИГНАЛ (отложенный ордер) по WTI. RR 1:8.5 На каком уровне выставлен ордер? На каком уровне SL? На каком уровне TP? Ответы на эти и другие вопросы, вы найдете в VIP...

Прогнозы и торговые сигналы, 4 часа назад

PRO ForexTimesКоличество подписчиков1427

«Сибстекло», «Нео-Пак» и «НЗРМ» вошли в ТОП-400 компаний реального сектора экономики СФО и ДФО

Портал Эксперт опубликовал ТОП-400 компаний реального сектора экономики Сибирского и Дальневосточного ФО за 2025 год. Три эмитента Юнисервис Капитал ООО Сибстекло , ООО Нео-Пак и ООО НЗРМ вошли в рейтинг. Согласно данным портала Эксперт ,...

Прочее, 5 часов назад

Юнисервис КапиталКоличество подписчиков3147

Массивы в MQL и их применение в торговле

ВведениеРынок это поток данных. Каждый тик, каждый бар, каждое значение индикатора отдельная величина, но по отдельности они почти бесполезны. Трейдер-разработчик мыслит сериями: последние сто закрытий, значения RSI за час, список сделок за...

Прогнозы и торговые сигналы, 7 часов назад

ВладимирКоличество подписчиков745

Почему цена меняется каждую секунду, если ничего заметного не происходит?

Причина в самой природе рыночной цены: это не свойство инструмента, а результат потока заявок. Каждая сделка возникает там, где совпали условия покупателя и продавца, поэтому цена смещается при изменении баланса спроса и предложения, а не по...

Обзоры и аналитика рынка, 7 часов назад

ВладимирКоличество подписчиков745

Прогноз по AUDUSD за 15.10.2026

Уровень хеджеров PUT (0.69315) выступает ключевым уровнем поддержки на сегодня. В случае успешного теста данного уровня поддержки (образование паттерна на BUY) ожидаем повышение в направлении уровня хеджеров CALL (0.70565). Управляйте своим...

Прогнозы и торговые сигналы, 9 часов назад

PRO ForexTimesКоличество подписчиков1427

Прогноз по GOLD за 15.10.2026

Сегодня, в случае формировании паттерна на SELL на текущей стоимости торгового инструмента, рекомендуется рассматривать продажи в направлении уровня хеджеров PUT (4054.00). Управляйте своим капиталом правильно и компетентно! Входите в сделки...

Прогнозы и торговые сигналы, 9 часов назад

PRO ForexTimesКоличество подписчиков1427

Прогноз по EURUSD за 15.10.2026

Сегодня, в случае формирования паттерна на SELL на текущей стоимости торгового инструмента, рекомендуется рассматривать продажи в направлении уровня скопления 5448 месячных опционных контрактов PUT (1.1070). Управляйте своим капиталом правильно и...

Прогнозы и торговые сигналы, 9 часов назад

PRO ForexTimesКоличество подписчиков1427

 
Рубрикатор
Рейтинг блогеров
Популярные записи
F.A.Q.

Обзоры и аналитика рынка

Владимир → Обзоры и аналитика рынка
745
Почему цена меняется каждую секунду, если ничего заметного не происходит?

Причина в самой природе рыночной цены: это не свойство инструмента, а результат потока заявок. Каждая сделка возникает там, где совпали условия покупателя и продавца, поэтому цена смещается при изменении баланса спроса и предложения, а не по чьей-то команде. Пока поток заявок уравновешен, котировка почти стоит на месте; как только он смещается, сдвигается и точка их пересечения.

На микроуровне движение формируют рыночные ордера, проходящие по разным ценам внутри спреда, и доступная в стакане ликвидность. Новости, смена торговых сессий и активность крупных участников меняют этот баланс, и цена реагирует мгновенно. Для алгоритмической торговли это означает, что статичные параметры — фиксированный стоп или шаг — работают неравномерно: в тонком рынке расширяется спред и растёт проскальзывание, а потому исполнительные цены отличаются от расчётных.

Практический вывод: воспринимайте цену как вероятностный процесс, а не как набор предсказуемых сигналов. Учитывайте ликвидность и время торговли, привязывайте стоп и объём к текущей волатильности и проверяйте логику на реальных котировках с учётом спреда. Больше разборов по трейдингу и автоматизации — на https://trading-go.ru/

Идея иллюстрации: биржевой стакан с уровнями bid и ask и расширяющимся спредом, без текста.

Профиль автора: https://www.mql5.com/ru/users/voldemar

Опубликовано 7 часов назад
Перейти к комментариям
Владимир → Обзоры и аналитика рынка
745
Локальные и глобальные переменные в советнике

Полный разбор области видимости переменных в MQL5 для начинающего разработчикаКогда торговый советник растёт, всё чаще возникает один и тот же вопрос: где объявить переменную, чтобы она вела себя предсказуемо? Ответ зависит от области видимости (scope) — то есть от того, из какого места программы переменную можно видеть и как долго она существует. Понимание этого механизма отделяет аккуратный, надёжный робот от кода, который «иногда работает, а иногда нет». В этой статье мы подробно разберём локальные и глобальные переменные программы, особенность функции OnTick, отличие от глобальных переменных терминала и типичные ошибки, которые встречаются у новичков.

Что такое область видимости переменнойОбласть видимости определяет, в каких местах кода доступна переменная. В MQL5, как и в языке C, действует правило блочной видимости: переменная видна внутри того блока, где она объявлена, и во всех вложенных блоках. Блок — это код, заключённый в фигурные скобки: тело функции, ветка условия, тело цикла.

Из этого правила следуют два следствия. Первое: переменная, объявленная внутри функции, недоступна за её пределами — компилятор просто не знает о ней в другом месте. Второе: две переменные с одинаковым именем в разных блоках никак не связаны между собой. Область видимости — это не про тип данных и не про значение, а именно про то, где имя переменной «действует».

Глобальные переменные программыГлобальные переменные программы объявляются вне функций — обычно в самом начале файла советника. Их видит любая функция, а существуют они всё время, пока советник загружен в память терминала. Именно поэтому здесь удобно хранить настройки, счётчики и состояние робота.

// =============================================================================================== //// = Глобальные переменные программы: доступны всем функциям советника = //// =============================================================================================== //int g_openedTrades = 0; // счётчик открытых роботом сделокdouble g_lastTradePrice = 0.0; // цена последней сделкиbool g_enteredThisBar = false; // флаг входа в текущем баре

// === Функция увеличивает счётчик сделок ===void RegisterTrade(double price) { g_openedTrades++; g_lastTradePrice = price; Print("Сделок открыто: ", g_openedTrades, ", цена: ", g_lastTradePrice); }

Здесь переменные g_openedTrades, g_lastTradePrice и g_enteredThisBar объявлены вне функций, поэтому любые изменения, сделанные внутри RegisterTrade, сохраняются и видны другим функциям. Префикс g_ — популярное соглашение об именовании, которое сразу подсказывает, что переменная глобальная.

Локальные переменныеЛокальные переменные объявляются внутри функции или блока. Они создаются в момент входа в блок и уничтожаются при выходе из него. Вне этого блока к ним обратиться нельзя.

// =============================================================================================== //// = Локальные переменные живут только во время выполнения функции = //// =============================================================================================== //double CalcRiskMoney(double percent) { double balance = AccountInfoDouble(ACCOUNT_BALANCE); // локальная double riskMoney = balance * percent / 100.0; // локальная

// === Возвращаем результат, локальные переменные прекращают существование === return riskMoney; }

Переменные balance и riskMoney существуют только внутри CalcRiskMoney. Это хорошо: они не засоряют пространство имён и не могут быть случайно изменены из другого места программы. Обратная сторона — их значение нигде не сохраняется.

Почему в OnTick нельзя обойтись локальными переменнымиГлавная особенность советника — в том, как работает функция OnTick. Она вызывается терминалом заново на каждом новом тике. Каждый такой вызов — отдельный проход: вошли в функцию, выполнили код, вышли. Значит, обычная локальная переменная внутри OnTick при каждом вызове создаётся заново.

Посмотрите на типичную ошибку новичка:

// === НЕПРАВИЛЬНО: счётчик всегда покажет 1 ===void OnTick() { int counter = 0; // создаётся заново на каждом тике counter++; Print(counter); // всегда 1 }

Значение counter сбрасывается на каждом тике, поэтому счётчик бесполезен. Счётчики открытых сделок, флаг «уже входили в этом баре», время последней сделки — всё это относится к состоянию робота, которое должно пережить вызов функции. Хранить такое состояние в обычной локальной переменной нельзя: на следующем тике оно обнулится.

Как сохранить состояние: static-переменныеПервое решение — ключевое слово static. Статическая переменная объявляется внутри функции, но, в отличие от обычной локальной, создаётся один раз и сохраняет значение между вызовами. При этом снаружи функции её по-прежнему не видно.

// =============================================================================================== //// = static-переменная помнит значение между вызовами OnTick = //// =============================================================================================== //bool IsNewBar() { static datetime lastBar = 0; datetime currentBar = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);

// === Первый вызов: запоминаем бар === if (lastBar == 0) { lastBar = currentBar; return false; }

// === Бар изменился — значит, новый === if (currentBar != lastBar) { lastBar = currentBar; return true; }

return false; }

Здесь lastBar — статическая переменная. Она сохраняет время последнего бара между вызовами, и именно поэтому функция может определить появление новой свечи. Если бы lastBar была обычной локальной переменной, она каждый раз обнулялась бы и сравнение теряло смысл.

Поля класса как альтернативаВторое решение — хранить состояние в полях класса. Когда советник оформлен как класс, его поля существуют вместе с объектом и доступны всем методам. Это более структурированный подход, чем разрозненные глобальные переменные.

// =============================================================================================== //// = Класс хранит состояние робота в своих полях = //// =============================================================================================== //class CTradeState {private: int m_trades; // число открытых сделок datetime m_lastTrade; // время последней сделки bool m_enteredBar; // входили ли в текущем баре

public: CTradeState() { m_trades = 0; m_lastTrade = 0; m_enteredBar = false; }

// === Метод обновляет состояние при открытии сделки === void OnTradeOpened() { m_trades++; m_lastTrade = TimeCurrent(); m_enteredBar = true; } };

Поля класса m_trades, m_lastTrade и m_enteredBar живут столько, сколько живёт объект класса. Такой код проще читать: видно, какие данные относятся к состоянию робота, а какие — временные.

Глобальные переменные терминала — это другой механизмОтдельно существуют глобальные переменные терминала. Несмотря на похожее название, это совершенно другой инструмент. Они хранятся не в памяти советника, а в самом терминале MetaTrader, доступны разным программам и переживают перезапуск. Для работы с ними есть отдельные функции: GlobalVariableSet, GlobalVariableGet, GlobalVariableCheck, GlobalVariableDel.

// =============================================================================================== //// = Глобальные переменные терминала: хранятся в терминале и переживают перезапуск = //// =============================================================================================== //void SaveState() { GlobalVariableSet("MyEA_Trades", 5); // записать значение }

// === Чтение значения с проверкой существования ===int ReadTrades() { if (GlobalVariableCheck("MyEA_Trades")) return (int)GlobalVariableGet("MyEA_Trades"); return 0; }

// === Удаление переменной терминала ===void ClearState() { GlobalVariableDel("MyEA_Trades"); }

Важно не путать эти два уровня. Глобальная переменная программы (g_openedTrades) существует только пока советник загружен, и её не видит никакая другая программа. Глобальная переменная терминала (MyEA_Trades) живёт в терминале, её видит любой советник и скрипт, и она сохраняется после перезапуска. Первые — для состояния внутри одного робота, вторые — для обмена данными и долговременного хранения.

Инициализация переменныхГлобальные переменные программы инициализируются один раз — в момент загрузки советника, ещё до первого вызова OnTick. Если при объявлении не задать значение, целочисленные переменные получат ноль, вещественные — тоже ноль, а строки останутся пустыми. Но полагаться на это не стоит: начальные значения лучше присваивать явно, чтобы намерение было очевидным.

Чего делать нельзя, так это инициализировать глобальную переменную значением, которое зависит от рыночных данных, прямо в строке объявления. Записать в глобальную переменную текущую цену инструмента при объявлении не получится надёжно: на момент запуска торговые данные могут быть ещё не готовы. Для таких значений служит функция OnInit — она вызывается при инициализации советника, в том числе после смены настроек и таймфрейма.

// =============================================================================================== //// = Инициализация глобального состояния в OnInit, а не при каждом тике = //// =============================================================================================== //datetime g_sessionStart = 0;double g_startPrice = 0.0;

// === OnInit вызывается один раз при загрузке советника ===int OnInit() { g_sessionStart = TimeCurrent(); g_startPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); Print("Старт сессии: ", g_sessionStart, ", цена: ", g_startPrice); return INIT_SUCCEEDED; }

Такой подход гарантирует, что начальные значения вычисляются один раз и корректно, а не заново на каждом тике.

Префиксы и правила именованияАккуратные префиксы почти полностью снимают риск перекрытия имён. Договоритесь с собой о простых правилах: глобальные переменные программы начинайте с g_, поля класса — с m_, а параметры функций и локальные переменные оставляйте без префикса. Тогда даже в большом проекте по одному имени видно, где и как долго живёт переменная.

Полезно отражать в имени смысл, а не тип. Переменная g_tradesOpened читается лучше, чем g_count, а m_lastTradeTime понятнее, чем m_time. Компилятор не подскажет, что g_count считает именно сделки, — а вам через полгода это может понадобиться.

И наоборот, никогда не давайте двум сущностям одно имя. Именно так и рождается эффект перекрытия: локальная переменная с именем глобальной молча скрывает её внутри функции, и вы меняете не то, что думали.

Типичные ошибки при работе с переменнымиПерекрытие имён (shadowing)Если локальная переменная названа так же, как глобальная, локальная «затеняет» глобальную внутри своей функции. Это классическая ловушка.

// === ОПАСНО: локальная переменная перекрывает глобальную ===int g_count = 100;

void Shadow() { int g_count = 0; // эта переменная перекрывает глобальную g_count++; // изменяется только локальная копия, глобальная остаётся 100 }

Чтобы избежать путаницы, придерживайтесь префиксов g_ для глобальных и m_ для полей класса, а локальным переменным давайте говорящие имена.

Случайное обнуление состоянияСамая частая ошибка — держать состояние робота в обычной локальной переменной внутри OnTick. Значение сбрасывается на каждом тике, и робот ведёт себя непредсказуемо. Всегда проверяйте: должна ли эта переменная помнить, что было на предыдущем тике? Если да — она обязана быть глобальной, статической или полем класса.

Конфликты при перезапускеЕсли хранить состояние в глобальных переменных программы, при перезапуске советника они сбрасываются. Если робот уже имел открытые позиции, его «внутренняя память» о них пропадёт. В таких случаях состояние лучше восстанавливать по фактическим позициям терминала или хранить в глобальных переменных терминала. При этом помните про конфликты: два экземпляра советника с одинаковыми именами глобальных переменных терминала будут мешать друг другу.

Глобальные переменные там, где нужны поля классаРазрозненные глобальные переменные удобны в маленьком советнике, но плохо масштабируются. Когда их становится десятки, отследить, кто и когда их изменяет, очень трудно. Если состояние логически связано с объектом стратегии, его стоит держать в полях класса, давая осмысленные имена.

Практические примерыСчётчик открытых сделокРоботу часто нужно ограничить число одновременных позиций. Надёжнее не хранить счётчик в памяти, а восстанавливать его из фактического состояния терминала.

// =============================================================================================== //// = Считаем число позиций робота по Magic Number = //// =============================================================================================== //int CountOwnPositions(long magic) { int count = 0;

// === Перебираем позиции терминала === for (int i = 0; i < PositionsTotal(); i++) { if (PositionGetTicket(i) == 0) continue; if (PositionGetInteger(POSITION_MAGIC) == magic) count++; }

return count; }

Такой счётчик не теряет данные при перезапуске, потому что каждый раз берётся из реальных позиций, а не из переменной в памяти.

Флаг «уже входили в этом баре»Часто нужно входить не более одного раза за свечу. Флаг должен сбрасываться при появлении нового бара и сохраняться между тиками, поэтому его держат глобально.

// === Глобальный флаг входа в текущем баре ===bool g_enteredThisBar = false;

void OnTick() { static datetime lastBar = 0; datetime currentBar = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);

// === Новый бар — сбрасываем флаг === if (currentBar != lastBar) { lastBar = currentBar; g_enteredThisBar = false; }

// === Вход только один раз за бар === if (!g_enteredThisBar) { // === ... здесь логика входа === g_enteredThisBar = true; } }

Время последней сделкиЕщё одно частое состояние — время последней сделки. Оно позволяет выдерживать паузу между входами.

// === Хранение времени последней сделки между тиками ===datetime g_lastTradeTime = 0;

// =============================================================================================== //// = Проверяем, прошёл ли заданный интервал с момента последней сделки = //// =============================================================================================== //bool CanTrade(int minSeconds) { return (TimeCurrent() - g_lastTradeTime >= minSeconds); }

Переменная g_lastTradeTime объявлена глобально, поэтому значение не теряется между тиками. После открытия очередной сделки достаточно присвоить ей TimeCurrent().

Что выбрать: глобальные переменные, поля класса или staticНебольшой советник вполне обходится глобальными переменными программы — это просто и наглядно. Когда логика усложняется, состояние стоит переносить в поля класса: так данные сгруппированы и защищены от случайных изменений. Статические переменные идеальны для небольших локальных «памятей» внутри одной функции, например для времени последнего бара. А для обмена данными между программами и хранения, не зависящего от жизненного цикла советника, используйте глобальные переменные терминала.

ЗаключениеОбласть видимости — это фундамент, на котором держится надёжность советника. Глобальные переменные программы видны всем функциям и живут, пока робот загружен. Локальные существуют только во время выполнения блока. Из-за того, что OnTick вызывается на каждом тике заново, состояние — счётчики, флаги, время — нельзя держать в обычных локальных переменных; для этого подходят глобальные переменные, статические переменные и поля класса. А глобальные переменные терминала — отдельный механизм для межпрограммного обмена и долговременного хранения.

Разберитесь с этими понятиями один раз, и вы перестанете удивляться «магическому» поведению робота: код станет предсказуемым, а ошибки с обнулением состояния исчезнут. Больше учебных материалов — на https://max.ru/channel_mqlmasters, а короткие практические разборы выходят в Телеграме: https://t.me/mqlmasters.

#MQL5 #переменные #программирование #MetaTrader5 #MQLMaster

Опубликовано 1 день назад
Перейти к комментариям
Владимир → Обзоры и аналитика рынка
745
Ценовой диапазон — это не просто разница между максимумом и минимумом

Ценовой диапазон — это не просто разница между максимумом и минимумом, а измеритель волатильности, от которого зависит корректный размер риска. Ширина диапазона задаёт масштаб движения: в узком коридоре цена колеблется между поддержкой и сопротивлением, формируя консолидацию, а расширение диапазона сигнализирует о росте волатильности и возможном пробое границ.

Для алгоритмической торговли это принципиально. Советник на MQL5 с фиксированными стоп-лоссом и объёмом в узком флэте будет получать частые ложные срабатывания, а при резком расширении диапазона — несоразмерный риск. Индикатор ATR, по сути, является усреднённым диапазоном и позволяет привязывать стоп и лот к текущей волатильности инструмента, а не к статичным значениям.

Практический вывод: измеряйте диапазон на том же таймфрейме, на котором торгуете, ставьте стоп за границей диапазона и корректируйте размер позиции так, чтобы риск оставался постоянным. Больше разборов по трейдингу и автоматизации — на https://trading-go.ru/.

Опубликовано 1 день назад
Перейти к комментариям
Владимир → Обзоры и аналитика рынка
745
Как создавать собственные функции в MQL

Полный разбор собственных функций для начинающего разработчика советниковЛюбой серьёзный торговый советник со временем перестаёт помещаться в одну функцию OnTick. Сначала это несколько строк проверки условий, затем — расчёт лота, работа с позициями, фильтры и логирование. Если писать всё подряд, код превращается в длинную и хрупкую конструкцию, в которой легко запутаться и ещё легче допустить ошибку. Именно на этом этапе разработчик открывает для себя главный инструмент структурирования программы — собственные функции.

Функция в MQL5 — это именованный блок кода, который выполняет одну законченную задачу и может быть вызван из любого места программы. Вместо того чтобы копировать одинаковые команды десятки раз, вы пишете их один раз, даёте блоку понятное имя и обращаетесь к нему по этому имени. Такой подход называется декомпозицией: большая задача разбивается на маленькие, каждая из которых решается отдельной функцией.

В этой статье мы подробно разберём, как объявлять собственные функции в MQL5, как работать с параметрами и возвращаемым значением, чем отличаются функции от процедур, как передавать данные по значению и по ссылке, а также рассмотрим практические примеры из реальных советников — расчёт лота, проверку нового бара и подсчёт прибыли по позициям.

Зачем торговому роботу собственные функцииПока стратегия работает с одной позицией, можно обойтись простыми проверками внутри OnTick. Но реальные советники почти всегда имеют дело с несколькими задачами: рассчитать объём, проверить условия входа, выставить уровни, вывести диагностику. Если писать всё в одном месте, функция OnTick быстро разрастается до сотен строк, и найти в ней нужный фрагмент становится всё труднее.

Собственная функция решает эту проблему. Она собирает связанные действия под одним понятным именем. Вместо десяти строк расчёта лота в середине OnTick появляется одна строка вызова CalcLot(...). Читая код, вы сразу понимаете, что происходит, не вникая в детали каждой формулы. Это первый и главный плюс — читаемость.

Второй плюс — повторное использование. Хорошо написанная функция проверки нового бара или подсчёта прибыли переносится из одного советника в другой без изменений. Вы один раз решаете задачу и больше к ней не возвращаетесь.

Третий плюс — удобство отладки. Когда логика разбита на небольшие функции, ошибку проще локализовать: вы точно знаете, какая именно функция отвечает за расчёт, а какая — за работу с позициями. Править поведение можно в одном месте, и изменение автоматически распространится на все вызовы.

Синтаксис функции в MQL5Объявление функции в MQL5 состоит из нескольких частей. Сначала указывается тип возвращаемого значения, затем — имя функции, после него в круглых скобках перечисляются параметры. Тело функции заключается в фигурные скобки.

// =============================================================================================== //// = Полный синтаксис собственной функции в MQL5 = //// =============================================================================================== //тип_возврата ИмяФункции(тип1 параметр1, тип2 параметр2) { // === Тело функции === return значение; }

Если функция не возвращает результат, а только выполняет действие, вместо типа пишут void. Такие функции часто называют процедурами. Например, функция вывода сообщения в журнал:

// =============================================================================================== //// = Функция печатает сообщение в журнал с меткой времени = //// =============================================================================================== //void LogMessage(string text) { Print(TimeToString(TimeCurrent(), TIME_DATE | TIME_MINUTES), " | ", text); }

Компилятор MQL5 должен «знать» о функции до момента её вызова. Поэтому либо объявляйте функцию выше места использования, либо добавляйте прототип — объявление без тела.

Параметры и возвращаемое значениеПараметры — это входные данные функции. Они позволяют одной и той же логике работать с разными значениями. В примере ниже функция принимает процент риска и расстояние до стоп-лосса, а возвращает уже готовый объём позиции.

Ключевое слово return завершает работу функции и передаёт результат в место вызова. Важно, чтобы тип результата совпадал с типом возврата: если функция объявлена как double, возвращать нужно число с плавающей точкой.

Параметры могут иметь значения по умолчанию. Тогда при вызове их можно не указывать — функция подставит заготовленное значение.

// =============================================================================================== //// = Параметры по умолчанию: риск можно не указывать при вызове = //// =============================================================================================== //double PositionRisk(double riskPercent = 1.0, double slPoints = 300) { // === Если параметры не заданы, используются 1% риска и 300 пунктов SL === return riskPercent * slPoints; }

Такой приём делает функции гибкими: в простых случаях вызов короткий, а при необходимости параметры легко переопределить.

Функции void и функции со значениемУсловно функции делят на два вида. Первый — те, что возвращают результат: их вызов можно поставить справа от знака присваивания, как в double lot = CalcLot(1.0, 300);. Второй — процедуры void, которые просто выполняют действие: закрывают позицию, печатают сообщение, отправляют ордер.

// =============================================================================================== //// = Процедура void: выполняет действие и ничего не возвращает = //// =============================================================================================== //void PrintBalance() { Print("Баланс счёта: ", AccountInfoDouble(ACCOUNT_BALANCE)); }

Понимание разницы помогает правильно проектировать код: если функция должна «отдать» значение — она возвращает его, если только «сделать» — остаётся void.

Практический пример: расчёт размера лотаСамый востребованный пример собственной функции в советниках — расчёт объёма позиции по заданному риску. Ниже функция принимает процент риска и расстояние до стоп-лосса в пунктах, а возвращает объём, приведённый к шагу и границам брокера.

// =============================================================================================== //// = Функция рассчитывает размер лота по риску и расстоянию до Stop Loss = //// =============================================================================================== //double CalcLot(double riskPercent, double slPoints) { double riskMoney = AccountInfoDouble(ACCOUNT_BALANCE) * riskPercent / 100.0; double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);

// === Стоимость одного пункта при объёме в один лот === double pointValue = tickValue * (point / tickSize); double lot = riskMoney / (slPoints * pointValue);

// === Приводим объём к шагу и границам брокера === double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); lot = MathRound(lot / step) * step; lot = MathMax(SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN), lot); lot = MathMin(SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX), lot);

return lot; }

Обратите внимание на структуру: сначала собираются рыночные данные, затем выполняется расчёт, и только в конце объём приводится к допустимым значениям. Каждый шаг отделён мини-разделителем — так код легче читать и проверять.

Практический пример: проверка нового бараВторая частая задача — выполнять торговую логику не на каждом тике, а только при появлении новой свечи. Для этого пишут функцию, которая хранит время последнего бара в статической переменной.

// =============================================================================================== //// = Функция проверяет, появился ли новый бар на текущем таймфрейме = //// =============================================================================================== //bool IsNewBar() { static datetime lastBar = 0; datetime currentBar = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);

// === Первый вызов: запоминаем время бара и выходим === if (lastBar == 0) { lastBar = currentBar; return false; }

// === Если время изменилось — бар новый === if (currentBar != lastBar) { lastBar = currentBar; return true; }

return false; }

Функция возвращает bool, и вызывать её можно прямо в условии: if (IsNewBar()) { /* логика на новой свече */ }. Ключевое слово static сохраняет значение переменной между вызовами — именно это и позволяет сравнивать время предыдущего и текущего бара.

Практический пример: подсчёт прибыли по своим позициямТретий пример — обход всех позиций и суммирование прибыли только по сделкам робота, отобранным по Magic Number.

// =============================================================================================== //// = Функция считает суммарную прибыль позиций робота по Magic Number = //// =============================================================================================== //double TotalProfitByMagic(long magic) { double total = 0;

// === Перебираем все позиции терминала === for (int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if (ticket == 0) continue;

// === Пропускаем чужие позиции === if (PositionGetInteger(POSITION_MAGIC) != magic) continue;

// === Накапливаем прибыль своей позиции === total += PositionGetDouble(POSITION_PROFIT); }

return total; }

Здесь хорошо видно, как функция скрывает от основной логики всю рутину: вызывающий код просто получает готовую сумму и не думает о переборе позиций.

Передача данных по значению и по ссылкеПо умолчанию параметры передаются по значению: функция получает копию, и её изменения не влияют на исходную переменную. Если же нужно, чтобы функция изменила переданные данные, используется передача по ссылке — перед именем параметра ставится знак &.

// =============================================================================================== //// = Функция заполняет массив и возвращает среднюю цену закрытия = //// =============================================================================================== //double FillAndAverage(double &closes[], int count) { double sum = 0; ArrayResize(closes, count);

// === Заполняем массив ценами закрытия === for (int i = 0; i < count; i++) { closes[i] = iClose(_Symbol, _Period, i); sum += closes[i]; }

// === Возвращаем среднее значение === return (count > 0) ? sum / count : 0.0; }

Массивы в MQL5 и так передаются по ссылке, но явный знак & делает намерение очевидным. Для обычных переменных & обязателен, если функция должна их изменить: например, чтобы вернуть сразу два результата — количество прибыльных и убыточных сделок.

Перегрузка функцийВ MQL5 можно объявлять несколько функций с одним именем, но разным набором параметров. Это называется перегрузкой. Компилятор сам выбирает нужный вариант по типам и количеству аргументов.

// =============================================================================================== //// = Перегрузка: две функции с одним именем и разными параметрами = //// =============================================================================================== //double Normalize(double value, int digits) { return NormalizeDouble(value, digits); }double Normalize(double value) { return NormalizeDouble(value, _Digits); }

Перегрузка удобна, когда одна и та же операция может выполняться с разными наборами данных. Но увлекаться ею не стоит: слишком много вариантов одного имени усложняют чтение кода.

Где хранить собственные функцииСуществует два подхода. Первый — держать функции прямо в файле советника, ниже OnTick. Это удобно для небольших проектов. Второй — выносить их в отдельный файл-библиотеку с расширением .mqh, а затем подключать через директиву #include.

Библиотечный подход хорош тем, что один набор функций можно использовать во всех своих советниках. Например, файл с расчётом лота и проверкой нового бара подключается в любой проект и сразу даёт готовые инструменты.

// === Подключаем свою библиотеку функций ===#include "MyTools.mqh"

Главное — держать функции в одном стиле и давать им говорящие имена, чтобы через полгода вы сами легко вспомнили, что делает каждая из них.

Типичные ошибки начинающихПервая и самая частая ошибка — вызов функции до её объявления без прототипа. Компилятор просто не знает о существовании функции и сообщает об ошибке. Решение — объявить функцию выше или добавить прототип.

Вторая ошибка — несовпадение типа возврата. Если функция объявлена как int, а возвращает double, компилятор предупредит о потере точности. Проверяйте соответствие типов.

Третья ошибка — забытый return. Если функция должна возвращать значение, но какая-то ветка кода не доходит до return, поведение станет непредсказуемым. Убедитесь, что все пути выполнения возвращают результат.

Четвёртая ошибка — слишком большая функция. Если внутри десятки строк и несколько условий, её стоит разбить на несколько маленьких. Каждая функция должна решать одну задачу.

Пятая ошибка — неудачные имена. Функция f1 или doIt ничего не говорит о своём назначении. Имя CalcLot или IsNewBar делает код самодокументируемым.

Отладка собственных функцийОтладка функций не сложнее, чем отладка обычного кода. Простейший приём — вывести ключевые значения в журнал с помощью Print() прямо из функции.

// === Диагностика внутри функции ===Print("CalcLot: riskMoney=", riskMoney, " lot=", lot);

Если значение не то, что ожидалось, сузьте область поиска: проверьте входные параметры, затем промежуточные вычисления и только потом результат. Разбивая расчёт на короткие шаги с мини-разделителями, вы облегчаете себе эту работу.

Ещё один приём — вызывать функцию из простого скрипта с заранее известными входами. Так вы проверите её отдельно от сложной логики советника и убедитесь, что она считает верно.

Производительность и функцииВызов функции в MQL5 — операция дешёвая, но не бесплатная. Внутри OnTick, который выполняется на каждом тике, не стоит без нужды вызывать тяжёлые функции сотни раз. Если результат функции не меняется между вызовами, посчитайте его один раз и сохраните в переменной.

Хорошая практика — выносить в функции понятные, законченные операции и не смешивать в одной функции расчёт и торговые действия. Тогда код остаётся читаемым, а производительность — предсказуемой.

Практические рекомендации новичкуНачинайте с одной небольшой функции. Вынесите в неё расчёт лота — и посмотрите, как упростился OnTick. Затем добавьте функцию проверки нового бара, следом — подсчёт прибыли. Каждый шаг проверяйте в тестере стратегий, чтобы видеть, как изменение структуры кода влияет на поведение робота.

Давайте функциям говорящие имена, следите за типами и не забывайте про return. Держите тело функции коротким и понятным, а повторяющуюся логику выносите в отдельные блоки. Так вы постепенно придёте к модульной архитектуре, где каждый файл и каждая функция отвечают за свою часть работы.

ЗаключениеСобственные функции — это фундамент аккуратного кода в MQL5. Они собирают повторяющиеся действия под понятными именами, делают советник читаемым, упрощают отладку и позволяют переиспользовать готовые решения. Функции со значением возвращают результат, процедуры void просто выполняют действие, параметры позволяют одной логике работать с разными данными, а передача по ссылке — изменять исходные переменные.

Освоив собственные функции, вы сделаете важный шаг от коротких скриптов к настоящим модульным советникам. Начните с малого: вынесите в функцию расчёт лота, затем проверку нового бара, потом подсчёт прибыли. Больше учебных материалов — на https://max.ru/channel_mqlmasters, а короткие практические разборы выходят в Телеграме: https://t.me/mqlmasters.

Опубликовано 2 дня назад
Перейти к комментариям
Владимир → Обзоры и аналитика рынка
745
Как выбрать таймфрейм для торговли и не переносить риск с чужого графика

Выбор таймфрейма — не косметическая настройка графика, а параметр, определяющий частоту сигналов, величину стопа и нагрузку на трейдера. Один и тот же инструмент на M5 и на H4 ведёт себя как два разных рынка: на младшем периоде преобладает шум, на старшем — структура тренда и ключевые уровни.

Практическое правило мультитаймфреймового анализа: направление и уровни оцениваются на старшем периоде, точка входа — на младшем. Старший таймфрейм имеет приоритет, поэтому сигнал против дневного тренда чаще оказывается коррекцией. Отдельная ошибка — переносить стоп-лосс и тейк-профит с одного таймфрейма на другой без пересчёта: размах свечи растёт вместе с периодом, а значит меняется и корректный размер риска.

Для алгоритмической торговли это же правило критично: советник на MQL5 получает данные конкретного таймфрейма, и та же логика на M5 и H4 даёт разную частоту сделок и разные результаты тестирования. Настраивать робота имеет смысл только на том периоде, на котором он будет работать.

Понимание таймфреймов — часть системного управления позицией. Больше разборов по трейдингу и автоматизации в MAX: https://max.ru/channel_tradinggo и в нашем Телеграм-канале: https://t.me/tgforexru.

Профиль автора: https://www.mql5.com/ru/users/voldemar

Опубликовано 2 дня назад
Перейти к комментариям
Подпишитесь на наш канал @fxmag в Telegram
Рейтинг брокеров 2026 ( TOP10 )
Публикующихся на сайте FXMAG.RU
1 Tickmill
tickmill.fxmag.ru
2 FreshForex
freshforex.fxmag.ru
3 Instaforex
instaforexanalytics.fxmag.ru
4 FXOpen
fxopen.fxmag.ru
5 NordFX
roman_butko.fxmag.ru
6 JustForex
justforex.fxmag.ru
7 ForexMart
forexmart.fxmag.ru
8 ФГ "Калита - Финанс"
kalita_finance.fxmag.ru
9 Альпари
alpari.fxmag.ru
10 MTrading
mtrading.fxmag.ru
Форекс-объявления:
Показано форекс-объявлений:
в декабре: 11 033 625;
вчера: 1 038 695 на 260 сайтах;
Разместить объявление от 10 коп. за переход
 
Новости компаний
Добавить новость

Сервис Bitbanker: покупка стейблкоинов (пресс-релиз)

В трейдинге и финансах есть железное правило: если сделка кажется сомнительной не лезь. С P2P-обменом стейблкоинов та же история. Вы ищете выгодный курс, а находите блокировку карты по 115-ФЗ. Банк видит перевод от серого контрагента и привет,...

Опубликовано: 3 месяца назад

МЕДИА ХИМИЯКоличество подписчиков4108

FRTX обзор: лицензия MISA, условия счета, отзывы (пресс-релиз)

В 2026 году вокруг брокера FRTX сформировалась разнородная информационная выдача: от положительных обзоров до материалов с жёсткой критикой. Официальный сайт frtx.global публикует данные о компании FRTX Ltd (регистрационный номер HV01125482,...

Опубликовано: 4 месяца назад

МЕДИА ХИМИЯКоличество подписчиков4108

Лучший форекс-брокер июнь 2026: как правильно выбрать брокера и на что смотреть (пресс-релиз)

В финансах и трейдинге нет универсального ответа на вопрос что лучше . Лучший актив для одного инвестора катастрофа для другого. То же самое с брокерами. В 2026 году рынок переполнен предложениями, и фраза лучший форекс-брокер потеряла всякий...

Опубликовано: 5 месяцев назад

МЕДИА ХИМИЯКоличество подписчиков4108

Для NPBFX 2025 год рекордов и проверок на прочность: чем запомнился уходящий год

Для трейдеров 2025 год стал одним из самых сложных за последние годы, и форекс-брокеру NPBFX пришлось работать в условиях постоянно растущего рыночного давления. Повышенная волатильность, перегретые ожидания, череда громких банкротств в...

Опубликовано: 9 месяцев назад

МЕДИА ХИМИЯКоличество подписчиков4108

Своя траектория роста: как развивается Freedom Finance в 2025 году (мнение)

Финансовый сектор редко прощает ошибки, но иногда умеет награждать тех, кто не боится перестраивать себя под новые реалии. История Freedom Finance хороший пример того, как локальная брокерская компания за относительно короткий срок может стать...

Опубликовано: 10 месяцев назад

МЕДИА ХИМИЯКоличество подписчиков4108

Как выбрать криптовалютный обменник по данным мониторинга и отзывам

Представляем вашему вниманию пресс-релиз одного из сервисов мониторинга обменных онлайн-пунктов обмена криптовалют. Представленный ниже текст подготовлен сторонними авторами, чье мнение может не совпадать с мнением нашего сайта и носит исключительно...

Опубликовано: 11 месяцев назад

МЕДИА ХИМИЯКоличество подписчиков4108

Первый и непобедимый: как листинг Freedom Holding Corp. на Nasdaq открыл новую эру для финансового рынка СНГ

В истории финансового пространства Содружества Независимых Государств есть знаковые вехи, которые навсегда меняют правила игры. Одной из таких стал листинг холдинга Тимура Турлова на американской бирже Nasdaq. Это событие, к которому компания шла...

Опубликовано: 11 месяцев назад

МЕДИА ХИМИЯКоличество подписчиков4108

Чем занимается холдинг СИБУР: от переработки газа до производства современных материалов

Холдинг СИБУР это ведущая нефтехимическая компания России, которая преобразует углеводородное сырье в продукцию, необходимую в повседневной жизни и промышленности. От упаковки и медицинских изделий до строительных материалов и компонентов для...

Опубликовано: 1 год назад

МЕДИА ХИМИЯКоличество подписчиков4108

Белсмета Онлайн База: Умное управление финансами и сметами в один клик (пресс-релиз)

Представляем вашему вниманию пресс-релиз сервиса "Белсмета". Материал подготовлен представителями сервиса и носит исключительно в информационно-справочных целях - его ни в коем случае не следует рассматривать в качестве нашей рекомендации и (или)...

Опубликовано: 1 год назад

МЕДИА ХИМИЯКоличество подписчиков4108

Знакомьтесь, база семян Семяныча (пресс-релиз)

Представляем вашему вниманию пресс-релиз сервиса "Семяныч". Информация предоставлена представителями сервиса - текст, пунктуация и орфография сохранены полностью. Информация публикуется исключительно с информационно-справочными целями и ее ни в коем...

Опубликовано: 1 год назад

МЕДИА ХИМИЯКоличество подписчиков4108

 Forex Magazine © 2004-2026