The Command Line can be used to change a chart symbol, for example by changing the data range or loading a minimum amount of days. But how can the data range be changed such that there are a minimum amount of price bars on the chart?

Changing a chart symbol with the MultiCharts .NET Command Line

The .csy command changes a symbol on the price chart (MultiCharts Wiki, 2014) and is executed programmatically with the ChartCommands.CommandLine() method (PowerLanguage .NET Help, n.d.).

One problem with wanting to change a chart such that a certain amount of bars are plotted is that the .csy command has no parameter for this (see MultiCharts Wiki, 2014). And since the from and to .csy parameters work with whole days, an exact amount of price bars cannot be loaded (unless the number of bars are an even multiple of the bars per day).

We can, however, aim for a minimum amount of price bars on the chart. To see how, let’s look at the programming example.

Calculating the number of price bars per trading day

To get the start date for the new data range, we need to calculate the number of price bars per day first and derive the start date from that. This is done in the StartDate() method:

private DateTime StartDate(int minAmountOfBars)
{
    // Step 1: Determine the average number of bars per day.
    int businessDays = 0;

    for (DateTime date = Bars.Request.Range.From; 
        date <= Bars.Request.Range.To; date = date.AddDays(1))
    {
        if (date.DayOfWeek != DayOfWeek.Saturday &&
            date.DayOfWeek != DayOfWeek.Sunday)
            businessDays++;
    }

    double barsPerDay = Bars.FullSymbolData.Count / businessDays;

    // Step 2: Determine the new start date.
    double daysNeeded = minAmountOfBars / barsPerDay;

    daysNeeded = Math.Ceiling(daysNeeded + (daysNeeded / 5 * 2)) + 1;

    DateTime startDate = Bars.Request.Range.To.AddDays(-daysNeeded);

    Output.WriteLine("The {0} bars and {1} business days " + 
        "give {2} bars per day.",
        Bars.FullSymbolData.Count,
        businessDays,
        barsPerDay);

    Output.WriteLine("To get at least {0} bars, {1} days are " + 
        "needed (incl. weekends).",
        minAmountOfBars,
        daysNeeded);

    return startDate;
}

//> Start date 20-11-2014, end date: 7-12-2014\. Bars: 576.
//> The 576 bars and 12 business days give 48 bars per day.
//> To get at least 750 bars, 23 days are needed (incl. weekends).
//> .csy from=14-11-2014, to=7-12-2014
//> Start date 14-11-2014, end date: 7-12-2014\. Bars: 768.

This method begins with declaring an integer variable (businessDays). Then a for loop begins to determine the number of business days in the current data range of the first data series.

This for loop header has three parts. First, a date DateTime variable is declared and assigned the value of Request.Range.From, which holds the begin date of the chart’s data range (PowerLanguage .NET Help, n.d.). Second, the loop continues as long as date is less than or equal to the end date of the chart (Bars.Request.Range.To). And third, after each loop iteration date has 1 day added to it (date = date.AddDays(1)).

Inside the for loop, an if statement checks whether the current value of date.DayOfWeek is unequal to a Saturday or Sunday. When that is the case, businessDays is incremented with 1 with the postfix increment operator.

Next the total number of bars on the chart (returned by Bars.FullSymbolData; MultiCharts, 2013) is divided by the number of business days to get the average price bars per trading day. Note that barsPerDay is only an estimate when the chart is non-time based (such as a tick or volume chart). And bank holidays and partial trading days are also not taken into account here.

Determining the new begin date of the price chart programmatically

The second step in StartDate() is determining the new begin date of the chart’s range. We first get an estimate of the number of days needed (daysNeeded) by dividing the minAmountOfBars parameter by the bars per day.

Since .csy’s from and to parameters are based on calendar days, the daysNeeded value needs to be corrected for weekends. This is done by dividing it by 5 and multiplying with 2, and adding that result back to the daysNeeded variable. That way, for every 5 business days, 2 extra weekend days are added. The Math.Ceiling() method, which rounds up (Albahari & Albahari, 2012), prevents that this calculation results in fractional days. With the + 1 at the end of this statement we err on the safe side in determining the number of calendar days needed.

Then a DateTime variable (startDate) is declared and assigned the end date of the primary data series (Bars.Request.Range.To) minus the number of calendar days (AddDays(-daysNeeded)). This DateTime value is returned by the method in its last statement, but first information is printed to the Output Window with Output.WriteLine() to verify the code.

Loading a minimum amount of price bars on a MultiCharts .NET chart

Changing the data range is done in the CalcBar() method:

protected override void CalcBar()
{
    if (Bars.LastBarOnChart)
    {
        DateTime newStartDate = StartDate(750);

        if (Bars.Request.Range.From.Date != newStartDate.Date)
        {
            string command = String.Format(".csy from={0}, to={1}",
                newStartDate.ToString("d/M/yyyy"),
                Bars.Request.Range.To.ToString("d/M/yyyy"));

            Output.WriteLine(command);

            ChartCommands.CommandLine(command);
        }
    }
}

The first if statement checks whether the current bar is the chart’s last bar, in which case the Bars.LastBarOnChart property returns true (PowerLanguage .NET Help, n.d.).

Inside the if statement, the newStartDate DateTime variable is assigned the value returned by the StartDate() method. In this method the value of 750 is passed as the minimum number of price bars.

A nested if statement then evaluates if the start date of the chart (Bars.Request.Range.From.Date) differs from the newStartDate.Date value. When these are unequal, the chart’s data range needs to be adjusted. This check revents the script from unnecessarily changing the chart’s range.

Generating a Command Line command programmatically

Changing the actual data range is done with the .csy Command Line command, generated in the String.Format() method and assigned to the command variable. Substitution parameters ({0} and {1}) are used to insert newStartDate as the from parameter and the chart’s current end date as the to parameter.

The required date format of the from and to .csy parameters depends on your Windows settings and is either day/month/year or month/day/year (Andrew MultiCharts, 2014). The dates are generated here with custom format specifiers where the "d" stands for the day of the month (1 to 31), "M" the month (1 through 12), and "yyyy" is the year as a four-digit number (Dorman, 2010).

The command variable is then passed into the Output.WriteLine() method for verification and finally into the ChartCommands.CommandLine() method for execution, which will cause the new data range to load.

For more on .csy and its parameters, see changing the symbol with the Command Line. Related examples are changing the data range and loading a minimum amount of trading days.

Summary

The .csy Command Line command can change the date range of a symbol with its from and to parameters, which accept calendar days in either the day/month/year or month/day/year format. This command can be executed with ChartCommands.CommandLine(). We can load a minimum amount of price bars on the chart by approximating the number of bars per weekday.

Complete code of the MultiCharts .NET indicator

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

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

        protected override void StartCalc()
        {
            Output.WriteLine("Start date {0}, end date: {1}. Bars: {2}.",
                Bars.Request.Range.From.ToString("d-M-yyyy"),
                Bars.Request.Range.To.ToString("d-M-yyyy"),
                Bars.FullSymbolData.Count);
        }

        protected override void CalcBar()
        {
            if (Bars.LastBarOnChart)
            {
                DateTime newStartDate = StartDate(750);

                if (Bars.Request.Range.From.Date != newStartDate.Date)
                {
                    string command = String.Format(".csy from={0}, to={1}",
                        newStartDate.ToString("d/M/yyyy"),
                        Bars.Request.Range.To.ToString("d/M/yyyy"));

                    Output.WriteLine(command);

                    ChartCommands.CommandLine(command);
                }
            }
        }

        private DateTime StartDate(int minAmountOfBars)
        {
            // Step 1: Determine the average number of bars per day.
            int businessDays = 0;

            for (DateTime date = Bars.Request.Range.From; 
                date <= Bars.Request.Range.To; date = date.AddDays(1))
            {
                if (date.DayOfWeek != DayOfWeek.Saturday &&
                    date.DayOfWeek != DayOfWeek.Sunday)
                    businessDays++;
            }

            double barsPerDay = Bars.FullSymbolData.Count / businessDays;

            // Step 2: Determine the new start date.
            double daysNeeded = minAmountOfBars / barsPerDay;

            daysNeeded = Math.Ceiling(daysNeeded + (daysNeeded / 5 * 2)) + 1;

            DateTime startDate = Bars.Request.Range.To.AddDays(-daysNeeded);

            Output.WriteLine("The {0} bars and {1} business days " + 
                "give {2} bars per day.",
                Bars.FullSymbolData.Count,
                businessDays,
                barsPerDay);

            Output.WriteLine("To get at least {0} bars, {1} days are " + 
                "needed (incl. weekends).",
                minAmountOfBars,
                daysNeeded);

            return startDate;
        }

        //> Start date 20-11-2014, end date: 7-12-2014\. Bars: 576.
        //> The 576 bars and 12 business days give 48 bars per day.
        //> To get at least 750 bars, 23 days are needed (incl. weekends).
        //> .csy from=14-11-2014, to=7-12-2014
        //> Start date 14-11-2014, end date: 7-12-2014\. Bars: 768.
    }
}

For more information about coding and the C# programming language, see Kodify.net.


References

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

Andrew MultiCharts (2014, October 6). Command line to change start date – forum discussion. Retrieved on November 21, 2014, from http://www.multicharts.com/discussion/viewtopic.php?f=1&t=47294#p108924

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

MultiCharts (2013). MultiCharts .NET Programming Guide (version 1.0). Retrieved from http://www.multicharts.com/downloads/MultiCharts.NET-ProgrammingGuide-v1.0.pdf

MultiCharts Wiki (2014, August 18). MultiCharts Work Area: Understanding Command Line. Retrieved on November 18, 2014, from http://www.multicharts.com/trading-software/index.php/MultiCharts_Work_Area#Understanding_Command_Line

PowerLanguage .NET Help (n.d.). Retrieved on November 18, 2014, from http://www.multicharts.com/downloads/PowerLanguage.NET.chm

Visit Kodify for more helpful coding articles.