Moving Average Crossover Alert

 

Can anyone teach me how to change the source code below so that Moving Average crossover alert happens once the cross has happened instead of on a current candle?

I know that this must be a simple problem for most of you probably but not for me since I am struggling with coding.

Thank you in advance.

#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1 clrRed
#property indicator_color2 clrGreen
#property indicator_type1 DRAW_LINE
#property indicator_type2 DRAW_LINE
#property indicator_label1 "Fast MA"
#property indicator_label2 "Slow MA"

#include <MQLTA ErrorHandling.mqh>
#include <MQLTA Utils.mqh>

enum ENUM_TRADE_SIGNAL
{
    SIGNAL_BUY = 1,    // Buy
    SIGNAL_SELL = -1,  // Sell
    SIGNAL_NEUTRAL = 0 // Neutral
};

enum ENUM_CANDLE_TO_CHECK
{
    CURRENT_CANDLE = 0,  // Current candle
    CLOSED_CANDLE = 1    // Previous candle
};

input string Comment1 = "========================";   // MQLTA Moving Average Crossover Alert
input string IndicatorName = "MQLTA-MACA";            // Indicator short name

input string Comment2 = "========================";        // Indicator parameters
input int MAFastPeriod = 25;                               // Fast moving average period
input int MAFastShift = 0;                                 // Fast moving average shift
input ENUM_MA_METHOD MAFastMethod = MODE_SMA;              // Fast moving average method
input ENUM_APPLIED_PRICE MAFastAppliedPrice = PRICE_CLOSE; // Fast moving average applied price
input int MASlowPeriod = 50;                               // Slow moving average period
input int MASlowShift = 0;                                 // Slow moving average shift
input ENUM_MA_METHOD MASlowMethod = MODE_SMA;              // Slow moving average method
input ENUM_APPLIED_PRICE MASlowAppliedPrice = PRICE_CLOSE; // Slow moving average applied price
input ENUM_CANDLE_TO_CHECK CandleToCheck = CURRENT_CANDLE; // Candle to use for analysis
input int BarsToScan = 500;                                // Number of candles to analyze

input string Comment_3 = "====================";   // Notification options
input bool EnableNotify = false;                   // Enable notifications feature
input bool SendAlert = false;                      // Send alert notification
input bool SendApp = false;                        // Send notification to mobile
input bool SendEmail = false;                      // Send notification via email

input string Comment_4 = "====================";   // Drawing options
input bool EnableDrawArrows = true;                // Draw signal arrows
input int ArrowBuy = 241;                          // Buy arrow code
input int ArrowSell = 242;                         // Sell arrow code
input int ArrowSize = 3;                           // Arrow size (1-5)
input color ArrowBuyColor = clrGreen;              // Buy arrow color
input color ArrowSellColor = clrRed;               // Sell arrow color

double BufferMASlow[];
double BufferMAFast[];

datetime LastNotificationTime;
ENUM_TRADE_SIGNAL LastNotificationDirection;
int Shift = 0;

int OnInit(void)
{

    IndicatorSetString(INDICATOR_SHORTNAME, IndicatorName);

    OnInitInitialization();
    if (!OnInitPreChecksPass())
    {
        return INIT_FAILED;
    }

    InitialiseBuffers();

    return INIT_SUCCEEDED;
}

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[])
{
    if ((rates_total <= MASlowPeriod) || (MASlowPeriod <= 0)) return 0;
    if ((rates_total <= MAFastPeriod) || (MAFastPeriod <= 0)) return 0;
    if (MAFastPeriod > MASlowPeriod) return 0;

    bool IsNewCandle = CheckIfNewCandle();
    
    if (Bars(Symbol(), PERIOD_CURRENT) < (MASlowPeriod + MASlowShift))
    {
        Print("Not Enough Historical Candles");
        return 0;
    }

    int pos = 0, upTo;
    if ((prev_calculated == 0) || (IsNewCandle))
    {
        upTo = BarsToScan - 1;
    }
    else
    {
        upTo = 0;
    }

    for (int i = pos; (i <= upTo) && (!IsStopped()); i++)
    {
        BufferMASlow[i] = iMA(Symbol(), PERIOD_CURRENT, MASlowPeriod, MASlowShift, MASlowMethod, MASlowAppliedPrice, i);
        BufferMAFast[i] = iMA(Symbol(), PERIOD_CURRENT, MAFastPeriod, MAFastShift, MAFastMethod, MAFastAppliedPrice, i);
    }

    if ((IsNewCandle) || (prev_calculated == 0))
    {
        if (EnableDrawArrows) DrawArrows();
    }

    if (EnableDrawArrows) DrawArrow(0);

    if (EnableNotify) NotifyHit();

    return rates_total;
}

void OnDeinit(const int reason)
{
    CleanChart();
}

void OnInitInitialization()
{
    LastNotificationTime = TimeCurrent();
    LastNotificationDirection = SIGNAL_NEUTRAL;
    Shift = CandleToCheck;
}

bool OnInitPreChecksPass()
{
    if ((MASlowPeriod <= 0) || (MAFastPeriod <= 0) || (MAFastPeriod > MASlowPeriod))
    {
        Print("Wrong input parameter");
        return false;
    }
    return true;
}

void CleanChart()
{
    ObjectsDeleteAll(ChartID(), IndicatorName);
}

void InitialiseBuffers()
{
    SetIndexBuffer(0, BufferMAFast);
    SetIndexShift(0, MAFastShift);
    SetIndexDrawBegin(0, MAFastPeriod + MAFastShift);
    SetIndexBuffer(1, BufferMASlow);
    SetIndexShift(1, MASlowShift);
    SetIndexDrawBegin(1, MASlowPeriod + MASlowShift);
}

datetime NewCandleTime = TimeCurrent();
bool CheckIfNewCandle()
{
    if (NewCandleTime == iTime(Symbol(), 0, 0)) return false;
    else
    {
        NewCandleTime = iTime(Symbol(), 0, 0);
        return true;
    }
}

//Check if it is a trade Signla 0 - Neutral, 1 - Buy, -1 - Sell
ENUM_TRADE_SIGNAL IsSignal(int i)
{
    int j = i + Shift;
    if(BufferMAFast[j + 1] < BufferMASlow[j + 1] && BufferMAFast[j] > BufferMASlow[j]) return SIGNAL_BUY;
    if(BufferMAFast[j + 1] > BufferMASlow[j + 1] && BufferMAFast[j] < BufferMASlow[j]) return SIGNAL_SELL;

    return SIGNAL_NEUTRAL;
}

void NotifyHit()
{
    if (!EnableNotify) return;
    if ((!SendAlert) && (!SendApp) && (!SendEmail)) return;
    if ((CandleToCheck == CLOSED_CANDLE) && (Time[0] <= LastNotificationTime)) return;
    ENUM_TRADE_SIGNAL Signal = IsSignal(0);
    if (Signal == SIGNAL_NEUTRAL)
    {
        LastNotificationDirection = Signal;
        return;
    }
    if (Signal == LastNotificationDirection) return;
    string EmailSubject = IndicatorName + " " + Symbol() + " Notification ";
    string EmailBody = AccountCompany() + " - " + AccountName() + " - " + IntegerToString(AccountNumber()) + "\r\n\r\n" + IndicatorName + " Notification for " + Symbol() + " @ " + EnumToString((ENUM_TIMEFRAMES)Period()) + "\r\n\r\n";
    string AlertText = IndicatorName + " - " + Symbol() + " @ " + EnumToString((ENUM_TIMEFRAMES)Period()) + " ";
    string AppText = AccountCompany() + " - " + AccountName() + " - " + IntegerToString(AccountNumber()) + " - " + IndicatorName + " - " + Symbol() + " @ " + EnumToString((ENUM_TIMEFRAMES)Period()) + " - ";
    string Text = "";

    Text += "Slow and Fast Moving Average Crossed - " + ((Signal == SIGNAL_SELL) ? "Sell" : "Buy");

    EmailBody += Text;
    AlertText += Text;
    AppText += Text;
    if (SendAlert) Alert(AlertText);
    if (SendEmail)
    {
        if (!SendMail(EmailSubject, EmailBody)) Print("Error sending email " + IntegerToString(GetLastError()));
    }
    if (SendApp)
    {
        if (!SendNotification(AppText)) Print("Error sending notification " + IntegerToString(GetLastError()));
    }
    LastNotificationTime = Time[0];
    LastNotificationDirection = Signal;
}

void DrawArrows()
{
    RemoveArrows();
    if(!EnableDrawArrows || BarsToScan == 0) return;
    if ((!EnableDrawArrows) || (BarsToScan == 0)) return;
    int MaxBars = Bars(Symbol(), PERIOD_CURRENT);
    if (MaxBars > BarsToScan) MaxBars = BarsToScan;
    for (int i = MaxBars - 2; i >= 1; i--)
    {
        DrawArrow(i);
    }
}

void RemoveArrows()
{
    ObjectsDeleteAll(ChartID(), IndicatorName + "-ARWS-");
}

void DrawArrow(int i)
{
    RemoveArrowCurr();
    if (!EnableDrawArrows)
    {
        RemoveArrows();
        return;
    }
    ENUM_TRADE_SIGNAL Signal = IsSignal(i);
    if (Signal == SIGNAL_NEUTRAL) return;
    datetime ArrowDate = iTime(Symbol(), 0, i);
    string ArrowName = IndicatorName + "-ARWS-" + IntegerToString(ArrowDate);
    double ArrowPrice = 0;
    int ArrowType = 0;
    color ArrowColor = 0;
    int ArrowAnchor = 0;
    string ArrowDesc = "";
    if (Signal == SIGNAL_BUY)
    {
        ArrowPrice = Low[i];
        ArrowType = ArrowBuy;
        ArrowColor = ArrowBuyColor;
        ArrowAnchor = ANCHOR_TOP;
        ArrowDesc = "BUY";
    }
    if(Signal == SIGNAL_SELL)
    {
        ArrowPrice = High[i];
        ArrowType = ArrowSell;
        ArrowColor = ArrowSellColor;
        ArrowAnchor = ANCHOR_BOTTOM;
        ArrowDesc = "SELL";
    }
    ObjectCreate(0, ArrowName, OBJ_ARROW, 0, ArrowDate, ArrowPrice);
    ObjectSetInteger(0, ArrowName, OBJPROP_COLOR, ArrowColor);
    ObjectSetInteger(0, ArrowName, OBJPROP_SELECTABLE, false);
    ObjectSetInteger(0, ArrowName, OBJPROP_HIDDEN, true);
    ObjectSetInteger(0, ArrowName, OBJPROP_ANCHOR, ArrowAnchor);
    ObjectSetInteger(0, ArrowName, OBJPROP_ARROWCODE, ArrowType);
    ObjectSetInteger(0, ArrowName, OBJPROP_WIDTH, ArrowSize);
    ObjectSetInteger(0, ArrowName, OBJPROP_STYLE, STYLE_SOLID);
    ObjectSetInteger(0, ArrowName, OBJPROP_BGCOLOR, ArrowColor);
    ObjectSetString(0, ArrowName, OBJPROP_TEXT, ArrowDesc);
}

void RemoveArrowCurr()
{
    datetime ArrowDate = iTime(Symbol(), 0, Shift);
    string ArrowName = IndicatorName + "-ARWS-" + IntegerToString(ArrowDate);
    ObjectDelete(0, ArrowName);
}
//+------------------------------------------------------------------+
 

So, no one is here to help me.

I guess this forum is full of mean people then.

I have never received any help here. Ever.

All I ever get is someone complaining about every single thing I ask help for.

I do not understand what this forum is all about then.

 

Could the problem be that maybe you seem to expect to be spoon fed, instead of actually putting effort into your own learning?

Your subject even states "Teach Me" as if that is somehow our obligation to do so. If you want to be taught then hire a teacher or mentor.

If others were able to study on their own, by analysing the code in the CodeBase, by referencing the documentation, by experimenting, and then on the odd occasion request guidance on a specific issue — then surely you can do the same too, right?

 
Fernando Carreiro #:

Could the problem be that maybe you seem to expect to be spoon fed, instead of actually putting effort into your own learning?

Your subject even states "Teach Me" as if that is somehow our obligation to do so. If you want to be taught then hire a teacher or mentor.

If others were able to study on their own, by analysing the code in the CodeBase, by referencing the documentation, by experimenting, and then on the odd occasion request guidance on a specific issue — then surely you can do the same too, right?

Dear Fernando,

I knew that you are going to come here and be mean to me.

First, answering to your question, technically, yes, you could put exactly the way you have just said. On the other hand, if someone is participating in this forum and KNOWS the solution to the question, is it hard just to quickly reply? Maybe, for you it is hard. I guess everyone has their own perspective.

Second, I think you have missed on something. I did not write ''Teach Me''. I wrote ''Please, Teach Me''. Does this still sound like an obligation? Or more like a help alarm?

Finally, yes, I can study CodeBase and I will for sure but now I just needed a quick help.

p.s. I am very disappointed with this forum and people. So, do not worry, Fernando, I will never ask for anything again here. Bye.

 
Sandra PJ #: Second, I think you have missed on something. I did not write ''Teach Me''. I wrote ''Please, Teach Me''. Does this still sound like an obligation? Or more like a help alarm?

Putting "please" in front of it is pure sophistry and you know that very well.

Sandra PJ #: On the other hand, if someone is participating in this forum and KNOWS the solution to the question, is it hard just to quickly reply? ...
Finally, yes, I can study CodeBase and I will for sure but now I just needed a quick help.

Forum on trading, automated trading systems and testing trading strategies

Is it easy (or difficult) to …?

Fernando Carreiro, 2017.08.30 21:43

This post could be considered a “rant”, but I would like to take the opportunity to relay to those that ask similar questions, the difficulty involved in answering such a simple query – “Is it is easy (or difficult) to …?”

  • “Easy” would imply that anyone can do it, but that is obviously not the case.
  • “Easy” depends on the ability, skill, knowledge and experience of the one that is tasked to do it.
  • That ability, skill, knowledge and experience took time to establish and it had a cost, and if you sum it all up, it can be quite high.
  • So, “easy” means that if you don’t know how to do it yourself (or not able to do it yourself), then you will have to “compensate” someone else to do it for you.
  • You will be “compensating” him or her for all the effort and cost that went into acquiring the ability, skill, knowledge and experience and NOT for the actual time spent carrying out the task.

So, even if the task is just changing one line of code; remember that you are “compensating” the person for his knowledge and skills and not for just one line of code.

The same applies to those that request for something “simple” to be done for free. It is NEVER “simple”. If it were that “simple” then you would be able to do it yourself.

So, respect the hard work and effort the coder went through to gain his skills and knowledge and “compensate” him/her for it. Wanting it for free is being totally disrespectful!

PS! In conclusion, learn to use the Freelance Jobs section and recognise and respect those more skilled, knowledgeable and experienced than you!

EDIT: Please note that I use the term "compensation" and not "payment", because there are many ways to "compensate" for work done. Sometimes a coder will be "compensated" with gained or shared knowledge instead of money, or by an exchange of ideas or other things. It is not always about the money but it is about the "compensation"!

 
Sandra PJ: Can anyone teach me how to change the source code below so that Moving Average crossover alert happens once the cross has happened instead of on a current candle? I know that this must be a simple problem for most of you probably but not for me since I am struggling with coding. Thank you in advance.

If you had actually put in the effort, then you would have seen this — No coding required — just change the parameter in the Properties dialogue box!.

enum ENUM_CANDLE_TO_CHECK
{
    CURRENT_CANDLE = 0,  // Current candle
    CLOSED_CANDLE = 1    // Previous candle
};

...

input ENUM_CANDLE_TO_CHECK CandleToCheck = CURRENT_CANDLE; // Candle to use for analysis
 
Fernando Carreiro #:

If you had actually put in the effort, then you would have seen this — No coding required — just change the parameter in the Properties dialogue box!.

Well, I have tried this and definitely did not come here to demand a free service or something.

I did put in my own efforts first before looking for help (of course, I understand that everyone wishes to get paid for work but I thought there are people on this planet who are maybe willing to help without any monetary compensation).

I guess, I did not manage to explain myself properly above. Sorry.

The problem here is not which candle to analyse (current or previous), the issue is the alert.

The alert appears while candle is still open/forming.

So, my concern is what to change to make the alert happen once the candle is fully closed.

p.s. I don't know how am I going to compensate you.

 
Sandra PJ #The problem here is not which candle to analyse (current or previous), the issue is the alert. The alert appears while candle is still open/forming. So, my concern is what to change to make the alert happen once the candle is fully closed.

The same parameter should also affect the Alerts! Try setting the parameter and test it!

The code you provided is incomplete due to the missing #include files, so I am unable to test it myself.

 
Fernando Carreiro #:

The same parameter should also affect the Alerts! Try setting the parameter and test it!

The code you provided is incomplete due to the missing #include files, so I am unable to test it myself.

Nope, it does not affect alert at all.

All it does it shifts one candle forward.

Very strange.

Usually, there is a possibility to set an alert on a current candle (0) or once the candle is closed (so usually it is called ''previous candle'' (1) - I am aware of this).

I attach files.

Files:
 
Sandra PJ #: Nope, it does not affect alert at all. All it does it shifts one candle forward. Very strange. Usually, there is a possibility to set an alert on a current candle (0) or once the candle is closed (so usually it is called ''previous candle'' (1) - I am aware of this). I attach files.

I've tested the indicator and the alerts are being correctly given when the "Previous candle" is selected for the "Candle to use for analysis" parameter.

Analysis of the source code confirms that the functionality is consistent and working correctly.

At the opening of a new bar, if the cross occurred in the previous bar, an arrow is plotted on the current bar and the alert message appears.

It is working correctly as expected. No correction is required.

 
Fernando Carreiro #:

I've tested the indicator and the alerts are being correctly given when the "Previous candle" is selected for the "Candle to use for analysis" parameter.

Analysis of the source code confirms that the functionality is consistent and working correctly.

At the opening of a new bar, if the cross occurred in the previous bar, an arrow is plotted on the current bar and the alert message appears.

It is working correctly as expected. No correction is required.

But is there a way to keep analysis/arrow on a ''current candle'' but move the alert to the next candle?

For example, I have a MACD indicator and there is an option ''alert shift'', so if I type 1 then alert shifts one candle forward.

Reason: