In MultiCharts .NET we can retrieve the most recently active trend line and then remove it from the chart. But what if there are multiple trend lines, and we don’t want to remove the last one but the first?

Creating trend lines in MultiCharts .NET and removing the first line

To draw trend lines we use DrwTrendLine.Create(), a method that returns a reference to the line made (MultiCharts, 2014; PowerLanguage .NET Help, n.d.). When put into an ITrendLineObject variable, that reference allows access to the line’s properties and methods. One of those methods is Delete() which removes the trend line (MultiCharts, 2014).

But this also means that, without a trend line reference, we cannot remove a line. Granted, the DrwTrendLine.Active property returns the most recently active trend line (see PowerLanguage .NET Help, n.d.), but that won’t help with multiple trend lines.

Luckily, we can retrieve the chart’s trend lines with DrwTrendLine.GetTrendLineObjects() (PowerLanguage .NET Help, n.d.). This method returns a collection that implements IEnumerable<T>, which is a type-safe collection interface that can be enumerated (that is, looped over) (Liberty & MacDonald, 2009). The <T> signals it’s generic: this T can be replaced with whichever type the collection should hold (Stellman & Greene, 2010). In our case we’ll use IEnumerable<ITrendLineObject>: a collection object with trend line reference variables. To see how we can carry that out for removing a line, let’s examine the programming example.

Example: removing the first trend line with each mouse click

The indicator draws five trend lines. When added to an AEX index future chart, it looks like:

Removing a MultiCharts .NET trend line - 1

Each click on the chart with Control held down removes the oldest trend line. So after one click, the chart becomes:

Removing a MultiCharts .NET trend line - 2

And each successive mouse with Control removes another line:

Removing a MultiCharts .NET trend line - 3 Removing a MultiCharts .NET trend line - 4 Removing a MultiCharts .NET trend line - 5

Until finally all trend lines are removed:

Removing a MultiCharts .NET trend line - 6

Programmatically removing the first line in MultiCharts .NET

The MultiCharts .NET indicator’s programming code is the following:

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

    protected override void CalcBar()
    {
        int barsTillLast = Bars.FullSymbolData.Count -
            Bars.FullSymbolData.Current;

        ITrendLineObject trendLine;

        ChartPoint startPoint = new ChartPoint(
            Bars.Time[20], Bars.Close[20]);

        ChartPoint endPoint = new ChartPoint(
            Bars.Time[0], Bars.Close[0]);

        // Create several trend lines on historical bars
        if (barsTillLast == 40)
        {
            trendLine = DrwTrendLine.Create(startPoint, endPoint);

            trendLine.Size  = 5;
            trendLine.Color = Color.GreenYellow;
        }
        else if (barsTillLast == 30)
        {
            trendLine = DrwTrendLine.Create(startPoint, endPoint);

            trendLine.Size  = 3;
            trendLine.Style = ETLStyle.ToolDashed;
            trendLine.Color = Color.ForestGreen;
        }
        else if (barsTillLast == 20)
        {
            trendLine = DrwTrendLine.Create(startPoint, endPoint);

            trendLine.Size  = 2;
            trendLine.Color = Color.Magenta;
        }
        else if (barsTillLast == 10)
        {
            trendLine = DrwTrendLine.Create(startPoint, endPoint);

            trendLine.Size  = 4;
            trendLine.Color = Color.SaddleBrown;
        }
        else if (barsTillLast == 1)
        {
            trendLine = DrwTrendLine.Create(startPoint, endPoint);

            trendLine.Size  = 3;
            trendLine.Color = Color.SteelBlue;
        }
    }

    protected override void OnMouseEvent(MouseClickArgs arg)
    {
        // Remove the 1st line with each click + Control
        if (arg.keys == Keys.Control)
        {
            // Get all trend lines made by the current script
            IEnumerable<ITrendLineObject> allTrendLines =
                DrwTrendLine.GetTrendLineObjects(EDrawingSource.CurrentTech);

            // Remove the collection's first trend line
            if (allTrendLines.Count() > 0)
            {
                allTrendLines.ElementAt(0).Delete();
            }
        }
    }
}

We define three MultiCharts .NET class attributes first. SameAsSymbol set to true displays the indicator on the data series instead of creating a subchart. With UpdateOnEveryTick set to false the indicator calculates on bar close instead of calculating on every real-time tick. And the third attribute, MouseEvents, we enable mouse click processing.

Drawing MultiCharts .NET trend lines programmatically

Next in the example is the CalcBar() method that’s executed on every price bar of the data series (MultiCharts, 2014). In it we create the barsTillLast integer variable to hold the number of bars till the last. This variable’s value is computed by subtracting Bars.FullSymbolData.Current (the bar number that the script is currently calculated on; see PowerLanguage .NET Help, n.d.) from the data series’ total number of bars (returned by the Bars.FullSymbolData.Count property; see MultiCharts, 2014).

Then we declare an ITrendLineObject variable named trendLine that’s used when drawing the trend lines. For the line’s begin and end chart coordinates two ChartPoint struct variables are made. The first, startPoint, is set to the time and close of 20 bars ago (Bars.Time[20] and Bars.Close[20]). The second chart location, endPoint, is initialised to the current bar’s time and close (Bars.Time[0] and Bars.Close[0]).

An if/else statement then evaluates if the amount of bars till the data series’ last bar equals (==) 40, 30, 20, 10, or 1. When one of these five conditions occurs, we call DrwTrendLine.Create() to draw a trend line and pass in the startPoint and endPoint variables for the line’s begin and end point. The trendLine variable is assigned the value returned by DrwTrendLine.Create(). We then use this variable to change the trend line’s appearance by setting different values to its Size, Color, and Style properties.

By the way, we prevent the last trend line from being drawn repeatedly in real-time by drawing it on the bar preceding the last price bar, instead of creating it on the last bar of the chart (when barsTillLast equals 0).

Removing the first trend line on the chart with a mouse click

The code for removing the chart’s first trend line is in OnMouseEvent(), a method that’s executed with each mouse click on the chart (see PowerLanguage .NET Help, n.d.) provided that the MouseEvents attribute is set to true.

This method’s if statement evaluates whether the arg.keys variable (which returns the key pressed during the click) equals (==) the Keys.Control value. This Keys enumeration originates from the System.Windows.Forms namespace that we added to the top of the indicator’s code with the using directive (see full code example below).

When Control was pressed during the mouse click, we retrieve the script’s trend lines by calling DrwTrendLine.GetTrendLineObjects() with the CurrentTech value (from the EDrawingSource enumeration) passed in. This causes that method to return a collection with all trend lines created by the script itself (see PowerLanguage .NET Help, n.d.), which we assign to the IEnumerable<ITrendLineObject> collection named allTrendLines. To prevent having to qualify the IEnumerable<T> interface with its namespace, we added System.Collections.Generics with a using directive to the top of the indicator’s code.

A nested if statement then checks if the number of elements in the allTrendLines collection (returned by its Count() method; see Microsoft Developer Network, n.d.) is greater than (>) 0. In that case we use ElementAt() (a method that returns the element at the specified position; Albahari & Albahari, 2012), to access the collection’s first item (which has an index of 0). By calling that item’s Delete() method we remove the trend line.

Tip: The order of elements returned by DrwTrendLine.GetTrendLineObjects() is affected by operations performed on the trend lines (MultiCharts Support, personal communication, May 28, 2015). When lines are removed, the first element in the collection returned by DrwTrendLine.GetTrendLineObjects() might not be the line that’s drawn first.

By verifying with Count() if the trend line collection contains at least one element, we prevent an ArgumentOutOfRangeException error. That exception is thrown when an argument is outside a given range (Dorman, 2010). In this case, using ElementAt() to access the first element when allTrendLines is empty (that happens when all lines are already removed) triggers that exception:

MultiCharts .NET out of range exception

We also discuss removing trend lines in removing a trend line and deleting a trend line object completely. In removing all trend lines we use a collection returned by DrwTrendLine.GetTrendLineObjects() to remove all trend lines from the chart.

Summary

The DrwTrendLine.Create() method draws trend lines while DrwTrendLine.GetTrendLineObjects() returns a collection of trend lines on the chart. This latter method requires an EDrawingSource value (which specifies the lines to include) and returns an IEnumerable<ITrendLineObject> collection. Accessing an individual line from that collection is possible with the ElementAt() method together with a zero-based index number. That way we can access the line’s Delete() method to remove the line. With the collection’s Count() method we can check if the collection’s length also includes the element we want to access.

Complete MultiCharts .NET indicator example

using System;
using System.Drawing;
using System.Linq;
using PowerLanguage.Function;
using System.Windows.Forms;         // Added for the Keys enumeration
using System.Collections.Generic;   // Added for the IEnumerable collection

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

        protected override void CalcBar()
        {
            int barsTillLast = Bars.FullSymbolData.Count -
                Bars.FullSymbolData.Current;

            ITrendLineObject trendLine;

            ChartPoint startPoint = new ChartPoint(
                Bars.Time[20], Bars.Close[20]);

            ChartPoint endPoint = new ChartPoint(
                Bars.Time[0], Bars.Close[0]);

            // Create several trend lines on historical bars
            if (barsTillLast == 40)
            {
                trendLine = DrwTrendLine.Create(startPoint, endPoint);

                trendLine.Size  = 5;
                trendLine.Color = Color.GreenYellow;
            }
            else if (barsTillLast == 30)
            {
                trendLine = DrwTrendLine.Create(startPoint, endPoint);

                trendLine.Size  = 3;
                trendLine.Style = ETLStyle.ToolDashed;
                trendLine.Color = Color.ForestGreen;
            }
            else if (barsTillLast == 20)
            {
                trendLine = DrwTrendLine.Create(startPoint, endPoint);

                trendLine.Size  = 2;
                trendLine.Color = Color.Magenta;
            }
            else if (barsTillLast == 10)
            {
                trendLine = DrwTrendLine.Create(startPoint, endPoint);

                trendLine.Size  = 4;
                trendLine.Color = Color.SaddleBrown;
            }
            else if (barsTillLast == 1)
            {
                trendLine = DrwTrendLine.Create(startPoint, endPoint);

                trendLine.Size  = 3;
                trendLine.Color = Color.SteelBlue;
            }
        }

        protected override void OnMouseEvent(MouseClickArgs arg)
        {
            // Remove the 1st line with each click + Control
            if (arg.keys == Keys.Control)
            {
                // Get all trend lines made by the current script
                IEnumerable<ITrendLineObject> allTrendLines =
                    DrwTrendLine.GetTrendLineObjects(EDrawingSource.CurrentTech);

                // Remove the collection's first trend line
                if (allTrendLines.Count() > 0)
                {
                    allTrendLines.ElementAt(0).Delete();
                }
            }
        }
    }
}

References

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

Dorman, S. (2010). Sams Teach Yourself Visual C# 2010 in 24 Hours. Indianapolis, IN: Sams/Pearson Education.

Liberty, J. & MacDonald, B. (2009). Learning C# 3.0: Master the Fundamentals of C# 3.0. Sebastopol, CA: O’Reilly Media.

Microsoft Developer Network (n.d.). Enumerable.Count<TSource> Method (IEnumerable<TSource>). Retrieved on May 21, 2015, from https://msdn.microsoft.com/en-us/library/vstudio/bb338038%28v=vs.100%29.aspx

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

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