After we’ve programmatically drawn a trend line we can automatically generate trend line alerts or create custom alerts triggered on bar close. But how do we generate custom intra-bar alerts without the script generating dozens of those notifications per bar?

MultiCharts .NET lines and automatically generated alerts

Drawing MultiCharts .NET trend lines requires the DrwTrendLine.Create() method, which returns a reference to the line it draws (MultiCharts, 2014; PowerLanguage .NET Help, n.d.). With that reference stored in an ITrendLineObject variable, we can access the line’s methods and properties such as the Alert property for enabling a trend line’s alert setting.

But the trend line alert messages generated when Alert is enabled have two disadvantages. First, we cannot change the message to something more informative than “Trend Line triggered”. Second, we cannot place a limit on how many intra-bar alerts are generated per bar. While seeing an alert a few times per bar is helpful when we missed the first message, seeing a bunch of alerts is only annoying.

Creating custom trend line alerts in MultiCharts .NET

Both disadvantages are not present when we programmatically generate the alerts ourselves. For that we first need to retrieve the trend line’s price value with the PriceValue() method, which returns the line’s price value for any DateTime value on the chart (MultiCharts, 2014; PowerLanguage .NET Help, n.d.). By knowing the trend line’s price coordinate for the current bar we can evaluate if the latest price update crossed above or below the line.

Of course the alerts themselves also need to be generated programmatically. That’s done with the Alerts.Alert() method, in which we can pass a textual string to generate an alert with that message (MultiCharts, 2014). The type of alerts this method can trigger are a pop-up window, sound, and/or email notification (MultiCharts Wiki, 2013).

With intra-bar alerts it’s also helpful to limit the number of alerts per bar. One way to prevent a flood of alert messages is by using a counter variable. By incrementing that variable each time an intra-bar alert is generated, and resetting it when the bar closes, we can track the amount of intra-bar alerts. Then, before calling the Alerts.Alert() method again, the variable’s value need to be checked to see it didn’t exceed our predefined maximum.

Enabling indicator alerts in MultiCharts .NET

Before a MultiCharts .NET indicator or strategy can generate alerts with Alerts.Alert(), the script’s alert settings need to be enabled manually. To find out an indicator’s current alert settings, right-click on the indicator’s name in the chart’s Status Line and select ‘Format [indicator name]‘. Then move to the ‘Alerts’ tab to specify the alert settings:

Configuring intra-bar alerts in MultiCharts .NET

Example: generating intra-bar alerts in MultiCharts .NET

When the example indicator is applied to an EUR/USD chart, it draws a trend line like the following:

Example of intra-bar alerts in MultiCharts .NET - 1

Once a real-time tick crosses the trend line, the script generates alerts like the following:

Example of intra-bar alerts in MultiCharts .NET - 2 Example of intra-bar alerts in MultiCharts .NET - 3

Generating intra-bar trend line alerts in MultiCharts .NET

The example indicator looks as follows:

[SameAsSymbol(true), RecoverDrawings(false), UpdateOnEveryTick(true)]
public class Example_TrendLineIntraBarAlert : IndicatorObject
{
    public Example_TrendLineIntraBarAlert(object _ctx) : base(_ctx) { }

    private ITrendLineObject trendLine;
    private int alertCounter;
    private double prevTick;

    protected override void StartCalc()
    {
        trendLine = null;
    }

    protected override void CalcBar()
    {
        // Draw the trend line on the last bar
        if (Bars.LastBarOnChart && trendLine == null)
        {
            double midpoint = (Bars.High[1] +
                Bars.Low[1]) / 2;

            ChartPoint begin = new ChartPoint(
                Bars.Time[2], midpoint);

            ChartPoint end = new ChartPoint(
                Bars.Time[1], midpoint);

            trendLine = DrwTrendLine.Create(begin, end);

            // Change line's appearance
            trendLine.Size     = 1;
            trendLine.Color    = Color.LimeGreen;
            trendLine.ExtLeft  = true;
            trendLine.ExtRight = true;
        }

        // Generate alerts when they're enabled and the line object is present
        if (Alerts.CheckAlertLastBar && trendLine != null)
        {
            double priceOfLine = trendLine.PriceValue(Bars.Time[0]);

            // Only generate alerts with a valid previous tick and don't generate
            // more than 3 alerts per bar
            if ((prevTick > 0) && (alertCounter < 2))
            {
                if ((Bars.Close[0] > priceOfLine) && (prevTick < priceOfLine))
                {
                    Alerts.Alert("Price ({0}) crossed above line intra-bar", 
                        Bars.Close[0]);

                    alertCounter++;
                }

                if ((Bars.Close[0] < priceOfLine) && (prevTick > priceOfLine))
                {
                    Alerts.Alert(
                        "Price ({0}) crossed below line intra-bar" + 
                        " (previous tick: {1})", 
                        Bars.Close[0], prevTick);

                    alertCounter++;
                }
            }

            prevTick = Bars.Close[0];
        }

        // Reset counter variable
        if (Bars.Status == EBarState.Close)
            alertCounter = 0;
    }
}

We start with adding a couple of MultiCharts .NET class attributes. With SameAsSymbol set to true the indicator is displayed on the data series and not in a separate subchart. RecoverDrawings set to false prevents intra-bar generated drawings from being removed (MultiCharts, 2014). And with UpdateOnEveryTick set to true the indicator is calculated on every real-time tick.

Next we declare several variables. The ITrendLineObject variable named trendLine is used to store the trend line reference, which we use later on to access the line’s methods and properties. The alertCounter integer variable is used with counting the number of intra-bar alerts. And the double variable (prevTick) will hold the price during the previous script update so it can be compared with the current tick.

In the StartCalc() method we set the trendLine variable to its default value of null, which makes this reference variable not point to any object (Albahari & Albahari, 2012). StartCalc() is, by the way, executed only once before a script is fully (re)calculated on all price bars (MultiCharts, 2014). Since we only draw a trend line in CalcBar() when trendLine equals null, we need to reset this variable at the start of every calculation cycle. Otherwise the trend line won’t be drawn when, for example, the indicator settings are changed or the script is turned off and on again.

Drawing a horizontal trend line in MultiCharts .NET

Next is the CalcBar() method that contains two parts: drawing the trend line and generating the alerts. Before we draw the horizontal trend line, an if statement evaluates two conditions: whether the current bar is the last of the data series (then Bars.LastBarOnChart returns true; PowerLanguage .NET Help, n.d.) and if the trendLine variable equals (==) null. This latter condition ensures we only draw the trend line once. That’s because trendLine is given a value different than null inside the if statement.

That if statement code block begins with calculating the midpoint of the previous bar by adding that bar’s high (Bars.High[1]) and low (Bars.Low[1]) together and then dividing by two. We assign that calculated value to the midpoint double variable.

Then we define two ChartPoint struct variables named begin and end. The first of these chart locations is set to the time of two bars ago (Bars.Time[2]) while the second is set to the previous bar’s time (Bars.Time[1]). Both chart coordinates have their price value set to the midpoint variable. With both coordinates defined, we call the DrwTrendLine.Create() method to draw a trend line between them. Since both ChartPoint variables have the same price value, but different time values, a small horizontal line is drawn.

We assign the value returned by DrwTrendLine.Create() to the trendLine variable. Through trendLine we can now access the line’s properties and methods. So we first change the line’s appearance by assigning new values to its Size and Color properties. Then we extend the trend line in both directions by setting ExtLeft and ExtRight to true.

Validating alert generation programmatically

The second part of CalcBar() contains the code to trigger alerts. But before these are generated, an if statement evaluates two conditions. The first is whether the Alerts.CheckAlertLastBar property returns true, which it does when alerts are enabled (see image above) and the current bar is the last of the data series (PowerLanguage .NET Help, n.d.). By verifying that property’s value we prevent the script from unnecessarily processing the code for generating alerts.

The second condition of the if statement is whether the trendLine variable is unequal to (!=) null. Since we use that reference variable in the if statement’s code block, a NullReferenceException will be triggered when it’s value is null (see Stellman & Greene, 2010). In other words, knowing that trendLine is something else than null means we can safely use this variable to access the line’s properties and methods.

When both conditions are true, we declare a double variable (priceOfLine) and assign it the value returned by the line’s PriceValue() method. By passing the DateTime value of the current bar (Bars.Time[0]) inside this method, it returns the line’s price value for that bar (see PowerLanguage .NET Help, n.d.). By knowing this price value, we can determine if the recent price is above or below the line.

But first a nested if statement evaluates two expressions: whether prevTick is greater than (>) 0 and if alertCounter less than (<) 2. The prevTick variable holds the price of the previous real-time tick; when this variable is 0, we don’t yet have a previous tick to compare the current price with. The zero-based alertCounter variable is incremented with 1 each time an alert is generated. By first checking this variable’s value, we prevent a flood of alert messages since now only three alerts per bar are allowed.

Generating custom trend line alerts intra-bar

When both if statement conditions evaluate to true, an additional two if statements compare the current tick (returned by Bars.Close[0] when the script is calculated intra-bar) and the previous tick (prevTick) with the trend line’s price value (priceOfLine). After all, an upward cross of the trend line requires that the current tick is above the line while the previous tick was below it.

Should one of those if statements evaluate to true (meaning the price crossed the trend line), we call the Alerts.Alert() method with a literal string that includes a substitution parameter. Those numbers between { and } are placeholders and will be replaced with the value(s) following the string (Sempf, Sphar, & Davis, 2010). That allows us to insert values (like Bars.Close[0]) and variables (such as prevTick) inside the alert message. Once the alert message is generated, we add 1 to the value of alertCounter with the postfix increment operator (++) to keep track of how many alerts are triggered.

Next we assign the current tick (Bars.Close[0]) to the prevTick variable. By doing so this variable holds the previous tick during the next script calculation. We end the example with an if statement that checks if the script is currently calculated on bar close, in which case the Bars.Status property equals the EBarState.Close enumerated value (see MultiCharts, 2014). When that happens the alertCounter variable is reset to zero so that it can starting counting anew with the next bar.

For more examples that combine MultiCharts .NET trend lines with alerts, see automatically generating trend line alerts (for enabling alert messages through the line’s Alert property) and generating custom trend line alerts on bar close.

Summary

The DrwTrendLine.Create() method draws trend lines and returns a reference to the line made. Through that reference we can access a line’s PriceValue() method, which returns the line’s price value for any DateTime value on the chart. By comparing that value with the current price we can see if the recent tick crossed above or below the trend line. When that happens, we can trigger an alert with the Alerts.Alert() method. With a counting variable we place a limit on how many intra-bar alerts can be generated.

Complete MultiCharts .NET indicator example

using System;
using System.Drawing;
using System.Linq;
using PowerLanguage.Function;

namespace PowerLanguage.Indicator
{
    [SameAsSymbol(true), RecoverDrawings(false), UpdateOnEveryTick(true)]
    public class Example_TrendLineIntraBarAlert : IndicatorObject
    {
        public Example_TrendLineIntraBarAlert(object _ctx) : base(_ctx) { }

        private ITrendLineObject trendLine;
        private int alertCounter;
        private double prevTick;

        protected override void StartCalc()
        {
            trendLine = null;
        }

        protected override void CalcBar()
        {
            // Draw the trend line on the last bar
            if (Bars.LastBarOnChart && trendLine == null)
            {
                double midpoint = (Bars.High[1] +
                    Bars.Low[1]) / 2;

                ChartPoint begin = new ChartPoint(
                    Bars.Time[2], midpoint);

                ChartPoint end = new ChartPoint(
                    Bars.Time[1], midpoint);

                trendLine = DrwTrendLine.Create(begin, end);

                // Change line's appearance
                trendLine.Size     = 1;
                trendLine.Color    = Color.LimeGreen;
                trendLine.ExtLeft  = true;
                trendLine.ExtRight = true;
            }

            // Generate alerts when they're enabled and the line object is present
            if (Alerts.CheckAlertLastBar && trendLine != null)
            {
                double priceOfLine = trendLine.PriceValue(Bars.Time[0]);

                // Only generate alerts with a valid previous tick and don't generate
                // more than 3 alerts per bar
                if ((prevTick > 0) && (alertCounter < 2))
                {
                    if ((Bars.Close[0] > priceOfLine) && (prevTick < priceOfLine))
                    {
                        Alerts.Alert("Price ({0}) crossed above line intra-bar", 
                            Bars.Close[0]);

                        alertCounter++;
                    }

                    if ((Bars.Close[0] < priceOfLine) && (prevTick > priceOfLine))
                    {
                        Alerts.Alert(
                            "Price ({0}) crossed below line intra-bar" + 
                            " (previous tick: {1})", 
                            Bars.Close[0], prevTick);

                        alertCounter++;
                    }
                }

                prevTick = Bars.Close[0];
            }

            // Reset counter variable
            if (Bars.Status == EBarState.Close)
                alertCounter = 0;
        }
    }
}

Want to learn more about C#, the programming language that drives MultiCharts .NET? Checkout my C# programming tutorials.


References

Albahari, J. & Albahari, B. (2012). C# 5.0 in a Nutshell: The Definitive Reference (5th edition). Sebastopol, CA: O’Reilly Media.

MultiCharts (2014). MultiCharts .NET Programming Guide (version 1.1). Retrieved from http://www.multicharts.com/downloads/MultiCharts.NET-ProgrammingGuide-v1.1.pdf

MultiCharts Wiki (2013, May 10). Using Alerts. Retrieved on April 15, 2015, from http://www.multicharts.com/trading-software/index.php/Using_Alerts

PowerLanguage .NET Help (n.d.). Retrieved on November 18, 2014, from http://www.multicharts.com/downloads/PowerLanguage.NET.chm

Sempf, B., Sphar, C., & Davis, S.R. (2010). C# 2010 All-In-One for Dummies. Hoboken, NJ: John Wiley & Sons.

Stellman, A. & Greene, J. (2010). Head First C#: A Brain-Friendly Guide (2nd edition). Sebastopol, CA: O’Reilly Media.

Visit programming tutorials for more helpful coding articles.