AlgoMasterNNFX-V1/Indicators/NewsIndicator.mq5
alexcercos 3064d829bf Remove repeated code
Already included in dependency used by the EA (probably could do the same with the rest of indicators)
2023-05-22 22:51:57 +02:00

531 lines
No EOL
16 KiB
MQL5

//+------------------------------------------------------------------+
//| NewsIndicator.mq5 |
//| Copyright 2020, Manuel Alejandro Cercós Pérez |
//| https://www.mql5.com/en/users/alexcercos |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, Manuel Alejandro Cercós Pérez"
#property link "https://www.mql5.com/en/users/alexcercos"
#property version "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots 2
#property indicator_label1 "News Indicator"
#property indicator_type1 DRAW_NONE
#property indicator_label2 "Impact"
#property indicator_type2 DRAW_NONE
#include "../CrossProjects/NewsImport.mqh"
#define LINE_PREFIX "News_VLine"
#define ARROW_PREFIX "News_Arrow"
#define FUTURE_PREFIX "News_FutArr"
#define LINE_NAME(index) LINE_PREFIX + "_"+(string)index
#define ARROW_NAME(index) ARROW_PREFIX + "_"+(string)index
#define FUT_ARROW_NAME(index) FUTURE_PREFIX + "_"+(string)index
#ifdef __MQL5__
#define TRIM_STRING_LEFT(param) StringTrimLeft(param)
#define TRIM_STRING_RIGHT(param) StringTrimRight(param)
#else
#define TRIM_STRING_LEFT(param) param=StringTrimLeft(param)
#define TRIM_STRING_RIGHT(param) param=StringTrimRight(param)
#endif
enum WhereAreNews
{
NO_NEWS,
NEWS_BASE,
NEWS_QUOTE,
NEWS_BOTH
};
//Inputs
input int indicatorDisplacement = 1; //Indicator Displacement
input NewsIndicatorMode indicatorMode = FILTER_IMPACT; // Indicator Mode
input bool show_news_lines = true; // Show Vertical Lines
//input group "Impact"
input bool showNoImpact = false; // Show news with no impact
input bool showLowImpact = true; // Show news with low impact
input bool showMediumImpact = true; // Show news with medium impact
input bool showHighImpact = true; // Show news with high impact
//input group "News Name"
input string newsEUR = "Monetary Policy Statement, Lagarde"; // EUR News
input string newsGBP = "MPC Official Bank Rate Votes, GDP"; // GBP News
input string newsAUD = "RBA Rate Statement, Unemployment Rate"; // AUD News
input string newsNZD = "Unemployment Rate, GDP, GDT, RBNZ Rate Statement"; // NZD News
input string newsUSD = "Non-Farm Employment, FOMC Statement, Fed Chair Powell, CPI"; // USD News
input string newsCAD = "BOC Rate Statement, Unemployment Rate, Retail Sales, CPI"; // CAD News
input string newsCHF = "SNB Monetary Policy Assessment"; // CHF News
input string newsJPY = "Monetary Policy Statement"; // JPY News
//input group "Indicator Display"
input int distanceFirst = 500; //Distance to first arrow
input int distanceBetween = 250; //Distance between arrows
color baseColor = clrDarkSlateBlue;
color quoteColor = clrSaddleBrown;
color bothColor = clrDimGray;
string currencyBase, currencyQuote;
string arrayFilterBase[], arrayFilterQuote[];
double ArrowBuffer[], impactBuffer[];
void ProcessArrayForCurrency(string currency, string &returnArray[])
{
if (currency == "EUR")
StringSplit(newsEUR, ',', returnArray);
if (currency == "GBP")
StringSplit(newsGBP, ',', returnArray);
if (currency == "AUD")
StringSplit(newsAUD, ',', returnArray);
if (currency == "NZD")
StringSplit(newsNZD, ',', returnArray);
if (currency == "USD")
StringSplit(newsUSD, ',', returnArray);
if (currency == "CAD")
StringSplit(newsCAD, ',', returnArray);
if (currency == "CHF")
StringSplit(newsCHF, ',', returnArray);
if (currency == "JPY")
StringSplit(newsJPY, ',', returnArray);
TrimArray(returnArray);
}
void TrimArray(string & array[])
{
for (int j=0; j<ArraySize(array); j++)
{
TRIM_STRING_LEFT(array[j]);
TRIM_STRING_RIGHT(array[j]);
}
}
int OnInitProcessCurrencies()
{
#ifdef __MQL5__
currencyBase = SymbolInfoString(Symbol(), SYMBOL_CURRENCY_BASE);
currencyQuote = SymbolInfoString(Symbol(), SYMBOL_CURRENCY_PROFIT);
#else
if (StringLen(Symbol())>=6) //Slice (forex/metals)
{
currencyBase = StringSubstr(Symbol(), 0, 3);
currencyQuote = StringSubstr(Symbol(), 3, 3);
}
#endif
int id1 = CurrencyIndex(currencyBase);
int id2 = CurrencyIndex(currencyQuote);
if (id1 == -1 || id2 == -1 || id1 == id2)
{
Print("News not suitable for symbol " + Symbol());
return INIT_FAILED;
}
ProcessArrayForCurrency(currencyBase, arrayFilterBase);
ProcessArrayForCurrency(currencyQuote, arrayFilterQuote);
lastRecordTime = 0;
arrowIndex = 0;
lineIndex = 0;
isTester = MQLInfoInteger(MQL_TESTER);
return(INIT_SUCCEEDED);
}
CNews* newsArray[];
MqlDateTime datetimeStruct;
datetime lastRecordTime;
int newsLastIndex = 0;
bool isTester;
int arrowIndex, lineIndex;
color GetColorLineNews(int whereNews)
{
switch (whereNews)
{
case NEWS_BASE:
return baseColor;
case NEWS_QUOTE:
return quoteColor;
default:
return bothColor;
}
}
color GetColorByImpact(NewsImpactEnum impact)
{
switch (impact)
{
case HIGH_IMPACT:
return clrRed;
case MEDIUM_IMPACT:
return clrOrange;
case LOW_IMPACT:
return clrYellow;
default:
return clrGray;
}
}
void CreateNewLine(color lineColor, datetime time)
{
if (!show_news_lines) return;
string timeName = TimeToString(time);
string lineName = LINE_NAME(lineIndex++);
if (isTester)
{
lineName = timeName;
}
ObjectCreate(ChartID(), lineName, OBJ_VLINE, 0, time, 0.0);
ObjectSetInteger(ChartID(), lineName, OBJPROP_COLOR, lineColor);
ObjectSetInteger(ChartID(), lineName, OBJPROP_STYLE, STYLE_DASHDOTDOT);
ObjectSetInteger(ChartID(), lineName, OBJPROP_BACK, true);
ObjectSetInteger(ChartID(), lineName, OBJPROP_SELECTABLE, false);
ObjectSetString(ChartID(), lineName, OBJPROP_TOOLTIP, timeName);
}
void CreateNewsArray(CNews* &newsArr[], datetime timeN, double priceN)
{
for (int k=ArraySize(newsArr)-1; k>=0; k--)
{
double priceEnd = priceN + Point() * (distanceFirst + distanceBetween* k);
string name;
string newsText = newsArr[k].NewsStringify();
if (isTester)
{
name = newsText;
}
else
{
arrowIndex++;
name = ARROW_NAME(arrowIndex);
}
ObjectCreate(ChartID(), name, OBJ_ARROW, 0, timeN, priceEnd);
ObjectSetInteger(ChartID(), name, OBJPROP_ARROWCODE, 174);
//ObjectSetInteger(ChartID(), name, OBJPROP_BACK, true);
ObjectSetInteger(ChartID(), name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(ChartID(), name, OBJPROP_ZORDER, 1);
ObjectSetInteger(ChartID(), name, OBJPROP_COLOR, GetColorByImpact(newsArr[k]._impact));
ObjectSetString(ChartID(), name, OBJPROP_TOOLTIP, newsText);
}
}
void MoveArrows(datetime timeCurrent, double highCurrent)
{
int lastArrowIndex = arrowIndex-1;
datetime arrowTime = (datetime)ObjectGetInteger(ChartID(), ARROW_NAME(lastArrowIndex), OBJPROP_TIME);
int id = 0;
while (arrowTime >= timeCurrent)
{
ObjectSetDouble(ChartID(), ARROW_NAME(lastArrowIndex), OBJPROP_PRICE, highCurrent + id * Point() * distanceBetween);
id++;
lastArrowIndex--;
arrowTime = (datetime)ObjectGetInteger(ChartID(), ARROW_NAME(lastArrowIndex), OBJPROP_TIME);
}
}
void DeInitEvent()
{
for (int j=ArraySize(newsArray)-1; j>=0;j--)
{
delete newsArray[j];
}
if (isTester) return;
Comment("Removing News Indicator...");
ObjectsDeleteAll(ChartID(), LINE_PREFIX);
ObjectsDeleteAll(ChartID(), ARROW_PREFIX);
ObjectsDeleteAll(ChartID(), FUTURE_PREFIX);
Sleep(500);
Comment("");
ChartRedraw(0);
}
void CalculateEvent(const datetime &time[], const double &high[], double &arrowBufer[], int limit)
{
ArraySetAsSeries(high, true);
ArraySetAsSeries(time, true);
for (int i=limit; i>0 && !IsStopped(); i--)
{
TimeToStruct(time[i-1], datetimeStruct);
TransformDayToWeekInit(datetimeStruct);
datetime currentWeek = StructToTime(datetimeStruct);
if (lastRecordTime < currentWeek)
{
ReadNewsFromFileAndCurrency(currencyBase, currencyQuote, currentWeek, newsArray);
lastRecordTime = currentWeek;
newsLastIndex = 0;
}
int drawLine = NO_NEWS;
int impact = NON_ECONOMIC;
CNews* currentNews[];
int j;
for (j=newsLastIndex; j<ArraySize(newsArray) && !IsStopped(); j++)
{
if (newsArray[j]._time >= GetEndOfCandle(time[i-1])) break;
if (newsArray[j]._currency == "All" || newsArray[j]._currency == currencyBase)
{
if ((indicatorMode == FILTER_NEWS && newsArray[j].CheckCoinciding(arrayFilterBase)) ||
(indicatorMode == FILTER_IMPACT && CheckImpact(newsArray[j].GetImpact())) )
{
drawLine = NEWS_BASE;
impact = MathMax(impact, newsArray[j].GetImpact());
AddNewToArray(currentNews, newsArray[j]);
}
}
if (newsArray[j]._currency == "All" || newsArray[j]._currency == currencyQuote)
{
if ((indicatorMode == FILTER_NEWS && newsArray[j].CheckCoinciding(arrayFilterQuote)) ||
(indicatorMode == FILTER_IMPACT && CheckImpact(newsArray[j].GetImpact())) )
{
if (drawLine == NEWS_BASE || drawLine == NEWS_BOTH) drawLine = NEWS_BOTH;
else drawLine = NEWS_QUOTE;
impact = MathMax(impact, newsArray[j].GetImpact());
AddNewToArray(currentNews, newsArray[j]);
}
}
}
newsLastIndex = j;
if (drawLine != NO_NEWS)
{
CreateNewLine(GetColorLineNews(drawLine), time[i-1]);
//Crear noticias individuales
//CreateNewsArray(currentNews, time[i-1], high[i-1]);
for (int k=ArraySize(currentNews)-1; k>=0; k--)
{
double priceEnd = high[i-1] + Point() * (distanceFirst + distanceBetween* k);
string name;
string newsText = currentNews[k].NewsStringify();
if (isTester)
{
name = newsText;
}
else
{
arrowIndex++;
name = ARROW_NAME(arrowIndex);
}
ObjectCreate(ChartID(), name, OBJ_ARROW, 0, time[i-1], priceEnd);
ObjectSetInteger(ChartID(), name, OBJPROP_ARROWCODE, 174);
//ObjectSetInteger(ChartID(), name, OBJPROP_BACK, true);
ObjectSetInteger(ChartID(), name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(ChartID(), name, OBJPROP_ZORDER, 1);
ObjectSetInteger(ChartID(), name, OBJPROP_COLOR, GetColorByImpact(currentNews[k]._impact));
ObjectSetString(ChartID(), name, OBJPROP_TOOLTIP, newsText);
}
}
ArrowBuffer[i-1]=drawLine;
impactBuffer[i-1]=impact;
ArrayFree(currentNews);
}
if (limit>=1 && !IsStopped()) //Only on new candles
{
ObjectsDeleteAll(ChartID(), FUTURE_PREFIX); //Restart future news
int future_index = 0;
datetime futureTime = time[0];
for (int i=0; i<indicatorDisplacement; i++)
{
futureTime = GetEndOfCandle(futureTime);
TimeToStruct(futureTime, datetimeStruct);
TransformDayToWeekInit(datetimeStruct);
datetime currentWeek = StructToTime(datetimeStruct);
if (lastRecordTime < currentWeek)
{
ReadNewsFromFileAndCurrency(currencyBase, currencyQuote, currentWeek, newsArray);
lastRecordTime = currentWeek;
newsLastIndex = 0;
}
int drawLine = NO_NEWS;
int impact = NON_ECONOMIC;
CNews* currentNews[];
int j;
for (j=newsLastIndex; j<ArraySize(newsArray) && !IsStopped(); j++)
{
if (newsArray[j]._time >= GetEndOfCandle(futureTime)) break;
if (newsArray[j]._currency == "All" || newsArray[j]._currency == currencyBase)
{
if ((indicatorMode == FILTER_NEWS && newsArray[j].CheckCoinciding(arrayFilterBase)) ||
(indicatorMode == FILTER_IMPACT && CheckImpact(newsArray[j].GetImpact())) )
{
drawLine = NEWS_BASE;
impact = MathMax(impact, newsArray[j].GetImpact());
AddNewToArray(currentNews, newsArray[j]);
}
}
if (newsArray[j]._currency == "All" || newsArray[j]._currency == currencyQuote)
{
if ((indicatorMode == FILTER_NEWS && newsArray[j].CheckCoinciding(arrayFilterQuote)) ||
(indicatorMode == FILTER_IMPACT && CheckImpact(newsArray[j].GetImpact())) )
{
if (drawLine == NEWS_BASE || drawLine == NEWS_BOTH) drawLine = NEWS_BOTH;
else drawLine = NEWS_QUOTE;
impact = MathMax(impact, newsArray[j].GetImpact());
AddNewToArray(currentNews, newsArray[j]);
}
}
}
newsLastIndex = j;
if (drawLine != NO_NEWS)
{
CreateNewLine(GetColorLineNews(drawLine), futureTime);
//Crear noticias individuales
//CreateNewsArray(currentNews,futureTime, high[0]);
for (int x=ArraySize(currentNews)-1; x>=0; x--)
{
future_index++;
string name;
string newsText = currentNews[x].NewsStringify();
if (isTester)
name = newsText;
else
{
arrowIndex++;
name = FUT_ARROW_NAME(future_index);
}
ObjectCreate(ChartID(), name, OBJ_ARROW, 0, futureTime, high[0] + Point()*(distanceFirst + distanceBetween*x));
ObjectSetInteger(ChartID(), name, OBJPROP_ARROWCODE, 174);
ObjectSetInteger(ChartID(), name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(ChartID(), name, OBJPROP_ZORDER, 1);
//ObjectSetInteger(ChartID(), name, OBJPROP_BACK, true);
ObjectSetInteger(ChartID(), name, OBJPROP_COLOR, GetColorByImpact(currentNews[x]._impact));
ObjectSetString(ChartID(), name, OBJPROP_TOOLTIP, newsText);
}
}
ArrayFree(currentNews);
}
}
if (!IsStopped())
MoveArrows(time[0], high[0] + distanceFirst * Point());
}
bool CheckImpact(NewsImpactEnum impact)
{
switch (impact)
{
case NON_ECONOMIC:
return showNoImpact;
case LOW_IMPACT:
return showLowImpact;
case MEDIUM_IMPACT:
return showMediumImpact;
case HIGH_IMPACT:
return showHighImpact;
}
return false;
}
int OnInit()
{
if (MQLInfoInteger(MQL_LICENSE_TYPE) == LICENSE_DEMO)
{
Print("Not valid license");
return INIT_FAILED;
}
//Remove all first
ObjectsDeleteAll(ChartID(), LINE_PREFIX);
ObjectsDeleteAll(ChartID(), ARROW_PREFIX);
IndicatorSetInteger(INDICATOR_DIGITS, 0);
SetIndexBuffer(0, ArrowBuffer, INDICATOR_DATA);
ArraySetAsSeries(ArrowBuffer, true);
SetIndexBuffer(1, impactBuffer, INDICATOR_DATA);
ArraySetAsSeries(impactBuffer, true);
return OnInitProcessCurrencies();
}
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
int limit = MathMin(rates_total, rates_total - prev_calculated);
CalculateEvent(time, high, ArrowBuffer, limit);
return(rates_total);
}
void OnDeinit(const int reason)
{
DeInitEvent();
}