MultiCharts .NET trend lines can be extended endlessly to the left or right. But sometimes a line just needs to be made a little bit longer. How can we do that programmatically?

MultiCharts .NET trend lines and extending them to future and past values

The DrwTrendLine.Create() method draws trend lines. This method requires two ChartPoint structs for the line’s begin and end point, and returns a reference to the trend line created (MultiCharts, 2014; PowerLanguage .NET Help, n.d.). When that reference is stored in an ITrendLineObject interface variable, the trend line’s properties and methods are accessible through that variable.

While we can extend a trend line indefinitely with the ExtRight and ExtLeft properties (MultiCharts, 2014), this doesn’t allow for extending the line with a certain amount of bars. For that we need to work with a line’s PriceValue() method, which returns a line’s price value for a given DateTime value (MultiCharts, 2014; PowerLanguage .NET Help, n.d.). That means we can use PriceValue() to, for example, determine what a line’s price value would be 10 bars from now, and then extend the trend line to that point.

Example of extending a trend line in MultiCharts .NET

With the example indicator (see below) added to an E-mini NASDAQ-100 chart, it draws a trend line like the following:

Extend a MultiCharts .NET trend line - before

After clicking on the chart with Control held down, the line is extended with 10 bars in both directions:

Extend a MultiCharts .NET trend line - after

Extending a trend line in MultiCharts .NET programmatically

The programming code for extending a trend line with a few bars looks as follows:

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

    private ITrendLineObject trendLine = null;
    private bool alreadyExtended;

    protected override void CalcBar()
    {
        if (Bars.FullSymbolData.Current == (Bars.FullSymbolData.Count - 20) 
            && trendLine == null)
        {
            // Create the trend line
            trendLine = DrwTrendLine.Create(
                new ChartPoint(Bars.Time[15], Bars.Close[15]),
                new ChartPoint(Bars.Time[0], Bars.Close[0])
                );

            // Set the line's visual appearance
            trendLine.Size  = 2;
            trendLine.Color = Color.Magenta;

            alreadyExtended = false;
        }
    }

    protected override void OnMouseEvent(MouseClickArgs arg)
    {
        if (arg.keys == Keys.Control && !alreadyExtended)
        {
            // Extend the line in both directions with 10 bars
            ExtendLine(trendLine, 10, 10);

            alreadyExtended = true;
        }
    }

    private void ExtendLine(ITrendLineObject lineToExtend, 
        int initialBarsBack, int initialBarsForward)
    {
        // Determine the line's new begin point
        int barsBack = (Bars.FullSymbolData.Current - 
            ((int)lineToExtend.Begin.BarNumber + ExecInfo.MaxBarsBack))
            + initialBarsBack;

        ChartPoint newBeginPoint = new ChartPoint(
            Bars.FullSymbolData.Time[barsBack],
            trendLine.PriceValue(Bars.FullSymbolData.Time[barsBack])
            );

        // Determine the line's new end point
        int barsForward = (Bars.FullSymbolData.Current - 
            ((int)lineToExtend.End.BarNumber + ExecInfo.MaxBarsBack))
            - initialBarsForward;

        ChartPoint newEndPoint = new ChartPoint();

        if (barsForward > 0)
        {
            newEndPoint = new ChartPoint(
                Bars.FullSymbolData.Time[barsForward],
                trendLine.PriceValue(Bars.FullSymbolData.Time[barsForward])
                );
        }
        else
        {
            newEndPoint = lineToExtend.End;
        }

        // Set the line's new begin and end point
        lineToExtend.Begin = newBeginPoint;
        lineToExtend.End   = newEndPoint;
    }
}

We first define three attributes. RecoverDrawings set to false prevents intra-bar generated trend lines from being removed (MultiCharts, 2014). Setting SameAsSymbol to true displays the indicator on the data series instead of on a separate subchart. And MouseEvents set to true enables mouse click processing.

In the top of the indicator’s class we create an ITrendLineObject variable named trendLine and set it to null. We’ll use this variable later on to access the trend line after creating it. A Boolean variable named alreadyExtended is also declared here. We use that variable to prevent that the line extends with every mouse click with Control.

Creating and modifying a trend line in MultiCharts .NET

Next is the CalcBar() method that has an if statement with two conditions. The first is whether the current bar number (returned by Bars.FullSymbolData.Current; see MultiCharts, 2014) equals the number of bars on the chart (Bars.FullSymbolData.Count) minus 20. This way we can draw a trend line several bars before the last.

The second condition checks whether the trendLine variable equals (==) null, which it does when this reference variable doesn’t point to an object yet (Albahari & Albahari, 2012). In other words, when trendLine is null it hasn’t been associated with a trend line on the chart yet. This condition is a way to only execute the if statement’s code block once, since after drawing the line, trendLine is different than null.

When both conditions evaluate to true, the if statement’s code block is executed. In it we call the DrwTrendLine.Create() method to draw the trend line and pass in two ChartPoint structs, which set the line’s starting point to 15 bars ago and the ending point to the current bar’s time (Bars.Time[0]) and close (Bars.Close[0]).

The value returned by DrwTrendLine.Create() is assigned to the trendLine variable. That allows trendLine to be used to adjust the trend line’s visual appearance by assigning new values to the line’s Size and Color properties. And since the line hasn’t been extended yet, we set the alreadyExtended variable to false.

Processing mouse clicks in MultiCharts .NET

Next is the OnMouseEvent() method, which processes registered mouse clicks on the chart (see PowerLanguage .NET Help, n.d.). Two expressions are evaluated by an if statement here.

The first is whether the arg.keys variable (which holds the keyboard key pressed during the click) equals the Keys.Control enumerated value. This Keys enumeration is included in the System.Windows.Forms namespace that we added with a using statement to the top of the indicator (see full code example below). The second expression uses the logical not operator (!) to see whether the alreadyExtended variable is false. That is, ! returns false when alreadyExtended is true and results in true when alreadyExtended is false (Sempf, Sphar, & Davis, 2010).

When both expressions evaluate to true, we call the ExtendLine() method with three arguments: the trendLine variable that holds the reference to the trend line we want to extend and the value of 10 twice. The first ‘10’ signals how many bars back the line needs to extend, while the second specifies the amount of bars that the line needs to be lengthened forward.

After calling that method we set the alreadyExtended variable to true. Since that invalidates the if statement in OnMouseEvent(), the ExtendLine() method only extends the line once.

Programmatically retrieving the line’s new starting point

The last part of the example is the ExtendLine() method, which we call when there’s a mouse click with Control on the chart while the line hasn’t been extended yet. This method has three parameters: an ITrendLineObject variable named lineToExtend and two integers (initialBarsBack and initialBarsForward) that specify how much previous and future bars the line should lengthen, respectively.

The first part of ExtendLine() gives the line a new starting point. For that we first determine how many bars ago this starting point is located. We do that by adding the bar number of the line’s begin point (accessible through its Begin.BarNumber property) to the indicator’s MaxBarsBack value (returned by ExecInfo.MaxBarsBack; MultiCharts, 2014). This results in the bar number for the line’s begin point. We then subtract this computed value from the current bar number (Bars.FullSymbolData.Current) followed by adding the initialBarsBack parameter. That gives us the amount of bars back, relative to the current bar, where the line’s begin point lays. The result of this operation is stored in the barsBack integer variable, with which we can retrieve the price data from the line’s new starting point.

We then create a ChartPoint struct variable named newBeginPoint. Its time coordinate is set to the DateTime value of the barsBack number of bars ago, retrieved with Bars.FullSymbolData.Time. By using a Bars.FullSymbolData property we can access data for any bar on the chart (see PowerLanguage .NET Help, n.d.). The price coordinate of this newBeginPoint chart location is set to the value returned by the line’s PriceValue() method. In that method we pass the DateTime value of the barsBack number of bars ago; that way it returns the trend line’s price value belonging to that bar’s time and date value.

Determining the line’s new ending point

Once we have the line’s new starting point, it’s time to determine its new end point. We first calculate the bar number to which the line should be extended and store this in the barsForward variable. Then we declare a ChartPoint struct variable named newEndPoint and initialise it with a parameterless constructor to an empty point.

In the following if/else statement that newEndPoint struct is given its actual value. For that we first check if the barsForward variable is greater than (>) 0. When that is the case, the newEndPoint variable is assigned chart coordinates with Bars.FullSymbolData.Time and the line’s PriceValue() method. The else portion is in place to catch negative barsForward values. Should that variable be negative, it means there aren’t enough bars on the chart to extend the line to. In that case we assign newEndPoint the line’s current end point (lineToExtend.End), and so won’t change it when there aren’t enough bars to extend it to.

Extending a trend line in MultiCharts .NET

Once the new coordinates for the line have been determined (the newBeginPoint and newEndPoint variables) we use these to extend the trend line. For that we assign the new coordinates to the line’s Begin and End properties, which we access with the lineToExtend trend line variable. And so the ExtendLine() method extends the trend line for a certain amount of bars in either direction.

More examples of a trend line’s PriceValue() method can be found in getting future values of a trend line, retrieving historical trend line values, and colouring price bars above or below a trend line.

Summary

The DrwTrendLine.Create() method draws trend lines and returns a reference to the line just created. When that returned value is assigned to an ITrendLineObject variable, we can access the line’s PriceValue() method through that variable. This method returns the line’s price value for a certain future or historical DateTime value. Once we know that value for a certain price bar, we can extend the trend line by setting its Begin and End properties to that chart coordinate.

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
{
    [RecoverDrawings(false), SameAsSymbol(true), MouseEvents(true)]
    public class Example_ExtendTrendLinePastFuture : IndicatorObject
    {
        public Example_ExtendTrendLinePastFuture(object _ctx) : base(_ctx) { }

        private ITrendLineObject trendLine = null;
        private bool alreadyExtended;

        protected override void CalcBar()
        {
            if (Bars.FullSymbolData.Current == (Bars.FullSymbolData.Count - 20) 
                && trendLine == null)
            {
                // Create the trend line
                trendLine = DrwTrendLine.Create(
                    new ChartPoint(Bars.Time[15], Bars.Close[15]),
                    new ChartPoint(Bars.Time[0], Bars.Close[0])
                    );

                // Set the line's visual appearance
                trendLine.Size  = 2;
                trendLine.Color = Color.Magenta;

                alreadyExtended = false;
            }
        }

        protected override void OnMouseEvent(MouseClickArgs arg)
        {
            if (arg.keys == Keys.Control && !alreadyExtended)
            {
                // Extend the line in both directions with 10 bars
                ExtendLine(trendLine, 10, 10);

                alreadyExtended = true;
            }
        }

        private void ExtendLine(ITrendLineObject lineToExtend, 
            int initialBarsBack, int initialBarsForward)
        {
            // Determine the line's new begin point
            int barsBack = (Bars.FullSymbolData.Current - 
                ((int)lineToExtend.Begin.BarNumber + ExecInfo.MaxBarsBack))
                + initialBarsBack;

            ChartPoint newBeginPoint = new ChartPoint(
                Bars.FullSymbolData.Time[barsBack],
                trendLine.PriceValue(Bars.FullSymbolData.Time[barsBack])
                );

            // Determine the line's new end point
            int barsForward = (Bars.FullSymbolData.Current - 
                ((int)lineToExtend.End.BarNumber + ExecInfo.MaxBarsBack))
                - initialBarsForward;

            ChartPoint newEndPoint = new ChartPoint();

            if (barsForward > 0)
            {
                newEndPoint = new ChartPoint(
                    Bars.FullSymbolData.Time[barsForward],
                    trendLine.PriceValue(Bars.FullSymbolData.Time[barsForward])
                    );
            }
            else
            {
                newEndPoint = lineToExtend.End;
            }

            // Set the line's new begin and end point
            lineToExtend.Begin = newBeginPoint;
            lineToExtend.End   = newEndPoint;
        }
    }
}

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

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.