The Command Line can be used to change a chart symbol or for cycling through a list of symbols. But this requires a fully correct symbol name. How can we prevent accidentally using a non-existing symbol name?

Switching the chart to a MultiCharts .NET symbol that doesn’t exist

Changing a chart symbol programmatically is done with the .csy command and the ChartCommands.CommandLine() method (MultiCharts Wiki, 2014; PowerLanguage .NET Help, n.d.).

While .csy has several parameters, its basic use looks like:

ChartCommands.CommandLine(".csy name=123/XYZ, df=LMAX");

This would change the symbol of the primary data series to LMAX’s “123/XYZ”. Even though that symbol doesn’t exist, MultiCharts .NET executes the command nonetheless:

Example of undefined symbol in MultiCharts .NET

This has a few drawbacks. Since this chart has no price data, scripts cannot be executed on it anymore. And this non-existing symbol is also added to the QuoteManager:

Example of undefined symbol in MultiCharts .NET's QuoteManager

If this happens often enough, the QuoteManager will be cluttered with all these fake symbols. So how can we check if a symbol exists in the database before changing the chart to it?

Checking whether a MultiCharts .NET QuoteManager symbol exists

Before we can verify if a symbol is in the QuoteManager database, we first need to retrieve a list of QuoteManager symbols. This is done with the SymbolStorage.GetSymbols() method, which returns an array of MTPA_MCSymbolInfo2 structs that correspond to instruments in the database (MultiCharts, 2013).

That provides the possibility to verify if a symbol exists in the QuoteManager, like this:

private bool DoesSymbolExist(string symbolName, string dataFeed)
{
    foreach (var symbol in SymbolStorage.GetSymbols(dataFeed))
    {
        if (symbol.SymbolName.Equals(symbolName))
        {
            Output.WriteLine("{0}'s {1} is in the QuoteManager.",
                dataFeed, symbolName);
            return true;
        }
    }

    Output.WriteLine("{0}'s {1} couldn't be found.",
        dataFeed, symbolName);

    return false;
}

This method begins with a foreach loop that iterates over the array returned by SymbolStorage.GetSymbols(). This latter method has the dataFeed parameter (from DoesSymbolExist()) passed in as an argument, and so all QuoteManager symbols from the specified data feed are loaded.

In the foreach loop, each symbol has its SymbolName variable compared with the symbolName parameter of the DoesSymbolExist() method. When both are equal, that means the symbolName parameter is in the QuoteManager. This information is then outputted to the Output Window followed by return true, which prematurely exits both the loop and the method.

When the foreach loop ends without the symbol being found in the QuoteManager, this information is also printed to the Output Window and the DoesSymbolExist() method returns false.

Changing the chart to a symbol that exists in the MultiCharts .NET QuoteManager

With the DoesSymbolExist() method and mouse events we can make sure that only symbols are loaded that actually exist in the database:

protected override void OnMouseEvent(MouseClickArgs arg)
{
    if (arg.buttons == MouseButtons.Left)
    {
        if (arg.keys == Keys.Control)
        {
            if (DoesSymbolExist("123/XYZ", "LMAX"))
                ChartCommands.CommandLine(".csy name=123/XYZ, df=LMAX");
        }

        else if (arg.keys == Keys.Shift)
        {
            if (DoesSymbolExist("EUR/USD", "MB Trading"))
                ChartCommands.CommandLine(".csy name=EUR/USD, df=MB Trading");
        }
    }
}

//> LMAX's 123/XYZ couldn't be found.
//> MB Trading's EUR/USD is in the QuoteManager.

The first if statement in this method verifies whether the click on the chart (retrievable through the arg.buttons variable) equals (==) the left mouse button (MouseButtons.Left). That latter value comes from the System.Windows.Form namespace, which we added to the script’s code with a using directive (see full code example below). When a click with Control occurred, a nested if/else statement then evaluates if the accompanying keyboard key was either a Control or Shift key.

With Control pressed, an if statement checks if the DoesSymbolExist() method returns true when its arguments are the “123/XYZ” symbol from “LMAX”. Since this symbol doesn’t exist in the QuoteManager, DoesSymbolExist() returns false. And so the if statement’s ChartCommands.CommandLine() method isn’t executed.

Should Shift be pressed, DoesSymbolExist() is called with the “EUR/USD” and “MB Trading” arguments. Because this symbol from this data feed exists in my QuoteManager database, DoesSymbolExist() returns true and the ChartCommands.CommandLine() method is executed to change the chart.

Summary

The ChartCommands.CommandLine() method can execute the .csy command to change a symbol on the chart. To prevent loading a non-existing chart symbol, we can first check if the symbol exists in the QuoteManager instruments returned by SymbolStorage.GetSymbols().

Complete code of the MultiCharts .NET indicator

using System;
using System.Drawing;
using System.Linq;
using PowerLanguage.Function;
using System.Windows.Forms;     // For Keys and MouseButtons enumerations

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

        protected override void CalcBar() { }

        protected override void OnMouseEvent(MouseClickArgs arg)
        {
            if (arg.buttons == MouseButtons.Left)
            {
                if (arg.keys == Keys.Control)
                {
                    if (DoesSymbolExist("123/XYZ", "LMAX"))
                        ChartCommands.CommandLine(".csy name=123/XYZ, df=LMAX");
                }

                else if (arg.keys == Keys.Shift)
                {
                    if (DoesSymbolExist("EUR/USD", "MB Trading"))
                        ChartCommands.CommandLine(".csy name=EUR/USD, df=MB Trading");
                }
            }
        }

        private bool DoesSymbolExist(string symbolName, string dataFeed)
        {
            foreach (var symbol in SymbolStorage.GetSymbols(dataFeed))
            {
                if (symbol.SymbolName.Equals(symbolName))
                {
                    Output.WriteLine("{0}'s {1} is in the QuoteManager.",
                        dataFeed, symbolName);
                    return true;
                }
            }

            Output.WriteLine("{0}'s {1} couldn't be found.",
                dataFeed, symbolName);

            return false;
        }

        //> LMAX's 123/XYZ couldn't be found.
        //> MB Trading's EUR/USD is in the QuoteManager.
    }
}

References

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