We can programmatically draw trend lines and generate standard trend line alerts in MultiCharts .NET. But the standard alert messages also display a standard text. How can we generate our own trend line alert messages?

MultiCharts .NET trend lines and triggering custom alerts

Drawing MultiCharts .NET trend lines is done with the DrwTrendLine.Create() method which returns a reference to the line made (MultiCharts, 2014; PowerLanguage .NET Help, n.d.). When we assign that reference to an ITrendLineObject interface variable, we can access the line’s properties and methods through that variable. That allows for things like enabling a line’s alerts with the Alert property.

But with those standard trend line alerts we cannot change the alert message: it will simply say “Trend Line triggered” each time. For more informative messages, we need to generate our own alerts programmatically. That requires two things: the line’s price value for the current bar (so we can compare it to the bar’s closing price) and triggering an alert when the price bar crossed the trend line.

We retrieve trend line prices with the PriceValue() method that returns the line’s price value for any DateTime value on the chart (MultiCharts, 2014; PowerLanguage .NET Help, n.d.). With that we can retrieve a line’s exact price value of a certain price bar, and use that to see if a bar closed above or below a line.

Generating alerts is done with the Alerts.Alert() method, in which we can pass a text in order to generate an alert with a certain message (MultiCharts, 2014). The type of alerts available in MultiCharts .NET are pop-up windows, sounds, and/or e-mails (MultiCharts Wiki, 2013).

But before a script can generate alerts programmatically, its alert setting needs to be manually enabled. To do so, 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:

MultiCharts .NET's alert setting

Example: trend lines and custom alert messages in MultiCharts .NET

When we apply the example indicator to a DX future (US dollar index) chart, it draws a trend line like the following:

Custom MultiCharts .NET alert - 1

Once a price bar closes above or below the line, it generates alerts like the following:

Custom MultiCharts .NET alert - 2 Custom MultiCharts .NET alert - 3

Generating MultiCharts .NET trend line alerts on bar close

The code of this example indicator looks as follows:

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

    private ITrendLineObject trendLine;

    protected override void StartCalc()
    {
        ExecInfo.MaxBarsBack = 10;
        trendLine = null;
    }

    protected override void CalcBar()
    {
        if (!Bars.LastBarOnChart)
            return;

        // Create the trend line
        if (trendLine == null)
        {
            ChartPoint begin = new ChartPoint(
                Bars.Time[10], Bars.Close[10]);

            ChartPoint end = new ChartPoint(
                Bars.Time[3], Bars.Close[3]);

            trendLine = DrwTrendLine.Create(begin, end);

            trendLine.Size     = 2;
            trendLine.Color    = Color.LimeGreen;
            trendLine.ExtRight = true;
        }

        // Generate alerts on trend line crossing
        if (Alerts.Enabled && trendLine != null)
        {
            double trendLinePrice = trendLine.PriceValue(Bars.Time[0]);
            double prevLinePrice  = trendLine.PriceValue(Bars.Time[1]);

            if ((Bars.Close[0] > trendLinePrice) && 
                (Bars.Close[1] < prevLinePrice))
                Alerts.Alert("Close @ {0} above trend line", 
                    Bars.Close[0]);

            if ((Bars.Close[0] < trendLinePrice) && 
                (Bars.Close[1] > prevLinePrice))
                Alerts.Alert("Price ({0}) crossed below trend line", 
                    Bars.Close[0]);
        }
    }
}

We start by setting two attributes: SameAsSymbol to true and UpdateOnEveryTick to false. The first displays the indicator on the data series (and not in a subchart) while the second sets the indicator to be calculated on bar close. That way the script’s alerts will only be generated on bar close and not intra-bar.

Then we declare an ITrendLineObject variable named trendLine. That variable is used to hold the trend line reference later on so that we can access the trend line’s properties and methods through the variable.

Setting the script’s MaxBarsBack setting programmatically

Next in the example is the StartCalc() method that’s executed before the script calculates on historical price bars (MultiCharts, 2014). In it, we set the number of bars back that the script will reference (also called MaxBarsBack) to 10 with the ExecInfo.MaxBarsBack property (see MultiCharts, 2014). By explicitly setting the MaxBarsBack value we prevent that the script will recalculate itself when the automatically determined MaxBarsBack is too small (MultiCharts Wiki, 2012).

We also set the trendLine variable to null in the StartCalc() method. Given that the default value of reference variables is null (Sempf, Sphar, & Davis, 2010), this will reset the variable each time StartCalc() is executed. That’s needed because in the CalcBar() method we only draw the trend line when this reference variable is null. So by setting trendLine to null in StartCalc() we ensure the line is redrawn each time the script is recalculated (which, for example, happens after changing the indicator’s settings manually).

Drawing a trend line in MultiCharts .NET

Next in the example is the CalcBar() method, which begins with evaluating whether the current bar is not the last of the data series. By preceding the Bars.LastBarOnChart property with the logical not operator (!), which returns true when the expression that it precedes is false (Liberty & MacDonald, 2009), we’re in effect evaluating whether the current bar isn’t the last. For each bar that isn’t the last, this if statement executes the return keyword in order to exit the current method (Albahari & Albahari, 2012), and so all code below return won’t be executed. The reasoning behind this is that we want to trigger alerts on the last, real-time price bar. And so CalcBar()’s code doesn’t need to be processed on any of the historical bars.

When the bar is the last of the data series, return won’t be executed and the code below it is processed. That code starts with an if statement to evaluate whether the trendLine variable equals (==) null. This value of null means the reference variable doesn’t refer to an object (Albahari & Albahari, 2012), and so trendLine hasn’t been associated with a trend line object yet.

To draw a trend line, we first create two ChartPoint struct variables (begin and end). The first of these chart locations is set to the time and close of 10 bars ago (Bars.Time[10] and Bars.Close[10]) while the second holds the chart coordinates of 3 bars ago.

Once both ChartPoint variables are defined, we call the DrwTrendLine.Create() method to draw a trend line and pass in the begin and end variables to define the line’s start and end point. The value returned by this method is assigned to the trendLine variable: that way we can access the line’s properties and methods through the variable once it’s drawn. This also makes trendLine different than null, and since that invalidates the if statement above it, the trend line is only drawn once.

After that we change the trend line’s appearance by setting the line’s Size and Color properties to different values. We also extend the trend line by setting the line’s ExtRight property to true, which makes the line continue indefinitely into the future.

Generating custom alerts when prices cross the trend line

The last part of CalcBar() generates the custom trend line alerts. An if statement first verifies whether the indicator’s alert settings are enabled (see image above), in which case the Alerts.Enabled property returns true (see PowerLanguage .NET Help, n.d.). That check prevents that we needlessly evaluate the alert’s conditions. The second condition of the if statement is whether trendLine is unequal to (!=) null.

This trendLine variable needs to be different from null since we use it to access the trend line object in the if statement’s code block. Should this variable equal null, then using it to access a trend line’s properties or methods will thrown a NullReferenceException error (see Stellman & Greene, 2010).

When both if statement conditions evaluate to true, two double variables (trendLinePrice and prevLinePrice) are declared and initialised to the value returned by the line’s PriceValue() method. The first variable, trendLinePrice, is set to the line’s price value of the current bar’s DateTime (Bars.Time[0]). The second, prevLinePrice, is assigned the line’s price value of the previous bar.

Both variables are used in the subsequent if statement to verify if the current bar’s close (Bars.Close[0]) is above the trend line while the previous bar’s close (Bars.Close[1]) was below the line’s price value for that bar (prevLinePrice). When both conditions evaluate to true, we call the Alerts.Alert() method and pass in a fixed string with the {0} substitution parameter. That placeholder is replaced by the bar’s closing price (Bars.Close[0]) when our custom alert message is generated.

The second if statement also generates an alert message with Alerts.Alert(). The difference is that here the alert is generated when the price bar crosses below the trend line’s price value while the previous bar was above the line.

For more on MultiCharts .NET trend lines and alerts, see automatically generating trend line alerts for how to create standard alert messages through the line’s Alert property. Custom intra-bar alerts are discussed in triggering trend line alerts intra-bar.

Summary

Trend lines are drawn by DrwTrendLine.Create() which returns a reference to the trend line created. With that reference assigned to an ITrendLineObject variable we can access the line’s PriceValue() method to determine the line’s price values for certain DateTime values. The value that’s returned by PriceValue() can be used to determine if a bar closed above or below the trend line. When that happens, we can generate a custom alert message with Alerts.Alert().

Complete MultiCharts .NET indicator example

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

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

        private ITrendLineObject trendLine;

        protected override void StartCalc()
        {
            ExecInfo.MaxBarsBack = 10;
            trendLine = null;
        }

        protected override void CalcBar()
        {
            if (!Bars.LastBarOnChart)
                return;

            // Create the trend line
            if (trendLine == null)
            {
                ChartPoint begin = new ChartPoint(
                    Bars.Time[10], Bars.Close[10]);

                ChartPoint end = new ChartPoint(
                    Bars.Time[3], Bars.Close[3]);

                trendLine = DrwTrendLine.Create(begin, end);

                trendLine.Size     = 2;
                trendLine.Color    = Color.LimeGreen;
                trendLine.ExtRight = true;
            }

            // Generate alerts on trend line crossing
            if (Alerts.Enabled && trendLine != null)
            {
                double trendLinePrice = trendLine.PriceValue(Bars.Time[0]);
                double prevLinePrice  = trendLine.PriceValue(Bars.Time[1]);

                if ((Bars.Close[0] > trendLinePrice) && (Bars.Close[1] < prevLinePrice))
                    Alerts.Alert("Close @ {0} above trend line", Bars.Close[0]);

                if ((Bars.Close[0] < trendLinePrice) && (Bars.Close[1] > prevLinePrice))
                    Alerts.Alert("Price ({0}) crossed below trend line", Bars.Close[0]);
            }
        }
    }
}

References

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

Liberty, J. & MacDonald, B. (2009). Learning C# 3.0: Master the Fundamentals of C# 3.0. 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

MultiCharts Wiki (2012, February 23). How Scripts Work. Retrieved on April 19, 2015, from http://www.multicharts.com/trading-software/index.php/How_Scripts_Work

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.