Trend lines can be drawn on price data and between pivot swing highs and lows. But how do we use trend lines to mark the high and low of an instrument’s opening range?

Drawing MultiCharts .NET trend lines during a certain time period

The DrwTrendLine.Create() method draws MultiCharts .NET trend lines and returns a reference to the line just created (MultiCharts, 2014; PowerLanguage .NET Help, n.d.). When that reference is assigned to an ITrendLineObject variable, that line’s properties and methods can be accessed through the variable. This allows for things like relocating a trend line or changing a line’s appearance.

Drawing trend lines during a certain time period (like the first hours of the trading day) requires programmatically working with time. We do that in C# with the DateTime and TimeSpan structures. The difference between these is that a TimeSpan holds a time duration (like 8 hours or 11 minutes) while DateTime defines moments in time (like January 1st, 2015) (Sempf, Sphar, & Davis, 2010).

In MultiCharts .NET, the time of a bar from the primary data series is returned by the Bars.Time property (see MultiCharts, 2014). For example, Bars.Time[1] returns a DateTime value with the previous bar’s closing time and date. The TimeOfDay property returns the time (without the date) from a DateTime value (Dorman, 2010; Sempf et al., 2010). For example, Bars.Time[2].TimeOfDay returns the closing time from 2 bars. To see if a bar time’s is within a certain time range, we can compare TimeOfDay with a TimeSpan value. Let’s look at the programming example to see how.

Example: trend lines with an opening range’s high and low

The example indicator draws red lines set to the high of the 8 to 10 a.m. opening range while the low of that time period is displayed with a green trend line. Applied to an EuroStoxx 50 future chart, that looks as follows:

MultiCharts .NET trend lines that highlight an opening range

Creating trend lines that indicate the opening range in MultiCharts .NET

The programming example’s code is:

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

    private ITrendLineObject openRangeHigh, openRangeLow;

    double highestHigh = 0;
    double lowestLow   = 9999;

    protected override void CalcBar()
    {
        if (Bars.Time[0].TimeOfDay >= new TimeSpan(8, 0, 0) &&
            Bars.Time[0].TimeOfDay <= new TimeSpan(10, 0, 0))
        {
            // Keep track of the highest high and lowest low
            highestHigh = Math.Max(highestHigh, Bars.High[0]);
            lowestLow   = Math.Min(lowestLow, Bars.Low[0]);

            // Create the line on the first bar of the day
            if (Bars.Time[0].Date != Bars.Time[1].Date)
            {
                openRangeHigh = DrwTrendLine.Create(
                    new ChartPoint(Bars.Time[1], highestHigh),
                    new ChartPoint(Bars.Time[0], highestHigh)
                    );

                openRangeLow = DrwTrendLine.Create(
                    new ChartPoint(Bars.Time[1], lowestLow),
                    new ChartPoint(Bars.Time[0], lowestLow)
                    );

                openRangeLow.Size  = 3;
                openRangeHigh.Size = 3;

                openRangeLow.Color  = Color.ForestGreen;
                openRangeHigh.Color = Color.Firebrick;
            }
            else if (openRangeHigh != null && openRangeLow != null)
            {
                // Adjust and extend the line
                openRangeHigh.End   = new ChartPoint(Bars.Time[0], highestHigh);
                openRangeHigh.Begin = new ChartPoint(openRangeHigh.Begin.Time, 
                    highestHigh);

                openRangeLow.End   = new ChartPoint(Bars.Time[0], lowestLow);
                openRangeLow.Begin = new ChartPoint(openRangeLow.Begin.Time, 
                    lowestLow);

                // Reset the variables for the next open range
                if (Bars.Time[0].TimeOfDay == new TimeSpan(10, 0, 0))
                {
                    highestHigh = 0;
                    lowestLow   = 9999;
                }
            }
        }
    }
}

We start with setting two attributes. SameAsSymbol set to true displays the indicator on the data series (and not in a separate subchart). And with UpdateOnEveryTick set to false the indicator calculates on bar close only as opposed to tick-by-tick calculation. This way our trend lines are made or updated only once per bar.

Then we declare two ITrendLineObject variables: openRangeHigh and openRangeLow. These will hold our trend line references so we can modify the lines after they’ve been created. We also declare and initialise two double variables (highestHigh and lowestLow) that keep track of the extreme high and low prices reached during the opening range.

Comparing times to determine an opening range

Next is the CalcBar() method. We want the code inside this method to only be executed when the bar that’s currently processed by the script is within the opening range (8-10 o’clock). To do so, an if statement evaluates whether the current bar’s time (Bars.Time[0]) TimeOfDay property is greater than or equal to (>=) a TimeSpan of 8 hours and less than or equal to (<=) a TimeSpan of 10 hours.

When both of those conditions evaluate to true, the bar’s time is within the opening range and the if statement’s code is executed. We then assign new values to the highestHigh and lowestLow variables with the Max() and Min() methods from the Math class. Both methods accept two arguments and return the maximum and minimum value of those arguments, respectively (Albahari & Albahari, 2012).

The highestHigh variable is assigned the maximum value of either highestHigh (so the variable’s current value) or the current bar’s high (Bars.High[0]). Similarly, the lowestLow variable is assigned the minimum value of either its current value or the current bar’s low (Bars.Low[0]). This also shows why this variable was given an initial value of 9999 instead of the default 0; otherwise, the current bar low would never be assigned to the lowestLow variable since 0 would always be returned by Math.Min().

We update the highestHigh and lowestLow variables on every bar in the opening range so that they hold the running high or low for that time window.

Creating trend lines that highlight the opening range

Then an if statement checks whether the current bar’s date (Bars.Time[0].Date) is unequal to (!=) the date of the previous bar (Bars.Time[1].Date). This evaluates to true when the current bar is the first of the day. Since the EuroStoxx50 future closes at 22:00 and opens at 8:00, the first bar with a different date than the preceding bar is also the first bar of the opening range.

And so we draw a trend line to mark that opening range by calling the DrwTrendLine.Create() method. In it, we pass two ChartPoint struct values: the first specifies the line’s begin point and is set to the time of the previous bar (to make the line more visible) and the highestHigh variable. The second chart coordinate is set to the current bar’s time and the highestHigh variable. The value returned by DrwTrendLine.Create() is assigned to the openRangeHigh variable. That allows us to use this variable to access the line once it has been made.

The trend line that highlights the lowest price in the opening range is created similarly. This line has its price coordinate set to the lowestLow variable and the value returned by DrwTrendLine.Create() assigned to the openRangeLow variable. Once both lines are made, we change their visual appearance by assigning new values to their Size and Color properties to end up with a red and green line.

Updating the opening range’s trend lines in MultiCharts .NET

The last part of the example is an else statement code block. These statements are executed when the current and previous bar have the same date, but with the current bar still within the opening range’s time period.

Since each bar in the opening range can make a new high or low for that range, we extend the trend lines to the then-current highestHigh and lowestLow values. We do that by assigning new ChartPoint struct values to the line’s End property (see PowerLanguage .NET Help, n.d.). So for the openRangeHigh line we set the new end point to the current bar’s time and the highestHigh variable. And the openRangeLow line has its new end point set to the time of the current bar and the value of lowestLow.

We also relocate the trend lines’ starting point in case a new high or low occurred since the line was created. We do that by assigning a new ChartPoint value to each line’s Begin property. The openRangeHigh trend line is set to its existing begin time (openRangeHigh.Begin.Time) and the current value of highestHigh. This way the line still begins on the same bar (meaning, the same time coordinate), but with an updated price coordinate. The same is done for the openRangeLow line, although this line has its begin point price set to the lowestLow value.

The example ends with an if statement that verifies whether the current bar’s TimeOfDay equals an 10 hour TimeSpan. When it does, we’ve reached the end of the opening range’s time window. The highestHigh and lowestLow variables are then reset so that the next day’s opening range is calculated correctly.

For more examples that use trend lines to highlight price action, see drawing trend lines between swing pivot highs and lows. Price bars can also be denoted with rectangles around price movement or with a triangle anchored to a recent high.

Summary

Trend lines are made by DrwTrendLine.Create(). The value returned by this method can be assigned to an ITrendLineObject variable to access the line’s properties and methods later on. We can draw trend line during a certain time period by comparing a bar’s time (Bars.Time[n].TimeOfDay) with a TimeSpan time duration.

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_TrendLinesOpeningRange : IndicatorObject
    {
        public Example_TrendLinesOpeningRange(object _ctx) : base(_ctx) { }

        private ITrendLineObject openRangeHigh, openRangeLow;

        double highestHigh = 0;
        double lowestLow   = 9999;

        protected override void CalcBar()
        {
            if (Bars.Time[0].TimeOfDay >= new TimeSpan(8, 0, 0) &&
                Bars.Time[0].TimeOfDay <= new TimeSpan(10, 0, 0))
            {
                // Keep track of the highest high and lowest low
                highestHigh = Math.Max(highestHigh, Bars.High[0]);
                lowestLow   = Math.Min(lowestLow, Bars.Low[0]);

                // Create the line on the first bar of the day
                if (Bars.Time[0].Date != Bars.Time[1].Date)
                {
                    openRangeHigh = DrwTrendLine.Create(
                        new ChartPoint(Bars.Time[1], highestHigh),
                        new ChartPoint(Bars.Time[0], highestHigh)
                        );

                    openRangeLow = DrwTrendLine.Create(
                        new ChartPoint(Bars.Time[1], lowestLow),
                        new ChartPoint(Bars.Time[0], lowestLow)
                        );

                    openRangeLow.Size  = 3;
                    openRangeHigh.Size = 3;

                    openRangeLow.Color  = Color.ForestGreen;
                    openRangeHigh.Color = Color.Firebrick;
                }
                else if (openRangeHigh != null && openRangeLow != null)
                {
                    // Adjust and extend the line
                    openRangeHigh.End   = new ChartPoint(Bars.Time[0], highestHigh);
                    openRangeHigh.Begin = new ChartPoint(openRangeHigh.Begin.Time, 
                        highestHigh);

                    openRangeLow.End   = new ChartPoint(Bars.Time[0], lowestLow);
                    openRangeLow.Begin = new ChartPoint(openRangeLow.Begin.Time, 
                        lowestLow);

                    // Reset the variables for the next open range
                    if (Bars.Time[0].TimeOfDay == new TimeSpan(10, 0, 0))
                    {
                        highestHigh = 0;
                        lowestLow   = 9999;
                    }
                }
            }
        }
    }
}

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.

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.