
Упрощаем торговлю на новостях (Часть 3): Совершаем сделки
Введение
Ранее мы создали советника для хранения экономических данных в базе данных нашего новостного календаря. Мы также разработали множество классов, чтобы заложить основу для адекватной работы нашего советника. В этой статье мы более подробно рассмотрим эти классы в нашем проекте, чтобы наконец достичь нашей цели — торговли на основе экономических данных. Нашей следующей целью станет прибыльность, о которой мы поговорим в следующих статьях. В этой статье мы добавим новое представление в нашу базу данных для отображения всех уникальных событий экономического календаря MQL5. Мы также добавим новые входные данные советнику для сортировки экономических данных при торговле, чтобы обеспечить гибкость. В предыдущей статье серии мы создали класс управления рисками.
Чего можно ожидать?
Улучшенная графика — лаконичная, современная и адаптируемая к текущему графику. На изображении ниже представлен такой график в светлом (дневном) режиме.
Разделы с первого по девятый будут автоматически отображаться каждый раз, когда советник находится на графике.
Разделы десятый (11-15 сгруппированы) и шестнадцатый являются необязательными и будут обновляться каждую новую 1-минутную свечу (это необходимо для повышения производительности при тестировании на истории).
Раздел
- 10 - дата и время терминала. Время отображается красным цветом при новостном событии, когда график находится в светлом режиме.
- 11 - дата и время текущей/следующей новости. Текст будет отображаться красным цветом, когда дата и время будут соответствовать времени терминала.
- 12 - название новостного события. Цвет текста будет меняться в зависимости от важности события, например, высокая важность отображается красным цветом.
- 13 - страна, к которой относится новость. Цвет текста будет меняться в зависимости от важности события и цветового режима графика (светлый/темный).
- 14 - валюта, к которой относится новость. Цвет текста варьируется.
- 15 - важность новости. Цвет текста варьируется.
- 16 - спред текущего символа и ранг, который рассчитывается на основе данных спреда за 2 недели по 1-минутным свечам и классифицируется по группам excellent (отлично), good (хорошо), normal (нормально), bad (плохо) и terrible (ужасно) с разными цветами для каждой категории с вариациями для темного и светлого режимов.
На изображении ниже показан темный (ночной) режим.
Раздел
- 17 - время всех событий, которые произойдут или произошли в текущий день.
Входные параметры DISPLAY
- CHART COLOUR MODE - переключение между темным и светлым режимом.
- DISPLAY NEWS INFO - показать/скрыть разделы 11-15 на графике.
- DISPLAY EVENT OBJ - показать/скрыть раздел 17 на графике.
- DISPLAY SPREAD RATING - показать/скрыть раздел 16 на графике.
- DISPLAY DATE - показать/скрыть раздел 10 на графике.
Входные параметры DST SCHEDULE
- SELECT DST OPTION - позволить пользователю выбрать собственный график перехода на летнее время или разрешить советнику автоматически выбирать рекомендуемый график перехода на летнее время для правильной настройки времени событий при тестировании на истории в тестере стратегий.
- SELECT CUSTOM DST - позволить пользователю вручную настроить летнее время.
Входные параметры RISK MANAGEMENT
- SELECT RISK OPTION - выбрать профиль управления рисками: MINIMUM LOTSIZE (минимальный размер лота), MAXIMUM LOTSIZE (максимальный размер лота) и так далее.
- RISK FLOOR - установить минимальный риск для всех профилей риска. Например, если средств недостаточно для размера позиции в 1 лот, но достаточно для минимального размера в 0,01 лота, для открытия сделки будет использовано 0,01 лота вместо того, чтобы не открывать ни одной сделки из-за недостатка средств. Это мера безопасности на случай, если профили риска не были настроены должным образом.
- MAX-RISK - открыть сделку с процентом свободной маржи на счете, если на счете недостаточно средств для открытия обычной сделки. Эта опция работает только в том случае, если для параметра RISK FLOOR установлено значение MAX-RISK.
- RISK CEILING - ограничить размер лота, когда средств на счете достаточно, чтобы открыть максимальный лот для определенного символа. Ограничение варьируется от MAX LOTSIZE, что означает, что максимально возможный размер лота — это тот, который установлен конкретным символом, тогда как MAX LOTSIZE(x2) откроет две сделки с максимальным размером лота, если это позволяет ограничение по объему.
- PERCENTAGE OF [BALANCE | FREE-MARGIN] - рисковать определенным процентом от доступного баланса или свободной маржи.
- AMOUNT PER [BALANCE | FREE-MARGIN] - рисковать определенной суммой баланса или свободной маржи, например, если [BALANCE | FREE-MARGIN] установлен на 1000 и EACH AMOUNT равно 10, это означает, что на каждые 1000 в валюте баланса или свободной маржи риск составляет 10 валютных единиц для каждой сделки. Таким образом, если ваш баланс/свободная маржа составляет USD 1000, то вы рискуете USD 10 в каждой сделке.
- LOTSIZE PER [BALANCE | FREE-MARGIN] - рисковать определенным размером лота для значения баланса или свободной маржи, например, если [BALANCE | FREE-MARGIN] установлен на 1000 и EACH LOTS(VOLUME) равно 0.1, это означает, что на каждые 1000 единиц валюты баланса или свободной маржи риск составляет 0,1 лота для каждой сделки. Таким образом, если ваш баланс/свободная маржа составляет USD 1000, то риск составляет 0,1 для каждой сделки.
- CUSTOM LOTSIZE - рисковать заранее определенным размером лота для каждой открытой сделки.
- PERCENTAGE OF MAX-RISK - рисковать процентом от максимального объема риска для символа при доступной свободной марже на счете, например, если максимальный объем риска для AUDUSD при свободной марже на счете в USD 10 000 составляет 100 лотов, то если мы установим PERCENTAGE OF MAX-RISK на 25%, то используемый размер лота составит 25% от 100 лотов, что составляет 25 лотов.
Входные параметры NEWS SETTINGS
Раздел содержит следующие настройки:
- CALENDAR IMPORTANCE
- EVENT FREQUENCY
- EVENT SECTOR
- EVENT TYPE
- EVENT CURRENCY
- CALENDAR IMPORTANCE - сортировать новостные по указанной важности.
- EVENT FREQUENCY - сортировать новости на основе частоты их появления.
- EVENT SECTOR - сортировать новости по сектору.
- EVENT TYPE - сортировать новости по типу: EVENT - это, как правило, высказывания и заседания, INDICATOR - это обычно процентные ставки, данные по занятости и так далее и HOLIDAY - разные праздники.
- EVENT CURRENCY - сортировать новости по валюте. SYMBOL CURRENCIES - учитывать все валюты из SYMBOL MARGIN, SYMBOL BASE и SYMBOL PROFIT.
Входные параметры TRADE SETTINGS
- STOPLOSS[0=NONE] - установить фиксированный стоп-лосс для всех сделок. 0 - не используется.
- TAKEPROFIT[0=NONE] - установить фиксированный тейк-профит для всех сделок. 0 - не используется.
- PRE-ENTRY SEC - позволить пользователю настроить количество секунд для открытия сделки перед событием. Таким образом, если PRE-ENTRY SEC установлен на 5, это означает, что 5 секунд до наступления события — это тот промежуток времени, в течение которого будут открыты сделки. Например, если время наступления события - 15:00, то сделки будут разрешены за 5 секунд до начала события с 14:59:45 по 14:59:59.
- TRADING DAY OF WEEK - сортировать по рабочим дням.
Давайте перейдем к коду.
Класс свойств символа
Добавлены изменения из Части 2:
- Объявление перечисления для оценки спреда
//Enumeration for Spread rating enum SpreadRating { SpreadRating_Terrible,//Terrible SpreadRating_Bad,//Bad SpreadRating_Normal,//Normal SpreadRating_Good,//Good SpreadRating_Excellent//Excellent };
- Объявление булевой переменной для настройки цветового режима графика
bool isLightMode;//Variable to configure Chart color mode
- Декларация булевой функции для извлечения плавающего спреда
bool SpreadFloat(string SYMBOL=NULL);//Retrieve Spread Float
//+------------------------------------------------------------------+ //|Retrieve Spread Float | //+------------------------------------------------------------------+ bool CSymbolProperties::SpreadFloat(string SYMBOL=NULL) { if(SetSymbolName(SYMBOL))//Set Symbol { return CSymbol.SpreadFloat(); } Print("Unable to retrieve Symbol's Spread Float"); return false;//Retrieve false when failed. }
- Объявление функции оценки спреда
SpreadRating SpreadValue(string SYMBOL=NULL);//Retrieve Spread Rating
Функция должна возвращать значение перечисления из SpreadRating.
//+------------------------------------------------------------------+ //|Retrieve Spread Rating | //+------------------------------------------------------------------+ SpreadRating CSymbolProperties::SpreadValue(string SYMBOL=NULL) { if(SetSymbolName(SYMBOL))//Set Symbol { if(SpreadFloat(SYMBOL))//Check if Symbol has a floating Spread { //Declarations vector Spreads; int SpreadArray[],SpreadAvg=0,SpreadMax=0,SpreadMin=0, SpreadUpper=0,SpreadLower=0,SpreadAvgUpper=0, SpreadAvgLower=0,SpreadMidUpper=0,SpreadMidLower=0; //Get Spread data from CopySpread built-in function for 2 weeks using M1 timeframe. if(CopySpread(GetSymbolName(),PERIOD_M1,iTime(GetSymbolName(),PERIOD_W1,2), iTime(GetSymbolName(),PERIOD_M1,0),SpreadArray)==-1) { Print("Error trying to retrieve spread values"); return SpreadRating_Normal;//Retrieve default value when failed. } else { Spreads.Assign(SpreadArray);//Assign spread array into Spreads vector variable SpreadMax = int(Spreads.Max());//Assign max spread SpreadMin = int(Spreads.Min());//Assign min spread SpreadAvg = int(Spreads.Median());//Assign average spread //Divide Spread into sectors based of different averages. SpreadMidUpper = int((SpreadAvg+SpreadMax)/2); SpreadMidLower = int((SpreadAvg+SpreadMin)/2); SpreadAvgUpper = int((SpreadAvg+SpreadMidUpper)/2); SpreadAvgLower = int((SpreadAvg+SpreadMidLower)/2); SpreadUpper = int((SpreadMidUpper+SpreadMax)/2); SpreadLower = int((SpreadMidLower+SpreadMin)/2); int Spread = Spread(SYMBOL);//Assign Symbol's Spread if(Spread<SpreadLower||Spread==SpreadMin)//Excellent { return SpreadRating_Excellent; } else if(Spread>=SpreadLower&&Spread<SpreadAvgLower)//Good { return SpreadRating_Good; } else if(Spread>=SpreadAvgLower&&Spread<=SpreadAvgUpper)//Normal { return SpreadRating_Normal; } else if(Spread>SpreadAvgUpper&&Spread<=SpreadUpper)//Bad { return SpreadRating_Bad; } else//Terrible { return SpreadRating_Terrible; } } } else { return SpreadRating_Normal;//Retrieve default value when spread is fixed. } } Print("Unable to retrieve Symbol's Spread Rating"); return SpreadRating_Normal;//Retrieve default value when failed. }
Сначала мы задаем символ, затем проверяем, имеет ли символ плавающий спред, прежде чем выполнить простые вычисления, чтобы оценить спред на основе его средних значений. Если настройка символа не удалась или символ не имеет плавающего спреда, возвращаем SpreadRating_Normal в качестве значения по умолчанию.
if(SetSymbolName(SYMBOL))//Set Symbol { if(SpreadFloat(SYMBOL))//Check if Symbol has a floating Spread {
Если мы успешно задали символ и у символа плавающий спред, объявляем векторную переменную Spreads и переменные int для хранения значений спреда. После объявления наших переменных мы будем использовать функцию CopySpread для сохранения значений спреда в нашей переменной SpreadArray, начиная с 1-минутной свечи 2 недели назад до текущей. Если функция CopySpread по какой-то причине не сработает, вернем SpreadRating_Normal в качестве значения по умолчанию.
//Declarations vector Spreads; int SpreadArray[],SpreadAvg=0,SpreadMax=0,SpreadMin=0, SpreadUpper=0,SpreadLower=0,SpreadAvgUpper=0, SpreadAvgLower=0,SpreadMidUpper=0,SpreadMidLower=0; //Get Spread data from CopySpread built-in function for 2 weeks using M1 timeframe. if(CopySpread(GetSymbolName(),PERIOD_M1,iTime(GetSymbolName(),PERIOD_W1,2), iTime(GetSymbolName(),PERIOD_M1,0),SpreadArray)==-1) { Print("Error trying to retrieve spread values"); return SpreadRating_Normal;//Retrieve default value when failed. }
После успешного выполнения CopySpread мы присвоим вектору Spreads целочисленные значения из нашего SpreadArray. Затем нам необходимо получить основную информацию из этих значений массива, например, максимальный спред за двухнедельный период, а также минимальный спред и спреды. Мы сохраним эти значения в SpreadMax, SpreadMin и SpreadAvg соответственно. Теперь мы хотим получить разные средние значения из этих трех предыдущих значений.
Для переменной SpreadMidUpper нам нужно среднее между SpreadAvg и SpreadMax, для переменной SpreadMidLower - среднее между SpreadAvg и SpreadMin, для переменной SpreadAvgUpper - среднее между SpreadAvg и SpreadMidUpper, для переменной SpreadAvgLower - среднее между SpreadAvg и SpreadMidLower, для переменной SpreadUpper - среднее между SpreadMidUpper и SpreadMax, для переменной SpreadLower - среднее между SpreadMidLower и SpreadMin. Нам также понадобится текущий спред символа для сравнения и классификации спреда.
Spreads.Assign(SpreadArray);//Assign spread array into Spreads vector variable SpreadMax = int(Spreads.Max());//Assign max spread SpreadMin = int(Spreads.Min());//Assign min spread SpreadAvg = int(Spreads.Median());//Assign average spread //Divide Spread into sectors based of different averages. SpreadMidUpper = int((SpreadAvg+SpreadMax)/2); SpreadMidLower = int((SpreadAvg+SpreadMin)/2); SpreadAvgUpper = int((SpreadAvg+SpreadMidUpper)/2); SpreadAvgLower = int((SpreadAvg+SpreadMidLower)/2); SpreadUpper = int((SpreadMidUpper+SpreadMax)/2); SpreadLower = int((SpreadMidLower+SpreadMin)/2); int Spread = Spread(SYMBOL);//Assign Symbol's Spread
У нас есть 5 классификаций спредов, а именно:
- Excellent (отлично): Текущий спред меньше переменной SpreadLower или равен SpreadMin.
- Good (хорошо): Текущий спред больше или равен переменной SpreadLower и меньше переменной SpreadAvgLower.
- Normal (нормально): Когда текущий спред меньше или равен переменной SpreadAvgLower, а также меньше или равен переменной SpreadAvgUpper.
- Bad (плохо): Текущий спред больше SpreadAvgUpper, а также меньше или равен SpreadUpper.
- Terrible (ужасно): Текущий спред больше SpreadUpper
if(Spread<SpreadLower||Spread==SpreadMin)//Excellent { return SpreadRating_Excellent; } else if(Spread>=SpreadLower&&Spread<SpreadAvgLower)//Good { return SpreadRating_Good; } else if(Spread>=SpreadAvgLower&&Spread<=SpreadAvgUpper)//Normal { return SpreadRating_Normal; } else if(Spread>SpreadAvgUpper&&Spread<=SpreadUpper)//Bad { return SpreadRating_Bad; } else//Terrible { return SpreadRating_Terrible; }
- Объявление функции для извлечения цвета спреда на основе его ранга
color SpreadColor(string SYMBOL=NULL);//Retrieve Spread Color
Чтобы получить цвет спреда для каждого значения перечисления спреда, рассмотрим использование оператора switch, поскольку значения перечисления являются постоянными. Цвета для каждого ранга показаны ниже:
- Excellent - clrBlue в светлом режиме
иначе - clrLightCyan
- Good - clrCornflowerBlue в светлом режиме
иначе - clrLightGreen
- Normal - clrBlack в светлом режиме
иначе - clrWheat
- Bad - clrOrange
- Terrible - clrRed
- По умолчанию: clrBlack в светлом режиме, иначе - clrWheat
//+------------------------------------------------------------------+ //|Retrieve Spread Color | //+------------------------------------------------------------------+ color CSymbolProperties::SpreadColor(string SYMBOL=NULL) { switch(SpreadValue(SYMBOL))//Get Spread Rating value { case SpreadRating_Excellent://Excellent Spread return (isLightMode)?clrBlue:clrLightCyan; break; case SpreadRating_Good://Good Spread return (isLightMode)?clrCornflowerBlue:clrLightGreen; break; case SpreadRating_Normal://Normal Spread return (isLightMode)?clrBlack:clrWheat; break; case SpreadRating_Bad://Bad Spread return clrOrange; break; case SpreadRating_Terrible://Terrible Spread return clrRed; break; default://failed to be identified return (isLightMode)?clrBlack:clrWheat;//Retrieve default color when failed. break; } }
- Объявление строковой функции для получения описания спреда
string SpreadDesc(string SYMBOL=NULL);//Retrieve Spread Description
//+------------------------------------------------------------------+ //|Retrieve Spread Description | //+------------------------------------------------------------------+ string CSymbolProperties::SpreadDesc(string SYMBOL=NULL) { switch(SpreadValue(SYMBOL))//Get Spread Rating value { case SpreadRating_Excellent://Excellent Spread return "Excellent"; break; case SpreadRating_Good://Good Spread return "Good"; break; case SpreadRating_Normal://Normal Spread return "Normal"; break; case SpreadRating_Bad://Bad Spread return "Bad"; break; case SpreadRating_Terrible://Terrible Spread return "Terrible"; break; default://failed to be identified return "Unknown";//Retrieve default value when failed. break; } }
- Объявление строковой функции для получения описания символа
string Description(string SYMBOL=NULL);//Retrieve Symbol's Description
//+------------------------------------------------------------------+ //|Retrieve Symbol's Description | //+------------------------------------------------------------------+ string CSymbolProperties::Description(string SYMBOL=NULL) { if(SetSymbolName(SYMBOL))//Set Symbol { return CSymbol.Description(); } Print("Unable to retrieve Symbol's Description"); return "";//Retrieve an empty string when failed. }
Класс свойств графика
Класс изменен по сравнению с Частью 2. Класс свойств графика теперь будет наследоваться от класса графика из включенных классов MQL5. Структура ChartProp будет хранить все свойства графика, в которые мы внесем изменения. Наша публичная функция ChartRefresh вызовет функцию ChartGet, которая инициализирует свойства графика, а затем мы вызовем функцию ChartSet, которая настроит график с использованием значений свойств графика из ChartGet.
#include "SymbolProperties.mqh" #include <Charts/Chart.mqh> CSymbolProperties CSymbol;//Symbol Properties object //+------------------------------------------------------------------+ //|ChartProperties class | //+------------------------------------------------------------------+ class CChartProperties : public CChart { private: //Structure for chart properties struct ChartProp { ENUM_CHART_MODE mode;//Chart Mode color clrBackground;//Chart Background Color color clrForeground;//Chart Foreground Color color clrLineLast;//Chart Line Color color clrCandleBear;//Chart Bear Candle Color color clrBarDown;//Chart Down Candle Color color clrCandleBull;//Chart Bull Candle Color color clrBarUp;//Chart Up Candle Color color clrLineAsk;//Chart Ask Color color clrLineBid;//Chart Bid Color color clrChartLine;//Chart Line Color color clrStopLevels;//Chart Stop Level Color color clrVolumes;//Chart Volumes Color bool Foreground;//Chart Foreground Visibility bool ShowLineAsk;//Chart Ask Line Visibility bool ShowLineBid;//Chart Bid Line Visibility bool ShowPeriodSep;//Chart Period Separator Visibility bool ShowOHLC;//Chart Open-High-Low-Close Visibility bool ShowGrid;//Chart Grid Visibility ENUM_CHART_VOLUME_MODE ShowVolumes;//Chart Volumes Visibility bool AutoScroll;//Chart Auto Scroll Option bool Shift;//Chart Shift Option double ShiftSize;//Chart Shift Size bool ShowObjectDescr;//Chart Object Descriptions ulong CHART_SHOW_TRADE_LEVELS;//Chart Trade Levels Visibility ulong CHART_SHOW_ONE_CLICK;//Chart One Click Trading Visibility ulong CHART_SHOW_TICKER;//Chart Ticker Visibility ulong CHART_DRAG_TRADE_LEVELS;//Chart Drag Trade levels ENUM_CHART_POSITION Navigate;//Chart Navigate }; ChartProp DefaultChart,MyChart;//Used to store chart properties void ChartSet(ChartProp &Prop);//Apply Chart format void ChartGet();//Assign Chart property values public: CChartProperties();//Constructor ~CChartProperties(void);//Destructor //Configure the chart void ChartRefresh() {ChartGet();ChartSet(MyChart);} string GetChartPeriodName();//Retrieve Period name };
В конструкторе мы присваиваем наследуемой переменной m_chart_id текущий идентификатор графика.
//+------------------------------------------------------------------+ //|Constructor | //+------------------------------------------------------------------+ CChartProperties::CChartProperties() { m_chart_id=ChartID();//Set chart id ChartGet();//Get chart values ChartSet(MyChart);//customize chart }
Для функции ChartGet мы присваиваем значения нашим переменным DefaultChart и MyChart, где DefaultChart будет хранить текущие свойства графика до того, как мы изменим график, а MyChart будет хранить наши пользовательские значения.
//+------------------------------------------------------------------+ //|Assign Chart property values | //+------------------------------------------------------------------+ void CChartProperties::ChartGet() { DefaultChart.mode = Mode();//assign chart mode MyChart.mode = CHART_CANDLES;//assign custom chart mode DefaultChart.clrBackground = ColorBackground();//assign Background color MyChart.clrBackground = (isLightMode)?clrWhite:clrBlack;//assign custom Background color DefaultChart.clrForeground = ColorForeground();//assign foreground color MyChart.clrForeground = (isLightMode)?clrBlack:clrWhite;//assign custom foreground color DefaultChart.clrLineLast = ColorLineLast();//assign Chart Line Color MyChart.clrLineLast = clrWhite;//assign custom Chart Line Color DefaultChart.clrCandleBear = ColorCandleBear();//assign Chart Bear Candle Color MyChart.clrCandleBear = clrBlack;//assign custom Chart Bear Candle Color DefaultChart.clrBarDown = ColorBarDown();//assign Chart Down Candle Color MyChart.clrBarDown = (isLightMode)?clrBlack:CSymbol.Background();//assign custom Chart Down Candle Color DefaultChart.clrCandleBull = ColorCandleBull();//assign Chart Bull Candle Color MyChart.clrCandleBull = CSymbol.Background();//assign custom Chart Bull Candle Color DefaultChart.clrBarUp = ColorBarUp();//assign Chart Up Candle Color MyChart.clrBarUp = (isLightMode)?clrBlack:CSymbol.Background();//assign custom Chart Up Candle Color DefaultChart.clrLineAsk = ColorLineAsk();//assign Chart Ask Color MyChart.clrLineAsk = (isLightMode)?clrBlack:clrWhite;//assign custom Chart Ask Color DefaultChart.clrLineBid = ColorLineBid();//assign Chart Bid Color MyChart.clrLineBid = (isLightMode)?clrBlack:CSymbol.Background();//assign custom Chart Bid Color DefaultChart.clrChartLine = ColorChartLine();//assign Chart Line Color MyChart.clrChartLine = (isLightMode)?clrBlack:clrWhite;//assign custom Chart Line Color DefaultChart.clrStopLevels = ColorStopLevels();//assign Chart Stop Level Color MyChart.clrStopLevels = clrRed;//assign custom Chart Stop Level Color DefaultChart.clrVolumes = ColorVolumes();//assign Chart Volumes Color MyChart.clrVolumes = clrGreen;//assign custom Chart Volumes Color DefaultChart.Foreground = Foreground();//assign Chart Foreground Visibility MyChart.Foreground = false;//assign custom Chart Foreground Visibility DefaultChart.ShowLineAsk = ShowLineAsk();//assign Chart Ask Line Visibility MyChart.ShowLineAsk = true;//assign custom Chart Ask Line Visibility DefaultChart.ShowLineBid = ShowLineBid();//assign Chart Bid Line Visibility MyChart.ShowLineBid = true;//assign custom Chart Bid Line Visibility DefaultChart.ShowPeriodSep = ShowPeriodSep();//assign Chart Period Separator Visibility MyChart.ShowPeriodSep = true;//assign custom Chart Period Separator Visibility DefaultChart.ShowOHLC = ShowOHLC();//assign Chart Open-High-Low-Close Visibility MyChart.ShowOHLC = false;//assign custom Chart Open-High-Low-Close Visibility DefaultChart.ShowGrid = ShowGrid();//assign Chart Grid Visibility MyChart.ShowGrid = false;//assign custom Chart Grid Visibility DefaultChart.ShowVolumes = ShowVolumes();//assign Chart Volumes Visibility MyChart.ShowVolumes = CHART_VOLUME_HIDE;//assign custom Chart Volumes Visibility DefaultChart.AutoScroll = AutoScroll();//assign Chart Auto Scroll Option MyChart.AutoScroll = true;//assign custom Chart Auto Scroll Option DefaultChart.Shift = Shift();//assign Chart Shift Option MyChart.Shift = true;//assign custom Chart Shift Option DefaultChart.ShiftSize = ShiftSize();//assign Chart Shift Size MyChart.ShiftSize = 15;//assign custom Chart Shift Size DefaultChart.ShowObjectDescr = ShowObjectDescr();//assign Chart Object Descriptions MyChart.ShowObjectDescr = false;//assign custom Chart Object Descriptions DefaultChart.Navigate = CHART_END;//assign Chart Navigate MyChart.Navigate = CHART_END;//assign custom Chart Navigate //---assign Chart Trade Levels Visibility DefaultChart.CHART_SHOW_TRADE_LEVELS = ChartGetInteger(ChartId(),CHART_SHOW_TRADE_LEVELS); //---assign custom Chart Trade Levels Visibility MyChart.CHART_SHOW_TRADE_LEVELS = ulong(true); //---assign Chart One Click Trading Visibility DefaultChart.CHART_SHOW_ONE_CLICK = ChartGetInteger(ChartId(),CHART_SHOW_ONE_CLICK); //---assign custom Chart One Click Trading Visibility MyChart.CHART_SHOW_ONE_CLICK = ulong(false); //---assign Chart Ticker Visibility DefaultChart.CHART_SHOW_TICKER = ChartGetInteger(ChartId(),CHART_SHOW_TICKER); //---assign custom Chart Ticker Visibility MyChart.CHART_SHOW_TICKER = ulong(false); //---assign Chart Drag Trade levels DefaultChart.CHART_DRAG_TRADE_LEVELS = ChartGetInteger(ChartId(),CHART_DRAG_TRADE_LEVELS); //---assign custom Chart Drag Trade levels MyChart.CHART_DRAG_TRADE_LEVELS = ulong(false); }
Наша функция ChartSet будет принимать нашу структуру ChartProp в качестве аргумента для настройки текущего графика.
//+------------------------------------------------------------------+ //|Apply Chart format | //+------------------------------------------------------------------+ void CChartProperties::ChartSet(ChartProp &Prop) { Mode(Prop.mode);//Set Chart Candle Mode ColorBackground(Prop.clrBackground);//Set Chart Background Color ColorForeground(Prop.clrForeground);//Set Chart Foreground Color ColorLineLast(Prop.clrLineLast);//Set Chart Line Color ColorCandleBear(Prop.clrCandleBear);//Set Chart Bear Candle Color ColorBarDown(Prop.clrBarDown);//Set Chart Down Candle Color ColorCandleBull(Prop.clrCandleBull);//Set Chart Bull Candle Color ColorBarUp(Prop.clrBarUp);//Set Chart Up Candle Color ColorLineAsk(Prop.clrLineAsk);//Set Chart Ask Color ColorLineBid(Prop.clrLineBid);//Set Chart Bid Color ColorChartLine(Prop.clrChartLine);//Set Chart Line Color ColorStopLevels(Prop.clrStopLevels);//Set Chart Stop Level Color ColorVolumes(Prop.clrVolumes);//Set Chart Volumes Color Foreground(Prop.Foreground);//Set if Chart is in Foreground Visibility ShowLineAsk(Prop.ShowLineAsk);//Set Chart Ask Line Visibility ShowLineBid(Prop.ShowLineBid);//Set Chart Bid Line Visibility ShowPeriodSep(Prop.ShowPeriodSep);//Set Chart Period Separator Visibility ShowOHLC(Prop.ShowOHLC);//Set Chart Open-High-Low-Close Visibility ShowGrid(Prop.ShowGrid);//Set Chart Grid Visibility ShowVolumes(Prop.ShowVolumes);//Set Chart Volumes Visibility AutoScroll(Prop.AutoScroll);//Set Chart Auto Scroll Option Shift(Prop.Shift);//Set Chart Shift Option ShiftSize(Prop.ShiftSize);//Set Chart Shift Size Value ShowObjectDescr(Prop.ShowObjectDescr);//Set Chart Show Object Descriptions ChartSetInteger(ChartId(),CHART_SHOW_TRADE_LEVELS,Prop.CHART_SHOW_TRADE_LEVELS);//Set Chart Trade Levels Visibility ChartSetInteger(ChartId(),CHART_SHOW_ONE_CLICK,Prop.CHART_SHOW_ONE_CLICK);//Set Chart One Click Trading Visibility ChartSetInteger(ChartId(),CHART_SHOW_TICKER,Prop.CHART_SHOW_TICKER);//Set Chart Ticker Visibility ChartSetInteger(ChartId(),CHART_DRAG_TRADE_LEVELS,Prop.CHART_DRAG_TRADE_LEVELS);//Set Chart Drag Trade levels Navigate(Prop.Navigate);//Set Chart Navigate }
Что касается функции GetChartPeriodName, мы получим имя периода текущего графика с помощью оператора switch.
//+------------------------------------------------------------------+ //|Retrieve Period name | //+------------------------------------------------------------------+ string CChartProperties::GetChartPeriodName() { switch(ChartPeriod(ChartId()))//Get chart Period with chart id { case PERIOD_M1: return("M1"); case PERIOD_M2: return("M2"); case PERIOD_M3: return("M3"); case PERIOD_M4: return("M4"); case PERIOD_M5: return("M5"); case PERIOD_M6: return("M6"); case PERIOD_M10: return("M10"); case PERIOD_M12: return("M12"); case PERIOD_M15: return("M15"); case PERIOD_M20: return("M20"); case PERIOD_M30: return("M30"); case PERIOD_H1: return("H1"); case PERIOD_H2: return("H2"); case PERIOD_H3: return("H3"); case PERIOD_H4: return("H4"); case PERIOD_H6: return("H6"); case PERIOD_H8: return("H8"); case PERIOD_H12: return("H12"); case PERIOD_D1: return("Daily"); case PERIOD_W1: return("Weekly"); case PERIOD_MN1: return("Monthly"); } return("unknown period"); }
Наш деструктор восстановит предыдущую конфигурацию графика до внесения в него каких-либо изменений.
//+------------------------------------------------------------------+ //|Destructor | //+------------------------------------------------------------------+ CChartProperties::~CChartProperties() { ChartSet(DefaultChart);//restore chart default configuration m_chart_id=-1;//reset chart id }
Класс свойств объекта
В этом классе было внесено несколько изменений для всех пользовательских цветов текста объектов. В Части 2 мы могли использовать только один цвет текста объекта для всех текстовых объектов. Наше решение — объявить переменную цвета вне класса с именем TextObj_color.
#include "ChartProperties.mqh" color TextObj_color; //+------------------------------------------------------------------+ //|ObjectProperties class | //+------------------------------------------------------------------+ class CObjectProperties:public CChartProperties { private: //Simple chart objects structure struct ObjStruct { long ChartId; string Name; } Objects[];//ObjStruct variable array //-- Add chart object to Objects array void AddObj(long chart_id,string name) { ArrayResize(Objects,Objects.Size()+1,Objects.Size()+2); Objects[Objects.Size()-1].ChartId=chart_id; Objects[Objects.Size()-1].Name=name; } protected: void DeleteObj() { for(uint i=0;i<Objects.Size();i++) { ObjectDelete(Objects[i].ChartId,Objects[i].Name); } } public: CObjectProperties(void) {}//Class constructor //-- Create Rectangle chart object void Square(long chart_ID,string name,int x_coord,int y_coord,int width,int height,ENUM_ANCHOR_POINT Anchor); //-- Create text chart object void TextObj(long chartID,string name,string text,int x_coord,int y_coord, ENUM_BASE_CORNER Corner=CORNER_LEFT_UPPER,int fontsize=10); //-- Create Event object void EventObj(long chartID,string name,string description,datetime eventdate); //-- Class destructor removes all chart objects created previously ~CObjectProperties(void) { DeleteObj(); } };
Как мы увидим ниже, параметров у функции Textobj довольно много. Чтобы их не стало еще больше, мы просто используем Textobj_color для изменения цвета текстового объекта.
//+------------------------------------------------------------------+ //|Create text chart object | //+------------------------------------------------------------------+ void CObjectProperties::TextObj(long chartID,string name,string text,int x_coord,int y_coord, ENUM_BASE_CORNER Corner=CORNER_LEFT_UPPER,int fontsize=10) { ObjectDelete(chartID,name);//Delete previous object with the same name and chart id if(ObjectCreate(chartID,name,OBJ_LABEL,0,0,0))//Create object label { AddObj(chartID,name);//Add object to array ObjectSetInteger(chartID,name,OBJPROP_XDISTANCE,x_coord);//Set x Distance/coordinate ObjectSetInteger(chartID,name,OBJPROP_YDISTANCE,y_coord);//Set y Distance/coordinate ObjectSetInteger(chartID,name,OBJPROP_CORNER,Corner);//Set object's corner anchor ObjectSetString(chartID,name,OBJPROP_TEXT,text);//Set object's text ObjectSetInteger(chartID,name,OBJPROP_COLOR,TextObj_color);//Set object's color ObjectSetInteger(chartID,name,OBJPROP_FONTSIZE,fontsize);//Set object's font-size } else { Print("Failed to create object: ",name); } }
В функцию Square было внесено небольшое изменение, позволяющее использовать разные цвета фона в зависимости от цветового режима графика.
//+------------------------------------------------------------------+ //|Create Rectangle chart object | //+------------------------------------------------------------------+ void CObjectProperties::Square(long chart_ID,string name,int x_coord,int y_coord,int width,int height,ENUM_ANCHOR_POINT Anchor) { const int sub_window=0; // subwindow index const int x=x_coord; // X coordinate const int y=y_coord; // Y coordinate const color back_clr=(isLightMode)?clrWhite:clrBlack;// background color const ENUM_BORDER_TYPE border=BORDER_SUNKEN; // border type const color clr=clrRed; // flat border color (Flat) const ENUM_LINE_STYLE style=STYLE_SOLID; // flat border style const int line_width=0; // flat border width const bool back=false; // in the background const bool selection=false; // highlight to move const bool hidden=true; // hidden in the object list ObjectDelete(chart_ID,name);//Delete previous object with the same name and chart id if(ObjectCreate(chart_ID,name,OBJ_RECTANGLE_LABEL,sub_window,0,0))//create rectangle object label { AddObj(chart_ID,name);//Add object to array ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x);//Set x Distance/coordinate ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y);//Set y Distance/coordinate ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,width);//Set object's width/x-size ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,height);//Set object's height/y-size ObjectSetInteger(chart_ID,name,OBJPROP_BGCOLOR,back_clr);//Set object's background color ObjectSetInteger(chart_ID,name,OBJPROP_BORDER_TYPE,border);//Set object's border type ObjectSetInteger(chart_ID,name,OBJPROP_ANCHOR,Anchor);//Set objects anchor point ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr);//Set object's color ObjectSetInteger(chart_ID,name,OBJPROP_STYLE,style);//Set object's style ObjectSetInteger(chart_ID,name,OBJPROP_WIDTH,line_width);//Set object's flat border width ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back);//Set if object is in foreground or not ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection);//Set if object is selectable/dragable ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection);//Set if object is Selected ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden);//Set if object is hidden in object list ChartRedraw(chart_ID); } else { Print("Failed to create object: ",name); } }
Файл заголовка CommonVariables
Для торговых целей я решил создать базу данных в памяти. Для этой базы данных необходимо имя, которое будет учитывать имя брокера, текущий идентификатор графика и то, находится ли советник в тестере стратегий или нет.
#define NEWS_DATABASE_MEMORY StringFormat("Calendar_%s_%d_%s.sqlite",broker,ChartID(),(MQLInfoInteger(MQL_TESTER)?"TESTER":"REAL"))
Перечисление Choice предназначено для персонализации и будет использоваться для ввода советника, оно заменит логический тип данных. Перечисление DayOfTheWeek будет использоваться для выбора торгового дня недели без учета субботы и воскресенья. Логическая функция Answer преобразует перечисление Choice в логический тип данных.
enum Choice { Yes,//YES No//NO }; enum DayOfTheWeek { Monday,//MONDAY Tuesday,//TUESDAY Wednesday,//WEDNESDAY Thursday,//THURSDAY Friday,//FRIDAY AllDays//ALL DAYS }; //+------------------------------------------------------------------+ //|Convert enumeration Choice into a boolean value | //+------------------------------------------------------------------+ bool Answer(Choice choose) { return (choose==Yes)?true:false; }
Класс Time Variables
Целью этого класса является хранение временных данных свечи. Эти данные будут использоваться для проверки того, сформировалась ли новая свеча.
//+------------------------------------------------------------------+ //|TimeVariables class | //+------------------------------------------------------------------+ class CTimeVariables { private: //---Array to store candlestick times datetime CandleTime[2000]; public: CTimeVariables(void); //---Set Array index time void SetTime(uint index,datetime time); //---Get Array index time datetime GetTime(uint index); };
В конструкторе мы установим время по умолчанию для всех индексов в массиве CandleTime.
//+------------------------------------------------------------------+ //|Constructor | //+------------------------------------------------------------------+ CTimeVariables::CTimeVariables() { for(uint i=0; i<CandleTime.Size(); i++) { CandleTime[i]=D'1970.01.01'; } }
В функции SetTime у нас есть два параметра: один - для индекса массива, а другой - для даты и времени. Если аргумент индекса больше или равен нулю и меньше размера CandleTime, то мы присвоим индекс массива с аргументом времени.
//+------------------------------------------------------------------+ //|Set Array index time | //+------------------------------------------------------------------+ void CTimeVariables::SetTime(uint index,datetime time) { if(index>=0&&index<CandleTime.Size()) { CandleTime[index] = time; } }
Функция GetTime принимает один положительный целочисленный аргумент для извлечения даты и времени из значения индекса массива CandleTime, если аргумент индекса допустим.
//+------------------------------------------------------------------+ //|Get Array index time | //+------------------------------------------------------------------+ datetime CTimeVariables::GetTime(uint index) { return (index>=0&&index<CandleTime.Size())?CandleTime[index]:datetime(0); }
Класс Time Management
Объявим перечисление DSTSchedule, чтобы пользователь/трейдер мог выбирать между автоматическим летним временем (Auto DST) и пользовательским летним временем (Custom DST) для входных параметров советника. Переменная MySchedule будет использоваться для хранения пользовательского летнего времени.
//-- Enumeration for DST schedule enum DSTSchedule { AutoDst_Selection,//AUTO DST CustomDst_Selection//CUSTOM DST } MyDST; DST_type MySchedule;//Variable for custom DST schedule
Функция ниже возвращает час для определенной даты в целочисленном типе данных.
int ReturnHour(datetime time);//Returns the Hour for a specific date
//+------------------------------------------------------------------+ //|Returns the Hour for a specific date | //+------------------------------------------------------------------+ int CTimeManagement::ReturnHour(datetime time) { return Time(time).hour; }
Функция ниже возвращает минуты для определенной даты в целочисленном типе данных.
int ReturnMinute(datetime time);//Returns the Minute for a specific date
//+------------------------------------------------------------------+ //|Returns the Minute for a specific date | //+------------------------------------------------------------------+ int CTimeManagement::ReturnMinute(datetime time) { return Time(time).min; }
Функция ниже возвращает секунду для определенной даты в целочисленном типе данных.
int ReturnSecond(datetime time);//Returns the Second for s specific date
//+------------------------------------------------------------------+ //|Returns the Second for s specific date | //+------------------------------------------------------------------+ int CTimeManagement::ReturnSecond(datetime time) { return Time(time).sec; }
Функция ниже возвращает MqlDateTime для аргумента datetime.
//-- Will convert datetime to MqlDateTime MqlDateTime Time(datetime Timetoformat);
//+------------------------------------------------------------------+ //|Will convert datetime to MqlDateTime | //+------------------------------------------------------------------+ MqlDateTime CTimeManagement::Time(datetime Timetoformat) { TimeToStruct(Timetoformat,timeFormat); return timeFormat; }
Функция ниже возвращает дату и время для аргумента datetime с изменениями в часах, минутах и секундах.
//-- Will return a datetime with changes to the hour,minute and second datetime Time(datetime time,int Hour,int Minute,int Second);
//+------------------------------------------------------------------+ //|Will return a datetime with changes to the hour,minute and second | //+------------------------------------------------------------------+ datetime CTimeManagement::Time(datetime time,int Hour,int Minute,int Second) { timeFormat=Time(time); timeFormat.hour=Hour; timeFormat.min=Minute; timeFormat.sec=Second; return StructToTime(timeFormat); }
Функция ниже возвращает дату и время для аргумента datetime с изменением часов и минут.
//-- Will return a datetime with changes to the hour and minute datetime Time(datetime time,int Hour,int Minute);
//+------------------------------------------------------------------+ //|Will return a datetime with changes to the hour and minute | //+------------------------------------------------------------------+ datetime CTimeManagement::Time(datetime time,int Hour,int Minute) { timeFormat=Time(time); timeFormat.hour=Hour; timeFormat.min=Minute; return StructToTime(timeFormat); }
Функция ниже возвращает true, если время TimeTradeServer находится в пределах аргументов BeginTime и EndTime.
//-- Check current time is within a time range bool TimeIsInRange(datetime BeginTime,datetime EndTime);
//+------------------------------------------------------------------+ //|Check current time is within a time range | //+------------------------------------------------------------------+ bool CTimeManagement::TimeIsInRange(datetime BeginTime,datetime EndTime) { if(BeginTime<=TimeTradeServer()&&EndTime>=TimeTradeServer()) { return true; } return false; }
Функция ниже вернет true, если PreEvent datetime меньше или равно TimeTradeServer, а EventTime больше TimeTradeServer.
//-- Check if current time is within preEvent time and Event time bool TimePreEvent(datetime PreEvent,datetime Event);
//+------------------------------------------------------------------+ //|Check if current time is within preEvent time and Event time | //+------------------------------------------------------------------+ bool CTimeManagement::TimePreEvent(datetime PreEventTime,datetime EventTime) { if(PreEventTime<=TimeTradeServer()&&EventTime>TimeTradeServer()) { return true; } return false; }
Функция ниже вернет MqlDateTime для текущего времени с изменением часов и минут.
//-- Return MqlDateTime for current date time with custom hour and minute MqlDateTime Today(int Hour,int Minute);
//+------------------------------------------------------------------+ //|Return MqlDateTime for current date time with custom hour and | //|minute | //+------------------------------------------------------------------+ MqlDateTime CTimeManagement::Today(int Hour,int Minute) { TimeTradeServer(today); today.hour=Hour; today.min=Minute; return today; }
Функция ниже вернет MqlDateTime для текущего времени с изменением часов, минут и секунд.
//-- Return MqlDateTime for current date time with custom hour, minute and second MqlDateTime Today(int Hour,int Minute,int Second);
//+------------------------------------------------------------------+ //|Return MqlDateTime for current date time with custom hour, minute | //|and second | //+------------------------------------------------------------------+ MqlDateTime CTimeManagement::Today(int Hour,int Minute,int Second) { TimeTradeServer(today); today.hour=Hour; today.min=Minute; today.sec=Second; return today; }
Функция ниже вернет значение true, если текущий день равен соответствующему дню недели или перечисление DayOfTheWeek равно AllDays.
//-- Check current day of the week bool isDayOfTheWeek(DayOfTheWeek Day);
//+------------------------------------------------------------------+ //|Check current day of the week | //+------------------------------------------------------------------+ bool CTimeManagement::isDayOfTheWeek(DayOfTheWeek Day) { switch(Day) { case Monday://Monday if(DayOfWeek(TimeTradeServer())==MONDAY) { return true; } break; case Tuesday://Tuesday if(DayOfWeek(TimeTradeServer())==TUESDAY) { return true; } break; case Wednesday://Wednesday if(DayOfWeek(TimeTradeServer())==WEDNESDAY) { return true; } break; case Thursday://Thursday if(DayOfWeek(TimeTradeServer())==THURSDAY) { return true; } break; case Friday://Friday if(DayOfWeek(TimeTradeServer())==FRIDAY) { return true; } break; case AllDays://All days return true; break; default://Unknown break; } return false; }
Функция ниже вернет день недели для определенной даты.
//-- Return enumeration Day of week for a certain date ENUM_DAY_OF_WEEK DayOfWeek(datetime time);
//+------------------------------------------------------------------+ //|Return enumeration Day of week for a certain date | //+------------------------------------------------------------------+ ENUM_DAY_OF_WEEK CTimeManagement::DayOfWeek(datetime time) { return (ENUM_DAY_OF_WEEK)Time(time).day_of_week; }
Класс Candle Properties
В этот класс добавлена новая функция.
//+------------------------------------------------------------------+ //|CandleProperties class | //+------------------------------------------------------------------+ class CCandleProperties : public CChartProperties { private: CTimeManagement Time;//TimeManagement object CTimeVariables CTV;//Timevariables object public: double Open(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle Open-Price double Close(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle Close-Price double High(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle High-Price double Low(int CandleIndex,ENUM_TIMEFRAMES Period=PERIOD_CURRENT,string SYMBOL=NULL);//Retrieve Candle Low-Price bool IsLargerThanPreviousAndNext(datetime CandleTime,int Offset,string SYMBOL);//Determine if one candle is larger than two others bool NewCandle(int index,ENUM_TIMEFRAMES period=PERIOD_CURRENT,string SYMBOL=NULL);//Check if a new candle is present };
Функция NewCandle вернет true при формировании новой свечи, а затем сохранит время открытия текущей свечи в классе Timevariables с помощью функции SetTime. Ранее сохраненное время будет сравниваться с временем открытия текущей свечи, чтобы проверить, отличается ли время. Если отличается, то мы предполагаем, что сформировалась новая свеча.
//+------------------------------------------------------------------+ //|Check if a new candle is present | //+------------------------------------------------------------------+ bool CCandleProperties::NewCandle(int index,ENUM_TIMEFRAMES period=PERIOD_CURRENT,string SYMBOL=NULL) { if(CTV.GetTime(index) == iTime(((SYMBOL==NULL)?Symbol():SYMBOL),period,0)) { return false;//Candle time are equal no new candles have formed } else { //--- Candle time has changed set the new time CTV.SetTime(index,iTime(((SYMBOL==NULL)?Symbol():SYMBOL),period,0)); return true; } }
Класс Sessions
Целью класса является обработка времени торговой сессии. В этой статье мы не будем использовать время торговой сессии, но этот класс нам пригодится позже. Класс будет наследоваться от TimeManagement, поскольку он будет использовать функции TimeManagement.
#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); };
Функция ниже проверит все действительные торговые сессии для текущего символа и дня недели. Как только будет найдена самая ранняя торговая сессия, мы добавим и выполним смещение к этому времени. Например, если самая ранняя торговая сессия начинается с 01:00-05:00, то наш час смещения равен 1, а минута смещения равна 0, наша торговая сессия начнется в 02:00-05:00. Цель определения времени начала сессии — избежать больших спредов, которые обычно возникают в начале торговой сессии. Если наша торговая сессия в данный момент активна, функция вернет true.
//+------------------------------------------------------------------+ //|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(startoffsetmin)); DateFrom[0] = TimePlusOffset(DateFrom[0],HoursS(startoffsethour)); //--- 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(DateFrom[i],DateTo[i])) { return true; } } } else { //--- If there are no trading session times return true; } return false; }
Функция ниже вернет значение true, если сессия уже закончилась. У некоторых брокеров за час до окончания торговой сессии спреды становятся огромными, поэтому эта функция может помочь нам избежать торговли в это время.
//+------------------------------------------------------------------+ //|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 = 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; }
Функция ниже вернет дату окончания торговой сессии на текущий день. В MQL5 существуют некоторые ограничения по времени торговых сессий. Я заметил, что во время праздников некоторые символы могут закрываться намного раньше, чем указано во времени торговой сессии, так что это следует иметь в виду.
//+------------------------------------------------------------------+ //|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(CTV.ReturnHour(dateto)==00||CTV.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 = 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; }
Класс News
Этот класс является самым важным во всем проекте и самым большим, охватывая более 1000 строк кода. По сравнению со второй частью мы внесли значительные улучшения в этот класс и код, который взаимодействует с базой данных календаря в общей папке. В этой статье мы создадим еще одну базу данных, но на этот раз она будет находиться в памяти.
Преимущества баз данных в памяти по сравнению с хранилищем
- Скорость: базы данных в оперативной памяти хранят данные непосредственно в оперативной памяти, что значительно ускоряет операции чтения и записи. Это особенно полезно для приложений, требующих обработки и аналитики данных в реальном времени. Поскольку оперативная память намного быстрее дискового хранилища, доступ к данным в базе данных в памяти осуществляется быстрее. Это сокращает время отклика и повышает общую производительность.
- Производительность: Сокращение задержки и более быстрый доступ к данным приводят к более высокой пропускной способности, что означает, что база данных может обрабатывать больше транзакций в секунду. Базы данных в памяти могут обрабатывать большие объемы данных более эффективно, что делает их подходящими для приложений больших данных, аналитики и других ресурсоемких вычислительных задач.
Мы по-прежнему будем использовать нашу базу данных в хранилище. Мы будем собирать данные из базы данных в хранилище и переносить эти данные в нашу новую базу данных в памяти для сбалансированной производительности при тестировании на истории, поскольку использование только базы данных в хранилище окажет существенное влияние в зависимости от характеристик вашего компьютера.
Начнем с объявлений вне класса для использования во входных параметрах советника. Целочисленная переменная DBMemoryConnection будет хранить в памяти целочисленный хэндл соединения для нашей базы данных. Перечисление Calendar_Importance будет использоваться для выбора различной важности событий во входном параметре советника. Перечисление Event_Sector будет использоваться для выбора различных секторов событий во входном параметре советника. Перечисление Event_Frequency будет использоваться для выбора различной частоты событий для входного параметра советника. Перечисление Event_Type будет использоваться для выбора различных типов событий для входного параметра советника. Перечисление Event_Currency будет использоваться для выбора различных вариантов валюты события во входном параметре советника. Структурная переменная календаря UpcomingNews будет хранить сведения о следующем экономическом событии для легкого доступа в других классах/файлах. Мы объявили ее вне класса News.
int DBMemoryConnection;//In memory database handle //--- Enumeration for Calendar Importance enum Calendar_Importance { Calendar_Importance_None,//NONE Calendar_Importance_Low,//LOW Calendar_Importance_Moderate,//MODERATE Calendar_Importance_High,//HIGH Calendar_Importance_All//ALL } myImportance; //--- Enumeration for Calendar Sector enum Event_Sector { Event_Sector_None,//NONE Event_Sector_Market,//MARKET Event_Sector_Gdp,//GDP Event_Sector_Jobs,//JOBS Event_Sector_Prices,//PRICES Event_Sector_Money,//MONEY Event_Sector_Trade,//TRADE Event_Sector_Government,//GOVERNMENT Event_Sector_Business,//BUSINESS Event_Sector_Consumer,//CONSUMER Event_Sector_Housing,//HOUSING Event_Sector_Taxes,//TAXES Event_Sector_Holidays,//HOLIDAYS Event_Sector_ALL//ALL } mySector; //--- Enumeration for Calendar Event Frequency enum Event_Frequency { Event_Frequency_None,//NONE Event_Frequency_Week,//WEEK Event_Frequency_Month,//MONTH Event_Frequency_Quarter,//QUARTER Event_Frequency_Year,//YEAR Event_Frequency_Day,//DAY Event_Frequency_ALL//ALL } myFrequency; //--- Enumeration for Calendar Event type enum Event_Type { Event_Type_Event,//EVENT Event_Type_Indicator,//INDICATOR Event_Type_Holiday,//HOLIDAY Event_Type_All//ALL } myType; //--- Enumeration for Calendar Event Currency enum Event_Currency { Event_Currency_Symbol,//SYMBOL CURRENCIES Event_Currency_Margin,//SYMBOL MARGIN Event_Currency_Base,//SYMBOL BASE Event_Currency_Profit,//SYMBOL PROFIT Event_Currency_ALL,//ALL CURRENCIES Event_Currency_NZD_NZ,//NZD -> NZ Event_Currency_EUR_EU,//EUR -> EU Event_Currency_JPY_JP,//JPY -> JP Event_Currency_CAD_CA,//CAD -> CA Event_Currency_AUD_AU,//AUD -> AU Event_Currency_CNY_CN,//CNY -> CN Event_Currency_EUR_IT,//EUR -> IT Event_Currency_SGD_SG,//SGD -> SG Event_Currency_EUR_DE,//EUR -> DE Event_Currency_EUR_FR,//EUR -> FR Event_Currency_BRL_BR,//BRL -> BR Event_Currency_MXN_MX,//MXN -> MX Event_Currency_ZAR_ZA,//ZAR -> ZA Event_Currency_HKD_HK,//HKD -> HK Event_Currency_INR_IN,//INR -> IN Event_Currency_NOK_NO,//NOK -> NO Event_Currency_USD_US,//USD -> US Event_Currency_GBP_GB,//GBP -> GB Event_Currency_CHF_CH,//CHF -> CH Event_Currency_KRW_KR,//KRW -> KW Event_Currency_EUR_ES,//EUR -> ES Event_Currency_SEK_SE,//SEK -> SE Event_Currency_ALL_WW//ALL -> WW } myCurrency; //--- Structure variable to store Calendar next Event data Calendar UpcomingNews;
Дополнительные функциональные возможности класса News:
- Расширение перечисления CalendarComponents: EventInfo_View добавлен для отображения сведений о событиях в базе данных календаря, Currencies_View добавлен для отображения всех валют, доступных в экономическом календаре.
- Структурный массив CalendarContents увеличился в размере с 10 до 12, чтобы вместить два новых представления EventInfo_View и Currencies_View.
- Объявление переменной DBMemory типа структуры MQL5CalendarContents для хранения свойств базы данных.
- Объявление структуры и переменных CalendarData для хранения всех данных из базы данных календаря в общей папке.
- Объявление функции GetCalendar, которая запросит все отсортированные данные из базы данных календаря в общей папке и сохранит эти данные в массиве структуры CalendarData. Данные этого массива будут вставлены в нашу новую базу данных календаря в памяти после ее создания.
- Объявление функции GetAutoDST для извлечения перечисления DST_type из таблицы AutoDST в базе данных Calendar в общей папке.
- Объявление функции Request_Importance для извлечения строкового запроса на важность события на основе перечисления Calendar_Importance.
- Объявление функции Request_Frequency для извлечения строкового запроса на частоту событий на основе перечисления Event_Frequency.
- Объявление функции Request_Sector для извлечения строкового запроса сектора события на основе перечисления Event_Sector.
- Объявление функции Request_Type для извлечения строкового запроса типа события на основе перечисления Event_Type.
- Объявление функции Request_Currency для извлечения строкового запроса валюты события на основе перечисления Event_Currency.
- Объявление функции EconomicDetailsMemory заполнит массив структуры календаря NewsTime событиями на определенную дату.
- Объявление функции CreateEconomicDatabaseMemory создаст базу данных календаря в памяти.
- Объявление функции EconomicNextEvent обновит структурную переменную UpcomingNews данными о следующем событии.
- Объявление функции GetImpact извлекает данные о влиянии предстоящих событий.
- Объявление функции IMPORTANCE преобразует строку Importance в перечисление важности событий календаря.
- Объявление функции IMPORTANCE преобразует перечисление Calendar_Importance Enumeration в перечисление важности событий календаря.
- Объявление функции GetImportance преобразует перечисление важности событий календаря в строковый ранг важности (Importance Rating).
- Объявление функции GetImportance_color извлекает цвет для каждого перечисления важности событий календаря.
- Объявление функции SECTOR преобразует перечисление Event_Sector в перечисление секторов календарных событий.
- Объявление функции FREQUENCY преобразует перечисление Event_Frequency в перечисление частоты событий календаря.
- Объявление функции TYPE преобразует перечисление Event_Type в перечисление типов событий календаря.
//+------------------------------------------------------------------+ //|News class | //+------------------------------------------------------------------+ class CNews : private CCandleProperties { //Private Declarations Only accessable by this class/header file private: //-- To keep track of what is in our database enum CalendarComponents { AutoDST_Table,//AutoDST Table CalendarAU_View,//View for DST_AU CalendarNONE_View,//View for DST_NONE CalendarUK_View,//View for DST_UK CalendarUS_View,//View for DST_US EventInfo_View,//View for Event Information Currencies_View,//View for Currencies Record_Table,// Record Table TimeSchedule_Table,//TimeSchedule Table MQL5Calendar_Table,//MQL5Calendar Table AutoDST_Trigger,//Table Trigger for AutoDST Record_Trigger//Table Trigger for Record }; //-- structure to retrieve all the objects in the database struct SQLiteMaster { string type;//will store object's type string name;//will store object's name string tbl_name;//will store table name int rootpage;//will store rootpage string sql;//Will store the sql create statement } DBContents[];//Array of type SQLiteMaster //-- MQL5CalendarContents inherits from SQLiteMaster structure struct MQL5CalendarContents:SQLiteMaster { CalendarComponents Content; string insert;//Will store the sql insert statement } CalendarContents[12],DBMemory;//Array to Store objects in our database CTimeManagement CTime;//TimeManagement Object declaration CDaylightSavings_UK Savings_UK;//DaylightSavings Object for the UK and EU CDaylightSavings_US Savings_US;//DaylightSavings Object for the US CDaylightSavings_AU Savings_AU;//DaylightSavings Object for the AU bool AutoDetectDST(DST_type &dstType);//Function will determine Broker DST DST_type DSTType;//variable of DST_type enumeration declared in the CommonVariables class/header file bool InsertIntoTables(int db,Calendar &Evalues[]);//Function for inserting Economic Data in to a database's table void CreateAutoDST(int db);//Function for creating and inserting Recommend DST for the Broker into a table bool CreateCalendarTable(int db,bool &tableExists);//Function for creating a table in a database bool CreateTimeTable(int db,bool &tableExists);//Function for creating a table in a database void CreateCalendarViews(int db);//Function for creating views in a database void CreateRecordTable(int db);//Creates a table to store the record of when last the Calendar database was updated/created string DropRequest;//Variable for dropping tables in the database //-- Function for retrieving the MQL5CalendarContents structure for the enumartion type CalendarComponents MQL5CalendarContents CalendarStruct(CalendarComponents Content) { MQL5CalendarContents Calendar; for(uint i=0;i<CalendarContents.Size();i++) { if(CalendarContents[i].Content==Content) { return CalendarContents[i]; } } return Calendar; } //--- To Store Calendar DB Data struct CalendarData { int EventId;//Event Id string Country;//Event Country string EventName;//Event Name string EventType;//Event Type string EventImportance;//Event Importance string EventCurrency;//Event Currency string EventCode;//Event Code string EventSector;//Event Sector string EventForecast;//Event Forecast Value string EventPreval;//Event Previous Value string EventImpact;//Event Impact string EventFrequency;//Event Frequency string DST_UK;//DST UK string DST_US;//DST US string DST_AU;//DST AU string DST_NONE;//DST NONE } DB_Data[],DB_Cal;//Structure variables //--- Will Retrieve all relevant Calendar data for DB in Memory from DB in Storage void GetCalendar(CalendarData &Data[]) { //--- Open calendar DB in Storage int db=DatabaseOpen(NEWS_DATABASE_FILE, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE| DATABASE_OPEN_COMMON); if(db==INVALID_HANDLE)//Checks if the database was able to be opened { //if opening the database failed if(!FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON))//Checks if the database Calendar exists in the common folder { return;//Returns true when the database was failed to be opened and the file doesn't exist in the common folder } } //--- Get filtered calendar DB data string SqlRequest = StringFormat("Select MQ.EventId,MQ.Country,MQ.EventName,MQ.EventType,MQ.EventImportance,MQ.EventCurrency," "MQ.EventCode,MQ.EventSector,MQ.EventForecast,MQ.EventPreValue,MQ.EventImpact,MQ.EventFrequency," "TS.DST_UK,TS.DST_US,TS.DST_AU,TS.DST_NONE from %s MQ " "Inner Join %s TS on TS.ID=MQ.ID " "Where %s and %s and %s and %s and %s;", CalendarStruct(MQL5Calendar_Table).name,CalendarStruct(TimeSchedule_Table).name, Request_Importance(myImportance),Request_Frequency(myFrequency), Request_Sector(mySector),Request_Type(myType),Request_Currency(myCurrency)); //--- Process Sql request int Request = DatabasePrepare(db,SqlRequest); if(Request==INVALID_HANDLE) { //--- Print details if request failed. Print("DB: ",NEWS_DATABASE_FILE, " request failed with code ", GetLastError()); Print("SQL"); Print(SqlRequest); } else { //--- Clear data from whole array ArrayRemove(Data,0,WHOLE_ARRAY); //--- create structure variable to get data from request CalendarData data; //Assigning values from the sql query into Data structure array for(int i=0; DatabaseReadBind(Request,data); i++) { //--- Resize Data Array ArrayResize(Data,i+1,i+2); Data[i] = data; } } DatabaseFinalize(Request);//Finalize request //--- Close Calendar database DatabaseClose(db); } //--- Retrieve the AutoDST enumeration data from calendar DB in storage DST_type GetAutoDST() { string Sch_Dst; //--- open the database 'Calendar' in the common folder int db=DatabaseOpen(NEWS_DATABASE_FILE, DATABASE_OPEN_READONLY|DATABASE_OPEN_COMMON); if(db==INVALID_HANDLE)//Checks if 'Calendar' failed to be opened { if(!FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON))//Checks if 'Calendar' database exists { Print("Could not find Database!"); return DST_NONE;//return default value when failed. } } //--- Sql query to get AutoDST value string request_text="SELECT DST FROM 'AutoDST'"; //--- Process sql request int request=DatabasePrepare(db,request_text); if(request==INVALID_HANDLE) { Print("DB: ",NEWS_DATABASE_FILE, " request failed with code ", GetLastError()); DatabaseClose(db);//Close Database return DST_NONE;//return default value when failed. } //--- Read Sql request output data if(DatabaseRead(request)) { //-- Store the first column data into string variable Sch_Dst if(!DatabaseColumnText(request,0,Sch_Dst)) { Print("DatabaseRead() failed with code ", GetLastError()); DatabaseFinalize(request);//Finalize request DatabaseClose(db);//Closes the database 'Calendar' return DST_NONE;//return default value when failed. } } DatabaseFinalize(request);//Finalize request DatabaseClose(db);//Closes the database 'Calendar' return (Sch_Dst=="DST_UK")?DST_UK:(Sch_Dst=="DST_US")?DST_US: (Sch_Dst=="DST_AU")?DST_AU:DST_NONE;//Returns the enumeration value for each corresponding string } //--- Retrieve Sql request string for calendar event Importance string Request_Importance(Calendar_Importance Importance) { //--- Constant request prefix string const string constant="MQ.EventImportance"; //--- switch statement for Calendar_Importance enumeration switch(Importance) { case Calendar_Importance_All://String Request for all event Importance return constant+"<>'"+EnumToString(myImportance)+"'"; break; default://String Request for any event Importance return constant+"='"+EnumToString(IMPORTANCE(myImportance))+"'"; break; } } //--- Retrieve Sql request string for calendar event Frequency string Request_Frequency(Event_Frequency Frequency) { //--- Constant request prefix string const string constant="MQ.EventFrequency"; //--- switch statement for Event_Frequency enumeration switch(Frequency) { case Event_Frequency_ALL://String Request for all event frequencies return constant+"<>'"+EnumToString(myFrequency)+"'"; break; default://String Request for any event frequency return constant+"='"+EnumToString(FREQUENCY(myFrequency))+"'"; break; } } //--- Retrieve Sql request string for calendar event Sector string Request_Sector(Event_Sector Sector) { //--- Constant request prefix string const string constant="MQ.EventSector"; //--- switch statement for Event_Sector enumeration switch(Sector) { case Event_Sector_ALL://String Request for all event sectors return constant+"<>'"+EnumToString(mySector)+"'"; break; default://String Request for any event sector return constant+"='"+EnumToString(SECTOR(mySector))+"'"; break; } } //--- Retrieve Sql request string for calendar event type string Request_Type(Event_Type Type) { //--- Constant request prefix string const string constant="MQ.EventType"; //--- switch statement for Event_Type enumeration switch(Type) { case Event_Type_All://String Request for all event types return constant+"<>'"+EnumToString(myType)+"'"; break; default://String request for any event type return constant+"='"+EnumToString(TYPE(myType))+"'"; break; } } //--- Retrieve Sql request string for calendar event Currency string Request_Currency(Event_Currency Currency) { //--- Constant request prefix string and request suffix const string constant_prefix="(MQ.EventCurrency",constant_suffix="')"; //--- switch statement for Event_Currency enumeration switch(Currency) { case Event_Currency_ALL://String Request for all currencies return constant_prefix+"<>'"+EnumToString(myCurrency)+constant_suffix; break; case Event_Currency_Symbol://String Request for all symbol currencies return constant_prefix+"='"+CSymbol.CurrencyBase()+"' or MQ.EventCurrency='"+ CSymbol.CurrencyMargin()+"' or MQ.EventCurrency='"+CSymbol.CurrencyProfit()+constant_suffix; break; case Event_Currency_Margin://String Request for Margin currency return constant_prefix+"='"+CSymbol.CurrencyMargin()+constant_suffix; break; case Event_Currency_Base://String Request for Base currency return constant_prefix+"='"+CSymbol.CurrencyBase()+constant_suffix; break; case Event_Currency_Profit://String Request for Profit currency return constant_prefix+"='"+CSymbol.CurrencyProfit()+constant_suffix; break; case Event_Currency_NZD_NZ://String Request for NZD currency return constant_prefix+"='NZD' and MQ.EventCode='NZ"+constant_suffix; break; case Event_Currency_EUR_EU://String Request for EUR currency and EU code return constant_prefix+"='EUR' and MQ.EventCode='EU"+constant_suffix; break; case Event_Currency_JPY_JP://String Request for JPY currency return constant_prefix+"='JPY' and MQ.EventCode='JP"+constant_suffix; break; case Event_Currency_CAD_CA://String Request for CAD currency return constant_prefix+"='CAD' and MQ.EventCode='CA"+constant_suffix; break; case Event_Currency_AUD_AU://String Request for AUD currency return constant_prefix+"='AUD' and MQ.EventCode='AU"+constant_suffix; break; case Event_Currency_CNY_CN://String Request for CNY currency return constant_prefix+"='CNY' and MQ.EventCode='CN"+constant_suffix; break; case Event_Currency_EUR_IT://String Request for EUR currency and IT code return constant_prefix+"='EUR' and MQ.EventCode='IT"+constant_suffix; break; case Event_Currency_SGD_SG://String Request for SGD currency return constant_prefix+"='SGD' and MQ.EventCode='SG"+constant_suffix; break; case Event_Currency_EUR_DE://String Request for EUR currency and DE code return constant_prefix+"='EUR' and MQ.EventCode='DE"+constant_suffix; break; case Event_Currency_EUR_FR://String Request for EUR currency and FR code return constant_prefix+"='EUR' and MQ.EventCode='FR"+constant_suffix; break; case Event_Currency_BRL_BR://String Request for BRL currency return constant_prefix+"='BRL' and MQ.EventCode='BR"+constant_suffix; break; case Event_Currency_MXN_MX://String Request for MXN currency return constant_prefix+"='MXN' and MQ.EventCode='MX"+constant_suffix; break; case Event_Currency_ZAR_ZA://String Request for ZAR currency return constant_prefix+"='ZAR' and MQ.EventCode='ZA"+constant_suffix; break; case Event_Currency_HKD_HK://String Request for HKD currency return constant_prefix+"='HKD' and MQ.EventCode='HK"+constant_suffix; break; case Event_Currency_INR_IN://String Request for INR currency return constant_prefix+"='INR' and MQ.EventCode='IN"+constant_suffix; break; case Event_Currency_NOK_NO://String Request for NOK currency return constant_prefix+"='NOK' and MQ.EventCode='NO"+constant_suffix; break; case Event_Currency_USD_US://String Request for USD currency return constant_prefix+"='USD' and MQ.EventCode='US"+constant_suffix; break; case Event_Currency_GBP_GB://String Request for GBP currency return constant_prefix+"='GBP' and MQ.EventCode='GB"+constant_suffix; break; case Event_Currency_CHF_CH://String Request for CHF currency return constant_prefix+"='CHF' and MQ.EventCode='CH"+constant_suffix; break; case Event_Currency_KRW_KR://String Request for KRW currency return constant_prefix+"='KRW' and MQ.EventCode='KR"+constant_suffix; break; case Event_Currency_EUR_ES://String Request for EUR currency and ES code return constant_prefix+"='EUR' and MQ.EventCode='ES"+constant_suffix; break; case Event_Currency_SEK_SE://String Request for SEK currency return constant_prefix+"='SEK' and MQ.EventCode='SE"+constant_suffix; break; case Event_Currency_ALL_WW://String Request for ALL currency return constant_prefix+"='ALL' and MQ.EventCode='WW"+constant_suffix; break; default://String Request for no currencies return constant_prefix+"='"+constant_suffix; break; } } //Public declarations accessable via a class's Object public: CNews(void);//Constructor ~CNews(void);//Destructor void CreateEconomicDatabase();//Creates the Calendar database for a specific Broker datetime GetLatestNewsDate();//Gets the latest/newest date in the Calendar database void EconomicDetails(Calendar &NewsTime[],datetime date_from=0,datetime date_to=0);//Gets values from the MQL5 economic Calendar void EconomicDetailsMemory(Calendar &NewsTime[],datetime date);//Gets values from the MQL5 DB Calendar in Memory void CreateEconomicDatabaseMemory();//Create calendar database in memory void EconomicNextEvent(datetime date=0);//Will update UpcomingNews structure variable with the next event data bool UpdateRecords();//Checks if the main Calendar database needs an update or not ENUM_CALENDAR_EVENT_IMPACT GetImpact();//Will retrieve Upcoming Event Impact data //--- Convert Importance string into Calendar Event Importance Enumeration ENUM_CALENDAR_EVENT_IMPORTANCE IMPORTANCE(string Importance) { //--- Calendar Importance is High if(Importance==EnumToString(CALENDAR_IMPORTANCE_HIGH)) { return CALENDAR_IMPORTANCE_HIGH; } else //--- Calendar Importance is Moderate if(Importance==EnumToString(CALENDAR_IMPORTANCE_MODERATE)) { return CALENDAR_IMPORTANCE_MODERATE; } else //--- Calendar Importance is Low if(Importance==EnumToString(CALENDAR_IMPORTANCE_LOW)) { return CALENDAR_IMPORTANCE_LOW; } else //--- Calendar Importance is None { return CALENDAR_IMPORTANCE_NONE; } } //--- Convert Calendar_Importance Enumeration into Calendar Event Importance Enumeration ENUM_CALENDAR_EVENT_IMPORTANCE IMPORTANCE(Calendar_Importance Importance) { //--- switch statement for Calendar_Importance enumeration switch(Importance) { case Calendar_Importance_None://None return CALENDAR_IMPORTANCE_NONE; break; case Calendar_Importance_Low://Low return CALENDAR_IMPORTANCE_LOW; break; case Calendar_Importance_Moderate://Moderate return CALENDAR_IMPORTANCE_MODERATE; break; case Calendar_Importance_High://High return CALENDAR_IMPORTANCE_HIGH; break; default://None return CALENDAR_IMPORTANCE_NONE; break; } } //--- Convert Calendar Event Importance Enumeration into string Importance Rating string GetImportance(ENUM_CALENDAR_EVENT_IMPORTANCE Importance) { //--- switch statement for ENUM_CALENDAR_EVENT_IMPORTANCE enumeration switch(Importance) { case CALENDAR_IMPORTANCE_HIGH://High return "HIGH"; break; case CALENDAR_IMPORTANCE_MODERATE://Moderate return "MODERATE"; break; case CALENDAR_IMPORTANCE_LOW://Low return "LOW"; break; default://None return "NONE"; break; } } //--- Retrieve color for each Calendar Event Importance Enumeration color GetImportance_color(ENUM_CALENDAR_EVENT_IMPORTANCE Importance) { //--- switch statement for ENUM_CALENDAR_EVENT_IMPORTANCE enumeration switch(Importance) { case CALENDAR_IMPORTANCE_HIGH://High return clrRed; break; case CALENDAR_IMPORTANCE_MODERATE://Moderate return clrOrange; break; case CALENDAR_IMPORTANCE_LOW://Low return (isLightMode)?clrBlue:clrLightBlue; break; default://None return (isLightMode)?clrBlack:clrWheat; break; } } //--- Convert Event_Sector Enumeration into Calendar Event Sector Enumeration ENUM_CALENDAR_EVENT_SECTOR SECTOR(Event_Sector Sector) { //--- switch statement for Event_Sector enumeration switch(Sector) { case Event_Sector_None://NONE return CALENDAR_SECTOR_NONE; break; case Event_Sector_Market://MARKET return CALENDAR_SECTOR_MARKET; break; case Event_Sector_Gdp://GDP return CALENDAR_SECTOR_GDP; break; case Event_Sector_Jobs://JOBS return CALENDAR_SECTOR_JOBS; break; case Event_Sector_Prices://PRICES return CALENDAR_SECTOR_PRICES; break; case Event_Sector_Money://MONEY return CALENDAR_SECTOR_MONEY; break; case Event_Sector_Trade://TRADE return CALENDAR_SECTOR_TRADE; break; case Event_Sector_Government://GOVERNMENT return CALENDAR_SECTOR_GOVERNMENT; break; case Event_Sector_Business://BUSINESS return CALENDAR_SECTOR_BUSINESS; break; case Event_Sector_Consumer://CONSUMER return CALENDAR_SECTOR_CONSUMER; break; case Event_Sector_Housing://HOUSING return CALENDAR_SECTOR_HOUSING; break; case Event_Sector_Taxes://TAXES return CALENDAR_SECTOR_TAXES; break; case Event_Sector_Holidays://HOLIDAYS return CALENDAR_SECTOR_HOLIDAYS; break; default://Unknown return CALENDAR_SECTOR_NONE; break; } } //--- Convert Event_Frequency Enumeration into Calendar Event Frequency Enumeration ENUM_CALENDAR_EVENT_FREQUENCY FREQUENCY(Event_Frequency Frequency) { //--- switch statement for Event_Frequency enumeration switch(Frequency) { case Event_Frequency_None://NONE return CALENDAR_FREQUENCY_NONE; break; case Event_Frequency_Day://DAY return CALENDAR_FREQUENCY_DAY; break; case Event_Frequency_Week://WEEK return CALENDAR_FREQUENCY_WEEK; break; case Event_Frequency_Month://MONTH return CALENDAR_FREQUENCY_MONTH; break; case Event_Frequency_Quarter://QUARTER return CALENDAR_FREQUENCY_QUARTER; break; case Event_Frequency_Year://YEAR return CALENDAR_FREQUENCY_YEAR; break; default://Unknown return CALENDAR_FREQUENCY_NONE; break; } } //--- Convert Event_Type Enumeration into Calendar Event Type Enumeration ENUM_CALENDAR_EVENT_TYPE TYPE(Event_Type Type) { //--- switch statement for Event_Type enumeration switch(Type) { case Event_Type_Event://EVENT return CALENDAR_TYPE_EVENT; break; case Event_Type_Indicator://INDICATOR return CALENDAR_TYPE_INDICATOR; break; case Event_Type_Holiday://HOLIDAY return CALENDAR_TYPE_HOLIDAY; break; default://Unknown return CALENDAR_TYPE_EVENT; break; } } };
Сначала мы рассмотрим оператор SQL в функции GetCalendar, который запросит все данные из базы данных календаря в общей папке для нашей базы данных календаря в памяти. В этом запросе мы выбираем все столбцы в таблицах MQL5Calendar и TimeSchedule и объединяем таблицы, у которых идентификаторы совпадают. Затем мы сортируем данные на основе перечислений, выбранных трейдером во входных параметрах советника для настроек новостей.
//--- Get filtered calendar DB data string SqlRequest = StringFormat("Select MQ.EventId,MQ.Country,MQ.EventName,MQ.EventType,MQ.EventImportance,MQ.EventCurrency," "MQ.EventCode,MQ.EventSector,MQ.EventForecast,MQ.EventPreValue,MQ.EventImpact,MQ.EventFrequency," "TS.DST_UK,TS.DST_US,TS.DST_AU,TS.DST_NONE from %s MQ " "Inner Join %s TS on TS.ID=MQ.ID " "Where %s and %s and %s and %s and %s;", CalendarStruct(MQL5Calendar_Table).name,CalendarStruct(TimeSchedule_Table).name, Request_Importance(myImportance),Request_Frequency(myFrequency), Request_Sector(mySector),Request_Type(myType),Request_Currency(myCurrency));
Мы рассмотрим SQL-запрос для следующей конфигурации настроек новостей по символу EURUSD.
Как показано ниже, когда мы выбираем всё для важности календаря, мы намеренно не преобразуем нашу переменную myImportance перечисления Calendar_Importance, поскольку нет ни одного уровня важности событий со значением Calendar_Importance_All. Таким образом мы можем легко выбрать все события, у которых важность события не равна Calendar_Importance_All. То же самое можно сказать и обо всех входных параметрах настроек новостей, для которых выбрано значение ALL (все).
case Calendar_Importance_All://String Request for all event Importance return constant+"<>'"+EnumToString(myImportance)+"'";
Select MQ.EventId,MQ.Country,MQ.EventName,MQ.EventType,MQ.EventImportance,MQ.EventCurrency,MQ.EventCode,MQ.EventSector, MQ.EventForecast,MQ.EventPreValue,MQ.EventImpact,MQ.EventFrequency,TS.DST_UK,TS.DST_US,TS.DST_AU,TS.DST_NONE from MQL5Calendar MQ Inner Join TimeSchedule TS on TS.ID=MQ.ID Where MQ.EventImportance<>'Calendar_Importance_All' and MQ.EventFrequency<>'Event_Frequency_ALL' and MQ.EventSector<>'Event_Sector_ALL' and MQ.EventType<>'Event_Type_All' and (MQ.EventCurrency='EUR' or MQ.EventCurrency='EUR' or MQ.EventCurrency='USD');
Рассмотрим еще раз один SQL-запрос из функции GetCalendar для следующей конфигурации настроек новостей по символу EURUSD.
Как показано ниже, когда мы выбираем любой другой параметр, отличный от "все" для важности календаря, мы преобразуем нашу переменную myImportance перечисления Calendar_Importance в тип перечисления ENUM_CALENDAR_EVENT_IMPORTANCE, чтобы строка могла совпадать со строкой, хранящейся в таблице MQL5Calendar, для корректного получения важности события определенного типа.
default://String Request for any event Importance return constant+"='"+EnumToString(IMPORTANCE(myImportance))+"'";
Select MQ.EventId,MQ.Country,MQ.EventName,MQ.EventType,MQ.EventImportance,MQ.EventCurrency,MQ.EventCode,MQ.EventSector,MQ.EventForecast, MQ.EventPreValue,MQ.EventImpact,MQ.EventFrequency,TS.DST_UK,TS.DST_US,TS.DST_AU,TS.DST_NONE from MQL5Calendar MQ Inner Join TimeSchedule TS on TS.ID=MQ.ID Where MQ.EventImportance='CALENDAR_IMPORTANCE_HIGH' and MQ.EventFrequency='CALENDAR_FREQUENCY_MONTH' and MQ.EventSector='CALENDAR_SECTOR_JOBS' and MQ.EventType='CALENDAR_TYPE_INDICATOR' and (MQ.EventCurrency<>'Event_Currency_ALL');
В конструкторе класса новостей нам необходимо инициализировать индексы массива для новых представлений нашей базы данных календаря в общей папке. Для представления информации о событии мы инициализируем индекс массива ниже следующим образом.
//--- initializing properties for the EventInfo view CalendarContents[5].Content = EventInfo_View; CalendarContents[5].name = "Event Info"; CalendarContents[5].sql = "CREATE VIEW IF NOT EXISTS 'Event Info' " "AS SELECT EVENTID as 'ID',COUNTRY as 'Country',EVENTNAME as 'Name'," "REPLACE(EVENTTYPE,'CALENDAR_TYPE_','') as 'Type',REPLACE(EVENTSECTOR,'CALENDAR_SECTOR_','') as 'Sector'," "REPLACE(EVENTIMPORTANCE,'CALENDAR_IMPORTANCE_','') as 'Importance',EVENTCURRENCY as 'Currency' " "FROM MQL5Calendar GROUP BY \"Name\" ORDER BY \"Country\" Asc," "CASE \"Importance\" WHEN 'HIGH' THEN 1 WHEN 'MODERATE' THEN 2 WHEN 'LOW' THEN 3 ELSE 4 END,\"Sector\" Desc;"; CalendarContents[5].tbl_name = "Event Info"; CalendarContents[5].type = "view";
Давайте рассмотрим оператор SQL для создания представления информации о событии (Event Info). Во-первых, мы создаем представление только в том случае, если оно еще не существует, затем выбираем столбец EVENTID и переименовываем его в ID, COUNTRY переименовываем в Country, EVENTNAME переименовываем в Name, в EVENTTYPE заменяем текст CALENDAR_TYPE_ пустой строкой и переименовываем столбец в Type, в EVENTSECTOR заменяем текст CALENDAR_SECTOR_ пустой строкой и переименовываем столбец в Sector, в EVENTIMPORTANCE заменяем текст CALENDAR_IMPORTANCE_ пустой строкой и переименовываем столбец в Importance, EVENTCURRENCY переименовываем в Currency из таблицы MQL5Calendar. Затем мы группируем запрос по EVENTNAME, который теперь называется Name, чтобы события с одинаковым именем не отображались в представлении несколько раз. Затем мы задаем запросу последовательность: сначала мы сортируем результат по значению Country (страна) в порядке возрастания, так что страна с начальной буквой А, например, Австралия, будет показана первой, затем мы сортируем результат по значению EVENTIMPORTANCE, которое теперь называется Importance для отображения событий с самой высокой важностью. Для этого нам нужно задать строковым значениям важности ранжирование. В этом случае, значение HIGH (высокая) имеет наивысший приоритет, значение MODERATE (средний) - средний приоритет, значение LOW (низкий) - низкий приоритет, и, наконец, любое другое значение имеет последний приоритет. Далее мы сортируем результаты запроса по EVENTSECTOR, который теперь называется Sector, в порядке убывания. Пример представления будет показан ниже.
CREATE VIEW IF NOT EXISTS 'Event Info' AS SELECT MQ.EVENTID as 'ID',MQ.COUNTRY as 'Country',MQ.EVENTNAME as 'Name',REPLACE(MQ.EVENTTYPE, 'CALENDAR_TYPE_','') as 'Type',REPLACE(MQ.EVENTSECTOR,'CALENDAR_SECTOR_','') as 'Sector',REPLACE(MQ.EVENTIMPORTANCE,'CALENDAR_IMPORTANCE_','') as 'Importance',MQ.EVENTCURRENCY as 'Currency' FROM MQL5Calendar MQ INNER JOIN TimeSchedule TS on TS.ID=MQ.ID GROUP BY "Name" ORDER BY "Country" Asc,CASE "Importance" WHEN 'HIGH' THEN 1 WHEN 'MODERATE' THEN 2 WHEN 'LOW' THEN 3 ELSE 4 END,"Sector" Desc;
ID Country Name Type Sector Importance Currency 36030006 Australia RBA Governor Lowe Speech EVENT MONEY HIGH AUD 36030008 Australia RBA Interest Rate Decision INDICATOR MONEY HIGH AUD 36010029 Australia PPI q/q INDICATOR PRICES MODERATE AUD 36030014 Australia RBA Trimmed Mean CPI q/q INDICATOR PRICES MODERATE AUD 36030009 Australia RBA Weighted Median CPI q/q INDICATOR PRICES MODERATE AUD 36010031 Australia Wage Price Index q/q INDICATOR PRICES MODERATE AUD 36030026 Australia RBA Assistant Governor Boulton Speech EVENT MONEY MODERATE AUD 36030024 Australia RBA Assistant Governor Bullock Speech EVENT MONEY MODERATE AUD 36030025 Australia RBA Assistant Governor Ellis Speech EVENT MONEY MODERATE AUD 62 lines later... 76020002 Brazil BCB Interest Rate Decision INDICATOR MONEY HIGH BRL 76020004 Brazil BCB Inflation Report EVENT PRICES MODERATE BRL 76050001 Brazil FIPE CPI m/m INDICATOR PRICES MODERATE BRL 76010005 Brazil Mid-Month CPI m/m INDICATOR PRICES MODERATE BRL 76020001 Brazil BCB Focus Market Report EVENT MONEY MODERATE BRL 76020003 Brazil BCB MPC (Copom) Minutes EVENT MONEY MODERATE BRL 76020005 Brazil BCB National Monetary Council Meeting EVENT MONEY MODERATE BRL 76010009 Brazil Unemployment Rate 3-months INDICATOR JOBS MODERATE BRL 76020010 Brazil Nominal Budget Balance INDICATOR GOVERNMENT MODERATE BRL 76020011 Brazil Primary Budget balance INDICATOR GOVERNMENT MODERATE BRL 76010014 Brazil Services Volume m/m INDICATOR BUSINESS MODERATE BRL 98 lines later... 124040017 Canada BoC Governor Macklem Speech EVENT MONEY HIGH CAD 124040003 Canada BoC Governor Poloz Speech EVENT MONEY HIGH CAD 124040006 Canada BoC Interest Rate Decision INDICATOR MONEY HIGH CAD 124040009 Canada BoC Monetary Policy Report Press Conference EVENT MONEY HIGH CAD 124010011 Canada Employment Change INDICATOR JOBS HIGH CAD 124010021 Canada GDP m/m INDICATOR GDP HIGH CAD 124010008 Canada Core Retail Sales m/m INDICATOR CONSUMER HIGH CAD 124020001 Canada Ivey PMI INDICATOR BUSINESS HIGH CAD 124010024 Canada IPPI m/m INDICATOR PRICES MODERATE CAD 124010026 Canada RMPI m/m INDICATOR PRICES MODERATE CAD 124040001 Canada BoC Business Outlook Survey EVENT MONEY MODERATE CAD
Для нашего представления валют мы выбираем уникальные значения EventCurrency и EventCode из таблицы MQL5Calendar.
//--- initializing properties for the Currencies view CalendarContents[6].Content = Currencies_View; CalendarContents[6].name = "Currencies"; CalendarContents[6].sql = "CREATE VIEW IF NOT EXISTS Currencies AS " "SELECT Distinct EventCurrency as 'Currency',EventCode as 'Code' FROM 'MQL5Calendar';"; CalendarContents[6].tbl_name = "Currencies"; CalendarContents[6].type = "view";
SELECT * FROM 'Currencies';
Currency Code NZD NZ EUR EU JPY JP CAD CA AUD AU CNY CN EUR IT SGD SG EUR DE EUR FR BRL BR MXN MX ZAR ZA HKD HK INR IN NOK NO USD US GBP GB CHF CH KRW KR EUR ES SEK SE ALL WW
Теперь инициализируем свойства нашей таблицы MQL5Calendar, которая будет находиться в нашей базе данных в памяти. Эта таблица будет представлять собой одну большую таблицу, которая по сути является комбинацией таблиц в нашей базе данных в хранилище. Это таблицы MQL5Calendar и TimeSchedule.
//-- initializing properties for the MQL5Calendar table for DB in System Memory DBMemory.Content = MQL5Calendar_Table; DBMemory.name = "MQL5Calendar"; DBMemory.sql = "CREATE TABLE IF NOT EXISTS MQL5Calendar(EVENTID INT NOT NULL,COUNTRY TEXT NOT NULL," "EVENTNAME TEXT NOT NULL,EVENTTYPE TEXT NOT NULL,EVENTIMPORTANCE TEXT NOT NULL," "EVENTCURRENCY TEXT NOT NULL,EVENTCODE TEXT NOT NULL,EVENTSECTOR TEXT NOT NULL," "EVENTFORECAST TEXT NOT NULL,EVENTPREVALUE TEXT NOT NULL,EVENTIMPACT TEXT NOT NULL," "EVENTFREQUENCY TEXT NOT NULL,DST_UK TEXT NOT NULL,DST_US TEXT NOT NULL," "DST_AU TEXT NOT NULL,DST_NONE TEXT NOT NULL)STRICT;"; DBMemory.tbl_name="MQL5Calendar"; DBMemory.type = "table"; DBMemory.insert = "INSERT INTO 'MQL5Calendar'(EVENTID,COUNTRY,EVENTNAME,EVENTTYPE,EVENTIMPORTANCE,EVENTCURRENCY,EVENTCODE," "EVENTSECTOR,EVENTFORECAST,EVENTPREVALUE,EVENTIMPACT,EVENTFREQUENCY,DST_UK,DST_US,DST_AU,DST_NONE) " "VALUES (%d,'%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s', '%s', '%s');";
В нашем хэндле мы закрываем соединение с базой данных в памяти. Это приводит к удалению всей базы данных. Поэтому мы закрываем соединение только тогда, когда база данных нам больше не нужна.
//+------------------------------------------------------------------+ //|Destructor | //+------------------------------------------------------------------+ CNews::~CNews(void) { if(FileIsExist(NEWS_TEXT_FILE,FILE_COMMON))//Check if the news database open text file exists { FileDelete(NEWS_TEXT_FILE,FILE_COMMON); } DatabaseClose(DBMemoryConnection);//Close DB in memory }
Ранее в нашей функции UpdateRecords мы выполнили последовательность действий, чтобы проверить, следует ли обновить базу данных календаря в хранилище. Последовательность была следующей:
- Проверяем, существует ли база данных календаря в общей папке. Если базы данных не существует, мы выполняем обновление.
- Проверяем, существуют ли все объекты в базе данных и соответствуют ли их SQL-операторы нашим ожиданиям. Если нет, выполняем обновление.
- Проверяем, совпадает ли дата в таблице Records с текущей датой. Если нет, обновляем.
В этой статье мы добавим еще один шаг в последовательность. Цель этого дополнительного шага — проверить точность новостных данных в представлении Calendar_NONE. Я заметил, что иногда новостные данные из календаря MQL5 могут со временем меняться, и если мы сохранили новости, которые не изменились, нам нужно иметь возможность проверить наличие несоответствий между тем, что мы сохранили, и тем, что было обновлено из календаря MQL5, если таковые имеются.
В приведенном ниже коде мы извлекаем новостные данные за текущий день с помощью функции EconomicDetails и сохраняем эти данные в массиве TodayNews. Как только новостные данные окажутся в массиве TodayNews, мы просматриваем каждое новостное событие в массиве и проверяем, есть ли совпадение в нашем представлении Calendar_NONE. Если совпадения не найдено, выполняем обновление. Если все данные новостей совпадают в представлении Calendar_NONE, мы не выполняем никаких обновлений в базе данных в хранилище.
Calendar TodaysNews[]; datetime Today = CTime.Time(TimeTradeServer(),0,0,0); EconomicDetails(TodaysNews,Today,Today+CTime.DaysS()); for(uint i=0;i<TodaysNews.Size();i++) { request_text=StringFormat("SELECT ID FROM %s where Replace(Date,'.','-')=Replace('%s','.','-') and ID=%d;", CalendarStruct(CalendarNONE_View).name,TodaysNews[i].EventDate,TodaysNews[i].EventId); request=DatabasePrepare(db,request_text);//Creates a handle of a request, which can then be executed using DatabaseRead() if(request==INVALID_HANDLE)//Checks if the request failed to be completed { Print("DB: ",NEWS_DATABASE_FILE, " request failed with code ", GetLastError()); DatabaseFinalize(request); DatabaseClose(db); return perform_update; } //PrintFormat(request_text); if(!DatabaseRead(request))//Will be true if there are results from the sql query/request { DatabaseFinalize(request); DatabaseClose(db); return perform_update; } DatabaseFinalize(request); } DatabaseClose(db);//Closes the database perform_update=false; return perform_update;
Приведенный ниже код будет отвечать за создание базы данных в памяти. Сначала мы открываем соединение с базой данных, затем удаляем таблицу MQL5Calendar, если она уже существует, затем создаем таблицу MQL5Calendar. После создания таблицы мы получаем все соответствующие данные из функции GetCalendar, затем вставляем все данные, полученные из функции, в нашу таблицу MQL5Calendar в нашей базе данных в памяти. Далее мы очищаем весь массив DB_Data и устанавливаем расписание перехода на летнее время в переменной MySchedule. При торговле вне тестера стратегий пользователь не сможет вручную изменить расписание летнего времени. Это сделано для того, чтобы предотвратить настройку неправильного расписания летнего времени, поскольку она необходима только для тестера стратегий.
//+------------------------------------------------------------------+ //|Create calendar database in memory | //+------------------------------------------------------------------+ void CNews::CreateEconomicDatabaseMemory() { //--- Open/create the database in memory DBMemoryConnection=DatabaseOpen(NEWS_DATABASE_MEMORY,DATABASE_OPEN_MEMORY); if(DBMemoryConnection==INVALID_HANDLE)//Checks if the database failed to open/create { Print("DB: ",NEWS_DATABASE_MEMORY, " open failed with code ", GetLastError()); return;//will terminate execution of the rest of the code below } //--- Drop the table if it already exists DatabaseExecute(DBMemoryConnection,StringFormat("Drop table IF EXISTS %s",DBMemory.name)); //--- Attempt to create the table if(!DatabaseExecute(DBMemoryConnection,DBMemory.sql)) { Print("DB: create the Calendar table failed with code ", GetLastError()); return; } //--- Check if the table exists if(DatabaseTableExists(DBMemoryConnection,DBMemory.tbl_name)) { //--- Get all news data and time from the database in storage GetCalendar(DB_Data); //--- Insert all the news data and times into the table for(uint i=0;i<DB_Data.Size();i++) { string request_text=StringFormat(DBMemory.insert,DB_Data[i].EventId,DB_Data[i].Country, DB_Data[i].EventName,DB_Data[i].EventType,DB_Data[i].EventImportance, DB_Data[i].EventCurrency,DB_Data[i].EventCode,DB_Data[i].EventSector, DB_Data[i].EventForecast,DB_Data[i].EventPreval,DB_Data[i].EventImpact, DB_Data[i].EventFrequency,DB_Data[i].DST_UK,DB_Data[i].DST_US, DB_Data[i].DST_AU,DB_Data[i].DST_NONE); if(!DatabaseExecute(DBMemoryConnection, request_text))//Will attempt to run this sql request/query { //--- failed to run sql request/query Print(GetLastError()); PrintFormat(request_text); return; } } } //--- Remove all data from the array ArrayRemove(DB_Data,0,WHOLE_ARRAY); //--- Assign the DST schedule MySchedule = (MQLInfoInteger(MQL_TESTER))?(MyDST==AutoDst_Selection)?GetAutoDST():MySchedule:DST_NONE; }
Функция EconomicDetailsMemory извлекает все новостные события, которые происходят в определенную дату, а затем сохраняет новостные данные в массиве NewsTime, который передается по ссылке.
//+------------------------------------------------------------------+ //|Gets values from the MQL5 DB Calendar in Memory | //+------------------------------------------------------------------+ void CNews::EconomicDetailsMemory(Calendar &NewsTime[],datetime date) { //--- SQL query to retrieve news data for a certain date string request_text=StringFormat("WITH MySubQuery AS (SELECT EventId as 'Id',Country,EventName as 'Name',EventType as 'Type'" ",EventImportance as 'Importance',%s as 'CTime',EventCurrency as 'Currency',EventCode as 'Code'," "EventSector as 'Sector',EventForecast as 'Forecast',EventPrevalue as 'Prevalue',EventImpact as'Impact'," "EventFrequency as 'Freq',RANK() OVER (PARTITION BY %s Order BY CASE EventPrevalue WHEN 'None' " "THEN 2 ELSE 1 END,CASE EventForecast WHEN 'None' THEN 2 ELSE 1 END,CASE EventImportance WHEN " "'CALENDAR_IMPORTANCE_HIGH' THEN 1 WHEN 'CALENDAR_IMPORTANCE_MODERATE' THEN 2 WHEN 'CALENDAR_IMPORTANCE_LOW'" " THEN 3 ELSE 4 END) Ranking FROM %s) SELECT Id,Country,Name,Type,Importance,CTime,Currency,Code,Sector," "Forecast,Prevalue,Impact,Freq FROM MySubQuery where Date(Replace(CTime,'.','-'))=Date(Replace('%s','.','-')) and " "Ranking<2 Group by CTime;",EnumToString(MySchedule),EnumToString(MySchedule),DBMemory.name, TimeToString(date)); int request=DatabasePrepare(DBMemoryConnection,request_text);//Creates a handle of a request, which can then be executed using DatabaseRead() if(request==INVALID_HANDLE)//Checks if the request failed to be completed { Print("DB: ",NEWS_DATABASE_MEMORY, " request failed with code ", GetLastError()); PrintFormat(request_text); } //--- Calendar structure variable Calendar ReadDB_Data; //--- Remove any data in the array ArrayRemove(NewsTime,0,WHOLE_ARRAY); for(int i=0; DatabaseReadBind(request,ReadDB_Data); i++)//Will read all the results from the sql query/request { //--- Resize array NewsTime ArrayResize(NewsTime,i+1,i+2); //--- Assign calendar structure values into NewsTime array index NewsTime[i] = ReadDB_Data; } //--- Removes a request created in DatabasePrepare() DatabaseFinalize(request); }
Давайте разберем SQL-запрос, так как он сложнее любого из наших предыдущих запросов. В этом запросе мы используем выражение WITH и функцию RANK().
Что такое выражение WITH и как оно работает?
Выражение WITH, также известное как Common Table Expression (CTE, общее табличное выражение), используется для определения временных наборов результатов, на которые можно ссылаться в операторах SELECT, INSERT, UPDATE или DELETE. Выражение WITH упрощает понимание и поддержку сложных запросов, разбивая их на более простые части. Оно также может помочь повысить производительность за счет повторного использования результатов ресурсоемких подзапросов. CTE являются временными и существуют только на протяжении запроса. Они не создают никаких постоянных объектов в базе данных.
Что такое функция RANK() и как она работает?
Функция RANK() используется для присвоения уникального ранга каждой строке в наборе результатов на основе значений в одном или нескольких столбцах. Строки упорядочиваются на основе указанных критериев, и ранг присваивается соответствующим образом. Функция RANK() является частью оконных функций в SQL, что означает, что она работает с окном (или подмножеством) строк и может возвращать несколько строк для каждой строки во входном наборе. Функция RANK() назначает ранг каждой строке на основе порядка, указанного в выражении ORDER BY в определении оконной функции. Если несколько строк имеют одинаковые значения в столбцах, указанных для упорядочивания, им присваивается одинаковый ранг, а следующий ранг пропускается на количество идентичных рангов. PARTITION BY (опционально) разбивает набор результатов на разделы, а функция RANK() применяется к каждому разделу по отдельности.
В выражении WITH, выделенном ниже, мы выбираем (SELECT) EventId, Country, EventName, EventType, EventImportance, DST_NONE (расписание перехода на летнее время, установленное пользователем во входных данных советника), EventCurrency, EventCode, EventSector, EventForecast, EventPrevalue, EventImpact, EventFrequency, затем мы используем функцию ранжирования. Цель этой функции — присвоить ранг каждому результату SELECT. Этот ранг всегда будет равен 1 для уникального времени события (DST_NONE). Если у нас есть несколько событий с одинаковым временем, то мы применяем ранжирование. Таким образом, если EventPrevalue события равно None, оно получает ранг 2, в противном случае оно получает рейтинг 1 и так далее для остальной части выражения ORDER BY.
WITH MySubQuery AS (SELECT EventId as 'Id',Country,EventName as 'Name',EventType as 'Type',EventImportance as 'Importance', DST_NONE as 'CTime',EventCurrency as 'Currency',EventCode as 'Code',EventSector as 'Sector',EventForecast as 'Forecast', EventPrevalue as 'Prevalue',EventImpact as'Impact',EventFrequency as 'Freq',RANK() OVER (PARTITION BY DST_NONE Order BY CASE EventPrevalue WHEN 'None' THEN 2 ELSE 1 END,CASE EventForecast WHEN 'None' THEN 2 ELSE 1 END, CASE EventImportance WHEN 'CALENDAR_IMPORTANCE_HIGH' THEN 1 WHEN 'CALENDAR_IMPORTANCE_MODERATE' THEN 2 WHEN 'CALENDAR_IMPORTANCE_LOW' THEN 3 ELSE 4 END) Ranking FROM MQL5Calendar) SELECT Id,Country,Name,Type, Importance,CTime,Currency,Code,Sector,Forecast,Prevalue,Impact,Freq FROM MySubQuery where Date(Replace(CTime,'.','-'))=Date(Replace('2024.07.30 00:00','.','-')) and Ranking<2 Group by CTime;
Ниже приведен пример результатов CTE(MySubQuery). Я выделил события, которые будут исключены из результатов, поскольку их ранг превышает 1. Мы рассматриваем только события ранга ниже 2, а затем группируем результаты по времени, поскольку при нанесении объектов событий на график мы не хотим создавать несколько объектов событий с одинаковым временем. Мы хотим, чтобы отображалось только событие с наибольшей важностью.
Id Country Name Type Importance CTime Currency Code Sector Forecast Prevalue Impact Freq Ranking 392030007 Japan Unemployment Rate CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 01:30 JPY JP CALENDAR_SECTOR_JOBS 2500000 2600000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 392050002 Japan Jobs to Applicants Ratio CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 01:30 JPY JP CALENDAR_SECTOR_JOBS 1240000 1240000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 36010001 Australia Building Approvals m/m CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 03:30 AUD AU CALENDAR_SECTOR_BUSINESS -900000 5500000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 36010002 Australia Private House Approvals m/m CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 03:30 AUD AU CALENDAR_SECTOR_BUSINESS None 2100000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 2 250010005 France GDP q/q CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 2024.07.30 07:30 EUR FR CALENDAR_SECTOR_GDP 200000 200000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 1 250010006 France GDP y/y CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 07:30 EUR FR CALENDAR_SECTOR_GDP 1000000 1100000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 2 250010008 France Consumer Spending m/m CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 07:30 EUR FR CALENDAR_SECTOR_CONSUMER 800000 1500000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 2 756050001 Switzerland KOF Economic Barometer CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 09:00 CHF CH CALENDAR_SECTOR_BUSINESS 101500000 102700000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 724010001 Spain CPI m/m CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 09:00 EUR ES CALENDAR_SECTOR_PRICES 200000 400000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 724010003 Spain HICP m/m CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 09:00 EUR ES CALENDAR_SECTOR_PRICES 300000 400000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 724010005 Spain GDP q/q CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 09:00 EUR ES CALENDAR_SECTOR_GDP 300000 800000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 1 724010002 Spain CPI y/y CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 09:00 EUR ES CALENDAR_SECTOR_PRICES 3000000 3400000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 5 724010004 Spain HICP y/y CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 09:00 EUR ES CALENDAR_SECTOR_PRICES 3500000 3600000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 5 724010006 Spain GDP y/y CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 09:00 EUR ES CALENDAR_SECTOR_GDP 1900000 2500000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 5 752020001 Sweden Business Confidence CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 09:00 SEK SE CALENDAR_SECTOR_BUSINESS 95500000 97300000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 5 752020002 Sweden Manufacturing Confidence CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 09:00 SEK SE CALENDAR_SECTOR_BUSINESS 99000000 99200000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 5 752020003 Sweden Consumer Confidence CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 09:00 SEK SE CALENDAR_SECTOR_CONSUMER 95300000 93300000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 5 752020004 Sweden Inflation Expectations CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 09:00 SEK SE CALENDAR_SECTOR_CONSUMER 6300000 6200000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 5 752020005 Sweden Economic Tendency Indicator CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 09:00 SEK SE CALENDAR_SECTOR_BUSINESS 94800000 96300000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 5 276010008 Germany GDP q/q CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 2024.07.30 10:00 EUR DE CALENDAR_SECTOR_GDP 100000 200000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 1 380010020 Italy GDP q/q CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 10:00 EUR IT CALENDAR_SECTOR_GDP 200000 300000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 2 276010009 Germany GDP y/y CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 10:00 EUR DE CALENDAR_SECTOR_GDP -400000 -900000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 2 380010021 Italy GDP y/y CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 10:00 EUR IT CALENDAR_SECTOR_GDP 400000 700000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 4 999030016 European Union GDP q/q CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 2024.07.30 11:00 EUR EU CALENDAR_SECTOR_GDP 200000 300000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 1 999030017 European Union GDP y/y CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 11:00 EUR EU CALENDAR_SECTOR_GDP 400000 400000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 2 999040003 European Union Industrial Confidence Indicator CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 11:00 EUR EU CALENDAR_SECTOR_BUSINESS -9700000 -10100000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 3 999040004 European Union Services Sentiment Indicator CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 11:00 EUR EU CALENDAR_SECTOR_BUSINESS 8300000 6500000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 3 999040005 European Union Economic Sentiment Indicator CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 11:00 EUR EU CALENDAR_SECTOR_BUSINESS 95300000 95900000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 3 999040006 European Union Consumer Confidence Index CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 11:00 EUR EU CALENDAR_SECTOR_CONSUMER -13000000 -13000000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 3 999040007 European Union Consumer Price Expectations CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 11:00 EUR EU CALENDAR_SECTOR_CONSUMER 14400000 13100000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 3 999040008 European Union Industry Selling Price Expectations CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 11:00 EUR EU CALENDAR_SECTOR_BUSINESS 6300000 6100000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 3 380020007 Italy 10-Year BTP Auction CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 11:30 EUR IT CALENDAR_SECTOR_MARKET None 4010000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_NONE 1 380020005 Italy 5-Year BTP Auction CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 11:30 EUR IT CALENDAR_SECTOR_MARKET None 3550000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_NONE 2 826130002 United Kingdom 10-Year Treasury Gilt Auction CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 11:30 GBP GB CALENDAR_SECTOR_MARKET None 4371000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_NONE 2 724080001 Spain Business Confidence CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 12:00 EUR ES CALENDAR_SECTOR_BUSINESS -4100000 -5700000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 76030002 Brazil FGV IGP-M Inflation Index m/m CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 13:00 BRL BR CALENDAR_SECTOR_PRICES 1000000 810000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 484020016 Mexico GDP q/q CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 2024.07.30 14:00 MXN MX CALENDAR_SECTOR_GDP 0 200000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 1 276010020 Germany CPI m/m CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 14:00 EUR DE CALENDAR_SECTOR_PRICES 0 100000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 2 276010022 Germany HICP m/m CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 14:00 EUR DE CALENDAR_SECTOR_PRICES -100000 200000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 2 76010012 Brazil PPI m/m CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 14:00 BRL BR CALENDAR_SECTOR_PRICES 700000 450000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 2 484020017 Mexico GDP n.s.a. y/y CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 14:00 MXN MX CALENDAR_SECTOR_GDP 2000000 1600000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 2 276010021 Germany CPI y/y CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 14:00 EUR DE CALENDAR_SECTOR_PRICES 2200000 2200000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 6 276010023 Germany HICP y/y CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 14:00 EUR DE CALENDAR_SECTOR_PRICES 2500000 2500000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 6 76010013 Brazil PPI y/y CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 14:00 BRL BR CALENDAR_SECTOR_PRICES 2200000 170000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 6 840170001 United States S&P/CS HPI Composite-20 y/y CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 15:00 USD US CALENDAR_SECTOR_HOUSING 6800000 7200000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 840110001 United States HPI m/m CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 15:00 USD US CALENDAR_SECTOR_HOUSING 300000 200000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 2 840110002 United States HPI y/y CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 15:00 USD US CALENDAR_SECTOR_HOUSING 5700000 6300000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 2 840110003 United States HPI CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 15:00 USD US CALENDAR_SECTOR_HOUSING 427700000 424300000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 2 840170002 United States S&P/CS HPI Composite-20 n.s.a. m/m CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 15:00 USD US CALENDAR_SECTOR_HOUSING 700000 1400000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 2 840170003 United States S&P/CS HPI Composite-20 s.a. m/m CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 15:00 USD US CALENDAR_SECTOR_HOUSING 200000 400000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 2 840030021 United States JOLTS Job Openings CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 2024.07.30 16:00 USD US CALENDAR_SECTOR_JOBS 7979000 8140000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 840180002 United States CB Consumer Confidence Index CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 2024.07.30 16:00 USD US CALENDAR_SECTOR_CONSUMER 108000000 100400000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 840060003 United States Dallas Fed Services Revenues CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 16:30 USD US CALENDAR_SECTOR_BUSINESS 3900000 1900000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 840060004 United States Dallas Fed Services Business Activity CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 16:30 USD US CALENDAR_SECTOR_BUSINESS -6700000 -4100000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 484010001 Mexico Fiscal Balance CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 22:30 MXN MX CALENDAR_SECTOR_TRADE -900000 -174071000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1
Окончательные результаты показаны ниже.
Id Country Name Type Importance CTime Currency Code Sector Forecast Prevalue Impact Freq Ranking 392030007 Japan Unemployment Rate CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 01:30 JPY JP CALENDAR_SECTOR_JOBS 2500000 2600000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 36010001 Australia Building Approvals m/m CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 03:30 AUD AU CALENDAR_SECTOR_BUSINESS -900000 5500000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 250010005 France GDP q/q CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 2024.07.30 07:30 EUR FR CALENDAR_SECTOR_GDP 200000 200000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 1 756050001 Switzerland KOF Economic Barometer CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 09:00 CHF CH CALENDAR_SECTOR_BUSINESS 101500000 102700000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 276010008 Germany GDP q/q CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 2024.07.30 10:00 EUR DE CALENDAR_SECTOR_GDP 100000 200000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 1 999030016 European Union GDP q/q CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 2024.07.30 11:00 EUR EU CALENDAR_SECTOR_GDP 200000 300000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 1 380020007 Italy 10-Year BTP Auction CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 11:30 EUR IT CALENDAR_SECTOR_MARKET None 4010000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_NONE 1 724080001 Spain Business Confidence CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 12:00 EUR ES CALENDAR_SECTOR_BUSINESS -4100000 -5700000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 76030002 Brazil FGV IGP-M Inflation Index m/m CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 13:00 BRL BR CALENDAR_SECTOR_PRICES 1000000 810000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 484020016 Mexico GDP q/q CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 2024.07.30 14:00 MXN MX CALENDAR_SECTOR_GDP 0 200000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_QUARTER 1 840170001 United States S&P/CS HPI Composite-20 y/y CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 15:00 USD US CALENDAR_SECTOR_HOUSING 6800000 7200000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 840030021 United States JOLTS Job Openings CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_HIGH 2024.07.30 16:00 USD US CALENDAR_SECTOR_JOBS 7979000 8140000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 840060003 United States Dallas Fed Services Revenues CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 16:30 USD US CALENDAR_SECTOR_BUSINESS 3900000 1900000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1 484010001 Mexico Fiscal Balance CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_LOW 2024.07.30 22:30 MXN MX CALENDAR_SECTOR_TRADE -900000 -174071000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1
Эти результаты будут сохранены в переменной ниже:
Calendar &NewsTime[]
Функция ниже EconomicNextEvent похожа на предыдущую упомянутую функцию EconomicDetailsMemory. Главное отличие состоит в том, что цель функции ниже — получить следующее событие, а не извлечь все события на определенную дату.
//+------------------------------------------------------------------+ //|Will update UpcomingNews structure variable with the next event | //|data | //+------------------------------------------------------------------+ void CNews::EconomicNextEvent(datetime date=0) { //--- Declare unassigned Calendar structure variable Empty Calendar Empty; //--- assign empty values to Calendar structure variable UpcomingNews UpcomingNews = Empty; //--- If date variable is zero then assign current date date = (date==0)?TimeTradeServer():date; //--- Query to retrieve next upcoming event. string request_text=StringFormat("WITH MySubQuery AS (SELECT EventId as 'Id',Country,EventName as 'Name'," "EventType as 'Type',EventImportance as 'Importance',%s as 'CTime',EventCurrency as 'Currency'," "EventCode as 'Code',EventSector as 'Sector',EventForecast as 'Forecast'," "EventPrevalue as 'Prevalue',EventImpact as 'Impact',EventFrequency as 'Freq'," "RANK() OVER (PARTITION BY %s ORDER BY CASE EventPrevalue WHEN 'None' THEN 2 ELSE 1 END" ", CASE EventForecast WHEN 'None' THEN 2 ELSE 1 END,CASE EventImportance " "WHEN 'CALENDAR_IMPORTANCE_HIGH' THEN 1 WHEN 'CALENDAR_IMPORTANCE_MODERATE' THEN 2 WHEN" " 'CALENDAR_IMPORTANCE_LOW' THEN 3 ELSE 4 END) Ranking FROM %s) SELECT Id,Country,Name," "Type,Importance,CTime,Currency,Code,Sector,Forecast,Prevalue,Impact,Freq FROM MySubQuery " "where Replace(CTime,'.','-')>=Replace('%s','.','-') AND Ranking<2 Group by CTime LIMIT 1;", EnumToString(MySchedule),EnumToString(MySchedule),DBMemory.name,TimeToString(date)); int request=DatabasePrepare(DBMemoryConnection,request_text);//Creates a handle of a request, which can then be executed using DatabaseRead() if(request==INVALID_HANDLE)//Checks if the request failed to be completed { Print("DB: ",NEWS_DATABASE_MEMORY, " request failed with code ", GetLastError()); PrintFormat(request_text); } //--- Assign query results to Calendar structure variable UpcomingNews DatabaseReadBind(request,UpcomingNews); //--- Removes a request created in DatabasePrepare() DatabaseFinalize(request); }
Приведенный ниже запрос для функции EconomicNextEvent аналогичен запросу в функции EconomicDetailsMemory, который мы ранее рассматривали. Единственное отличие состоит в том, что приведенный ниже запрос приводит только один результат.
WITH MySubQuery AS (SELECT EventId as 'Id',Country,EventName as 'Name',EventType as 'Type',EventImportance as 'Importance', DST_NONE as 'CTime',EventCurrency as 'Currency',EventCode as 'Code',EventSector as 'Sector',EventForecast as 'Forecast', EventPrevalue as 'Prevalue',EventImpact as'Impact',EventFrequency as 'Freq',RANK() OVER (PARTITION BY DST_NONE Order BY CASE EventPrevalue WHEN 'None' THEN 2 ELSE 1 END,CASE EventForecast WHEN 'None' THEN 2 ELSE 1 END, CASE EventImportance WHEN 'CALENDAR_IMPORTANCE_HIGH' THEN 1 WHEN 'CALENDAR_IMPORTANCE_MODERATE' THEN 2 WHEN 'CALENDAR_IMPORTANCE_LOW' THEN 3 ELSE 4 END) Ranking FROM MQL5Calendar) SELECT Id,Country,Name,Type, Importance,CTime,Currency,Code,Sector,Forecast,Prevalue,Impact,Freq FROM MySubQuery where Date(Replace(CTime,'.','-'))=Date(Replace('2024.07.30 00:00','.','-')) and Ranking<2 Group by CTime LIMIT 1;
Пример результатов для указанного выше запроса показан ниже.
Id Country Name Type Importance CTime Currency Code Sector Forecast Prevalue Impact Freq Ranking 392030007 Japan Unemployment Rate CALENDAR_TYPE_INDICATOR CALENDAR_IMPORTANCE_MODERATE 2024.07.30 01:30 JPY JP CALENDAR_SECTOR_JOBS 2500000 2600000 CALENDAR_IMPACT_NA CALENDAR_FREQUENCY_MONTH 1
Приведенный выше пример результата будет сохранен в переменной, которую мы объявили вне класса News под названием UpcomingNews.
//--- Assign query results to Calendar structure variable UpcomingNews DatabaseReadBind(request,UpcomingNews);
Последняя новая добавленная функция в классе News из части 2 называется GetImpact и показана ниже. Целью этой функции является возврат значения перечисления ENUM_CALENDAR_EVENT_IMPACT для предстоящего события на основе предыдущих событий со свойствами или значениями, аналогичными предстоящему событию. Поясню подробнее.
//+------------------------------------------------------------------+ //|Will retrieve Upcoming Event Impact data | //+------------------------------------------------------------------+ ENUM_CALENDAR_EVENT_IMPACT CNews::GetImpact() { //--- Declaration of string variable string impact=NULL; //--- Query to get impact data from previous event with the same event id and matching EventPrevalue and EventForecast scenarios. string request_text=StringFormat("SELECT EventImpact FROM %s where Replace(%s,'.','-')<Replace('%s','.','-') AND EventId=%d" " %s ORDER BY %s DESC LIMIT 1;",DBMemory.name,EnumToString(MySchedule),UpcomingNews.EventDate, UpcomingNews.EventId,((UpcomingNews.EventPreval=="None"||UpcomingNews.EventForecast=="None")? "AND EventImpact='CALENDAR_IMPACT_NA'":(int(UpcomingNews.EventPreval)<int(UpcomingNews.EventForecast))? "AND EventPrevalue<EventForecast AND EventImpact<>'CALENDAR_IMPACT_NA'": (int(UpcomingNews.EventPreval)>int(UpcomingNews.EventForecast))? "AND EventPrevalue>EventForecast AND EventImpact<>'CALENDAR_IMPACT_NA'": "AND EventPrevalue=EventForecast AND EventImpact<>'CALENDAR_IMPACT_NA'"), EnumToString(MySchedule)); int request=DatabasePrepare(DBMemoryConnection,request_text);//Creates a handle of a request, which can then be executed using DatabaseRead() if(request==INVALID_HANDLE)//Checks if the request failed to be completed { Print("DB: ",NEWS_DATABASE_MEMORY, " request failed with code ", GetLastError()); PrintFormat(request_text); } if(DatabaseRead(request))//Will read the one record in the 'Record' table { //--- assign first column result into impact variable DatabaseColumnText(request,0,impact); } //--- Removes a request created in DatabasePrepare() DatabaseFinalize(request); //--- Return equivalent Event impact in the enumeration ENUM_CALENDAR_EVENT_IMPACT if(impact=="CALENDAR_IMPACT_POSITIVE") { return CALENDAR_IMPACT_POSITIVE; } else if(impact=="CALENDAR_IMPACT_NEGATIVE") { return CALENDAR_IMPACT_NEGATIVE; } else { return CALENDAR_IMPACT_NA; } }
По моим наблюдениям, влияние событий в основном работает таким образом, что все предстоящие события или события, которые происходят в текущий день, имеют значение перечисления CALENDAR_IMPACT_NA, что означает, что влияние недоступно для события. Влияние события обновляется только по истечении дня события и только в том случае, если до наступления события были доступны как предыдущее значение события, так и прогнозируемые значения события. В некоторых случаях влияние события все равно будет отмечено как недоступное, даже если все эти требования соблюдены.
Зачем нам нужна величина влияния предыдущего события и как ее использовать?
Во-первых, нам требуется информация о влиянии предыдущего события, чтобы предсказать, каким может быть результат воздействия следующего события. Таким образом, если предыдущее значение предстоящего события не равно None, а также прогнозируемому значению, мы проверим, равны ли эти значения или какое из них больше другого. Как только мы выяснили, какое значение больше другого, или если значения одинаковы, мы ищем предыдущие события с такой же конфигурацией и извлекаем влияние этого предыдущего события, чтобы предсказать, каким может быть влияние предстоящего события. Например, если предыдущее значение предстоящего события равно 3000, а прогноз — 10 000, мы ищем последнее событие с тем же идентификатором события, предыдущее значение которого было меньше прогнозируемого значения, и используем влияние этого события для предстоящего события.
Каково влияние события и на чем оно основано?
Влияние события — это измерение воспринимаемого эффекта, который определенное событие оказало на валюту. Влияние основано на валюте события. Таким образом, если уровень безработицы является предстоящим событием, а предыдущее событие с аналогичным предыдущим значением и конфигурацией прогнозируемого значения имеет влияние события CALENDAR_IMPACT_NEGATIVE, а валютой события является USD, это по сути означает, что на USD негативно повлиял предыдущий уровень безработицы. Поэтому при наблюдении за символом EURUSD сразу после того, как произошел предыдущий уровень безработицы, мы в идеале увидели бы укрепление EURUSD, то есть рост EUR относительно USD.
В запросе ниже мы выбираем (SELECT) влияние события из таблицы MQL5Calendar в базе данных в памяти. В выражении WHERE мы ищем даты событий до даты предстоящего события и сортируем по тому же идентификатору события. Затем мы сортируем тот же сценарий взаимосвязи между предыдущим значением события (EventPreval) и прогнозируемым значение события (EventForecast), если UpcomingNews.EventPreval равен None. Если же UpcomingNews.EventForecast равен None, мы уже знаем, что влияние события равно None, если UpcomingNews.EventPreval меньше UpcomingNews. Затем мы ищем предыдущие события, где EventPrevalue < EventForecast и применяется влияние события. Если UpcomingNews.EventPreval больше UpcomingNews.EventForecast, мы ищем предыдущие события, где EventPreval > EventForecast и применяется влияние события. В противном случае мы ищем, где EventPrevalue = EventForecast и применяется влияние события. Все результаты фильтруются по самой последней дате, поскольку выражение ORDER BY упорядочено по убыванию даты события, а выражение LIMIT позволяет вернуть/вывести только одну запись.
//--- Query to get impact data from previous event with the same event id and matching EventPrevalue and EventForecast scenarios. string request_text=StringFormat("SELECT EventImpact FROM %s where Replace(%s,'.','-')<Replace('%s','.','-') AND EventId=%d" " %s ORDER BY %s DESC LIMIT 1;",DBMemory.name,EnumToString(MySchedule),UpcomingNews.EventDate, UpcomingNews.EventId,((UpcomingNews.EventPreval=="None"||UpcomingNews.EventForecast=="None")? "AND EventImpact='CALENDAR_IMPACT_NA'":(int(UpcomingNews.EventPreval)<int(UpcomingNews.EventForecast))? "AND EventPrevalue<EventForecast AND EventImpact<>'CALENDAR_IMPACT_NA'": (int(UpcomingNews.EventPreval)>int(UpcomingNews.EventForecast))? "AND EventPrevalue>EventForecast AND EventImpact<>'CALENDAR_IMPACT_NA'": "AND EventPrevalue=EventForecast AND EventImpact<>'CALENDAR_IMPACT_NA'"), EnumToString(MySchedule));
Пример того, как выглядит запрос выше, когда все переменные заполнены данными. Итак, как вы можете видеть ниже, мы выбираем (SELECT) EventImpact из (FROM) MQL5Calendar, где (WHERE) DST_NONE не менее '2024.08.01 16:30', EventId равен 124500001, EventPrevalue меньше EventForecast, а EventImpact не равен CALENDAR_IMPACT_NA. Затем мы устанавливаем результаты в порядке убывания относительно DST_NONE и, наконец, ограничиваем результаты одним результатом.
SELECT EventImpact FROM MQL5Calendar where Replace(DST_NONE,'.','-')<Replace('2024.08.01 16:30','.','-') AND EventId=124500001 AND EventPrevalue<EventForecast AND EventImpact<>'CALENDAR_IMPACT_NA' ORDER BY DST_NONE DESC LIMIT 1;
Класс Common Graphics
Класс отвечает за отображение визуальных элементов советника на графике. К настоящему моменту вы, наверно, уже заметили, что визуальные элементы для Части 3 представляют собой шаг вперед по сравнению с Частью 2, как и код, который их создает. Давайте взглянем на код. Классу потребуется доступ к информации о новостях, управлении рисками и счете, поэтому эти классы включены.
#include "ObjectProperties.mqh" #include "RiskManagement.mqh" #include "CommonVariables.mqh" #include "News.mqh" //+------------------------------------------------------------------+ //|CommonGraphics class | //+------------------------------------------------------------------+ class CCommonGraphics:CObjectProperties { private: //--- GraphicText structure this structure is responsible for managing the graphical text struct GraphicText { //--- private declaration for struct GraphicText private: //--- this structure will store properties for the subtext struct subtextformat { string Label;//Store text label string Text;//Store text value }; //--- this structure inherits from subtextformat and is responsible for finding text struct found:subtextformat { bool isFound;//Check if text is found int index;//Get index for the text }; //--- structure array for subtexts subtextformat sub_text[]; //--- function to find text properties from text's label found FoundText(string label) { found find; find.Label=""; find.Text=""; find.isFound=false; find.index=-1; for(uint i=0;i<sub_text.Size();i++) { //--- If text label is found in array if(label==sub_text[i].Label) { //--- Assign text properties find.Label=sub_text[i].Label; find.Text=sub_text[i].Text; find.isFound=true; find.index=int(i); return find;//return found text properties } } return find;//return text properties } //--- public declaration for struct GraphicText public: //--- string variable string text; //--- function to set/add text properties void subtext(string label,string value) { //--- Get text properties from label found result = FoundText(label); //--- Check if text label was found/exists in array sub_text if(!result.isFound) { //--- Resize array sub_text ArrayResize(sub_text,sub_text.Size()+1,sub_text.Size()+2); //--- Add text properties for new array index sub_text[sub_text.Size()-1].Label = label; sub_text[sub_text.Size()-1].Text = value; } else { /* Set new text/override text from text label that exists in the array sub_text array */ sub_text[result.index].Text = value; } } //--- function to retrieve text from text label string subtext(string label) { return FoundText(label).Text; } };// End of struct GraphicText //--- AccountInfo object declaration CAccountInfo CAccount; //--- News object declaration CNews NewsObj; //--- TimeManagement object declaration CTimeManagement CTime; //--- Calendar structure array declaration Calendar CalendarArray[]; //--- color variable declaration color EventColor; //--- unit variable declarations uint Fontsize,X_start,Y_start; //--- void function declarations for Graphical blocks void Block_1(); void Block_2(uint SecondsPreEvent=5); CRiskManagement CRisk;//Risk management class object //--- GraphicText structure array declarations GraphicText Texts_Block1[9],Texts_Block2[7]; //--- structure to store text height and width struct Text_Prop_Size { uint Height;//store text height uint Width;//store text width }; //--- void function to retrieve sum of the texts height and the maximum width of texts from GraphicText array Texts void GetTextMaxWidthAndHeight(GraphicText &Texts[],uint &Max_Height,uint &Max_Width,uint FontSize) { //--- set fontsize properties to get accurate text height and width sizes TextSetFont("Arial",(-1*FontSize)-100); //--- set variables to default value of zero Max_Height=0; Max_Width=0; //--- loop through all texts in the GraphicText array Texts for(uint i=0;i<Texts.Size();i++) { //--- temporary declarations for height and width uint Height=0,Width=0; //--- retrieve text height and width from index in Texts array TextGetSize(Texts[i].text,Width,Height); //--- sum texts height to variable Max_Height Max_Height+=Height; //--- assign width if text width is more than variable Max_Width value Max_Width=(Width>Max_Width)?Width:Max_Width; } } //--- function to retrieve text height and width properties in the structure Text_Prop_Size format Text_Prop_Size GetText(string Text,uint FontSize) { //--- structure Text_Prop_Size variable Text_Prop_Size Size; //--- set fontsize properties to get accurate text height and width sizes TextSetFont("Arial",(-1*FontSize)-100); //--- retrieve text height and width from Text string variable TextGetSize(Text,Size.Width,Size.Height); //--- return structure Text_Prop_Size variable return Size; } //--- Function to get texts height sum and max width in the structure Text_Prop_Size format Text_Prop_Size GetTextMax(GraphicText &Texts[],uint FontSize) { //--- structure Text_Prop_Size variable Text_Prop_Size Size; //--- uint variable declarations for text properties uint Max_Height; uint Max_Width; //--- Retrieve sum of texts height and maximum texts width into Max_Height,Max_Width GetTextMaxWidthAndHeight(Texts,Max_Height,Max_Width,FontSize); //--- assign values into structure Text_Prop_Size variable Size.Height = Max_Height; Size.Width = Max_Width; //--- return structure Text_Prop_Size variable return Size; } //--- Boolean declarations bool is_date,is_spread,is_news,is_events; public: //--- class constructor CCommonGraphics(bool display_date,bool display_spread,bool display_news,bool display_events); ~CCommonGraphics(void) {}//class destructor void GraphicsRefresh(uint SecondsPreEvent=5);//will create/refresh the chart objects //--- will update certain graphics at an interval void Block_2_Realtime(uint SecondsPreEvent=5); //--- will create chart event objects void NewsEvent(); };
Приведенная ниже структура GraphicText не является обычной структурой в MQL5. Эта структура содержит частные и/или публичные объявления структур, массивов структур, функций и строковой переменной. В этом случае структура GraphicText ведет себя как целый отдельный класс. Целью этой структуры является управление и хранение графических текстов. Структурный массив sub_text сохранит все свойства текста, а функция FoundText будет искать любые совпадения текстовой метки в пределах массива sub_text. Если совпадение есть, мы вернем подробности этого совпадения в структуре found, которая наследуется от структуры subtextformat. В публичных объявлениях строковая переменная с именем text будет хранить полную длину текста вструктурном массиве sub_text. Функция void subtext добавит текстовую метку и текст/подтекст в массив sub_text или перезапишет текст/подтекст, хранящийся в массиве, если внутри массива находится метка строковой переменной. Строковая функция subtext извлечет текст для метки, связанной с метками в структурном массиве sub_text.
//--- GraphicText structure this structure is responsible for managing the graphical text struct GraphicText { //--- private declaration for struct GraphicText private: //--- this structure will store properties for the subtext struct subtextformat { string Label;//Store text label string Text;//Store text value }; //--- this structure inherits from subtextformat and is responsible for finding text struct found:subtextformat { bool isFound;//Check if text is found int index;//Get index for the text }; //--- structure array for subtexts subtextformat sub_text[]; //--- function to find text properties from text's label found FoundText(string label) { found find; find.Label=""; find.Text=""; find.isFound=false; find.index=-1; for(uint i=0;i<sub_text.Size();i++) { //--- If text label is found in array if(label==sub_text[i].Label) { //--- Assign text properties find.Label=sub_text[i].Label; find.Text=sub_text[i].Text; find.isFound=true; find.index=int(i); return find;//return found text properties } } return find;//return text properties } //--- public declaration for struct GraphicText public: //--- string variable string text; //--- function to set/add text properties void subtext(string label,string value) { //--- Get text properties from label found result = FoundText(label); //--- Check if text label was found/exists in array sub_text if(!result.isFound) { //--- Resize array sub_text ArrayResize(sub_text,sub_text.Size()+1,sub_text.Size()+2); //--- Add text properties for new array index sub_text[sub_text.Size()-1].Label = label; sub_text[sub_text.Size()-1].Text = value; } else { /* Set new text/override text from text label that exists in the array sub_text array */ sub_text[result.index].Text = value; } } //--- function to retrieve text from text label string subtext(string label) { return FoundText(label).Text; } };// End of struct GraphicText
Функция ниже создана для извлечения суммы высот текста в структурном массиве Texts, а также максимальной ширины из текстов в массиве.
//--- void function to retrieve sum of the texts height and the maximum width of texts from GraphicText array Texts void GetTextMaxWidthAndHeight(GraphicText &Texts[],uint &Max_Height,uint &Max_Width,uint FontSize) { //--- set fontsize properties to get accurate text height and width sizes TextSetFont("Arial",(-1*FontSize)-100); //--- set variables to default value of zero Max_Height=0; Max_Width=0; //--- loop through all texts in the GraphicText array Texts for(uint i=0;i<Texts.Size();i++) { //--- temporary declarations for height and width uint Height=0,Width=0; //--- retrieve text height and width from index in Texts array TextGetSize(Texts[i].text,Width,Height); //--- sum texts height to variable Max_Height Max_Height+=Height; //--- assign width if text width is more than variable Max_Width value Max_Width=(Width>Max_Width)?Width:Max_Width; } }
Функция ниже создана для извлечения свойств высоты и ширины в формате структуры Text_Prop_Size из строковой переменной Text.
//--- function to retrieve text height and width properties in the structure Text_Prop_Size format Text_Prop_Size GetText(string Text,uint FontSize) { //--- structure Text_Prop_Size variable Text_Prop_Size Size; //--- set fontsize properties to get accurate text height and width sizes TextSetFont("Arial",(-1*FontSize)-100); //--- retrieve text height and width from Text string variable TextGetSize(Text,Size.Width,Size.Height); //--- return structure Text_Prop_Size variable return Size; }
Функция ниже создана для извлечения суммы свойств высоты и максимальной ширины в формате структуры Text_Prop_Size из структурного массива Texts.
//--- Function to get texts height sum and max width in the structure Text_Prop_Size format Text_Prop_Size GetTextMax(GraphicText &Texts[],uint FontSize) { //--- structure Text_Prop_Size variable Text_Prop_Size Size; //--- uint variable declarations for text properties uint Max_Height; uint Max_Width; //--- Retrieve sum of texts height and maximum texts width into Max_Height,Max_Width GetTextMaxWidthAndHeight(Texts,Max_Height,Max_Width,FontSize); //--- assign values into structure Text_Prop_Size variable Size.Height = Max_Height; Size.Width = Max_Width; //--- return structure Text_Prop_Size variable return Size; }
В конструкторе класса инициализируем наши ранее объявленные переменные: is_date (отображать ли информацию о дате на графике), is_spread (отображать ли информацию о спреде на графике), is_news (отображать ли информацию о новостях на графике) и is_events (отображать ли информацию об объекте события на графике). NewsObject. Функция EconomicNextEvent обновит переменную UpcomingNews сведениями о следующих новостях.
//+------------------------------------------------------------------+ //|Constructor | //+------------------------------------------------------------------+ CCommonGraphics::CCommonGraphics(bool display_date,bool display_spread,bool display_news,bool display_events): //--- Assign variables is_date(display_date),is_spread(display_spread),is_news(display_news),is_events(display_events) { //--- get next news event NewsObject.EconomicNextEvent(); }
GraphicsRefresh будет отвечать за очистку графика от ранее созданных объектов (если таковые имеются), а затем будет вызывать другие функции, которые будут создавать объекты графика для отображения различной информации на текущем графике.
//+------------------------------------------------------------------+ //|will create/refresh the chart objects | //+------------------------------------------------------------------+ void CCommonGraphics::GraphicsRefresh(uint SecondsPreEvent=5) { //--- create graphics if outside the strategy tester or in the strategy tester and visual mode is enabled if((!MQLInfoInteger(MQL_TESTER))||(MQLInfoInteger(MQL_TESTER)&&MQLInfoInteger(MQL_VISUAL_MODE))) { //--- Delete chart objects DeleteObj();//function from Object properties class Block_1();//Create graphics for block 1 //--- Check whether to create graphics for block 2 if(is_date||is_news||is_spread) { Block_2(SecondsPreEvent);//Create graphics for block 2 } //--- creates event objects NewsEvent(); } }
Функция Block_1 ниже отвечает за создание графических элементов для графического блока 1, состоящего из:
- Имени символа
- Периода символа
- Описания символа
- Размер контракта по символу
- Минимального размера лота по символу
- Максимального размера лота по символу
- Шага объема символа
- Ограничения объема символа
- Вариант управления рисками
- Нижнего уровня риска
- Верхнего уровня риска
//+------------------------------------------------------------------+ //|Graphical Block 1 | //+------------------------------------------------------------------+ void CCommonGraphics::Block_1() { //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color = (isLightMode)?clrBlack:clrWheat; //--- Set text properties for Symbol name,Symbol period and Symbol description # section 1 Texts_Block1[0].text = Symbol()+", "+GetChartPeriodName()+": "+CSymbol.Description();//set main text Texts_Block1[0].subtext("Symbol Name",Symbol()+",");//set subtext - label,value Texts_Block1[0].subtext("Symbol Period",GetChartPeriodName());//set subtext - label,value Texts_Block1[0].subtext("Symbol Desc",": "+CSymbol.Description());//set subtext - label,value //--- Set text properties for Contract size # section 2 Texts_Block1[1].text = "Contract Size: "+string(CSymbol.ContractSize());//set main text Texts_Block1[1].subtext("Contract Size Text","Contract Size:");//set subtext - label,value Texts_Block1[1].subtext("Contract Size",string(CSymbol.ContractSize()));//set subtext - label,value //--- Set text properties for Minimum lot # section 3 Texts_Block1[2].text = "Minimum Lot: "+string(CSymbol.LotsMin());//set main text Texts_Block1[2].subtext("Minimum Lot Text","Minimum Lot:");//set subtext - label,value Texts_Block1[2].subtext("Minimum Lot",string(CSymbol.LotsMin()));//set subtext - label,value //--- Set text properties for Max lot # section 4 Texts_Block1[3].text = "Max Lot: "+string(CSymbol.LotsMax());//set main text Texts_Block1[3].subtext("Max Lot Text","Max Lot:");//set subtext - label,value Texts_Block1[3].subtext("Max Lot",string(CSymbol.LotsMax()));//set subtext - label,value //--- Set text properties for Volume step # section 5 Texts_Block1[4].text = "Volume Step: "+string(CSymbol.LotsStep());//set main text Texts_Block1[4].subtext("Volume Step Text","Volume Step:");//set subtext - label,value Texts_Block1[4].subtext("Volume Step",string(CSymbol.LotsStep()));//set subtext - label,value //--- Set text properties for Volume limit # section 6 Texts_Block1[5].text = "Volume Limit: "+string(CSymbol.LotsLimit());//set main text Texts_Block1[5].subtext("Volume Limit Text","Volume Limit:");//set subtext - label,value Texts_Block1[5].subtext("Volume Limit",string(CSymbol.LotsLimit()));//set subtext - label,value //--- Set text properties for Risk option # section 7 Texts_Block1[6].text = "Risk Option: "+CRisk.GetRiskOption();//set main text Texts_Block1[6].subtext("Risk Option Text","Risk Option:");//set subtext - label,value Texts_Block1[6].subtext("Risk Option",CRisk.GetRiskOption());//set subtext - label,value //--- Set text properties for Risk floor # section 8 Texts_Block1[7].text = "Risk Floor: "+CRisk.GetRiskFloor();//set main text Texts_Block1[7].subtext("Risk Floor Text","Risk Floor:");//set subtext - label,value Texts_Block1[7].subtext("Risk Floor",CRisk.GetRiskFloor());//set subtext - label,value //--- Set text properties for Risk ceiling # section 9 Texts_Block1[8].text = "Risk Ceiling: "+CRisk.GetRiskCeil();//set main text Texts_Block1[8].subtext("Risk Ceiling Text","Risk Ceiling:");//set subtext - label,value Texts_Block1[8].subtext("Risk Ceiling",CRisk.GetRiskCeil());//set subtext - label,value //--- Set basic properties Fontsize=10;//Set Fontsize X_start=2;//Set X distance Y_start=2;//Set Y distance /* Create objects # section 1*/ //-- Create the background object for width and height+3 of section 1 text Square(0,"Symbol Name background",X_start,Y_start,GetText(Texts_Block1[0].text,Fontsize).Width, GetText(Texts_Block1[0].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER); Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //-- Will create the text objects for section 1 TextObj(0,"Symbol Name",Texts_Block1[0].subtext("Symbol Name"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block1[0].subtext("Symbol Name"),Fontsize).Width;//Re-adjust X distance TextObj(0,"Symbol Period",Texts_Block1[0].subtext("Symbol Period"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block1[0].subtext("Symbol Period"),Fontsize).Width;//Re-adjust X distance TextObj(0,"Symbol Desc",Texts_Block1[0].subtext("Symbol Desc"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Create objects # section 2*/ X_start=2;//Reset X distance Y_start+=GetText(Texts_Block1[0].text,Fontsize).Height;//Re-adjust Y distance, add height from section 1 //-- Create the background object for width and height+3 of section 2 text Square(0,"Symbol Contract Size background",X_start,Y_start,GetText(Texts_Block1[1].text,Fontsize).Width, GetText(Texts_Block1[1].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER); Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //-- Will create the text objects for section 2 TextObj(0,"Symbol Contract Size Text",Texts_Block1[1].subtext("Contract Size Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block1[1].subtext("Contract Size Text"),Fontsize).Width;//Re-adjust X distance TextObj(0,"Symbol Contract Size",Texts_Block1[1].subtext("Contract Size"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Create objects # section 3*/ X_start=2;//Reset X distance Y_start+=GetText(Texts_Block1[1].text,Fontsize).Height;//Re-adjust Y distance, add height from section 2 //-- Create the background object for width and height+3 of section 3 text Square(0,"Symbol MinLot background",X_start,Y_start,GetText(Texts_Block1[2].text,Fontsize).Width, GetText(Texts_Block1[2].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER); Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //-- Will create the text objects for section 3 TextObj(0,"Symbol MinLot Text",Texts_Block1[2].subtext("Minimum Lot Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block1[2].subtext("Minimum Lot Text"),Fontsize).Width;//Re-adjust X distance TextObj(0,"Symbol MinLot",Texts_Block1[2].subtext("Minimum Lot"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Create objects # section 4*/ X_start=2;//Reset X distance Y_start+=GetText(Texts_Block1[2].text,Fontsize).Height;//Re-adjust Y distance, add height from section 3 //-- Create the background object for width and height+3 of section 4 text Square(0,"Symbol MaxLot background",X_start,Y_start,GetText(Texts_Block1[3].text,Fontsize).Width, GetText(Texts_Block1[3].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER); Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //-- Will create the text objects for section 4 TextObj(0,"Symbol MaxLot Text",Texts_Block1[3].subtext("Max Lot Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block1[3].subtext("Max Lot Text"),Fontsize).Width;//Re-adjust X distance TextObj(0,"Symbol MaxLot",Texts_Block1[3].subtext("Max Lot"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Create objects # section 5*/ X_start=2;//Reset X distance Y_start+=GetText(Texts_Block1[3].text,Fontsize).Height;//Re-adjust Y distance, add height from section 4 //-- Create the background object for width and height+3 of section 5 text Square(0,"Symbol Volume Step background",X_start,Y_start,GetText(Texts_Block1[4].text,Fontsize).Width, GetText(Texts_Block1[4].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER); Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //-- Will create the text objects for section 5 TextObj(0,"Symbol Volume Step Text",Texts_Block1[4].subtext("Volume Step Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block1[4].subtext("Volume Step Text"),Fontsize).Width;//Re-adjust X distance TextObj(0,"Symbol Volume Step",Texts_Block1[4].subtext("Volume Step"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Create objects # section 6*/ X_start=2;//Reset X distance Y_start+=GetText(Texts_Block1[4].text,Fontsize).Height;//Re-adjust Y distance, add height from section 5 //-- Create the background object for width and height+3 of section 6 text Square(0,"Symbol Volume Limit background",X_start,Y_start,GetText(Texts_Block1[5].text,Fontsize).Width, GetText(Texts_Block1[5].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER); Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //-- Will create the text objects for section 6 TextObj(0,"Symbol Volume Limit Text",Texts_Block1[5].subtext("Volume Limit Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block1[5].subtext("Volume Limit Text"),Fontsize).Width;//Re-adjust X distance TextObj(0,"Symbol Volume Limit",Texts_Block1[5].subtext("Volume Limit"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Create objects # section 7*/ X_start=2;//Reset X distance Y_start+=GetText(Texts_Block1[5].text,Fontsize).Height;//Re-adjust Y distance, add height from section 6 //-- Create the background object for width and height+3 of section 7 text Square(0,"Risk Option background",X_start,Y_start,GetText(Texts_Block1[6].text,Fontsize).Width, GetText(Texts_Block1[6].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER); Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //-- Will create the text objects for section 7 TextObj(0,"Risk Option Text",Texts_Block1[6].subtext("Risk Option Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block1[6].subtext("Risk Option Text"),Fontsize).Width;//Re-adjust X distance TextObj(0,"Risk Option",Texts_Block1[6].subtext("Risk Option"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Create objects # section 8*/ X_start=2;//Reset X distance Y_start+=GetText(Texts_Block1[6].text,Fontsize).Height;//Re-adjust Y distance, add height from section 7 //-- Create the background object for width and height+3 of section 8 text Square(0,"Risk Floor background",X_start,Y_start,GetText(Texts_Block1[7].text,Fontsize).Width, GetText(Texts_Block1[7].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER); Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //-- Will create the text objects for section 8 TextObj(0,"Risk Floor Text",Texts_Block1[7].subtext("Risk Floor Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block1[7].subtext("Risk Floor Text"),Fontsize).Width;//Re-adjust X distance TextObj(0,"Risk Floor",Texts_Block1[7].subtext("Risk Floor"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Create objects # section 9*/ X_start=2;//Reset X distance Y_start+=GetText(Texts_Block1[7].text,Fontsize).Height;//Re-adjust Y distance, add height from section 8 //-- Create the background object for width and height+3 of section 9 text Square(0,"Risk Ceil background",X_start,Y_start,GetText(Texts_Block1[8].text,Fontsize).Width, GetText(Texts_Block1[8].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER); Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //-- Will create the text objects for section 9 TextObj(0,"Risk Ceil Text",Texts_Block1[8].subtext("Risk Ceiling Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block1[8].subtext("Risk Ceiling Text"),Fontsize).Width;//Re-adjust X distance TextObj(0,"Risk Ceil",Texts_Block1[8].subtext("Risk Ceiling"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); }
Функция Block_2 ниже отвечает за создание графических элементов для графического блока 2, состоящего из:
- Текущих даты и времени
- Даты события
- Названия события
- Страны события
- Валюты события
- Важности события
- Ранга спреда
//+------------------------------------------------------------------+ //|Graphical Block 2 | //+------------------------------------------------------------------+ void CCommonGraphics::Block_2(uint SecondsPreEvent=5) { //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; if(is_date)//Check whether to display date information { //--- Set text properties for Date and Time # section 10 Texts_Block2[0].text = "Date:"+TimeToString(TimeTradeServer(),TIME_DATE)+"|| Time:"+TimeToString(TimeTradeServer(),TIME_MINUTES) +" ";//set main text Texts_Block2[0].subtext("Date Text","Date:");//set subtext - label,value Texts_Block2[0].subtext("Date",TimeToString(TimeTradeServer(),TIME_DATE));//set subtext - label,value Texts_Block2[0].subtext("Time Text","|| Time:");//set subtext - label,value Texts_Block2[0].subtext("Time",TimeToString(TimeTradeServer(),TIME_MINUTES));//set subtext - label,value } if(is_news)//Check whether to display news information { //--- Set text object color depending on upcoming news event's Importance EventColor = NewsObj.GetImportance_color(NewsObj.IMPORTANCE(UpcomingNews.EventImportance)); //--- Set text properties for Event Date # section 11 Texts_Block2[1].text = "Event: @"+UpcomingNews.EventDate+" ";//set main text Texts_Block2[1].subtext("Event Date Text","Event: @");//set subtext - label,value Texts_Block2[1].subtext("Event Date",UpcomingNews.EventDate);//set subtext - label,value //--- Set text properties for Event Name # section 12 Texts_Block2[2].text = "Name: "+UpcomingNews.EventName+" ";//set main text Texts_Block2[2].subtext("Event Name Text","Name: ");//set subtext - label,value Texts_Block2[2].subtext("Event Name",UpcomingNews.EventName);//set subtext - label,value //--- Set text properties for Event Country # section 13 Texts_Block2[3].text = "Country: "+UpcomingNews.CountryName+" ";//set main text Texts_Block2[3].subtext("Event Country Text","Country: ");//set subtext - label,value Texts_Block2[3].subtext("Event Country",UpcomingNews.CountryName);//set subtext - label,value //--- Set text properties for Event Currency # section 14 Texts_Block2[4].text = "Currency: "+UpcomingNews.EventCurrency+" ";//set main text Texts_Block2[4].subtext("Event Currency Text","Currency: ");//set subtext - label,value Texts_Block2[4].subtext("Event Currency",UpcomingNews.EventCurrency);//set subtext - label,value //--- Set text properties for Event Importance # section 15 Texts_Block2[5].text = "Importance: "+NewsObj.GetImportance(NewsObj.IMPORTANCE(UpcomingNews.EventImportance))+" ";//set main text Texts_Block2[5].subtext("Importance Text","Importance: ");//set subtext - label,value Texts_Block2[5].subtext("Importance",NewsObj.GetImportance(NewsObj.IMPORTANCE(UpcomingNews.EventImportance)));//set subtext - label,value } if(is_spread)//Check whether to display spread information { //--- Set text properties for Spread # section 16 Texts_Block2[6].text = "Spread: "+string(CSymbol.Spread())+" Rating: "+CSymbol.SpreadDesc()+" ";//set main text Texts_Block2[6].subtext("Spread Text","Spread:");//set subtext - label,value Texts_Block2[6].subtext("Spread",string(CSymbol.Spread()));//set subtext - label,value Texts_Block2[6].subtext("Rating Text"," Rating:");//set subtext - label,value Texts_Block2[6].subtext("Rating Desc",CSymbol.SpreadDesc());//set subtext - label,value } //--- Set basic properties Fontsize=10; X_start=2;//Reset X distance Y_start=GetTextMax(Texts_Block1,Fontsize).Height+29;//Re-adjust Y distance from graphical block 1 Height /* Create objects # section 10*/ if(is_date)//Check whether to display date information { //-- Create the background object for width and height+3 of section 10 text Square(0,"Datetime background",X_start,Y_start,GetText(Texts_Block2[0].text,Fontsize).Width, GetText(Texts_Block2[0].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER); Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //-- Will create the text objects for section 10 TextObj(0,"Date Text",Texts_Block2[0].subtext("Date Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[0].subtext("Date Text"),Fontsize).Width;//Re-adjust X distance TextObj(0,"Date",Texts_Block2[0].subtext("Date"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[0].subtext("Date"),Fontsize).Width;//Re-adjust X distance TextObj(0,"Time Text",Texts_Block2[0].subtext("Time Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[0].subtext("Time Text"),Fontsize).Width;//Re-adjust X distance //--- Adjust text color depending if chart color mode is LightMode and if a news event is occurring TextObj_color = CTime.TimeIsInRange(CTime.TimeMinusOffset(datetime(UpcomingNews.EventDate),SecondsPreEvent), CTime.TimePlusOffset(datetime(UpcomingNews.EventDate),59))?clrRed:(isLightMode)?clrBlack:clrWheat; TextObj(0,"Time",Texts_Block2[0].subtext("Time"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// if(UpcomingNews.CountryName!=NULL&&is_news)//Check whether to display news information and if upcoming news is available { /* Create objects # section 11*/ Y_start+=(is_date)?GetText(Texts_Block2[0].text,Fontsize).Height:0;//Re-adjust Y distance depending if section 10 is shown X_start=2;//Reset X distance //-- Create the background object for width and height+3 of section 11 text Square(0,"Event Date background",X_start,Y_start,GetText(Texts_Block2[1].text,Fontsize).Width, GetText(Texts_Block2[1].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER); Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; //-- Will create the text objects for section 11 TextObj(0,"Event Date Text",Texts_Block2[1].subtext("Event Date Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[1].subtext("Event Date Text"),Fontsize).Width;//Re-adjust X distance //--- Adjust text color depending if chart color mode is LightMode and if a news event is occurring TextObj_color = CTime.TimeIsInRange(CTime.TimeMinusOffset(datetime(UpcomingNews.EventDate),SecondsPreEvent), CTime.TimePlusOffset(datetime(UpcomingNews.EventDate),59))?clrRed:(isLightMode)?clrBlack:clrWheat; TextObj(0,"Event Date",Texts_Block2[1].subtext("Event Date"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Create objects # section 12*/ Y_start+=GetText(Texts_Block2[1].text,Fontsize).Height;//Re-adjust Y distance X_start=2;//Reset X distance //-- Create the background object for width and height+3 of section 12 text Square(0,"Event Name background",X_start,Y_start,GetText(Texts_Block2[2].text,Fontsize).Width, GetText(Texts_Block2[2].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER); Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; //-- Will create the text objects for section 12 TextObj(0,"Event Name Text",Texts_Block2[2].subtext("Event Name Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[2].subtext("Event Name Text"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending on upcoming news event's Importance TextObj_color=EventColor; TextObj(0,"Event Name",Texts_Block2[2].subtext("Event Name"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Create objects # section 13*/ Y_start+=GetText(Texts_Block2[2].text,Fontsize).Height;//Re-adjust Y distance X_start=2;//Reset X distance //-- Create the background object for width and height+3 of section 13 text Square(0,"Event Country background",X_start,Y_start,GetText(Texts_Block2[3].text,Fontsize).Width, GetText(Texts_Block2[3].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER); Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; //-- Will create the text objects for section 13 TextObj(0,"Event Country Text",Texts_Block2[3].subtext("Event Country Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[3].subtext("Event Country Text"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending on upcoming news event's Importance TextObj_color=EventColor; TextObj(0,"Event Country",Texts_Block2[3].subtext("Event Country"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Create objects # section 14*/ Y_start+=GetText(Texts_Block2[3].text,Fontsize).Height;//Re-adjust Y distance X_start=2;//Reset X distance //-- Create the background object for width and height+3 of section 14 text Square(0,"Event Currency background",X_start,Y_start,GetText(Texts_Block2[4].text,Fontsize).Width, GetText(Texts_Block2[4].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER); Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; //-- Will create the text objects for section 14 TextObj(0,"Event Currency Text",Texts_Block2[4].subtext("Event Currency Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[4].subtext("Event Currency Text"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending on upcoming news event's Importance TextObj_color=EventColor; TextObj(0,"Event Currency",Texts_Block2[4].subtext("Event Currency"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Create objects # section 15*/ Y_start+=GetText(Texts_Block2[4].text,Fontsize).Height;//Re-adjust Y distance X_start=2;//Reset X distance //-- Create the background object for width and height+3 of section 15 text Square(0,"Event Importance background",X_start,Y_start,GetText(Texts_Block2[5].text,Fontsize).Width, GetText(Texts_Block2[5].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER); Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; //-- Will create the text objects for section 15 TextObj(0,"Importance Text",Texts_Block2[5].subtext("Importance Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[5].subtext("Importance Text"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending on upcoming news event's Importance TextObj_color=EventColor; TextObj(0,"Importance",Texts_Block2[5].subtext("Importance"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Create objects # section 16*/ if(is_spread)//Check whether to display spread information { Y_start+=GetText(Texts_Block2[5].text,Fontsize).Height;//Re-adjust Y distance X_start=2;//Reset X distance //-- Create the background object for width and height+3 of section 16 text Square(0,"Spread background",X_start,Y_start,GetText(Texts_Block2[6].text,Fontsize).Width, GetText(Texts_Block2[6].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER); Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //--- Set text object color depending on spread rating color Spread_clr=CSymbol.SpreadColor(); //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; //-- Will create the text objects for section 16 TextObj(0,"Symbol Spread Text",Texts_Block2[6].subtext("Spread Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[6].subtext("Spread Text"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending on spread rating TextObj_color=Spread_clr; TextObj(0,"Symbol Spread",Texts_Block2[6].subtext("Spread"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[6].subtext("Spread"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; TextObj(0,"Symbol Rating Text",Texts_Block2[6].subtext("Rating Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[6].subtext("Rating Text"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending on spread rating TextObj_color=Spread_clr; TextObj(0,"Symbol Rating",Texts_Block2[6].subtext("Rating Desc"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); } } else /* Create objects # section 16*/ if(is_spread)//Check whether to display spread information { Y_start+=(is_date)?GetText(Texts_Block2[0].text,Fontsize).Height:0;//Re-adjust Y distance depending if section 10 is shown X_start=2;//Reset X distance //-- Create the background object for width and height+3 of section 16 text Square(0,"Spread background",X_start,Y_start,GetText(Texts_Block2[6].text,Fontsize).Width, GetText(Texts_Block2[6].text,Fontsize).Height+3,ANCHOR_LEFT_UPPER); Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //--- Set text object color depending on spread rating color Spread_clr=CSymbol.SpreadColor(); //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; //-- Will create the text objects for section 16 TextObj(0,"Symbol Spread Text",Texts_Block2[6].subtext("Spread Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[6].subtext("Spread Text"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending on spread rating TextObj_color=Spread_clr; TextObj(0,"Symbol Spread",Texts_Block2[6].subtext("Spread"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[6].subtext("Spread"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; TextObj(0,"Symbol Rating Text",Texts_Block2[6].subtext("Rating Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[6].subtext("Rating Text"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending on spread rating TextObj_color=Spread_clr; TextObj(0,"Symbol Rating",Texts_Block2[6].subtext("Rating Desc"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); } }
Функция ниже обновит любые изменения в разделах графического блока 2. Она не будет пересоздавать все элементы функции Block_2.
//+------------------------------------------------------------------+ //|will update certain graphics at an interval | //+------------------------------------------------------------------+ void CCommonGraphics::Block_2_Realtime(uint SecondsPreEvent=5) { if(MQLInfoInteger(MQL_TESTER)&&!MQLInfoInteger(MQL_VISUAL_MODE)) { return;//exit if in strategy tester and not in visual mode } if(is_date)//Check whether to display date information { //--- Set text properties for Date and Time # section 10 Texts_Block2[0].text = "Date:"+TimeToString(TimeTradeServer(),TIME_DATE)+"|| Time:"+TimeToString(TimeTradeServer(),TIME_SECONDS) +" ";//set main text Texts_Block2[0].subtext("Date Text","Date:");//set subtext - label,value Texts_Block2[0].subtext("Date",TimeToString(TimeTradeServer(),TIME_DATE));//set subtext - label,value Texts_Block2[0].subtext("Time Text","|| Time:");//set subtext - label,value Texts_Block2[0].subtext("Time",TimeToString(TimeTradeServer(),TIME_SECONDS));//set subtext - label,value } if(is_news)//Check whether to display news information { //--- Set text object color depending on upcoming news event's Importance EventColor = NewsObj.GetImportance_color(NewsObj.IMPORTANCE(UpcomingNews.EventImportance)); //--- Set text properties for Event Date # section 11 Texts_Block2[1].text = "Event: @"+UpcomingNews.EventDate+" ";//set main text Texts_Block2[1].subtext("Event Date Text","Event: @");//set subtext - label,value Texts_Block2[1].subtext("Event Date",UpcomingNews.EventDate);//set subtext - label,value //--- Set text properties for Event Name # section 12 Texts_Block2[2].text = "Name: "+UpcomingNews.EventName+" ";//set main text Texts_Block2[2].subtext("Event Name Text","Name: ");//set subtext - label,value Texts_Block2[2].subtext("Event Name",UpcomingNews.EventName);//set subtext - label,value //--- Set text properties for Event Country # section 13 Texts_Block2[3].text = "Country: "+UpcomingNews.CountryName+" ";//set main text Texts_Block2[3].subtext("Event Country Text","Country: ");//set subtext - label,value Texts_Block2[3].subtext("Event Country",UpcomingNews.CountryName);//set subtext - label,value //--- Set text properties for Event Currency # section 14 Texts_Block2[4].text = "Currency: "+UpcomingNews.EventCurrency+" ";//set main text Texts_Block2[4].subtext("Event Currency Text","Currency: ");//set subtext - label,value Texts_Block2[4].subtext("Event Currency",UpcomingNews.EventCurrency);//set subtext - label,value //--- Set text properties for Event Importance # section 15 Texts_Block2[5].text = "Importance: "+NewsObj.GetImportance(NewsObj.IMPORTANCE(UpcomingNews.EventImportance))+" ";//set main text Texts_Block2[5].subtext("Importance Text","Importance: ");//set subtext - label,value Texts_Block2[5].subtext("Importance",NewsObj.GetImportance(NewsObj.IMPORTANCE(UpcomingNews.EventImportance)));//set subtext - label,value } if(is_spread)//Check whether to display spread information { //--- Set text properties for Spread # section 16 Texts_Block2[6].text = "Spread: "+string(CSymbol.Spread())+" Rating: "+CSymbol.SpreadDesc()+" ";//set main text Texts_Block2[6].subtext("Spread Text","Spread:");//set subtext - label,value Texts_Block2[6].subtext("Spread",string(CSymbol.Spread()));//set subtext - label,value Texts_Block2[6].subtext("Rating Text"," Rating:");//set subtext - label,value Texts_Block2[6].subtext("Rating Desc",CSymbol.SpreadDesc());//set subtext - label,value } //--- Set basic properties Fontsize=10; X_start=2;//Reset X distance Y_start=GetTextMax(Texts_Block1,Fontsize).Height+29;//Re-adjust Y distance from section block 1 /* Create objects # section 10*/ if(is_date)//Check whether to display date information { //-- Check if the background object x-size for section 10 is the same size as section 10 text width if(ObjectGetInteger(0,"Datetime background",OBJPROP_XSIZE)!=GetText(Texts_Block2[0].text,Fontsize).Width) { //-- Will re-adjust background object to any changes of section 10 text width ObjectSetInteger(0,"Datetime background",OBJPROP_XSIZE,long(GetText(Texts_Block2[0].text,Fontsize).Width)); } Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; //-- Will update the text objects for section 10 TextObj(0,"Date Text",Texts_Block2[0].subtext("Date Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[0].subtext("Date Text"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; TextObj(0,"Date",Texts_Block2[0].subtext("Date"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[0].subtext("Date"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; TextObj(0,"Time Text",Texts_Block2[0].subtext("Time Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[0].subtext("Time Text"),Fontsize).Width;//Re-adjust X distance //--- Adjust text color depending if chart color mode is LightMode and if a news event is occurring TextObj_color = CTime.TimeIsInRange(CTime.TimeMinusOffset(datetime(UpcomingNews.EventDate),SecondsPreEvent), CTime.TimePlusOffset(datetime(UpcomingNews.EventDate),59))?clrRed:(isLightMode)?clrBlack:clrWheat; TextObj(0,"Time",Texts_Block2[0].subtext("Time"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// if(UpcomingNews.CountryName!=NULL&&is_news)//Check whether to display news information { /* Create objects # section 11*/ Y_start+=(is_date)?GetText(Texts_Block2[0].text,Fontsize).Height:0; X_start=2;//Reset X distance //-- Check if the background object x-size for section 11 is the same size as section 11 text width if(ObjectGetInteger(0,"Event Date background",OBJPROP_XSIZE)!=GetText(Texts_Block2[1].text,Fontsize).Width) { //-- Will re-adjust background object to any changes of section 11 text width ObjectSetInteger(0,"Event Date background",OBJPROP_XSIZE,long(GetText(Texts_Block2[1].text,Fontsize).Width)); } Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; //-- Will update the text objects for section 11 TextObj(0,"Event Date Text",Texts_Block2[1].subtext("Event Date Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[1].subtext("Event Date Text"),Fontsize).Width;//Re-adjust X distance //--- Adjust text color depending if chart color mode is LightMode and if a news event is occurring TextObj_color = CTime.TimeIsInRange(CTime.TimeMinusOffset(datetime(UpcomingNews.EventDate),SecondsPreEvent), CTime.TimePlusOffset(datetime(UpcomingNews.EventDate),59))?clrRed:(isLightMode)?clrBlack:clrWheat; TextObj(0,"Event Date",Texts_Block2[1].subtext("Event Date"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Create objects # section 12*/ Y_start+=GetText(Texts_Block2[1].text,Fontsize).Height;//Re-adjust Y distance X_start=2;//Reset X distance //-- Check if the background object x-size for section 12 is the same size as section 12 text width if(ObjectGetInteger(0,"Event Name background",OBJPROP_XSIZE)!=GetText(Texts_Block2[2].text,Fontsize).Width) { //-- Will re-adjust background object to any changes of section 12 text width ObjectSetInteger(0,"Event Name background",OBJPROP_XSIZE,long(GetText(Texts_Block2[2].text,Fontsize).Width)); } Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; //-- Will update the text objects for section 12 TextObj(0,"Event Name Text",Texts_Block2[2].subtext("Event Name Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[2].subtext("Event Name Text"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending on upcoming news event's Importance TextObj_color=EventColor; TextObj(0,"Event Name",Texts_Block2[2].subtext("Event Name"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Create objects # section 13*/ Y_start+=GetText(Texts_Block2[2].text,Fontsize).Height;//Re-adjust Y distance X_start=2;//Reset X distance //-- Check if the background object x-size for section 13 is the same size as section 13 text width if(ObjectGetInteger(0,"Event Country background",OBJPROP_XSIZE)!=GetText(Texts_Block2[3].text,Fontsize).Width) { //-- Will re-adjust background object to any changes of section 13 text width ObjectSetInteger(0,"Event Country background",OBJPROP_XSIZE,long(GetText(Texts_Block2[3].text,Fontsize).Width)); } Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; //-- Will update the text objects for section 13 TextObj(0,"Event Country Text",Texts_Block2[3].subtext("Event Country Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[3].subtext("Event Country Text"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending on upcoming news event's Importance TextObj_color=EventColor; TextObj(0,"Event Country",Texts_Block2[3].subtext("Event Country"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Create objects # section 14*/ Y_start+=GetText(Texts_Block2[3].text,Fontsize).Height;//Re-adjust Y distance X_start=2;//Reset X distance //-- Check if the background object x-size for section 14 is the same size as section 14 text width if(ObjectGetInteger(0,"Event Currency background",OBJPROP_XSIZE)!=GetText(Texts_Block2[4].text,Fontsize).Width) { //-- Will re-adjust background object to any changes of section 14 text width ObjectSetInteger(0,"Event Currency background",OBJPROP_XSIZE,long(GetText(Texts_Block2[4].text,Fontsize).Width)); } Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; //-- Will update the text objects for section 14 TextObj(0,"Event Currency Text",Texts_Block2[4].subtext("Event Currency Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[4].subtext("Event Currency Text"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending on upcoming news event's Importance TextObj_color=EventColor; TextObj(0,"Event Currency",Texts_Block2[4].subtext("Event Currency"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Create objects # section 15*/ Y_start+=GetText(Texts_Block2[4].text,Fontsize).Height;//Re-adjust Y distance X_start=2;//Reset X distance //-- Check if the background object x-size for section 15 is the same size as section 15 text width if(ObjectGetInteger(0,"Event Importance background",OBJPROP_XSIZE)!=GetText(Texts_Block2[5].text,Fontsize).Width) { //-- Will re-adjust background object to any changes of section 15 text width ObjectSetInteger(0,"Event Importance background",OBJPROP_XSIZE,long(GetText(Texts_Block2[5].text,Fontsize).Width)); } Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; //-- Will update the text objects for section 15 TextObj(0,"Importance Text",Texts_Block2[5].subtext("Importance Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[5].subtext("Importance Text"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending on upcoming news event's Importance TextObj_color=EventColor; TextObj(0,"Importance",Texts_Block2[5].subtext("Importance"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /* Create objects # section 16*/ if(is_spread)//Check whether to display spread information { Y_start+=GetText(Texts_Block2[5].text,Fontsize).Height;//Re-adjust Y distance X_start=2;//Reset X distance //-- Check if the background object x-size for section 16 is the same size as section 16 text width if(ObjectGetInteger(0,"Spread background",OBJPROP_XSIZE)!=GetText(Texts_Block2[6].text,Fontsize).Width) { //-- Will re-adjust background object to any changes of section 16 text width ObjectSetInteger(0,"Spread background",OBJPROP_XSIZE,long(GetText(Texts_Block2[6].text,Fontsize).Width)); } Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //--- Set text object color depending on spread rating color Spread_clr=CSymbol.SpreadColor(); //-- Will create the text object for the Symbol's name X_start+=GetText(Texts_Block2[6].subtext("Spread Text"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending on spread rating TextObj_color=Spread_clr; //-- Will update the text objects for section 16 TextObj(0,"Symbol Spread",Texts_Block2[6].subtext("Spread"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[6].subtext("Spread"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; TextObj(0,"Symbol Rating Text",Texts_Block2[6].subtext("Rating Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[6].subtext("Rating Text"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending on spread rating TextObj_color=Spread_clr; TextObj(0,"Symbol Rating",Texts_Block2[6].subtext("Rating Desc"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); } } else /* Create objects # section 16*/ if(is_spread)//Check whether to display spread information { Y_start+=(is_date)?GetText(Texts_Block2[0].text,Fontsize).Height:0;//Re-adjust Y distance X_start=2;//Reset X distance //-- Check if the background object x-size for section 16 is the same size as section 16 text width if(ObjectGetInteger(0,"Spread background",OBJPROP_XSIZE)!=GetText(Texts_Block2[6].text,Fontsize).Width) { //-- Will re-adjust background object to any changes of section 16 text width ObjectSetInteger(0,"Spread background",OBJPROP_XSIZE,long(GetText(Texts_Block2[6].text,Fontsize).Width)); } Y_start+=3;//Re-adjust Y distance X_start+=2;//Re-adjust X distance //--- Set text object color depending on spread rating color Spread_clr=CSymbol.SpreadColor(); //-- Will create the text object for the Symbol's name X_start+=GetText(Texts_Block2[6].subtext("Spread Text"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending on spread rating TextObj_color=Spread_clr; //-- Will update the text objects for section 16 TextObj(0,"Symbol Spread",Texts_Block2[6].subtext("Spread"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[6].subtext("Spread"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending if the chart color mode is LightMode or not TextObj_color=(isLightMode)?clrBlack:clrWheat; TextObj(0,"Symbol Rating Text",Texts_Block2[6].subtext("Rating Text"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); X_start+=GetText(Texts_Block2[6].subtext("Rating Text"),Fontsize).Width;//Re-adjust X distance //--- Set text object color depending on spread rating TextObj_color=Spread_clr; TextObj(0,"Symbol Rating",Texts_Block2[6].subtext("Rating Desc"),X_start,Y_start,CORNER_LEFT_UPPER,Fontsize); } }
Функция ниже создаст объект события на графике со всеми доступными новостными событиями за текущий день.
//+------------------------------------------------------------------+ //|will create chart event objects | //+------------------------------------------------------------------+ void CCommonGraphics::NewsEvent() { if(!is_events||(MQLInfoInteger(MQL_TESTER)&&!MQLInfoInteger(MQL_VISUAL_MODE))) {return;}//exit if in strategy tester and not in visual mode or is_events variable is false //--- Retrieve news events for the current Daily period into array CalendarArray NewsObj.EconomicDetailsMemory(CalendarArray,iTime(Symbol(),PERIOD_D1,0)); //--- Iterate through all events in CalendarArray for(uint i=0;i<CalendarArray.Size();i++) { //--- Create event object with the news properties EventObj(0,CalendarArray[i].EventName+" "+CalendarArray[i].CountryName+" "+CalendarArray[i].EventDate, CalendarArray[i].EventName+"["+CalendarArray[i].CountryName+"]",StringToTime(CalendarArray[i].EventDate)); } //--- Refresh the chart/ update the chart ChartRefresh(); }
Класс Trade Management
Класс отвечает за открытие сделок для нашего советника. Функциональность этого класса, скорее всего, будет расширена в последующих статьях. Класс Trade Management будет наследовать класс управления рисками для настройки размеров лотов для каждой сделки.
#include <Trade\Trade.mqh> #include <Trade\OrderInfo.mqh> #include <Trade\SymbolInfo.mqh> #include "RiskManagement.mqh" #include "TimeManagement.mqh" //+------------------------------------------------------------------+ //|TradeManagement class | //+------------------------------------------------------------------+ class CTradeManagement:CRiskManagement { private: CTrade Trade;//Trade class object CSymbolProperties CSymbol;//SymbolProperties class object CTimeManagement CTime;//TimeManagement class object bool TradeResult;//boolean to store trade result double mySL;//double variable to store Stoploss double myTP;//double variable to store Takeprofit public: //--- Class constructor CTradeManagement(string SYMBOL=NULL) { //--- Set symbol name CSymbol.SetSymbolName(SYMBOL); } //--- Class destructor ~CTradeManagement(void) {} //--- 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 attempt open buy trade bool Buy(double SL,double TP,ulong Magic,string COMMENT=NULL); //--- Will attempt open buy trade with integer SL bool Buy(int SL,double TP,ulong Magic,string COMMENT=NULL); //--- Will attempt open buy trade with integer TP bool Buy(double SL,int TP,ulong Magic,string COMMENT=NULL); //--- Will attempt open buy trade with integer SL & TP bool Buy(int SL,int TP,ulong Magic,string COMMENT=NULL); //--- Will attempt open sell trade bool Sell(double SL,double TP,ulong Magic,string COMMENT=NULL); //--- Will attempt open sell trade with integer SL bool Sell(int SL,double TP,ulong Magic,string COMMENT=NULL); //--- Will attempt open sell trade with integer TP bool Sell(double SL,int TP,ulong Magic,string COMMENT=NULL); //--- Will attempt open sell trade with integer SL & TP bool Sell(int SL,int TP,ulong Magic,string COMMENT=NULL); };
Функция ниже проверит наличие открытых позиций с определенным типом позиции (Buy/Sell), магическим числом и комментарием (сведениями о позиции). Мы будем использовать эту функцию, чтобы проверить, открыли ли мы уже сделку на момент появления новости, чтобы избежать открытия дополнительных/ненужных сделок.
//+------------------------------------------------------------------+ //|Will retrieve if there are any open trades | //+------------------------------------------------------------------+ bool CTradeManagement::OpenTrade(ENUM_POSITION_TYPE Type,ulong Magic,string COMMENT=NULL) { //--- Iterate through all open positions for(int i=0; i<PositionsTotal(); i++) { //--- Check if Position ticket is above zero if(PositionGetTicket(i)>0) { //--- Check if the Position's Symbol,Magic,Type,Comment is correct if(PositionGetString(POSITION_SYMBOL)==CSymbol.GetSymbolName()&&PositionGetInteger(POSITION_MAGIC)==Magic &&PositionGetInteger(POSITION_TYPE)==Type&&PositionGetString(POSITION_COMMENT)==COMMENT) { //--- Return true when there is an open position return true; } } } //--- No open positions found. return false; }
Функция OpenedDeal проверит наличие открытых сделок на покупку/продажу. Эта функция нужна нам, чтобы помешать нам (советнику) открывать новые сделки, если сделка была закрыта во время новостного события. Без этой функции советник откроет сделку на покупку, например, во время выхода данных по занятости в несельскохозяйственном секторе (NFP). Если сделка будет закрыта из-за волатильности, мы не хотим, чтобы советник открыл еще одну. Это происходит потому, что мы уже совершили сделку по событию, и нет необходимости в дополнительных сделках, которые могут привести к убыткам.
//+------------------------------------------------------------------+ //|Will retrieve if there are any deals | //+------------------------------------------------------------------+ bool CTradeManagement::OpenedDeal(ENUM_DEAL_TYPE Type,ulong Magic,string COMMENT=NULL) { //--- Check History starting from 2 minutes ago if(HistorySelect(CTime.TimeMinusOffset(TimeTradeServer(),CTime.MinutesS(2)),TimeTradeServer())) { //--- Iterate through all history deals for(int i=0; i<HistoryDealsTotal(); i++) { //--- Assign history deal ticket ulong ticket = HistoryDealGetTicket(i); //--- Check if ticket is more than zero if(ticket>0) { //--- Check if the Deal's Symbol,Magic,Type,Comment is correct if(HistoryDealGetString(ticket,DEAL_SYMBOL)==CSymbol.GetSymbolName()&& HistoryDealGetInteger(ticket,DEAL_MAGIC)==Magic&&HistoryDealGetInteger(ticket,DEAL_TYPE)==Type &&HistoryDealGetString(ticket,DEAL_COMMENT)==COMMENT) { //--- Return true when there are any deals return true; } } } } //--- No deals found. return false; }
Функция ниже открывает рыночные ордера на покупку.
//+------------------------------------------------------------------+ //|Will attempt open buy trade | //+------------------------------------------------------------------+ bool CTradeManagement::Buy(double SL,double TP,ulong Magic,string COMMENT=NULL) { //--- Normalize the SL Price CSymbol.NormalizePrice(SL); //--- Normalize the TP Price CSymbol.NormalizePrice(TP); //--- Set the order type for Risk management calculation SetOrderType(ORDER_TYPE_BUY); //--- Set open price for Risk management calculation OpenPrice = CSymbol.Ask(); //--- Set close price for Risk management calculation ClosePrice = SL; //--- Set Trade magic number Trade.SetExpertMagicNumber(Magic); //--- Check if there are any open trades or opened deals already if(!OpenTrade(POSITION_TYPE_BUY,Magic,COMMENT)&&!OpenedDeal(DEAL_TYPE_BUY,Magic,COMMENT)) { //--- Iterate through the Lot-sizes if they're more than max-lot for(double i=Volume();i>=CSymbol.LotsMin();i-=CSymbol.LotsMax()) { //--- normalize Lot-size NormalizeLotsize(i); //--- Open trade with a Lot-size not more than max-lot TradeResult = Trade.Buy((i>CSymbol.LotsMax())?CSymbol.LotsMax():i,CSymbol.GetSymbolName(),CSymbol.Ask(),SL,TP,COMMENT); //--- Check if trade failed. if(!TradeResult) { return TradeResult; } } } else { //--- Trade failed because there is an open trade or opened deal return false; } //--- Return trade result. return TradeResult; }
Функция ниже открывает рыночные ордера на продажу.
//+------------------------------------------------------------------+ //|Will attempt open sell trade | //+------------------------------------------------------------------+ bool CTradeManagement::Sell(double SL,double TP,ulong Magic,string COMMENT=NULL) { //--- Normalize the SL Price CSymbol.NormalizePrice(SL); //--- Normalize the TP Price CSymbol.NormalizePrice(TP); //--- Set the order type for Risk management calculation SetOrderType(ORDER_TYPE_SELL); //--- Set open price for Risk management calculation OpenPrice = CSymbol.Bid(); //--- Set close price for Risk management calculation ClosePrice = SL; //--- Set Trade magic number Trade.SetExpertMagicNumber(Magic); //--- Check if there are any open trades or opened deals already if(!OpenTrade(POSITION_TYPE_SELL,Magic,COMMENT)&&!OpenedDeal(DEAL_TYPE_SELL,Magic,COMMENT)) { //--- Iterate through the Lot-sizes if they're more than max-lot for(double i=Volume();i>=CSymbol.LotsMin();i-=CSymbol.LotsMax()) { //--- normalize Lot-size NormalizeLotsize(i); //--- Open trade with a Lot-size not more than max-lot TradeResult = Trade.Sell((i>CSymbol.LotsMax())?CSymbol.LotsMax():i,CSymbol.GetSymbolName(),CSymbol.Bid(),SL,TP,COMMENT); //--- Check if trade failed. if(!TradeResult) { return TradeResult; } } } else { //--- Trade failed because there is an open trade or opened deal return false; } //--- Return trade result. return TradeResult; }
Советник для новостной торговли
У нас есть новые входные данные для советника. Пояснения приведены во введении.
//--- width and height of the canvas (used for drawing) #define IMG_WIDTH 200 #define IMG_HEIGHT 100 //--- enable to set color format ENUM_COLOR_FORMAT clr_format=COLOR_FORMAT_XRGB_NOALPHA; //--- drawing array (buffer) uint ExtImg[IMG_WIDTH*IMG_HEIGHT]; #include "News.mqh" CNews NewsObject;//Class object for News #include "TimeManagement.mqh" CTimeManagement CTM;//Class object for Time Management #include "WorkingWithFolders.mqh" CFolders Folder;//Class object for Folders #include "ChartProperties.mqh" CChartProperties Chart;//Class object for Chart Properties #include "RiskManagement.mqh" CRiskManagement CRisk;//Class object for Risk Management #include "CommonGraphics.mqh" CCommonGraphics *CGraphics;//Class pointer object for Common Graphics CCandleProperties *CP;//Class pointer object for Candle Properties #include "TradeManagement.mqh" CTradeManagement Trade;//Class object for Trade Management //--- used to separate Input Menu enum iSeparator { Delimiter//__________________________ }; //--- for chart color Mode selection enum DisplayMode { Display_LightMode,//LIGHT MODE Display_DarkMode//DARK MODE }; sinput group "+--------| DISPLAY |--------+"; sinput DisplayMode iDisplayMode=Display_LightMode;//CHART COLOUR MODE sinput Choice iDisplay_NewsInfo=Yes;//DISPLAY NEWS INFO sinput Choice iDisplay_EventObj=Yes;//DISPLAY EVENT OBJ sinput Choice iDisplay_Spread=Yes;//DISPLAY SPREAD RATING sinput Choice iDisplay_Date=Yes;//DISPLAY DATE sinput group ""; sinput group "+--------| DST SCHEDULE |--------+"; input DSTSchedule ScheduleDST=AutoDst_Selection;//SELECT DST OPTION sinput iSeparator iCustomSchedule=Delimiter;//__________________________ sinput iSeparator iCustomScheduleL=Delimiter;//CUSTOM DST input DST_type CustomSchedule=DST_NONE;//SELECT CUSTOM DST sinput group ""; sinput group "+--------| RISK MANAGEMENT |--------+"; input RiskOptions RISK_Type=MINIMUM_LOT;//SELECT RISK OPTION input RiskFloor RISK_Mini=RiskFloorMin;//RISK FLOOR input double RISK_Mini_Percent=75;//MAX-RISK [100<-->0.01]% input RiskCeil RISK_Maxi=RiskCeilMax;//RISK CEILING sinput iSeparator iRisk_1=Delimiter;//__________________________ sinput iSeparator iRisk_1L=Delimiter;//PERCENTAGE OF [BALANCE | FREE-MARGIN] input double Risk_1_PERCENTAGE=3;//[100<-->0.01]% sinput iSeparator iRisk_2=Delimiter;//__________________________ sinput iSeparator iRisk_2L=Delimiter;//AMOUNT PER [BALANCE | FREE-MARGIN] input double Risk_2_VALUE=1000;//[BALANCE | FREE-MARGIN] input double Risk_2_AMOUNT=10;//EACH AMOUNT sinput iSeparator iRisk_3=Delimiter;//__________________________ sinput iSeparator iRisk_3L=Delimiter;//LOTSIZE PER [BALANCE | FREE-MARGIN] input double Risk_3_VALUE=1000;//[BALANCE | FREE-MARGIN] input double Risk_3_LOTSIZE=0.1;//EACH LOTS(VOLUME) sinput iSeparator iRisk_4=Delimiter;//__________________________ sinput iSeparator iRisk_4L=Delimiter;//CUSTOM LOTSIZE input double Risk_4_LOTSIZE=0.01;//LOTS(VOLUME) sinput iSeparator iRisk_5=Delimiter;//__________________________ sinput iSeparator iRisk_5L=Delimiter;//PERCENTAGE OF MAX-RISK input double Risk_5_PERCENTAGE=1;//[100<-->0.01]% sinput group ""; sinput group "+--------| NEWS SETTINGS |--------+"; input Calendar_Importance iImportance=Calendar_Importance_High;//CALENDAR IMPORTANCE input Event_Frequency iFrequency=Event_Frequency_ALL;//EVENT FREQUENCY input Event_Sector iSector=Event_Sector_ALL;//EVENT SECTOR input Event_Type iType=Event_Type_Indicator;//EVENT TYPE input Event_Currency iCurrency=Event_Currency_Symbol;//EVENT CURRENCY sinput group ""; sinput group "+--------| TRADE SETTINGS |--------+"; input uint iStoploss=500;//STOPLOSS [0=NONE] input uint iTakeprofit=500;//TAKEPROFIT [0=NONE] input uint iSecondsPreEvent=5;//PRE-ENTRY SEC input DayOfTheWeek TradingDay=AllDays;//TRADING DAY OF WEEK sinput group ""; //--- to keep track of start-up time datetime Startup_date;
В функции OnInit Integer ниже мы выполняем различные процедуры при настройке советника для торговли, независимо от того, находится ли он в тестере стратегий или нет.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Assign if in LightMode or not isLightMode=(iDisplayMode==Display_LightMode)?true:false; //--- call function for common initialization procedure InitCommon(); //--- store Init result int InitResult; if(!MQLInfoInteger(MQL_TESTER))//Checks whether the program is in the strategy tester { //--- initialization procedure outside strategy tester InitResult=InitNonTester(); } else { //--- initialization procedure inside strategy tester InitResult=InitTester(); } //--- Create DB in memory NewsObject.CreateEconomicDatabaseMemory(); //--- Initialize Common graphics class pointer object CGraphics = new CCommonGraphics(Answer(iDisplay_Date),Answer(iDisplay_Spread),Answer(iDisplay_NewsInfo),Answer(iDisplay_EventObj)); CGraphics.GraphicsRefresh(iSecondsPreEvent);//-- Create chart objects //--- Initialize Candle properties pointer object CP = new CCandleProperties(); //--- Store start-up time. Startup_date = TimeTradeServer(); //--- return Init result return InitResult; }
В приведенной ниже функции мы инициализируем свойства как для тестера стратегий, так и для обычной торговой среды.
//+------------------------------------------------------------------+ //|function for common initialization procedure | //+------------------------------------------------------------------+ void InitCommon() { //Initializing CRiskManagement variable for Risk options RiskProfileOption = RISK_Type; //Initializing CRiskManagement variable for Risk floor RiskFloorOption = RISK_Mini; //Initializing CRiskManagement variable for RiskFloorMax RiskFloorPercentage = (RISK_Mini_Percent>100)?100: (RISK_Mini_Percent<0.01)?0.01:RISK_Mini_Percent;//Percentage cannot be more than 100% or less than 0.01% //Initializing CRiskManagement variable for Risk ceiling RiskCeilOption = RISK_Maxi; //Initializing CRiskManagement variable for Risk options (PERCENTAGE OF BALANCE and PERCENTAGE OF FREE-MARGIN) Risk_Profile_1 = (Risk_1_PERCENTAGE>100)?100: (Risk_1_PERCENTAGE<0.01)?0.01:Risk_1_PERCENTAGE;//Percentage cannot be more than 100% or less than 0.01% //Initializing CRiskManagement variables for Risk options (AMOUNT PER BALANCE and AMOUNT PER FREE-MARGIN) Risk_Profile_2.RiskAmountBoF = Risk_2_VALUE; Risk_Profile_2.RiskAmount = Risk_2_AMOUNT; //Initializing CRiskManagement variables for Risk options (LOTSIZE PER BALANCE and LOTSIZE PER FREE-MARGIN) Risk_Profile_3.RiskLotBoF = Risk_3_VALUE; Risk_Profile_3.RiskLot = Risk_3_LOTSIZE; //Initializing CRiskManagement variable for Risk option (CUSTOM LOTSIZE) Risk_Profile_4 = Risk_4_LOTSIZE; //Initializing CRiskManagement variable for Risk option (PERCENTAGE OF MAX-RISK) Risk_Profile_5 = (Risk_5_PERCENTAGE>100)?100: (Risk_5_PERCENTAGE<0.01)?0.01:Risk_5_PERCENTAGE;//Percentage cannot be more than 100% or less than 0.01% //--- Initializing DST Schedule variables MyDST = ScheduleDST; MySchedule = CustomSchedule; //--- Initializing News filter variables myFrequency=iFrequency; myImportance=iImportance; mySector=iSector; myType=iType; myCurrency=iCurrency; Chart.ChartRefresh();//Load chart configurations }
Функция ниже будет инициализирована только для обычной торговой среды.
//+------------------------------------------------------------------+ //|function for initialization procedure outside strategy tester | //+------------------------------------------------------------------+ int InitNonTester() { //--- Check if in Strategy tester! if(MQLInfoInteger(MQL_TESTER)) { //--- Initialization failed. return(INIT_SUCCEEDED); } //--- create OBJ_BITMAP_LABEL object for drawing ObjectCreate(0,"STATUS",OBJ_BITMAP_LABEL,0,0,0); ObjectSetInteger(0,"STATUS",OBJPROP_XDISTANCE,5); ObjectSetInteger(0,"STATUS",OBJPROP_YDISTANCE,22); //--- specify the name of the graphical resource ObjectSetString(0,"STATUS",OBJPROP_BMPFILE,"::PROGRESS"); uint w,h; // variables for receiving text string sizes uint x,y; // variables for calculation of the current coordinates of text string anchor points /* In the Do while loop below, the code will check if the terminal is connected to the internet. If the the program is stopped the loop will break, if the program is not stopped and the terminal is connected to the internet the function CreateEconomicDatabase will be called from the News.mqh header file's object called NewsObject and the loop will break once called. */ bool done=false; do { //--- clear the drawing buffer array ArrayFill(ExtImg,0,IMG_WIDTH*IMG_HEIGHT,0); if(!TerminalInfoInteger(TERMINAL_CONNECTED)) { //-- integer dots used as a loading animation static int dots=0; //--- set the font TextSetFont("Arial",-150,FW_EXTRABOLD,0); TextGetSize("Waiting",w,h);//get text width and height values //--- calculate the coordinates of the 'Waiting' text x=10;//horizontal alignment y=IMG_HEIGHT/2-(h/2);//alignment for the text to be centered vertically //--- output the 'Waiting' text to ExtImg[] buffer TextOut("Waiting",x,y,TA_LEFT|TA_TOP,ExtImg,IMG_WIDTH,IMG_HEIGHT,ColorToARGB(CSymbol.Background()),clr_format); //--- calculate the coordinates for the dots after the 'Waiting' text x=w+13;//horizontal alignment y=IMG_HEIGHT/2-(h/2);//alignment for the text to be centered vertically TextSetFont("Arial",-160,FW_EXTRABOLD,0); //--- output of dots to ExtImg[] buffer TextOut(StringSubstr("...",0,dots),x,y,TA_LEFT|TA_TOP,ExtImg,IMG_WIDTH,IMG_HEIGHT,ColorToARGB(CSymbol.Background()),clr_format); //--- update the graphical resource ResourceCreate("::PROGRESS",ExtImg,IMG_WIDTH,IMG_HEIGHT,0,0,IMG_WIDTH,clr_format); //--- force chart update Chart.Redraw(); dots=(dots==3)?0:dots+1; //-- Notify user that program is waiting for connection Print("Waiting for connection..."); Sleep(500); continue; } else { //--- set the font TextSetFont("Arial",-120,FW_EXTRABOLD,0); TextGetSize("Getting Ready",w,h);//get text width and height values x=20;//horizontal alignment y=IMG_HEIGHT/2-(h/2);//alignment for the text to be centered vertically //--- output the text 'Getting Ready...' to ExtImg[] buffer TextOut("Getting Ready...",x,y,TA_LEFT|TA_TOP,ExtImg,IMG_WIDTH,IMG_HEIGHT,ColorToARGB(CSymbol.Background()),clr_format); //--- update the graphical resource ResourceCreate("::PROGRESS",ExtImg,IMG_WIDTH,IMG_HEIGHT,0,0,IMG_WIDTH,clr_format); //--- force chart update Chart.Redraw(); //-- Notify user that connection is successful Print("Connection Successful!"); NewsObject.CreateEconomicDatabase();//calling the database create function done=true; } } while(!done&&!IsStopped()); //-- Delete chart object ObjectDelete(0,"STATUS"); //-- force chart to update Chart.Redraw(); //--- Initialization succeeded. return(INIT_SUCCEEDED); }
Приведенная ниже функция будет инициализирована только для среды тестера стратегий.
//+------------------------------------------------------------------+ //|function for initialization procedure inside strategy tester | //+------------------------------------------------------------------+ int InitTester() { //--- Check if not in Strategy tester! if(!MQLInfoInteger(MQL_TESTER)) { //--- Initialization failed. return(INIT_SUCCEEDED); } //Checks whether the database file exists if(!FileIsExist(NEWS_DATABASE_FILE,FILE_COMMON)) { //--- Warning messages Print("Necessary Files Do not Exist!"); Print("Run Program outside of the Strategy Tester"); Print("Necessary Files Should be Created First"); //--- Initialization failed. return(INIT_FAILED); } else { //Checks whether the latest database date includes the time and date being tested datetime latestdate = CTM.TimeMinusOffset(NewsObject.GetLatestNewsDate(),CTM.DaysS());//Day before the latest recorded time in the database if(latestdate<TimeTradeServer()) { Print("Necessary Files outdated!"); Print("To Update Files: Run Program outside of the Strategy Tester"); } Print("Database Dates End at: ",latestdate); PrintFormat("Dates after %s will not be available for backtest",TimeToString(latestdate)); } //--- Initialization succeeded. return(INIT_SUCCEEDED); }
Функция ниже будет запускаться на каждом новом тике.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Run procedures Execution(); }
В функции ниже мы вызываем все функции, которые сделают советника функциональным. Не каждая функция должна запускаться на каждом тике, поэтому мы вызываем определенные функции только на каждой новой определенной свече, например, на каждой дневной свече. Это повышает производительность и сокращает количество ненужных вызовов функций и использование ресурсов.
//+------------------------------------------------------------------+ //|Execute program procedures | //+------------------------------------------------------------------+ void Execution() { //--- Update realtime Graphic every 1 min if(CP.NewCandle(1,PERIOD_M1)) { CGraphics.Block_2_Realtime(iSecondsPreEvent); } //--- function to open trades EnterTrade(); //--- Check if not start-up date if(!CTM.DateisToday(Startup_date)) { //--- Run every New Daily Candle if(CP.NewCandle(2,PERIOD_D1)) { //--- Check if not in strategy tester if(!MQLInfoInteger(MQL_TESTER)) { //--- Update/Create DB in Memory NewsObject.CreateEconomicDatabaseMemory(); } CGraphics.GraphicsRefresh(iSecondsPreEvent);//-- Create/Re-create chart objects //--- Update Realtime Graphics CGraphics.Block_2_Realtime(iSecondsPreEvent); } //--- Check if not in strategy tester if(!MQLInfoInteger(MQL_TESTER)) { //--- Run every New Hourly Candle if(CP.NewCandle(3,PERIOD_H1)) { //--- Check if DB in Storage needs an update if(NewsObject.UpdateRecords()) { //--- initialization procedure outside strategy tester InitNonTester(); } } } } else { //--- Run every New Daily Candle if(CP.NewCandle(4,PERIOD_D1)) { //--- Update Event objects on chart CGraphics.NewsEvent(); } } }
Функция ниже будет отвечать за открытие сделок по рыночным ордерам на основе влияния и валюты события. Если валюта события равна валюте прибыли, а тип воздействия — CALENDAR_IMPACT_NEGATIVE, мы открываем сделку на покупку, так как предполагаем, что валюта прибыли ослабнет во время выхода новости, если валюта события равна валюте прибыли, а тип воздействия — CALENDAR_IMPACT_POSITIVE, мы открываем сделку на продажу, так как предполагаем, что валюта прибыли укрепится во время выхода новости.
//+------------------------------------------------------------------+ //|function to open trades | //+------------------------------------------------------------------+ void EnterTrade() { //--- static variable for storing upcoming event Impact value static ENUM_CALENDAR_EVENT_IMPACT Impact=CALENDAR_IMPACT_NA; //--- Check if Upcoming news date has passed and if upcoming news is not null and if new minute candle has formed. if(datetime(UpcomingNews.EventDate)<TimeTradeServer()&&UpcomingNews.CountryName!=NULL&&CP.NewCandle(5,PERIOD_M1)) { //--- Update for next upcoming news NewsObject.EconomicNextEvent(); //--- Get impact value for upcoming news Impact=NewsObject.GetImpact(); } //--- Check if upcoming news date is about to occur and if it is the trading day of week if(CTM.TimePreEvent(CTM.TimeMinusOffset(datetime(UpcomingNews.EventDate),(iSecondsPreEvent==0)?1:iSecondsPreEvent) ,datetime(UpcomingNews.EventDate)) &&CTM.isDayOfTheWeek(TradingDay)) { //--- Check each Impact value type switch(Impact) { //--- When Impact news is negative case CALENDAR_IMPACT_NEGATIVE: //--- Check if profit currency is news event currency if(UpcomingNews.EventCurrency==CSymbol.CurrencyProfit()) { //--- Open buy trade with Event id as Magic number Trade.Buy(iStoploss,iTakeprofit,ulong(UpcomingNews.EventId),"NewsTrading"); } else { //--- Open sell trade with Event id as Magic number Trade.Sell(iStoploss,iTakeprofit,ulong(UpcomingNews.EventId),"NewsTrading"); } break; //--- When Impact news is positive case CALENDAR_IMPACT_POSITIVE: //--- Check if profit currency is news event currency if(UpcomingNews.EventCurrency==CSymbol.CurrencyProfit()) { //--- Open sell trade with Event id as Magic number Trade.Sell(iStoploss,iTakeprofit,ulong(UpcomingNews.EventId),"NewsTrading"); } else { //--- Open buy trade with Event id as Magic number Trade.Buy(iStoploss,iTakeprofit,ulong(UpcomingNews.EventId),"NewsTrading"); } break; //--- Unknown default: break; } } }
Заключение
В этой статье мы рассмотрели добавление базы данных в память и создание дополнительных представлений для предоставления дополнительной информации о событиях в экономическом календаре MQL5. Мы создали дополнительные графические объекты на графике для отображения информации о предстоящем событии, а также реализовали темный режим. Кроме того, мы добавили необходимые входные параметры, позволяющие пользователю сортировать новостные данные в соответствии со своими предпочтениями, а также входные данные торгового советника. Кроме того, в статье объясняется, как мы открываем рыночные ордера в зависимости от важности события и какое значение событие имеет для нашей торговой стратегии.
Я открыт для ваших мнений и буду признателен вашим комментариям. В следующей статье мы добавим больше функциональных возможностей к входным параметрам новостей, чтобы учитывать отдельные экономические события, а также торговлю с использованием отложенных ордеров для большей гибкости и торговлю, не требующую учета важности событий. Спасибо за внимание!
Видео
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/15359





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Большое спасибо
Лоран
Однако, я не хочу визуализировать это
Комментарии я не хочу визуализировать их.
Спасибо за статью, очень хорошая.
Однако, я не хочу визуализировать это
Комментарии я не хочу визуализировать их.