
Упрощаем торговлю на новостях (Часть 5): Совершаем сделки (II)
Введение
Основной целью является написание кода для внедрения стоп-ордеров в советника по новостной торговле. Эти стоп-ордера будут использоваться в следующей статье для торговли на новостях. Кроме того, мы создадим функции для управления проскальзыванием для стоп-ордеров, закрытия сделок и выполнения проверок на возможность открыть сделку или ордер. Управление торговлей имеет решающее значение в любой алгоритмической торговой системе, поскольку оно включает в себя такие задачи, как открытие и закрытие сделок, корректировка стоп-лоссов и управление тейк-профитами. Эффективное управление торговлей может помочь трейдеру получить большую прибыль, минимизируя при этом подверженность неблагоприятным изменениям на рынке.
Для чего нужны стоп-ордера?
Использование стоп-ордеров при торговле на новостях является распространенной стратегией, поскольку они помогают трейдерам извлекать выгоду из резких колебаний цен, которые часто следуют за важными экономическими публикациями, а также минимизировать определенные риски.
Захват прорывов
Новости, такие как экономические отчеты или заявления центральных банков, часто приводят к внезапным и значительным колебаниям цен - прорывам. Стоп-ордер позволяет трейдеру войти на рынок по заранее определенному ценовому уровню, когда цена превышает определенный порог, что идеально подходит для отслеживания таких прорывных движений без необходимости вручную мониторить рынок.
- Ордер Buy Stop размещается выше текущей рыночной цены. Он активирует ордер на покупку, когда цена растет, улавливая восходящий импульс после позитивных новостей.
- Ордер Sell Stop размещается ниже текущей рыночной цены. Он активирует ордер на продажу, когда цена падает на негативных новостях.
Избегание "пилы цен"
Волатильность, связанная с новостями, может вызвать колебания цен до того, как установится четкое направление. Трейдеры, которые открывают позиции слишком рано, могут попасть в ситуацию резких движений в обоих направлениях, которые могут привести к срабатыванию стоп-лоссов. Стоп-ордера помогают избежать подобных ложных срабатываний, гарантируя, что сделка сработает только тогда, когда рынок определит направление, выходящее за пределы определенной ценовой отметки.
Ограничение чрезмерного анализа в трейдинге
Торговля на новостях может быть напряженной из-за быстрых движений рынка. Заблаговременная установка стоп-ордеров позволяет трейдерам входить в позиции и выходить из них без излишних эмоций или необходимости следить за направлением событий. После размещения ордеров решение трейдера уже принято, что исключает соблазн чрезмерно отреагировать на краткосрочные колебания цен. В контексте статьи это означает, что выступления и другие новостные события, не имеющие слишком большого влияния, можно торговать с использованием стоп-ордеров, поскольку направление заранее предсказать сложно или невозможно.
Минимизация проскальзывания
Используя стоп-ордера, трейдеры могут избежать проскальзывания, связанного с рыночными ордерами, размещенными в периоды высокой волатильности. Хотя проскальзывание все еще может иметь место, стоп-ордер имеет больше шансов исполниться вблизи предполагаемого уровня, чем рыночный ордер, размещенный после того, как новость уже вызвала существенное изменение цены.
Управление рисками
Стоп-ордера помогают автоматизировать управление рисками. Например, если трейдер ожидает волатильной реакции на новости, он может разместить как стоп-ордер на покупку, так и стоп-ордер на продажу (стратегия Straddle). Такой подход помогает трейдеру входить в рынок в любом направлении, в зависимости от того, как падает цена. После срабатывания противоположный стоп-ордер может быть отменен для управления риском.
Сценарий:
Вы торгуете USD на фоне объявления о количестве рабочих мест в несельскохозяйственном секторе (NFP) - одного из самых важных новостных событий. Ожидая, что данные приведут к существенному движению пары EURUSD, вы размещаете стоп-ордер на покупку выше текущей цены и стоп-ордер на продажу ниже текущей цены (стратегия Straddle).
- Положительные данные NFP: доллар укрепляется, что приводит к падению EURUSD и срабатыванию вашего стоп-ордера на продажу.
- Негативные данные NFP: доллар слабеет, что приводит к росту EURUSD и срабатыванию вашего стоп-ордера на покупку.
Используя стоп-ордера, вы готовы к движению в любом направлении и автоматически входите в рынок, как только ценовое действие подтверждает направление прорыва.
Класс AccountProperties
Класс CAccountProperties наследует от класса CAccountInfo в MQL5. Целью этого класса является расширение CAccountInfo с помощью специального метода для получения общего количества определенных типов ордеров на торговом счете, таких как лимитные/стоп-ордера на покупку или продажу. Эта дополнительная функция будет использоваться в классе Trade management, чтобы гарантировать, что советник останется в пределах лимитных ордеров счета.
Например: если лимит ордеров на счете составляет 200, это означает, что на счете пользователя не может быть больше 200 ордеров, включая открытые и отложенные ордера. Таким образом, если мы хотим открыть ордер buy stop и sell stop, но на счете пользователя 199 ордеров, советник определит, что осталось недостаточно ордеров, поэтому в этом сценарии на счет пользователя не будут добавлены дополнительные ордера.
Заголовок и Include:
Приведенный ниже код включает файл AccountInfo.mqh из стандартной библиотеки MQL5. Файл содержит предопределенные функции и структуры для доступа к информации о торговом счете.
#include <Trade/AccountInfo.mqh>
Определение класса CAccountProperties:
Наследование: Класс CAccountProperties открыто наследует от CAccountInfo. Благодаря наследованию CAccountProperties получает доступ к функциям и членам данных CAccountInfo.
CAccountInfo предоставляет основные данные по счету, такие как баланс, эквити, свободная маржа и другие.
Назначение класса: Класс CAccountProperties добавляет метод (numOrders), который подсчитывает определенные типы ордеров.
class CAccountProperties: public CAccountInfo
Функция numOrders:
Функция numOrders() — это место, где происходит большая часть событий. Эта функция подсчитывает количество определенных типов ордеров (лимитных и стоп-ордеров), которые в данный момент открыты на торговом счете.
- Тип возвращаемого значения: Целое число.
- Инициализация: Переменная num инициализируется значением 0. Это сохранит количество ордеров, соответствующих желаемым типам.
int CAccountProperties::numOrders(void) { int num=0;
Итерация ордера:
- OrdersTotal() — MQL5-функция, которая возвращает общее количество ордеров.
- Цикл используется для перебора всех ордеров, от индекса 0 до OrdersTotal() - 1.
for(int i=0; i<OrdersTotal(); i++)
Проверка ордера:
- OrderGetTicket(i) — это функция MQL5, которая извлекает номер тикета для ордера с индексом i. Каждый ордер имеет уникальный номер тикета.
- Если номер тикета больше 0, это означает, что ордер действителен, и код переходит к проверке его типа.
if(OrderGetTicket(i)>0)
Проверка типа ордера:
- OrderGetInteger(ORDER_TYPE) извлекает тип ордера по индексу i. Это значение соответствует целому числу, которое представляет различные типы ордеров (лимитный ордер на покупку, стоп-ордер на продажу и так далее).
- Оператор switch проверяет тип каждого ордера и сравнивает его с предопределенными константами типа ордера.
switch(int(OrderGetInteger(ORDER_TYPE)))
Обработка определенных типов ордеров:
- Типы ордеров:
- ORDER_TYPE_BUY_LIMIT - лимитный ордер на покупку.
- ORDER_TYPE_BUY_STOP - стоп-ордер на покупку.
- ORDER_TYPE_BUY_STOP_LIMIT - стоп-лимитный ордер на покупку.
- ORDER_TYPE_SELL_LIMIT - лимитный ордер на продажу.
- ORDER_TYPE_SELL_STOP - стоп-ордер на продажу.
- ORDER_TYPE_SELL_STOP_LIMIT - стоп-лимитный ордер на продажу.
Для каждого распознанного типа ордера номер счетчика увеличивается. Если ордер относится к любому другому типу, то срабатывает случай по умолчанию, и никакие действия не предпринимаются.
case ORDER_TYPE_BUY_LIMIT: num++; break; case ORDER_TYPE_BUY_STOP: num++; break; case ORDER_TYPE_BUY_STOP_LIMIT: num++; break; case ORDER_TYPE_SELL_LIMIT: num++; break; case ORDER_TYPE_SELL_STOP: num++; break; case ORDER_TYPE_SELL_STOP_LIMIT: num++; break; default: break;
Возврат общего количества:
- После перебора всех ордеров и подсчета действительных, функция возвращает общее количество, хранящееся в num.
return num;
}
Класс Risk Management
Класс обеспечивает контроль подверженности трейдера рыночному риску, ограничивая потенциальные потери и оставляя место для прибыли, при этом в этот класс были внесены незначительные изменения. Код ниже определяет перечисление OrderTypeSelection, которое используется для представления различных типов классификаций ордеров. Пользователю будет предоставлена возможность выбрать, с какими из этих типов ордеров он предпочитает торговать. //-- Enumeration for Order type enum OrderTypeSelection { MarketPositionType,//MARKET POSITION StopOrdersType,//STOP ORDERS StopOrderType,//SINGLE STOP ORDER } myOrderSelection;
Перечисление: OrderTypeSelection
Перечисление OrderTypeSelection состоит из трех разных значений:
MarketPositionType:
- Представляет собой ордер рыночной позиции.
- Этот тип открывается на основе текущей рыночной цены и исполняется немедленно по текущей цене.
- Требуется уровень важности события.
StopOrdersType:
- Представляет собой стоп-ордера.
- Стоп-ордера — это условные ордера, которые исполняются только тогда, когда цена достигает определенного уровня. Открываются как стоп-ордера на покупку, так и стоп-ордера на продажу.
- Не требует воздействия события.
StopOrderType:
- Представляет собой единый стоп-ордер.
- Открывается индивидуальный ордер buy stop или sell stop.
- Требуется уровень важности события.
Объявление переменной: myOrderSelection
- myOrderSelection — переменная, в которой будет храниться текущий тип ордера, выбранный трейдером.
Нормализация размера лота
В приведенной ниже функции лимит объема будет меняться в зависимости от OrderTypeSelection. Когда myOrderSelection равен StopOrdersType(STOP ORDERS), лимит объема уменьшается вдвое, чтобы удовлетворить как ордер buy stop, так и ордер sell stop, поскольку оба ордера должны быть открыты одновременно, тогда как MarketPositionType(MARKET POSITION) и StopOrderType(SINGLE STOP ORDER) открывают только одиночные ордера.
void CRiskManagement::NormalizeLotsize(double &Lotsize) { // Adjust lot size to match symbol's step size or minimum size if (Lotsize <= 0.0) return; double VolumeLimit = (myOrderSelection != StopOrdersType) ? CSymbol.LotsLimit() : CSymbol.LotsLimit() / 2; // Check and adjust if volume exceeds limits // ... }
Класс Sessions
Приведенный ниже код определяет класс CSessions, который отвечает за управление и отслеживание времени торговых сессий для определенного торгового символа. Он использует методы базового класса CTimeManagement (включенного через Timemanagement.mqh) для определения начала и окончания торговой сессии, а также для получения времени окончания сессии. Для контекста нам необходимо знать, когда закончится торговая сессия, чтобы заранее закрыть сделки и установить даты истечения срока действия ордеров, чтобы избежать переноса торгов на следующий день.
Почему следует избегать ночной торговли?
Повышенный рыночный риск из-за волатильности
Рынки могут быть крайне волатильными, особенно в ответ на экономические и геополитические события или новости с других мировых рынков. Поскольку важные события могут происходить вне обычных торговых часов, позиции, удерживаемые ночью, подвержены непредсказуемым колебаниям цен. Возможные последствия:
- Значительные разрывы в цене при открытии рынка на следующий день, что может привести к существенной прибыли или убыткам.
- Ограниченная способность реагировать на быстрые изменения, поскольку ликвидность может быть низкой, а ваши возможности быстро изменять или закрывать позиции могут быть ограничены.
Ограниченная ликвидность
Ликвидность, как правило, ниже во время ночных торговых сессий, поскольку активность участников рынка снижается. Это может привести к:
- Более широким спредам между ценами bid и ask, увеличивающим транзакционные издержки.
- Проскальзыванию, когда сделки исполняются по цене, отличной от ожидаемой, из-за недостаточной глубины рынка.
- Трудностям с закрытием крупных позиций без изменения ситуации на рынке, что может привести к невыгодному ценовому исполнению.
Повышенный риск появления разрывов
Ночная торговля может подвергнуть трейдеров риску возникновения ценовых разрывов, когда цена открытия рынка существенно отличается от цены закрытия предыдущего дня. Пробелы могут возникать из-за выпусков новостей или других событий и могут привести к:
- Более крупным, чем ожидалось, убыткам.
- Стоп-лоссы в этом случае неэффективны, поскольку разрывы могут превышать заранее установленные вами стоп-уровни, исполняя ваш ордер по гораздо худшей цене, чем ожидалось.
Комиссии за своп
Для некоторых инструментов, таких как Forex, удержание позиций ночью может привести к взиманию комиссий за своп или процентов в зависимости от разницы процентных ставок между задействованными валютами. Со временем эти затраты могут накапливаться и снижать прибыльность. Комиссии за своп могут:
- Превратить прибыльные сделки в убыточные, особенно если они удерживаются в течение длительного времени.
- Меняться в зависимости от рыночных условий, что затрудняет их прогнозирование и планирование.
Снижение контроля за исполнением сделок
Ночью, особенно на рынках с ограниченным или нерегулярным графиком работы, исполнение сделок может быть более медленным или менее точным из-за:
- Часов работы брокера: некоторые брокеры могут не поддерживать определенные торговые действия или изменения вне своих обычных часов работы, что снижает вашу способность реагировать на рыночные условия.
//+------------------------------------------------------------------+ //| NewsTrading | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com/en/users/kaaiblo | //+------------------------------------------------------------------+ #include "Timemanagement.mqh" //+------------------------------------------------------------------+ //|Sessions Class | //+------------------------------------------------------------------+ class CSessions:CTimeManagement { public: CSessions(void) {} ~CSessions(void) {} //--- Check if trading Session has began bool isSessionStart(int offsethour=0,int offsetmin=0); //--- Check if trading Session has ended bool isSessionEnd(int offsethour=0,int offsetmin=45); //--- Get Session End datetime datetime SessionEnd(int offsethour=0,int offsetmin=45); }; //+------------------------------------------------------------------+ //|Check if trading Session has started | //+------------------------------------------------------------------+ bool CSessions::isSessionStart(int offsethour=0,int offsetmin=0) { //--- Declarations datetime datefrom,dateto,DateFrom[],DateTo[]; //--- Find all session times for(int i=0; i<10; i++) { //--- Get the session dates for the current symbol and Day of week if(SymbolInfoSessionTrade(Symbol(),DayOfWeek(TimeTradeServer()),i,datefrom,dateto)) { //--- Check if the end date's hour is at midnight if(ReturnHour(dateto)==00||ReturnHour(dateto)==24) { //--- Adjust the date to one minute before midnight dateto = Time(TimeTradeServer(),23,59); } //--- Re-adjust DateFrom Array size ArrayResize(DateFrom,int(ArraySize(DateFrom))+1,int(ArraySize(DateFrom))+2); //--- Assign the last array index datefrom value DateFrom[int(ArraySize(DateFrom))-1] = datefrom; //--- Re-adjust DateTo Array size ArrayResize(DateTo,int(ArraySize(DateTo))+1,int(ArraySize(DateTo))+2); //--- Assign the last array index dateto value DateTo[int(ArraySize(DateTo))-1] = dateto; } } //--- Check if there are session times if(DateFrom.Size()>0) { /* Adjust DateFrom index zero date as the first index date will be the earliest date from the whole array, we add the offset to this date only*/ DateFrom[0] = TimePlusOffset(DateFrom[0],MinutesS(offsetmin)); DateFrom[0] = TimePlusOffset(DateFrom[0],HoursS(offsethour)); //--- Iterate through the whole array for(uint i=0; i<DateFrom.Size(); i++) { //--- Check if the current time is within the trading session if(TimeIsInRange(Time(Today(ReturnHour(DateFrom[i]),ReturnMinute(DateFrom[i]))) ,Time(Today(ReturnHour(DateTo[i]),ReturnMinute(DateTo[i]))))) { return true; } } } else { //--- If there are no trading session times return true; } return false; } //+------------------------------------------------------------------+ //|Check if trading Session has ended | //+------------------------------------------------------------------+ bool CSessions::isSessionEnd(int offsethour=0,int offsetmin=45) { //--- Declarations datetime datefrom,dateto,DateTo[],lastdate=0,sessionend; //--- Find all session times for(int i=0; i<10; i++) { //--- Get the session dates for the current symbol and Day of week if(SymbolInfoSessionTrade(Symbol(),DayOfWeek(TimeTradeServer()),i,datefrom,dateto)) { //--- Check if the end date's hour is at midnight if(ReturnHour(dateto)==00||ReturnHour(dateto)==24) { //--- Adjust the date to one minute before midnight dateto = Time(TimeTradeServer(),23,59); } //--- Re-adjust DateTo Array size ArrayResize(DateTo,int(ArraySize(DateTo))+1,int(ArraySize(DateTo))+2); //--- Assign the last array index dateto value DateTo[int(ArraySize(DateTo))-1] = dateto; } } //--- Check if there are session times if(DateTo.Size()>0) { //--- Assign lastdate a default value lastdate = DateTo[0]; //--- Iterate through the whole array for(uint i=0; i<DateTo.Size(); i++) { //--- Check for the latest date in the array if(DateTo[i]>lastdate) { lastdate = DateTo[i]; } } } else { //--- If there are no trading session times return false; } /* get the current time and modify the hour and minute time to the lastdate variable and assign the new datetime to sessionend variable*/ sessionend = Time(Today(ReturnHour(lastdate),ReturnMinute(lastdate))); //--- Re-adjust the sessionend dates with the minute and hour offsets sessionend = TimeMinusOffset(sessionend,MinutesS(offsetmin)); sessionend = TimeMinusOffset(sessionend,HoursS(offsethour)); //--- Check if sessionend date is more than the current time if(TimeTradeServer()<sessionend) { return false; } return true; } //+------------------------------------------------------------------+ //|Get Session End datetime | //+------------------------------------------------------------------+ datetime CSessions::SessionEnd(int offsethour=0,int offsetmin=45) { //--- Declarations datetime datefrom,dateto,DateTo[],lastdate=0,sessionend; //--- Find all session times for(int i=0; i<10; i++) { //--- Get the session dates for the current symbol and Day of week if(SymbolInfoSessionTrade(Symbol(),DayOfWeek(TimeTradeServer()),i,datefrom,dateto)) { //--- Check if the end date's hour is at midnight if(ReturnHour(dateto)==00||ReturnHour(dateto)==24) { //--- Adjust the date to one minute before midnight dateto = Time(TimeTradeServer(),23,59); } //--- Re-adjust DateTo Array size ArrayResize(DateTo,int(ArraySize(DateTo))+1,int(ArraySize(DateTo))+2); //--- Assign the last array index dateto value DateTo[int(ArraySize(DateTo))-1] = dateto; } } //--- Check if there are session times if(DateTo.Size()>0) { //--- Assign lastdate a default value lastdate = DateTo[0]; //--- Iterate through the whole array for(uint i=0; i<DateTo.Size(); i++) { //--- Check for the latest date in the array if(DateTo[i]>lastdate) { lastdate = DateTo[i]; } } } else { //--- If there are no trading session times return 0; } /* get the current time and modify the hour and minute time to the lastdate variable and assign the new datetime to sessionend variable*/ sessionend = Time(Today(ReturnHour(lastdate),ReturnMinute(lastdate))); //--- Re-adjust the sessionend dates with the minute and hour offsets sessionend = TimeMinusOffset(sessionend,MinutesS(offsetmin)); sessionend = TimeMinusOffset(sessionend,HoursS(offsethour)); //--- return sessionend date return sessionend; } //+------------------------------------------------------------------+
Ключевые компоненты
Определение класса: CSessions
- Наследование:
- Класс CSessions наследуется от CTimeManagement, поэтому он имеет доступ ко всем методам и атрибутам, связанным с управлением временем, определенным в этом базовом классе.
- Методы:
- isSessionStart(int offsethour=0, int offsetmin=0):
- Определяет, началась ли торговая сессия, путем сверки текущего времени сервера со временем начала сессии. Также допускается возможность дополнительных смещений по времени.
- isSessionEnd(int offsethour=0, int offsetmin=45):
- Определяет, завершилась ли торговая сессия, сравнивая текущее время сервера со временем окончания сессии. Как и isSessionStart, принимает необязательные смещения (по умолчанию - 45 минут).
- SessionEnd(int offsethour=0, int offsetmin=45):
- Возвращает значение даты и времени окончания текущей торговой сессии, корректируя его с учетом предоставленных смещений (по умолчанию - 45 минут).
Подробности метода
isSessionStart(int offsethour=0, int offsetmin=0)
Метод проверяет, началась ли торговая сессия, на основе времени сервера и расписания сессий торгового символа.
Переменные:
- datefrom, dateto - используются для хранения времени начала и окончания сеанса для каждого торгового дня.
- DateFrom[], DateTo[] - массивы для хранения времени начала и окончания сеанса после корректировок.
Логика:
- Извлечение сессии:
- Функция SymbolInfoSessionTrade извлекает время начала (datefrom) и окончания (dateto) торговой сессии для текущего символа и дня недели (извлекается функцией DayOfWeek(TimeTradeServer())).
- Если время окончания сессии (dateto) приходится на полночь (00:00 или 24:00), время корректируется на одну минуту до полуночи (23:59).
- Манипуляции с массивами:
- Время начала сессии (datefrom) хранится в массиве DateFrom[], а время окончания (dateto) - в массиве DateTo[].
- Применение смещения:
- Метод корректирует время начала первого сеанса по заданным offsethour и offsetmin, используя вспомогательные функции MinutesS() и HoursS().
- Проверка текущего времени:
- Затем метод перебирает время сессии в DateFrom[] и DateTo[] и проверяет, попадает ли текущее время сервера (TimeTradeServer()) в какой-либо из интервалов сессии.
- Если найдена допустимая сессия, возвращается значение true, что указывает на то, что сессия началась. В противном возвращается false.
isSessionEnd(int offsethour=0, int offsetmin=45)
Этот метод проверяет, завершилась ли торговая сессия.
Переменные:
- Аналогично isSessionStart(), но с упором на время окончания (dateto).
- lastdate - сохраняет последнее время окончания сессии для сравнения.
- sessionend - содержит окончательное расчетное время окончания сессии с примененными смещениями.
Логика:
- Извлечение сессии:
- Извлекает время сессии (datefrom, dateto), как в isSessionStart().
- Проверяет, есть ли допустимые значения времени сессии в DateTo[], и определяет самое позднее время окончания сессии (lastdate).
- Применение смещения:
- Корректирует время окончания сессии, применяя смещения (offsethour и offsetmin).
- Сравнение с серверным временем:
- Если текущее время предшествует скорректированному времени окончания сессии, возвращается false, что указывает на то, что сессия все еще продолжается. Если сессия завершена (текущее время после sessionend), возвращается значение true.
SessionEnd(int offsethour=0, int offsetmin=45)
Этот метод возвращает значение даты и времени для окончания текущей сессии, применяя предоставленные смещения времени.
Логика:
- Подобно isSessionEnd(), извлекает время сессии, применяет смещения и возвращает окончательное скорректированное время окончания сессии (sessionend).
- Повышенная волатильность и непредсказуемость движений рынка.
- Низкая ликвидность, что приводит к более широким спредам и более высоким издержкам.
- Риск ценовых разрывов, потенциально приводящих к большим потерям.
- Подверженность глобальным событиям и их влиянию на рынок.
- Процентные сборы или комиссии за свопы на рынках Форекс.
- Снижение контроля за исполнением сделок и принятием решений.
Класс Trade Management
Класс CTradeManagement расширяет CRiskManagement и содержит функциональные возможности для управления сделками, такие как размещение ордеров на покупку/продажу, обработка уровней стоп-лосса и тейк-профита, а также управление стоп-ордерами.
Блок-схема открытия ордера на покупку или продажу:
+---------------------------+
| Receive Trade Signal |
+---------------------------+
|
V
+---------------------------+
| Check for Available Margin|
+---------------------------+
|
V
+---------------------------+
| Place Buy/Sell Order |
+---------------------------+
|
V
+----------------------------+
| Set Stop Loss & Take Profit|
+----------------------------+
|
V
+----------------------------+
| Monitor Open Position |
+----------------------------+
|
If conditions met:
|
V
+----------------------------+
| Adjust SL/TP or Close Trade|
+----------------------------+
//+------------------------------------------------------------------+ //| NewsTrading | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com/en/users/kaaiblo | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> #include <Trade\OrderInfo.mqh> #include <Trade\SymbolInfo.mqh> #include "RiskManagement.mqh" #include "TimeManagement.mqh" #include "Sessions.mqh" //+------------------------------------------------------------------+ //|TradeManagement class | //+------------------------------------------------------------------+ class CTradeManagement:CRiskManagement { private: CTrade Trade;//Trade class object CSymbolProperties CSymbol;//SymbolProperties class object CTimeManagement CTime;//TimeManagement class object CSessions CTS;//Sessions class object bool TradeResult;//boolean to store trade result double mySL;//double variable to store Stoploss double myTP;//double variable to store Takeprofit uint myDeviation;//store price deviation for stop orders double myOpenPrice;//store open price for stop orders //--- Will retrieve if there are any open trades bool OpenTrade(ENUM_POSITION_TYPE Type,ulong Magic,string COMMENT=NULL); //--- Will retrieve if there are any deals bool OpenedDeal(ENUM_DEAL_TYPE Type,ulong Magic,string COMMENT=NULL); //--- Will retrieve if there are any open orders bool OpenOrder(ENUM_ORDER_TYPE Type,ulong Magic,string COMMENT=NULL); //-- Check if trade is valid bool Valid_Trade(ENUM_POSITION_TYPE Type,double Price,double SL,double TP); //-- Check if stop order is valid bool Valid_Order(ENUM_ORDER_TYPE Type,double Price,double SL,double TP); //--- Will attempt to open buy trade bool Buy(double SL,double TP,ulong Magic,string COMMENT=NULL); //--- Will attempt to open sell trade bool Sell(double SL,double TP,ulong Magic,string COMMENT=NULL); //--- class to set and retrieve an order's properties class OrderSettings { private: struct TradeProperties { //store open-price,take-profit,stop-loss for stop orders double Open,Take,Stop; } myTradeProp; public: OrderSettings() {} //--- Set order properties void Set(double myOpen,double myTake,double myStop) { //--- Set open-price myTradeProp.Open=myOpen; //--- Set take-profit myTradeProp.Take=myTake; //--- Set stop-loss myTradeProp.Stop=myStop; } TradeProperties Get() { //--- retrieve order properties return myTradeProp; } }; //--- Declare variables for different order types OrderSettings myBuyStop,mySellStop,myBuyTrade,mySellTrade; //--- Will set buy-stop order properties void SetBuyStop(int SL,int TP); //--- Will set buy position properties void SetBuyTrade(int SL,int TP,double OP); //--- Will set sell-stop order properties void SetSellStop(int SL,int TP); //--- Will set sell position properties void SetSellTrade(int SL,int TP,double OP); public: //--- Class constructor CTradeManagement(uint deviation,string SYMBOL=NULL) :myDeviation(deviation)//Assign deviation value { //--- Set symbol name CSymbol.SetSymbolName(SYMBOL); } //--- Class destructor ~CTradeManagement(void) { } //--- Will attempt to open buy trade bool Buy(int SL,int TP,ulong Magic,string COMMENT=NULL); //--- Will attempt to open sell trade bool Sell(int SL,int TP,ulong Magic,string COMMENT=NULL); /*This function will delete a pending order if the previous opposing pending order is opened into a position, this function is used when trading with StopOrdersType(STOP ORDERS)*/ void FundamentalMode(string COMMENT_COMMON); /* Function will attempt to re-adjust stop-losses or take-profit values that have been changed due to slippage on an order when opening. */ void SlippageReduction(int SL,int TP,string COMMENT_COMMON); //--- This function will open both buy-stop and sell-stop orders for StopOrdersType(STOP ORDERS) bool OpenStops(int SL,int TP,ulong Magic,string COMMENT=NULL); //--- Will attempt to open a sell-stop order bool OpenSellStop(int SL,int TP,ulong Magic,string COMMENT=NULL); //--- Will attempt to open a buy-stop order bool OpenBuyStop(int SL,int TP,ulong Magic,string COMMENT=NULL); //--- Function will attempt to close all trades depending on the position comment void CloseTrades(string COMMENT_COMMON); }; // ...
Приватные члены:
- Объекты и переменные:
- CTrade Trade - управляет исполнением сделок (ордера на покупку/продажу) и другие.
- CSymbolProperties CSymbol - управляет свойствами символа.
- CTimeManagement CTime - обрабатывает логику, связанную со временем.
- CSessions CTS - управляет сессиями, связанными с часами торговли.
- bool TradeResult - логический флаг результата торговли (успех/неудача).
- double mySL - значение стоп-лосса.
- double myTP - значение тейк-профита.
- uint myDeviation; - отклонение цены для стоп-ордеров.
- double myOpenPrice - хранит цену открытия для стоп-ордеров.
Внутренний класс OrderSettings:
class OrderSettings { private: struct TradeProperties { double Open, Take, Stop; } myTradeProp; public: OrderSettings() {} void Set(double myOpen, double myTake, double myStop) { myTradeProp.Open = myOpen; myTradeProp.Take = myTake; myTradeProp.Stop = myStop; } TradeProperties Get() { return myTradeProp; } };
- Назначение: Этот внутренний класс инкапсулирует свойства торговли (цена открытия, тейк-профит, стоп-лосс). Позволяет легко устанавливать и получать эти свойства.
- myTradeProp - структура для хранения торговых свойств.
- Set() - установка значений открытия, тейк-профита и стоп-лосса.
- Get() - извлекает сохраненные свойства торговли.
- Переменные, использующие OrderSettings:
OrderSettings myBuyStop, mySellStop, myBuyTrade, mySellTrade;
- Эти объекты хранят свойства для различных типов ордеров (buy stop, sell stop, buy trade, sell trade).
Функция FundamentalMode в приведенном ниже коде предназначена для удаления отложенного стоп-ордера, когда его противоположный ордер был исполнен и открыт как позиция. Эта функциональность актуальна для данной стратегии, которая использует как стоп-ордера на покупку, так и стоп-ордера на продажу (например, стратегию Straddle) для входа в позиции на волатильных рынках. После срабатывания одного из стоп-ордеров и открытия позиции оставшийся противоположный ордер (который теперь является избыточным) удаляется, чтобы избежать ненужных сделок.
Пример:
Buy-stop открывается на уровне 1.13118, а sell stop открывается на уровне 1.12911, buy stop исполняется первым с сообщением в журнале 'deal performed [#2 buy 0.01 EURUSD at 1.13134]'. Таким образом, в этом случае функция удалит/отменит оставшийся ордер на продажу с сообщением в журнале 'order canceled [#3 sell stop 0.01 EURUSD at 1.12911]', как показано на рисунке ниже.
//+------------------------------------------------------------------+ //|This function will delete a pending order if the previous opposing| //|pending order is opened into a position, this function is used | //|when trading with StopOrdersType(STOP ORDERS) | //+------------------------------------------------------------------+ void CTradeManagement::FundamentalMode(string COMMENT_COMMON) { //--- Iterate through all open positions if Orders are more than zero for(int P=0; P<PositionsTotal()&&OrdersTotal()>0; P++) { //--- Check if Position ticket is above zero if(PositionGetTicket(P)>0) { //--- Check if the Position's Symbol,Magic,Type,Comment is correct if(PositionGetString(POSITION_SYMBOL)==CSymbol.GetSymbolName()&& StringFind(PositionGetString(POSITION_COMMENT),COMMENT_COMMON)>=0) { //--- Iterate through all open orders for(int O=0; O<OrdersTotal(); O++) { //--- Check if Order ticket is above zero if(OrderGetTicket(O)>0) { //--- Check if the Order's Symbol,Magic,Comment is correct if(OrderGetString(ORDER_SYMBOL)==CSymbol.GetSymbolName() &&OrderGetInteger(ORDER_MAGIC)==PositionGetInteger(POSITION_MAGIC) &&StringFind(OrderGetString(ORDER_COMMENT),COMMENT_COMMON)>=0 &&OrderGetString(ORDER_COMMENT)==PositionGetString(POSITION_COMMENT)) { //--- Identify Position type switch(int(PositionGetInteger(POSITION_TYPE))) { /* In the case that the Position type is a buy and if the corresponding order type is a sell-stop then delete this order*/ case POSITION_TYPE_BUY: if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) { //--- Delete the sell-stop order Trade.OrderDelete(OrderGetTicket(O)); } break; /* In the case that the Position type is a sell and if the corresponding order type is a buy-stop then delete this order*/ case POSITION_TYPE_SELL: if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP) { //--- Delete the sell-stop order Trade.OrderDelete(OrderGetTicket(O)); } break; default: break; } } } } } } } }
Назначение функции:
Функция гарантирует, что при открытии отложенного ордера (например, ордера buy stop или sell stop) в качестве сделки противоположный ордер (например, sell stop или buy stop) удаляется. Это предотвращает исполнение обоих ордеров, что может привести к открытию нежелательных позиций.
void CTradeManagement::FundamentalMode(string COMMENT_COMMON)
- FundamentalMode - функция пытается удалить отложенный стоп-ордер, если открыта противоположная позиция.
- COMMENT_COMMON - строковый параметр, используемый для идентификации сделок/ордеров на основе связанного с ними комментария.
Цикл по открытым позициям:
for(int P = 0; P < PositionsTotal() && OrdersTotal() > 0; P++)
- PositionsTotal() - возвращает общее количество открытых позиций.
- OrdersTotal() - возвращает общее количество отложенных ордеров.
- Этот цикл for перебирает все открытые позиции и обеспечивает наличие отложенных ордеров для обработки.
Проверка тикета позиции:
if(PositionGetTicket(P) > 0)
- PositionGetTicket(P) - извлекает номер тикета позиции с индексом P.
- Проверяет, что тикет позиции действителен (то есть выше нуля).
Проверка свойства позиции:
if(PositionGetString(POSITION_SYMBOL) == CSymbol.GetSymbolName() && StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON) >= 0)
- PositionGetString(POSITION_SYMBOL) - извлекает символ открытой позиции.
- CSymbol.GetSymbolName() - извлекает имя символа для набора символов в конструкторе.
- PositionGetString(POSITION_COMMENT) - извлекает комментарий, связанный с позицией.
- StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON) - проверяет, присутствует ли подстрока COMMENT_COMMON в комментарии позиции. Это гарантирует принадлежность позиции стратегии/советнику.
Цикл открытых ордеров:
for(int O = 0; O < OrdersTotal(); O++)
- Перебирает все открытые отложенные ордера.
Проверка тикетов ордеров:
if(OrderGetTicket(O) > 0)
- OrderGetTicket(O) - извлекает номер тикета ордера с индексом O.
- Гарантирует, что тикет ордера действителен.
Проверьте свойства ордера:
if(OrderGetString(ORDER_SYMBOL) == CSymbol.GetSymbolName() && OrderGetInteger(ORDER_MAGIC) == PositionGetInteger(POSITION_MAGIC) && StringFind(OrderGetString(ORDER_COMMENT), COMMENT_COMMON) >= 0 && OrderGetString(ORDER_COMMENT) == PositionGetString(POSITION_COMMENT))
- OrderGetString(ORDER_SYMBOL) - извлекает символ, связанный с ордером.
- OrderGetInteger(ORDER_MAGIC) - извлекает магический номер ордера (который помогает идентифицировать ордеры для каждого события).
- PositionGetInteger(POSITION_MAGIC) - извлекает магическое число позиции, чтобы сопоставить его с магическим числом ордера.
- OrderGetString(ORDER_COMMENT) - извлекает комментарий к ордеру.
- Гарантирует, что символ ордера, магическое число и комментарий соответствуют свойствам позиции. Это гарантирует, что отложенный ордер связан с открытой позицией.
Поиск и удаление противоречащих ордеров:
switch(int(PositionGetInteger(POSITION_TYPE))) { case POSITION_TYPE_BUY: if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_STOP) { Trade.OrderDelete(OrderGetTicket(O)); } break; case POSITION_TYPE_SELL: if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_STOP) { Trade.OrderDelete(OrderGetTicket(O)); } break; default: break; }
- PositionGetInteger(POSITION_TYPE) - извлекает тип открытой позиции (POSITION_TYPE_BUY или POSITION_TYPE_SELL).
- OrderGetInteger(ORDER_TYPE) - извлекает тип отложенного ордера (ORDER_TYPE_BUY_STOP или ORDER_TYPE_SELL_STOP).
Оператор ветвления:
- При позиции на покупку (POSITION_TYPE_BUY), функция проверяет наличие отложенного ордера на продажу (ORDER_TYPE_SELL_STOP). Если такой ордер найден, он удаляется, поскольку больше не нужен.
- Аналогично, при позиции на продажу (POSITION_TYPE_SELL), функция проверяет наличие отложенного ордера на покупку (ORDER_TYPE_BUY_STOP). При обнаружении он удаляется.
Trade.OrderDelete(OrderGetTicket(O)) - метод удаляет отложенный ордер, используя его номер тикета, фактически удаляя ненужный противоположный ордер после открытия соответствующей позиции.
Функция SlippageReduction предназначена для обеспечения корректной установки уровней стоп-лосса (SL) и тейк-профита (TP) открытой позиции в соответствии с их ожидаемыми значениями. Если проскальзывание (разница между ожидаемой и фактической ценой исполнения ордера) привело к отклонению SL и TP от предполагаемых значений при исполнении ордера, функция корректирует позицию, чтобы отразить ожидаемые SL и TP.
Пример: трейдер хочет открыть стоп-ордера на покупку и продажу для торговли на NFP. Трейдер хочет, чтобы отклонение цены для обоих ордеров составляло 100 пунктов от текущей цены, а также соотношение риска к прибыли 1:6, поэтому ему требуется стоп-лосс в размере 100 пунктов и тейк-профит в размере 600 пунктов. В этом случае, как показано на рисунке ниже, стор-ордер на покупку устанавливается на уровне 1.13118, SL устанавливается на уровне 1.13018, а TP — на уровне 1.13718, что позволяет поддерживать рентабельность инвестиций 1:6. Из-за волатильности новости NFP сделка на покупку исполняется по невыгодной цене 1.13134, на что указывает сообщение в журнале 'order performed buy 0.01 at 1.13134 [#2 buy stop 0.01 EURUSD at 1.13118]'.
Проскальзывание, произошедшее при стоп-ордере на покупку:
Проскальзывание для цены открытия будет рассчитываться как [Фактическая цена - Ожидаемая цена]/Пункт
Ожидаемая цена: 1.13118|Фактическая цена: 1.13134 |Проскальзывание: (1.13134 - 1.13118)/0.00001 -> 16 пипсов (разница в цене)
Таким образом, для поддержания рентабельности инвестиций и стоп-лосса значения SL и TP будут скорректированы на 16 пунктов соответственно.
//+------------------------------------------------------------------+ //|Function will attempt to re-adjust stop-losses or take-profit | //|values that have been changed due to slippage on an order when | //|opening. | //+------------------------------------------------------------------+ void CTradeManagement::SlippageReduction(int SL,int TP,string COMMENT_COMMON) { //--- Iterate through all open positions for(int i=0; i<PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); //--- Check if Position ticket is above zero if(ticket>0) { //--- Check if the Position's Symbol,Comment is correct if(PositionGetString(POSITION_SYMBOL)==CSymbol.GetSymbolName() &&StringFind(PositionGetString(POSITION_COMMENT),COMMENT_COMMON)>=0) { //--- Identify Position type switch(int(PositionGetInteger(POSITION_TYPE))) { case POSITION_TYPE_BUY: //--- set expect buy trade properties SetBuyTrade(SL,TP,PositionGetDouble(POSITION_PRICE_OPEN)); //--- assign sl price mySL = PositionGetDouble(POSITION_SL); //--- assign tp price myTP = PositionGetDouble(POSITION_TP); //--- Normalize sl price CSymbol.NormalizePrice(mySL); mySL = double(DoubleToString(mySL,CSymbol.Digits())); //--- Normalize tp price CSymbol.NormalizePrice(myTP); myTP = double(DoubleToString(myTP,CSymbol.Digits())); //--- check if expected properties match actual trade properties if((myBuyTrade.Get().Stop!=mySL|| myBuyTrade.Get().Take!=myTP) &&Valid_Trade(POSITION_TYPE_BUY,myBuyTrade.Get().Open, myBuyTrade.Get().Stop,myBuyTrade.Get().Take)) { //--- Modify position to respect expected properties Trade.PositionModify(ticket,myBuyTrade.Get().Stop,myBuyTrade.Get().Take); } break; case POSITION_TYPE_SELL: //--- set expect sell trade properties SetSellTrade(SL,TP,PositionGetDouble(POSITION_PRICE_OPEN)); //--- assign sl price mySL = PositionGetDouble(POSITION_SL); //--- assign tp price myTP = PositionGetDouble(POSITION_TP); //--- Normalize sl price CSymbol.NormalizePrice(mySL); mySL = double(DoubleToString(mySL,CSymbol.Digits())); //--- Normalize tp price CSymbol.NormalizePrice(myTP); myTP = double(DoubleToString(myTP,CSymbol.Digits())); //--- check if expected properties match actual trade properties if((mySellTrade.Get().Stop!=mySL|| mySellTrade.Get().Take!=myTP) &&Valid_Trade(POSITION_TYPE_SELL,mySellTrade.Get().Open, mySellTrade.Get().Stop,mySellTrade.Get().Take)) { //--- Modify position to respect expected properties Trade.PositionModify(ticket,mySellTrade.Get().Stop,mySellTrade.Get().Take); } break; default: break; } } } } }
Назначение функции:
Цель этой функции:
- Проверить каждую открытую позицию.
- Проверить, соответствуют ли значения стоп-лосса и тейк-профита позиции ожидаемым.
- Если они не совпадают из-за проскальзывания, позиция изменяется для отражения правильных значений SL и TP.
void CTradeManagement::SlippageReduction(int SL, int TP, string COMMENT_COMMON)
- SlippageReduction - корректировка SL и TP, если на них повлияло проскальзывание во время исполнения ордера.
Параметры:
- SL - ожидаемое значение стоп-лосса.
- TP - ожидаемое значение тейк-профита.
- COMMENT_COMMON - Строка, используемая для фильтрации и идентификации сделок, принадлежащих этому советнику.
Перебор открытых позиций:
for (int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i);
- PositionsTotal() - возвращает количество открытых позиций.
- PositionGetTicket(i) - извлекает номер тикета для позиции с индексом i. Тикет однозначно идентифицирует открытую позицию.
Проверка тикета позиции:
if (ticket > 0)
- Гарантирует, что позиция имеет действительный номер тикета, прежде чем перейти к следующим шагам
Проверка свойства позиции:
if (PositionGetString(POSITION_SYMBOL) == CSymbol.GetSymbolName() && StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON) >= 0)
- PositionGetString(POSITION_SYMBOL) - извлекает символ, связанный с открытой позицией.
- CSymbol.GetSymbolName() - получает имя символа для торговой стратегии (например, "EURUSD").
- PositionGetString(POSITION_COMMENT) - извлекает комментарий, прикрепленный к открытой позиции.
- StringFind() - проверяет, является ли COMMENT_COMMON частью комментария позиции, гарантируя, что функция обрабатывает только позиции, связанные с текущей стратегией.
Определение и обработка типов позиций (покупка или продажа):
Вариант 1: Позиция на покупку
case POSITION_TYPE_BUY:
- Функция обрабатывает позиции типа BUY.
Установка ожидаемые свойства покупки/торговли:
SetBuyTrade(SL, TP, PositionGetDouble(POSITION_PRICE_OPEN));
- SetBuyTrade(SL, TP, PositionGetDouble(POSITION_PRICE_OPEN)) - устанавливает ожидаемый стоп-лосс, тейк-профит и цену открытия для сделки на покупку. Функция SetBuyTrade присваивает эти ожидаемые значения объекту myBuyTrade.
Назначение фактических значений SL и TP:
mySL = PositionGetDouble(POSITION_SL); myTP = PositionGetDouble(POSITION_TP);
- PositionGetDouble(POSITION_SL) - извлекает фактическое значение стоп-лосса из открытой позиции.
- PositionGetDouble(POSITION_TP) - извлекает фактическое значение тейк-профита из открытой позиции.
Нормализация значений SL и TP:
CSymbol.NormalizePrice(mySL); mySL = double(DoubleToString(mySL, CSymbol.Digits())); CSymbol.NormalizePrice(myTP); myTP = double(DoubleToString(myTP, CSymbol.Digits()));
- NormalizePrice(mySL) - нормализует значение стоп-лосса до правильного количества знаков после запятой (на основе свойств символа, например, валютные пары могут иметь 4 или 5 знаков после запятой).
- DoubleToString(mySL, CSymbol.Digits()) - преобразует нормализованное значение SL в строку, а затем обратно в число double для обеспечения точности.
- Тот же процесс применяется к значению тейк-профита (myTP).
Сравним ожидаемые и фактические SL/TP:
if ((myBuyTrade.Get().Stop != mySL || myBuyTrade.Get().Take != myTP) && Valid_Trade(POSITION_TYPE_BUY, myBuyTrade.Get().Open, myBuyTrade.Get().Stop, myBuyTrade.Get().Take)) { Trade.PositionModify(ticket, myBuyTrade.Get().Stop, myBuyTrade.Get().Take); }
- myBuyTrade.Get().Stop - извлекает ожидаемое значение стоп-лосса.
- mySL - фактическое значение стоп-лосса, полученное из позиции.
- Если фактические значения SL и TP не соответствуют ожидаемым, функция вызывает Trade.PositionModify для обновления позиции и установки корректных стоп-лосса и тейк-профита.
- Valid_Trade() - проверяет, допустимы ли параметры сделки (цена, SL, TP) для изменения, прежде чем пытаться изменить позицию.
Изменение позиции:
Trade.PositionModify(ticket, myBuyTrade.Get().Stop, myBuyTrade.Get().Take);
- Функция изменяет позицию, связанную с тикетом, чтобы отразить правильные значения стоп-лосса и тейк-профита на основе ожидаемых значений.
Вариант 2: Позиция на продажу
- Здесь всё аналогично позициям на покупку.
Функция Valid_Trade проверяет, являются ли параметры сделки (такие как цена, уровни стоп-лосса и тейк-профита) действительными в зависимости от типа сделки (покупка или продажа) и правил, регулирующих уровни стоп-лосса и тейк-профита по отношению к текущим свойствам символа. Функция возвращает значение true, если сделка действительна, и false, если нет.
//+------------------------------------------------------------------+ //|Check if a trade is valid | //+------------------------------------------------------------------+ bool CTradeManagement::Valid_Trade(ENUM_POSITION_TYPE Type,double Price,double SL,double TP) { //--- Identify Position type switch(Type) { case POSITION_TYPE_BUY: if((Price<TP||TP==0)&&(Price>SL||SL==0)&& ((int((Price-SL)/CSymbol.Point())>=CSymbol.StopLevel()) ||(SL==0))&& ((int((TP-Price)/CSymbol.Point())>=CSymbol.StopLevel()) ||(TP==0))&& Price>0 ) { //--- Trade properties are valid. return true; } break; case POSITION_TYPE_SELL: if((Price>TP||TP==0)&&(Price<SL||SL==0)&& ((int((Price-TP)/CSymbol.Point())>=CSymbol.StopLevel()) ||(TP==0))&& ((int((SL-Price)/CSymbol.Point())>=CSymbol.StopLevel()) ||(SL==0))&& Price>0 ) { //--- Trade properties are valid. return true; } break; default://Unknown return false; break; } //--- Trade properties are not valid. Print("Something went wrong, SL/TP/Open-Price is incorrect!"); return false; }
Назначение функции:
Целью этой функции является:
- Проверка параметров торговли (цена, SL и TP) для позиций покупки или продажи.
- Проверка, что уровни стоп-лосса и тейк-профита установлены правильно относительно цены открытия и соответствуют требованиям к стоп-уровням символа.
Параметры:
bool CTradeManagement::Valid_Trade(ENUM_POSITION_TYPE Type, double Price, double SL, double TP)
- Type - тип позиции: POSITION_TYPE_BUY (покупка) или POSITION_TYPE_SELL (продажа).
- Price - цена открытия сделки.
- SL - уровень стоп-лосса сделки.
- TP - уровень тейк-профита сделки.
Оператор switch:
Функция использует оператор switch для обработки различных типов позиций (покупка или продажа) и применяет различную логику проверки для каждого случая.
Вариант 1: Позиция на покупку
case POSITION_TYPE_BUY:
Логика позиции на покупку проверяет следующие условия:
Проверка тейк-профита (TP):
if ((Price < TP || TP == 0)
- Уровень тейк-профита должен быть больше цены открытия (Price < TP), или его можно установить равным нулю (нет тейк-профита).
Проверка стоп-лосса (SL):
(Price > SL || SL == 0)
- Уровень стоп-лосса должен быть меньше цены открытия (Price > SL), или его можно установить равным нулю (нет стоп-лосса).
Проверка стоп-уровня символа:
- Стоп-уровень символа определяет минимальное расстояние между ценой и стоп-лоссом/тейк-профитом.
- Уровни стоп-лосса и тейк-профита должны находиться на минимальном расстоянии (равном или больше уровня стопа) от цены открытия.
Проверка расстояния стоп-лосса:
((int((Price - SL) / CSymbol.Point()) >= CSymbol.StopLevel()) || SL == 0)
- Разница между ценой и стоп-лоссом должна быть не менее стоп-уровня символа (в пунктах). Если стоп-лосс равен нулю, это условие игнорируется.
Проверка расстояния тейк-профита:
((int((TP - Price) / CSymbol.Point()) >= CSymbol.StopLevel()) || TP == 0)
- Разница между тейк-профитом и ценой также должна соответствовать требованию стоп-уровня.
Окончательная проверка:
&& Price > 0
- Цена должна быть больше нуля.
Если все эти условия выполнены, сделка считается действительной, и функция возвращает true.
Вариант 2: Позиция на продажу
Для позиции на продажу логика похожая, но обратная.
Вариант по умолчанию:
default: return false;
Если тип позиции не является ни POSITION_TYPE_BUY, ни POSITION_TYPE_SELL, функция возвращает false, что указывает на недействительность сделки.
Недействительные торговые свойства:
Если какая-либо из проверок не пройдена, функция отправляет сообщение об ошибке и возвращает false.
Print("Something went wrong, SL/TP/Open-Price is incorrect!"); return false;
Это сообщение указывает на то, что стоп-лосс, тейк-профит или цена открытия неверны.
Приведенный ниже код определяет метод Valid_Order, который проверяет действительность стоп-ордера (BUY STOP или SELL STOP) путем проверки условий для стоп-лосса (SL), тейк-профита (TP), цены и отклонения ордера. Функция возвращает true, если ордер действителен, и false в противном случае.
//+------------------------------------------------------------------+ //|Check if stop order is valid | //+------------------------------------------------------------------+ bool CTradeManagement::Valid_Order(ENUM_ORDER_TYPE Type,double Price,double SL,double TP) { //--- Identify Order type switch(Type) { case ORDER_TYPE_BUY_STOP: if((Price<TP||TP==0)&&(Price>SL||SL==0)&&(Price>CSymbol.Ask())&& ((int((Price-SL)/CSymbol.Point())>=CSymbol.StopLevel()) ||(SL==0))&& ((int((TP-Price)/CSymbol.Point())>=CSymbol.StopLevel()) ||(TP==0))&& myDeviation>=uint(CSymbol.StopLevel()) ) { //--- Order properties are valid. return true; } break; case ORDER_TYPE_SELL_STOP: if((Price>TP||TP==0)&&(Price<SL||SL==0)&&(Price<CSymbol.Bid())&& ((int((Price-TP)/CSymbol.Point())>=CSymbol.StopLevel()) ||(TP==0))&& ((int((SL-Price)/CSymbol.Point())>=CSymbol.StopLevel()) ||(SL==0))&& myDeviation>=uint(CSymbol.StopLevel()) ) { //--- Order properties are valid. return true; } break; default://Other return false; break; } //--- Order properties are not valid. Print("Something went wrong, SL/TP/Deviation/Open-Price is incorrect!"); return false; }
Общие сведения:
Функция выполняет проверку на основе типа стоп-ордера:
- BUY STOP - ордер buy stop размещается выше текущей рыночной цены (цены ask) и срабатывает, когда рыночная цена достигает или превышает этот уровень.
- SELL STOP - ордер на продажу размещается ниже текущей рыночной цены (цены bid) и срабатывает, когда рыночная цена падает до этого уровня или ниже.
Параметры:
bool CTradeManagement::Valid_Order(ENUM_ORDER_TYPE Type, double Price, double SL, double TP)
- Type - тип стоп-ордера (ORDER_TYPE_BUY_STOP или ORDER_TYPE_SELL_STOP).
- Price - цена стоп-ордера.
- SL - цена стоп-лосса, связанная со стоп-ордером.
- TP - цена тейк-профита, связанная со стоп-ордером.
Оператор switch:
Оператор switch обрабатывает различные типы ордеров и применяет отдельные правила проверки для каждого из них.
Вариант 1: Ордер BUY STOP
case ORDER_TYPE_BUY_STOP:
Для ордера BUY STOP должны быть выполнены следующие условия:
Проверка тейк-профита (TP):
(Price < TP || TP == 0)
Цена тейк-профита должна быть больше цены ордера (Price < TP), или ее можно установить равной нулю (нет тейк-профита).
Проверка стоп-лосса (SL):
(Price > SL || SL == 0)
Цена стоп-лосса должна быть меньше цены ордера (Price > SL), или ее можно установить равной нулю (нет стоп-лосса).
Цена выше цены ask:
(Price > CSymbol.Ask())
Цена ордера должна быть больше текущей цены ask, что является ключевым условием для ордера BUY STOP.
Проверка стоп-уровня символа: Проверка расстояния стоп-лосса:
((int((Price - SL) / CSymbol.Point()) >= CSymbol.StopLevel()) || SL == 0)
Разница между ценой ордера и стоп-лоссом должна соответствовать или превышать стоп-уровень символа, который представляет собой минимально допустимое расстояние в пунктах между ценой ордера и уровнем стоп-лосса. Если стоп-лосс установлен на ноль, это условие игнорируется.
Проверка расстояния тейк-профита:
((int((TP - Price) / CSymbol.Point()) >= CSymbol.StopLevel()) || TP == 0)
Разница между тейк-профитом и ценой ордера также должна соответствовать требованию стоп-уровня, в противном случае тейк-профит может быть равен нулю.
Проверка отклонения цены:
myDeviation >= uint(CSymbol.StopLevel())
Отклонение цены (допуск проскальзывания) должно быть больше или равно стоп-уровню символа. Отклонение позволяет исполнить ордер, даже если рыночная цена немного выйдет за установленный уровень стопа.
Если все эти условия выполнены, ордер BUY STOP считается действительным, и функция возвращает true.
Вариант 2: Ордер SELL STOP
Для ордера SELL STOP логика проверки обратна ордеру BUY STOP.
Вариант по умолчанию:
default: return false;
Если тип ордера не является ни ORDER_TYPE_BUY_STOP, ни ORDER_TYPE_SELL_STOP, функция возвращает false, что указывает на то, что ордер недействителен.
Обработка ошибок:
Если условия не выполняются и функция не может проверить ордер, она отправляет сообщение об ошибке:
Print("Something went wrong, SL/TP/Deviation/Open-Price is incorrect!");
Это сообщение информирует пользователя о том, что что-то не так со стоп-лоссом, тейк-профитом, отклонением цены или ценой ордера, что делает ордер недействительным.
Приведенный ниже код определяет функцию SetBuyStop, отвечающую за установку свойств ордера buy stop. Она вычисляет цены стоп-лосса (SL) и тейк-профита (TP) на основе заданных входных данных и назначает их ордеру. Функция выполняет следующие шаги:
- Рассчитывает цену открытия для ордера buy stop.
- Рассчитывает и устанавливает цены стоп-лосса и тейк-профита относительно цены открытия.
- Нормализует цены, чтобы обеспечить их соответствие точности символа (количество знаков посоле запятой).
//+------------------------------------------------------------------+ //|Will set buy-stop order properties | //+------------------------------------------------------------------+ void CTradeManagement::SetBuyStop(int SL,int TP) { //-- Get Open-price myOpenPrice=CSymbol.Ask()+myDeviation*CSymbol.Point(); CSymbol.NormalizePrice(myOpenPrice); NormalizeDouble(myOpenPrice,CSymbol.Digits()); //--- Get SL value mySL=SL*CSymbol.Point(); mySL=myOpenPrice-mySL; //--- Normalize the SL Price CSymbol.NormalizePrice(mySL); NormalizeDouble(mySL,CSymbol.Digits()); //--- Get TP value myTP=TP*CSymbol.Point(); myTP+=myOpenPrice; //--- Normalize the TP Price CSymbol.NormalizePrice(myTP); NormalizeDouble(myTP,CSymbol.Digits()); //--- Set BuyStop properties myBuyStop.Set(myOpenPrice,myTP,mySL); }
Параметры:
- SL - стоп-лосс в пунктах.
- TP - тейк-профит в пунктах.
void CTradeManagement::SetBuyStop(int SL, int TP)
SL и TP — это целые числа, представляющие собой количество пунктов (приращений цены) от цены открытия до уровня стоп-лосса или тейк-профита.
Шаг 1: Расчет цены открытия для ордера Buy Stop
myOpenPrice = CSymbol.Ask() + myDeviation * CSymbol.Point();
- CSymbol.Ask() извлекает текущую цену ask для символа (валютной пары или актива).
- myDeviation * CSymbol.Point() добавляет отклонение (ценовой буфер) в пунктах, чтобы гарантировать, что цена открытия находится выше текущей цены ask. CSymbol.Point() возвращает размер одного пункта (наименьшее изменение цены) для символа.
Нормализация:
CSymbol.NormalizePrice(myOpenPrice); NormalizeDouble(myOpenPrice, CSymbol.Digits());
- CSymbol.NormalizePrice(myOpenPrice) корректирует цену, чтобы она соответствовала точности символа (то есть соответствовала допустимому формату цены).
- NormalizeDouble(myOpenPrice, CSymbol.Digits()) обеспечивает округление цены до правильного количества знаков после запятой, определяемого точностью символа (CSymbol.Digits()).
Шаг 2: Расчет и нормализация цены стоп-лосса (SL)
mySL = SL * CSymbol.Point();
mySL = myOpenPrice - mySL;
- SL * CSymbol.Point() преобразует целочисленное значение стоп-лосса (в пунктах) в разницу цен.
- myOpenPrice - mySL рассчитывает цену стоп-лосса путем вычитания значения стоп-лосса (в пунктах) из цены открытия. Это происходит потому, что стоп-лосс для ордера на покупку размещается ниже цены открытия.
Нормализация:
CSymbol.NormalizePrice(mySL); NormalizeDouble(mySL, CSymbol.Digits());
- Цена стоп-лосса нормализуется и округляется до нужной точности, как описано выше.
Шаг 3: Расчет и нормализация цены тейк-профита (TP)
myTP = TP * CSymbol.Point();
myTP += myOpenPrice;
- TP * CSymbol.Point() преобразует целочисленное значение тейк-профита (в пунктах) в разницу цен.
- myTP += myOpenPrice добавляет это значение к цене открытия, поскольку тейк-профит для ордера buy stop размещается выше цены открытия.
Нормализация:
CSymbol.NormalizePrice(myTP); NormalizeDouble(myTP, CSymbol.Digits());
- Цена тейк-профита нормализована и округлена, аналогично ценам стоп-лосса и открытия.
Шаг 4: Присвоение рассчитанных значений ордеру Buy Stop
myBuyStop.Set(myOpenPrice, myTP, mySL);
- myBuyStop.Set(myOpenPrice, myTP, mySL) присваивает рассчитанные значения цены открытия, тейк-профита и стоп-лосса объекту ордера myBuyStop.
- Объект myBuyStop теперь будет содержать свойства, необходимые для размещения ордера buy stop на рынке.
Функция SetBuyTrade устанавливает свойства позиции покупки. Она вычисляет и назначает правильную цену открытия, стоп-лосс (SL) и тейк-профит (TP) для сделки. Эта функция используется для настройки параметров для позиции покупки (не отложенного ордера, как buy stop). Позиция на покупку открывается по текущей рыночной цене с указанием уровней стоп-лосса и тейк-профита относительно цены открытия.
Параметры:
- SL - стоп-лосс в пунктах (шагах цены).
- TP - тейк-профит в пунктах.
- OP - цена открытия сделки (цена, по которой открывается позиция).
Шаг 1: Установка цены открытия
myOpenPrice = OP; CSymbol.NormalizePrice(myOpenPrice); myOpenPrice = double(DoubleToString(myOpenPrice, CSymbol.Digits()));
- myOpenPrice = OP - принимает предоставленную цену открытия (OP) и присваивает ее myOpenPrice.
- CSymbol.NormalizePrice(myOpenPrice) - нормализует цену открытия в соответствии с точностью символа, гарантируя, что она соответствует правильному количеству знаков после запятой.
- myOpenPrice = double(DoubleToString(myOpenPrice, CSymbol.Digits())) - Цена далее преобразуется в строку и обратно в double с правильным количеством знаков после запятой (точностью) для символа. Это обеспечивает единообразие в обработке значений цен, позволяя избежать проблем с точностью чисел с плавающей запятой.
Шаг 2: Расчет и нормализация цены стоп-лосса (SL)
mySL = SL * CSymbol.Point();
mySL = myOpenPrice - mySL;
- SL * CSymbol.Point() - преобразует значение стоп-лосса из пунктов в фактическую разницу цен. CSymbol.Point() возвращает размер одного пункта (минимально возможное приращение цены для символа).
- myOpenPrice - mySL - цена стоп-лосса устанавливается путем вычитания рассчитанного значения стоп-лосса из цены открытия. В позиции покупки стоп-лосс размещается ниже цены открытия, чтобы защитить сделку от чрезмерных потерь.
Нормализация:
CSymbol.NormalizePrice(mySL); mySL = double(DoubleToString(mySL, CSymbol.Digits()));
- Цена стоп-лосса нормализуется, чтобы обеспечить ее правильную точность, а затем округляется до соответствующего количества знаков после запятой.
Шаг 3: Расчет и нормализация цены тейк-профита (TP)
myTP = TP * CSymbol.Point();
myTP += myOpenPrice;
- TP * CSymbol.Point() - преобразует значение тейк-профита из пунктов в разницу цен.
- myTP += myOpenPrice - цена тейк-профита добавляется к цене открытия, поскольку для позиции на покупку тейк-профит размещается выше цены открытия.
Нормализация:
CSymbol.NormalizePrice(myTP); myTP = double(DoubleToString(myTP, CSymbol.Digits()));
- Цена тейк-профита нормализуется и округляется в соответствии с точностью символа.
Шаг 4: Присвоение рассчитанных значений позиции на покупку
myBuyTrade.Set(myOpenPrice, myTP, mySL);
- myBuyTrade.Set(myOpenPrice, myTP, mySL) - рассчитанные значения цены открытия, стоп-лосса и тейк-профита присваиваются объекту myBuyTrade. Этот объект теперь содержит все соответствующие свойства позиции покупки, которая будет открыта или изменена.
Функция OpenBuyStop пытается открыть ордер buy stop с указанным стоп-лоссом (SL), тейк-профитом (TP), магическим числом и необязательным комментарием.
Параметры:
- SL - стоп-лосс в пунктах.
- TP - тейк-профит в пунктах.
- Magic - уникальное магическое число для идентификации ордера, обычно используется для советников.
- COMMENT - комментарий к ордеру для идентификации (опционально).
//+------------------------------------------------------------------+ //|Will attempt to open a buy-stop order | //+------------------------------------------------------------------+ bool CTradeManagement::OpenBuyStop(int SL,int TP,ulong Magic,string COMMENT=NULL) { SetBuyStop(SL,TP); //--- Set the order type for Risk management calculation SetOrderType(ORDER_TYPE_BUY); //--- Set open price for Risk management calculation OpenPrice = myBuyStop.Get().Open; //--- Set close price for Risk management calculation ClosePrice = myBuyStop.Get().Stop; //--- Set Trade magic number Trade.SetExpertMagicNumber(Magic); //--- Check if there are any open trades or opened deals or canceled deals already if(!OpenOrder(ORDER_TYPE_BUY_STOP,Magic,COMMENT)&&!OpenedDeal(DEAL_TYPE_BUY,Magic,COMMENT) //--- Check if the buy-stop properties are valid &&Valid_Order(ORDER_TYPE_BUY_STOP,myBuyStop.Get().Open,myBuyStop.Get().Stop,myBuyStop.Get().Take)) { //--- Iterate through the Lot-sizes if they're more than max-lot for(double i=Volume();i>=CSymbol.LotsMin()&& /* Check if current number of orders +1 more orders is less than account orders limit.*/ (PositionsTotal()+Account.numOrders()+1)<Account.LimitOrders() ;i-=CSymbol.LotsMax()) { //--- normalize Lot-size NormalizeLotsize(i); /* Open buy-stop order with a Lot-size not more than max-lot and set order expiration to the Symbol's session end time for the current day. */ if(!Trade.BuyStop((i>CSymbol.LotsMax())?CSymbol.LotsMax():i,myBuyStop.Get().Open, CSymbol.GetSymbolName(),myBuyStop.Get().Stop,myBuyStop.Get().Take, ORDER_TIME_SPECIFIED,CTS.SessionEnd(),COMMENT)) { //--- Order failed to open return false; } } } else { //--- Order failed return false; } //--- Return trade result. return true; }
Шаг 1: Установим свойства Buy Stop
SetBuyStop(SL, TP);
- Эта строка вызывает метод SetBuyStop (рассмотренный выше) для расчета и установки цены открытия, стоп-лосса и тейк-профита для ордера buy stop.
- Результат сохраняется в объекте myBuyStop, который содержит ключевые свойства ордера buy stop (цена открытия, SL и TP).
Шаг 2: Установим тип ордера для управления рисками
SetOrderType(ORDER_TYPE_BUY);
- Это устанавливает внутренний тип ордера на ORDER_TYPE_BUY. Хотя это и ордер buy stop, по сути, это ордер на покупку, и он используется для расчета показателей управления рисками, таких как стоп-лосс, тейк-профит и размер позиции.
Шаг 3: Установим цены открытия и закрытия для управления рисками
OpenPrice = myBuyStop.Get().Open; ClosePrice = myBuyStop.Get().Stop;
- OpenPrice - устанавливается на основе рассчитанной цены открытия ордера buy stop (myBuyStop.Get().Open).
- ClosePrice - устанавливается на основе рассчитанной цены стоп-лосса ордера buy stop (myBuyStop.Get().Stop).
Шаг 4: Установим магическое число
Trade.SetExpertMagicNumber(Magic);
- Устанавливает магическое число для ордера с помощью Trade.SetExpertMagicNumber. Магическое число однозначно идентифицирует ордер, что полезно при управлении сделками, открытыми советником.
Шаг 5: Проверка на наличие открытых сделок
if (!OpenOrder(ORDER_TYPE_BUY_STOP, Magic, COMMENT) && !OpenedDeal(DEAL_TYPE_BUY, Magic, COMMENT)
- OpenOrder - проверяет, есть ли уже открытые отложенные ордера buy stop с тем же магическим числом и комментарием. Если таковой существует, функция пропускает открытие нового ордера.
- OpenedDeal - проверяет, открыта ли уже позиция на покупку с использованием того же магического числа и комментария. Если такая сделка существует, функция избегает открытия новой.
Шаг 6: Проверка свойств ордера Buy-Stop
&& Valid_Order(ORDER_TYPE_BUY_STOP, myBuyStop.Get().Open, myBuyStop.Get().Stop, myBuyStop.Get().Take))
- Проверяет, являются ли рассчитанные свойства ордера buy stop действительными с использованием метода Valid_Order (рассмотренного ранее). Метод проверяет правильность цены открытия, стоп-лосса и тейк-профита, а также соответствие ордера правилам символа (например, минимальному уровню стопа, значению пункта и так далее).
Шаг 7: Перебор размеров лотов
for (double i = Volume(); i >= CSymbol.LotsMin() && (PositionsTotal() + Account.numOrders() + 1) < Account.LimitOrders(); i -= CSymbol.LotsMax())
- Volume() - извлекает текущий объем торговли (размер лота) для ордера.
- Цикл начинается с текущего уровня объема (i = Volume()) и продолжается до тех пор, пока:
- Размер лота больше или равен минимальному размеру лота (CSymbol.LotsMin()).
- Общее количество позиций плюс существующие ордера на счете меньше лимита ордеров на счете (Account.LimitOrders()).
Он уменьшает размер лота на величину, равную максимальному размеру лота (CSymbol.LotsMax()) для каждой итерации, чтобы гарантировать соответствие ордера ограничениям по объему.
Шаг 8: Нормализация размера лота
NormalizeLotsize(i);
- Размер лота (i) нормализуется, гарантируя, что он соответствует правилам точности символа.
Шаг 9: Попытка открыть ордер Buy Stop
if (!Trade.BuyStop((i > CSymbol.LotsMax()) ? CSymbol.LotsMax() : i, myBuyStop.Get().Open, CSymbol.GetSymbolName(), myBuyStop.Get().Stop, myBuyStop.Get().Take, ORDER_TIME_SPECIFIED, CTS.SessionEnd(), COMMENT))
- Trade.BuyStop() - пытается разместить ордер buy stop со следующими параметрами:
- Lot size - размер лота ограничен значением CSymbol.LotsMax() (максимально допустимый размер лота для символа). Если (i) больше максимального размера лота, используется максимальный размер. В противном случае используется текущий размер лота (i).
- Open price - цена, по которой должен быть исполнен стоп-ордер на покупку.
- Symbol name - название торгового символа (валютной пары и так далее).
- Stop-loss - рассчитанная цена стоп-лосса для ордера.
- Take-profit - рассчитанная цена тейк-профита для ордера.
- Order expiration time - срок действия ордера истекает в конце торговой сессии символа за текущий день (CTS.SessionEnd()).
- Comment - необязательный комментарий, предоставленный ранее.
Если ордер не открывается, функция возвращает значение false, указывая на то, что ордер buy stop не был успешно размещен.
Шаг 10: Возврат результата
return true;
- Если ордер успешно открыт, функция возвращает значение true, что указывает на то, что ордер buy stop был успешно размещен.
Функция OpenSellStop
Для функции OpenSellStop логика функции аналогична функции OpenBuyStop, описанной ранее.
Функция OpenStops пытается открыть как ордера buy stop, так и ордера sell stop.
//+------------------------------------------------------------------+ //|This function will open both buy-stop and sell-stop orders for | //|StopOrdersType(STOP ORDERS) | //+------------------------------------------------------------------+ bool CTradeManagement::OpenStops(int SL,int TP,ulong Magic,string COMMENT=NULL) { //--- Set buy-stop properties SetBuyStop(SL,TP); //--- Set sell-stop properties SetSellStop(SL,TP); //--- Set the order type for Risk management calculation SetOrderType(ORDER_TYPE_BUY); //--- Set open price for Risk management calculation OpenPrice = myBuyStop.Get().Open; //--- Set close price for Risk management calculation ClosePrice = myBuyStop.Get().Stop; //--- Set Trade magic number Trade.SetExpertMagicNumber(Magic); //--- Check if there are any open trades or opened deals or canceled deals already if(!OpenOrder(ORDER_TYPE_BUY_STOP,Magic,COMMENT)&&!OpenedDeal(DEAL_TYPE_BUY,Magic,COMMENT) &&!OpenedDeal(DEAL_TYPE_BUY_CANCELED,Magic,COMMENT) //--- Check if the buy-stop properties are valid &&Valid_Order(ORDER_TYPE_BUY_STOP,myBuyStop.Get().Open,myBuyStop.Get().Stop,myBuyStop.Get().Take) &&!OpenOrder(ORDER_TYPE_SELL_STOP,Magic,COMMENT) &&!OpenedDeal(DEAL_TYPE_SELL,Magic,COMMENT)&&!OpenedDeal(DEAL_TYPE_SELL_CANCELED,Magic,COMMENT) //--- Check if the sell-stop properties are valid &&Valid_Order(ORDER_TYPE_SELL_STOP,mySellStop.Get().Open,mySellStop.Get().Stop,mySellStop.Get().Take)) { //--- Iterate through the Lot-sizes if they're more than max-lot for(double i=Volume();i>=CSymbol.LotsMin()&& /* Check if current number of orders +2 more orders is less than account orders limit.*/ (PositionsTotal()+Account.numOrders()+2)<Account.LimitOrders() ;i-=CSymbol.LotsMax()) { //--- normalize Lot-size NormalizeLotsize(i); /* Open orders with a Lot-size not more than max-lot and set order expiration to the Symbol's session end time for the current day. */ if(!Trade.BuyStop((i>CSymbol.LotsMax())?CSymbol.LotsMax():i,myBuyStop.Get().Open, CSymbol.GetSymbolName(),myBuyStop.Get().Stop,myBuyStop.Get().Take, ORDER_TIME_SPECIFIED,CTS.SessionEnd(),COMMENT) ||!Trade.SellStop((i>CSymbol.LotsMax())?CSymbol.LotsMax():i,mySellStop.Get().Open, CSymbol.GetSymbolName(),mySellStop.Get().Stop,mySellStop.Get().Take, ORDER_TIME_SPECIFIED,CTS.SessionEnd(),COMMENT)) { //--- one or more orders failed to open. return false; } } } else { //--- Orders failed return false; } //--- Return trade result. return true; }
Шаг 1: Установим свойства Buy Stop и Sell Stop
SetBuyStop(SL, TP); SetSellStop(SL, TP);
- SetBuyStop - рассчитывает и устанавливает цену открытия, стоп-лосс и тейк-профит для ордера buy stop. Эти значения хранятся в объекте myBuyStop.
- SetSellStop - аналогично, вычисляет и устанавливает цену открытия, стоп-лосс и тейк-профит для ордера sell stop, хранящегося в объекте mySellStop.
Шаг 2: Установим тип ордера для управления рисками
SetOrderType(ORDER_TYPE_BUY);
- Устанавливает внутренний тип ордера для расчета управления рисками на ордер на покупку, даже если размещаются как ордера buy stop, так и sell stop. Эта настройка используется позже для оценки риска на основе стоп-лосса и тейк-профита ордера на покупку.
Шаг 3: Установим цены открытия и закрытия для управления рисками
OpenPrice = myBuyStop.Get().Open; ClosePrice = myBuyStop.Get().Stop;
- OpenPrice - рассчитанная цена открытия для ордера buy stop.
- ClosePrice - рассчитанная цена стоп-лосса для ордера buy stop. Эти цены используются для внутренних расчетов управления рисками.
Шаг 4: Установим магическое число
Trade.SetExpertMagicNumber(Magic);
- Присваивает сделке магический номер, который однозначно идентифицирует ордера, управляемые советником (EA).
Шаг 5: Проверим наличие существующих ордеров или сделок
if(!OpenOrder(ORDER_TYPE_BUY_STOP, Magic, COMMENT) && !OpenedDeal(DEAL_TYPE_BUY, Magic, COMMENT) && !OpenedDeal(DEAL_TYPE_BUY_CANCELED, Magic, COMMENT)
- OpenOrder - проверяет, существует ли уже открытый ордер buy stop с тем же магическим номером и комментарием. Если таковой существует, новый ордер buy stop открыт не будет.
- OpenedDeal - проверяет, есть ли уже активная или отмененная позиция на покупку с тем же магическим числом и комментарием. Если есть, новый ордер buy stop не будет размещен.
Шаг 6: Проверка свойств ордера Buy-Stop
&& Valid_Order(ORDER_TYPE_BUY_STOP, myBuyStop.Get().Open, myBuyStop.Get().Stop, myBuyStop.Get().Take)
- Valid_Order - проверяет свойства ордера buy stop (цена открытия, стоп-лосс, тейк-профит), чтобы убедиться, что они соответствуют правилам символа (например, минимальный уровень стопа). Если проверка пройдена, происходит проверка ордера sell stop.
Шаг 7: Проверяем наличие существующих ордеров на продажу или сделок
&& !OpenOrder(ORDER_TYPE_SELL_STOP, Magic, COMMENT) && !OpenedDeal(DEAL_TYPE_SELL, Magic, COMMENT) && !OpenedDeal(DEAL_TYPE_SELL_CANCELED, Magic, COMMENT)
- Подобно проверкам buy stop, эти условия проверяют, существует ли уже ордер sell stop, активная сделка на продажу или отмененная сделка на продажу с тем же магическим числом и комментарием. Если существует, функция избегает размещения нового ордера sell stop.
Шаг 8: Проверка свойств ордера Sell Stop
&& Valid_Order(ORDER_TYPE_SELL_STOP, mySellStop.Get().Open, mySellStop.Get().Stop, mySellStop.Get().Take))
- Valid_Order - проверяет свойства ордера на продажу (цена открытия, стоп-лосс, тейк-профит). Если проверка пройдена, приступает к открытию ордеров buy stop и sell stop.
Шаг 9: Перебор размеров лотов
for (double i = Volume(); i >= CSymbol.LotsMin() && (PositionsTotal() + Account.numOrders() + 2) < Account.LimitOrders(); i -= CSymbol.LotsMax())
- Volume() - извлекает текущий объем сделки (размер лота).
- Цикл начинается с полного объема (i = Volume()) и уменьшает размер лота (i -= CSymbol.LotsMax()), если он превышает максимально допустимый размер лота (CSymbol.LotsMax()).
- Он гарантирует, что общее количество открытых позиций и отложенных ордеров находится в пределах лимита счета (Account.LimitOrders()).
Цикл гарантирует, что если размер лота превысит максимально допустимый размер лота символа, ордера будут разделены на несколько более мелких.
Шаг 10: Нормализация размера лота
NormalizeLotsize(i);
- Размер лота (i) нормализуется для соответствия допустимой точности символа.
Шаг 11: Попытаемся открыть ордера Buy Stop и Sell Stop
if(!Trade.BuyStop((i > CSymbol.LotsMax()) ? CSymbol.LotsMax() : i, myBuyStop.Get().Open, CSymbol.GetSymbolName(), myBuyStop.Get().Stop, myBuyStop.Get().Take, ORDER_TIME_SPECIFIED, CTS.SessionEnd(), COMMENT) || !Trade.SellStop((i > CSymbol.LotsMax()) ? CSymbol.LotsMax() : i, mySellStop.Get().Open, CSymbol.GetSymbolName(), mySellStop.Get().Stop, mySellStop.Get().Take, ORDER_TIME_SPECIFIED, CTS.SessionEnd(), COMMENT))
- Trade.BuyStop - пытается разместить ордер buy stop со следующими параметрами:
- Lot size - если текущий размер лота (i) превышает максимально допустимый (CSymbol.LotsMax()), размещается ордер с максимально допустимым размером лота.
- Open price - рассчитанная цена открытия для ордера buy-stop.
- Symbol name - название торгового инструмента.
- Stop-loss - цена стоп-лосса для ордера buy-stop.
- Take-profit - цена тейк-профита для ордера buy stop.
- Expiration time - устанавливается на конец торговой сессии символа за текущий день (CTS.SessionEnd()).
- Comment - необязательный комментарий для описания ордера.
- Trade.SellStop - аналогично, пытается разместить стоп-ордер на продажу с той же логикой, что и стоп-ордер на покупку.
Если какой-либо ордер не открывается, функция возвращает false.
Шаг 12: Возврат результата
return true;
- Если оба ордера buy stop и sell stop успешно открыты, функция возвращает true. Если какая-либо часть процесса завершается неудачей, функция возвращает false.
Функция CloseTrades предназначена для закрытия всех открытых позиций, соответствующих указанному комментарию (COMMENT_COMMON).
//+------------------------------------------------------------------+ //|Function will attempt to close all trades depending on the | //|position comment | //+------------------------------------------------------------------+ void CTradeManagement::CloseTrades(string COMMENT_COMMON) { //--- Iterate through all open positions for(int i=0; i<PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); //--- Check if Position ticket is above zero if(ticket>0) { //--- Check if the Position's Symbol,Comment is correct if(PositionGetString(POSITION_SYMBOL)==CSymbol.GetSymbolName() &&StringFind(PositionGetString(POSITION_COMMENT),COMMENT_COMMON)>=0) { //--- close trade. Trade.PositionClose(ticket); } } } }
Шаг 1: Перебираем все открытые позиции
for (int i = 0; i < PositionsTotal(); i++)
- PositionsTotal() - возвращает общее количество открытых позиций.
- Цикл повторяется по всем открытым в данный момент позициям. Переменная i — это индекс позиции, варьирующийся от 0 до PositionsTotal() - 1.
Шаг 2: Получаем номер тикета позиции
ulong ticket = PositionGetTicket(i);
- PositionGetTicket(i) - извлекает номер тикета открытой позиции с индексом i. Номер тикета является уникальным идентификатором каждой позиции.
- Номер тикета сохраняется в переменной ticket.
Шаг 3: Проверяем, действителен ли тикет
if (ticket > 0)
- Проверяет, что номер тикета больше нуля, гарантируя, что полученный тикет действителен, прежде чем продолжить. Номер тикета 0 будет означать, что по данному индексу не существует позиции, что не является допустимым состоянием.
Шаг 4: Проверка символа позиции и комментария
if (PositionGetString(POSITION_SYMBOL) == CSymbol.GetSymbolName() && StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON) >= 0)
- PositionGetString(POSITION_SYMBOL) - извлекает имя символа позиции с индексом i (например, валютная пара).
- CSymbol.GetSymbolName() - извлекает имя символа, связанного с объектом CSymbol.
- Первое условие проверяет, совпадает ли символ позиции с символом, управляемым CSymbol.
- PositionGetString(POSITION_COMMENT) - извлекает строку комментария, прикрепленную к открытой позиции.
- StringFind(PositionGetString(POSITION_COMMENT), COMMENT_COMMON) - проверяет, присутствует ли указанная строка COMMENT_COMMON в комментарии позиции.
- Если комментарий содержит COMMENT_COMMON, функция возвращает индекс, с которого начинается совпадение.
- Условие >= 0 гарантирует, что тест будет выполнен, только если комментарий содержит подстроку.
Это гарантирует, что для закрытия будут выбраны только позиции с соответствующим символом и комментарием.
Шаг 5: Закрываем сделку
Trade.PositionClose(ticket);
- Trade.PositionClose(ticket) - пытается закрыть открытую позицию, идентифицированную по номеру тикета.
- Если позиция соответствует условиям (правильные символ и комментарий), то она закрывается.
Заключение
В этой статье мы реализовали код для открытия стоп-ордеров и проверки действительности сделок и ордеров перед их открытием. Мы создали функцию FundamentalMode, которая управляет особым режимом торговли, управляя противоположными стоп-ордерами. Кроме того, было реализовано снижение проскальзывания стоп-ордеров, что снизило риск проскальзывания цен в условиях нестабильности рынка, вызванной выходом новостей.
Основные выводы:
- Точность исполнения: Класс Trade Management охватывает все аспекты размещения, изменения и закрытия сделок, гарантируя точность исполнения сделок даже на нестабильных рынках.
- Корректировки в реальном времени: Возможность динамической корректировки стоп-лоссов и тейк-профитов гарантирует, что советник реагирует на изменения рынка в реальном времени, что позволяет лучше управлять рисками.
- Управление проскальзыванием: Учет проскальзывания и реализация логики для динамической корректировки параметров торговли позволяют классу Trade Management гарантировать, что сделки будут выполняться максимально близко к предполагаемым условиям, что снижает потенциальные убытки.
Спасибо за внимание! До встречи в новой статье!
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/16169
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Здравствуйте, Кабело,
очень интересно!!!
К сожалению, zip-файл(NewsTrading_Part5.zip )такой же, как и в статье 4(NewsTrading_Part4.zip )???
Привет Хамид, К сожалению, я не могу поместить весь код в одну статью. В части 5 новостей больше кода, чем в части 4, но она не полная. В части 6 будет реализован оставшийся код, чтобы все работало вместе.
Спасибо за ваше время и понимание.
Здравствуйте,
Есть ли простой способ отображать перед новостями тип ордера, buy, sel или NAN?
Спасибо
Спасибо
Здравствуйте, Хамид Рабиа, спасибо за предложение. Я обязательно реализую решение и отправлю вам личное сообщение, как только оно будет готово.
Это увлекательная статья, которую вы создали.
С нетерпением жду 6-й части и полного кода.
Вы написали увлекательную статью.
С нетерпением жду 6-й части и полного кода.
Привет, Veeral10, спасибо за теплые слова!