In MultiCharts .NET we can programmatically draw trend lines and then trigger intra-bar alerts or alerts on bar close. But how can we create alerts programmatically for a trend line that’s drawn manually?

Creating alerts with a manual trend line in MultiCharts .NET

Programmatically working with MultiCharts .NET trend lines is often done by calling the DrwTrendLine.Create() method (see MultiCharts, 2014). By assigning that method’s returned value to an ITrendLineObject variable we can access the line’s properties and methods to do things like relocating, extending, or removing the trend line.

But manual trend lines require a different approach. That’s because they aren’t available by default in an indicator or trading strategy; the script is simply unaware of the manual trend lines on the chart. And so there are three steps required to generate an alert based on a manual trend line. First the trend line needs to become accessible to the script. Then we need a line’s price value to figure out if the bar close above or below the line. And the last step will be to generate the alert itself.

So to make a manually drawn trend line available to the script it needs to be ‘retrieved’ from the chart. One way to do that is with the DrwTrendLine.Active property, which programmatically selects a trend line and returns a reference to it (MultiCharts, 2014; PowerLanguage .NET Help, n.d.). This property selects the ‘active’ trend line, which is the line that was created or moved last, or that is selected with a mouse click (MultiCharts, 2014).

Once we have a reference to the trend line, we can assign it to an ITrendLineObject variable and use that variable to access the line’s PriceValue() method. This method requires any DateTime value on the chart as an argument and then returns the line’s price value belonging to that date and time (MultiCharts, 2014; PowerLanguage .NET Help, n.d.). And by knowing a trend line’s price value for a certain bar we can decide if that bar closed above or below the line.

The last step is creating the alert. We do that with the Alerts.Alert() method, in which we can pass a string of text to generate an alert message with that content (MultiCharts, 2014). The type of alerts available in MultiCharts .NET are a pop-up window, sound, and/or email notification (MultiCharts Wiki, 2013).

Example: a manual trend line and a script’s alert in MultiCharts .NET

Let’s see how the example indicator works before discussing its code. When added to the chart it doesn’t do anything yet:

Added the MultiCharts .NET script to the chart

By the way, a script’s alert settings need to be enabled before it can generate an alert programmatically. To verify those settings, right-click on the indicator’s name in the Status Line (the text in the top of the chart, where in the image above it says ‘Example_TrendLineAlertManually’) and select ‘Format [indicator name]‘. Then move to the ‘Alerts’ tab:

Alert settings for the MultiCharts .NET script

Once the script is set up to generate alerts, we draw a manual trend line by clicking on the ‘Trend Line’ icon in the toolbar:

Draw a manual trend line in MultiCharts .NET

If you can’t locate this icon, verify if its toolbar is enabled by going to ‘View’ -> ‘Toolbars’ and check the ‘Drawing’ option (the image below is edited for brevity; ‘Toolbars’ is near the bottom of the long ‘View’ menu):

Enable drawing toolbar in MultiCharts .NET

With the ‘Trend Line’ button selected, click once on the chart to set the line’s begin point and a second time for its end point. That would give something like the following:

Example of a manually drawn trend line in MultiCharts .NET

Now the script needs to access the manual trend line. For that, click somewhere on the chart with the Control key held down. If the indicator succeeds in accessing the trend line, you’ll see its appearance change:

Changed look of MultiCharts .NET trend line

Now we have to wait until a bar closes above or below the line to see the programmatically generated alerts:

Alert triggered with a manual MultiCharts .NET trend line

Creating programmatic alerts based on a manual trend line

The indicator’s programming code is the following:

[SameAsSymbol(true), MouseEvents(true)]
public class Example_TrendLineAlertManually : IndicatorObject
{
    public Example_TrendLineAlertManually(object _ctx) : base(_ctx) { }

    private ITrendLineObject manualTrendLine;

    protected override void OnMouseEvent(MouseClickArgs arg)
    {
        // Check if the click happened with Control held down
        if (arg.keys == Keys.Control)
        {
            // Get the most recently active trend line
            manualTrendLine = DrwTrendLine.Active;

            // Adjust the line's appearance
            if (manualTrendLine != null)
            {
                manualTrendLine.Size     = 2;
                manualTrendLine.Color    = Color.Violet;
                manualTrendLine.ExtRight = true;
            }
        }
    }

    protected override void CalcBar()
    {
        if (Alerts.CheckAlertLastBar && manualTrendLine != null &&
            Bars.Status == EBarState.Close)
        {
            // Retrieve the trend line's price values
            double linePriceValue = manualTrendLine.PriceValue(Bars.Time[0]);
            double prevLineValue  = manualTrendLine.PriceValue(Bars.Time[1]);

            // Generate the alerts
            if ((Bars.Close[0] > linePriceValue) && 
                (Bars.Close[1] < prevLineValue))
            {
                Alerts.Alert(
                    "The bar closed at {0} which crossed the trend line @ {1}",
                    Bars.Close[0], linePriceValue);
            }
            else if ((Bars.Close[0] < linePriceValue) && 
                (Bars.Close[1] > prevLineValue))
            {
                Alerts.Alert("The bar closed below the trend line ({0})",
                    linePriceValue);
            }
        }
    }
}

We begin by adding two MultiCharts .NET class attributes. With SameAsSymbol set to true the indicator is displayed on the data series (and not in its own subchart), while MouseEvents set to true enables mouse click processing.

Then we declare an ITrendLineObject variable named manualTrendLine. Later on we use this variable to hold the reference to the trend line in order to access the line’s properties and methods through this variable.

Processing mouse clicks and selecting the active trend line

Next in the example is the OnMouseEvent() method. When the MouseEvents attribute is set to true, this method is executed each time a mouse click is registered on the chart (see PowerLanguage .NET Help, n.d.). With the if statement inside this method we evaluate whether the keyboard key pressed during the click (returned by arg.keys) equals (==) the Keys.Control enumerated value. This latter value originates from the System.Windows.Forms namespace, which we added to the top of the indicator with a using directive (see full code example below).

When that condition signals that a mouse click with Control occurred, we use the DrwTrendLine.Active property to retrieve the chart’s active trend line. The value returned by this method is assigned to the manualTrendLine variable. We then use a nested if statement to check if this variable is unequal to (!=) null. This is done because using a reference variable that has a value of null gives a NullReferenceException error (Stellman & Greene, 2010).

A value of null indicates that the reference variable doesn’t point to anything (Stephens, 2014), meaning that our manualTrendLine variable isn’t connected with an actual line on the chart. This is possible because, when DrwTrendLine.Active cannot locate a trend line, it returns null (see PowerLanguage .NET Help, n.d.). Situations in which that can happen are when the recently active trend line has been removed or when no trend line has been made yet.

When DrwTrendLine.Active succeeds in selecting a line (causing manualTrendLine to be different than null), we use the manualTrendlLine variable to change the line’s appearance. For that we assign new values to the line’s Size and Color properties. And with the ExtRight property we extend the trend line to the right so that the line continues regardless of how many bars are needed before an alert is generated.

Triggering trend line alerts in MultiCharts .NET

The next part of the example is the CalcBar() method. This method consists out of an if statement that evaluates three conditions. The first is whether the Alerts.CheckAlertLastBar property returns true, which it does when alerts are enabled (see image above) and when the current bar is the last of the data series (PowerLanguage .NET Help, n.d.). The second expression evaluates if manualTrendLine is unequal to null so that we don’t generate a NullReferenceException in the if statement’s code block. The third and last condition verifies if the Bars.Status property equals the EBarState.Close enumerated value, which happens when the script is calculated on the bar’s closing tick (see MultiCharts, 2014). This later condition means we’ll generate trend line alerts on bar close instead of intra-bar trend line alerts.

When all three expressions evaluate to true, the code inside the if statement begins with retrieving the trend line’s price values for the current and previous bars. We do this with the line’s PriceValue() method that has the DateTime values of the current (Bars.Time[0]) and previous (Bars.Time[1]) passed in. The values returned by this method are assigned to the linePriceValue and prevLineValue double variables, both of which are used in the subsequent if/else statement to see if the price bar crossed above or below the trend line.

The if portion of the if/else statement evaluates if the current bar’s closing price (Bars.Close[0]) is greater than (>) the value of linePriceValue while the preceding bar (Bars.Close[1]) is less than (<) the line’s price value for the previous bar (prevLineValue). When both of these conditions are true we know that the price has crossed the trend line. To generate a dynamic alert message when that happens, we call Alerts.Alert() and pass a quoted string with placeholders (the numbers between { and }) inside this method. Those placeholders are replaced by the values after the string (Sempf, Sphar, & Davis, 2010), which are the values of Bars.Close[0] and linePriceValue here.

The else portion of the if/else statement checks if the current bar closed below the trend line while the previous bar was still above it. Another alert message is generated when that happens, this time with the message that the price bar closed below the line.

For more on working with manual trend lines, see retrieving manually drawn trend lines and using a manually drawn trend line as a stop-loss. Other ways to generate trend line alerts are with the line’s Alert property to generate trend line alerts automatically. We can also generate custom alerts messages, which is discussed in trend line alerts on bar close and intra-bar trend line alerts.

Summary

The DrwTrendLine.Active property selects the chart’s active trend line, whether this line is drawn manually or not. When we assign the reference returned by this property to an ITrendLineObject variable, we can access that line’s properties and methods through that variable. With the PriceValue() method we can retrieve the trend line’s price values for any DateTime value on the chart. Comparing those values with the bar’s closing prices allows us to determine if a bar crossed the manual trend line. When it did, we generate an alert with Alerts.Alert().

Complete MultiCharts .NET indicator example

using System;
using System.Drawing;
using System.Linq;
using PowerLanguage.Function;
using System.Windows.Forms;         // For the Keys enumeration

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

        private ITrendLineObject manualTrendLine;

        protected override void OnMouseEvent(MouseClickArgs arg)
        {
            // Check if the click happened with Control held down
            if (arg.keys == Keys.Control)
            {
                // Get the most recently active trend line
                manualTrendLine = DrwTrendLine.Active;

                // Adjust the line's appearance
                if (manualTrendLine != null)
                {
                    manualTrendLine.Size     = 2;
                    manualTrendLine.Color    = Color.Violet;
                    manualTrendLine.ExtRight = true;
                }
            }
        }

        protected override void CalcBar()
        {
            if (Alerts.CheckAlertLastBar && manualTrendLine != null &&
                Bars.Status == EBarState.Close)
            {
                // Retrieve the trend line's price values
                double linePriceValue = manualTrendLine.PriceValue(Bars.Time[0]);
                double prevLineValue  = manualTrendLine.PriceValue(Bars.Time[1]);

                // Generate the alerts
                if ((Bars.Close[0] > linePriceValue) && 
                    (Bars.Close[1] < prevLineValue))
                {
                    Alerts.Alert(
                        "The bar closed at {0} which crossed the trend line @ {1}",
                        Bars.Close[0], linePriceValue);
                }
                else if ((Bars.Close[0] < linePriceValue) && 
                    (Bars.Close[1] > prevLineValue))
                {
                    Alerts.Alert("The bar closed below the trend line ({0})",
                        linePriceValue);
                }
            }
        }
    }
}

References

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.

Stephens, R. (2014). C# 5.0 Programmer Reference. Indianapolis, IN: John Wiley & Sons.