With the Command Line we can change the chart symbol and cycle through a list of user-defined symbols. But how to cycle through a list of symbols pulled from the MultiCharts .NET QuoteManager?

Retrieving symbols from the MultiCharts .NET QuoteManager

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

When we want to rotate through a list of data feed symbols, that list will first need to be retrieved from the MultiCharts .NET QuoteManager. That is done with the SymbolStorage.GetSymbols() method which returns an array of MTPA_MCSymbolInfo2 structs (MultiCharts, 2013).

One way to use the overloaded SymbolStorage.GetSymbols() method is as follows:

MTPA_MCSymbolInfo2[] mySymbols = SymbolStorage.GetSymbols(
    "dataFeedName", ESymbolCategory.Category, "exchangeName");

Here a mySymbols array consisting out of MPTA_MCSymbolInfo2 structs is populated with the SymbolStorage.GetSymbols() method, which uses three arguments: the data feed name, an ESymbolCategory enumerated value for the type of instrument (like ESymbolCategory.Future or ESymbolCategory.Forex), and the exchange name (such as "CME").

Getting symbol information from the QuoteManager

The programming example starts as follows:

private MTPA_MCSymbolInfo2[] symbols;
private int symbolIndex;

protected override void Create()
{
    symbols = SymbolStorage.GetSymbols(
        "IQFeed", ESymbolCategory.Future, "CME");

    Output.WriteLine("All CME IQFeed QuoteManager symbols:");

    for (int i = 0; i < symbols.Length; i++)
        Output.Write(symbols[i].SymbolName + "  ");

    Output.WriteLine("");
}
//> All CME IQFeed QuoteManager symbols:
//> @PX#  @SF#  @NE#  @JY#  @AD#  @BP#  @CD#  @EU#  @ES#
//> @NQ#  @M6A#  @M6E#  @M6B#  @MCD#  @M6C#  @MSF#  @M6S# 
//> @MJY#  @M6J#  @MNY#  @MIR#

Here we declare a symbols array consisting out of MTPA_MCSymbolInfo2 structs and an integer (symbolIndex). This integer will hold the array index of the next symbol to switch to.

In the Create() method, executed once immediately after applying the script to the chart (MultiCharts, 2013), the symbols array is populated with SymbolStorage.GetSymbols(). The three arguments passed into this method signal that the symbols retrieved from the QuoteManager should be IQFeed’s CME’s futures.

These symbols are then outputted to the Output Window with Output.Write() in a for loop. In this loop, each struct’s SymbolName variable is used to retrieve the name of the symbol.

Determining the index of the next symbol to switch to

Next is the StartCalc() method, executed once at the beginning of each calculation cycle (MultiCharts, 2013):

protected override void StartCalc()
{
    symbolIndex = 0;

    for (int i = 0; i < symbols.Length; i++)
    {
        if (Bars.Info.Name == symbols[i].SymbolName &&
            i + 1 <= symbols.Length - 1)
        {
            symbolIndex = i + 1;
            break;
        }
    }
}

We start here by assigning symbolIndex a default value of 0. That way, if the for loop below it doesn’t come up with a value, the next symbol to switch to will be the first array element at index 0.

The for loop iterates through all symbols in the symbols array. Its if statement checks if the current symbol of the primary data series (Bars.Info.Name) equals a symbol name from the array, and whether the next index (i + 1) is still inside the bounds of the array (the non-zero based symbols.Length minus 1). With both expressions true, symbolIndex is assigned the index of the next element in the array and break is executed to stop the loop prematurely.

Switching to another symbol with mouse clicks in MultiCharts .NET

The OnMouseEvent() method, which processes mouse clicks, performs the actual symbol changing:

protected override void OnMouseEvent(MouseClickArgs arg)
{
    if (arg.buttons == MouseButtons.Left && arg.keys == Keys.Shift)
    {
        Output.WriteLine("Switching to {0}", 
            symbols[symbolIndex].SymbolName);

        ChartCommands.CommandLine(".csy name=" +
            symbols[symbolIndex].SymbolName);
    }
}

//> Switching to @CD#
//> Switching to @EU#
//> Switching to @ES#
//> Switching to @NQ#
//> Switching to @M6A#
//> Switching to @M6E#

This if statement evaluates whether the click on the chart was done with the left mouse button while the Shift key was pressed down.

When that is the case, Output.WriteLine() prints a message to the Output Window and the ChartCommands.CommandLine() method is executed. This method has a string passed in that consists out of the .csy command, its name parameter, and the next symbol from the array, retrieved with symbols[symbolIndex].SymbolName. This way, each mouse click + Shift on the chart switches the chart to the next symbol in the list.

Summary

The .csy command changes the chart’s symbol and can be executed with ChartCommands.CommandLine(). Retrieving a list of symbols from the QuoteManager is done with SymbolStorage.GetSymbols(), which returns an array of MTPA_MCSymbolInfo2 structs.

Complete code of the MultiCharts .NET indicator

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

namespace PowerLanguage.Indicator
{
    [SameAsSymbol(true), MouseEvents(true)]
    public class Example_DataFeedSymbolsCommandLine : IndicatorObject
    {
        public Example_DataFeedSymbolsCommandLine(object _ctx) : base(_ctx) { }
        private MTPA_MCSymbolInfo2[] symbols;
        private int symbolIndex;

        protected override void Create()
        {
            symbols = SymbolStorage.GetSymbols(
                "IQFeed", ESymbolCategory.Future, "CME");

            Output.WriteLine("All CME IQFeed QuoteManager symbols:");

            for (int i = 0; i < symbols.Length; i++)
                Output.Write(symbols[i].SymbolName + "  ");

            Output.WriteLine("");
        }
        //> All CME IQFeed QuoteManager symbols:
        //> @PX#  @SF#  @NE#  @JY#  @AD#  @BP#  @CD#  @EU#  @ES#
        //> @NQ#  @M6A#  @M6E#  @M6B#  @MCD#  @M6C#  @MSF#  @M6S# 
        //> @MJY#  @M6J#  @MNY#  @MIR#

        protected override void StartCalc()
        {
            symbolIndex = 0;

            for (int i = 0; i < symbols.Length; i++)
            {
                if (Bars.Info.Name == symbols[i].SymbolName &&
                    i + 1 <= symbols.Length - 1)
                {
                    symbolIndex = i + 1;
                    break;
                }
            }
        }

        protected override void OnMouseEvent(MouseClickArgs arg)
        {
            if (arg.buttons == MouseButtons.Left && arg.keys == Keys.Shift)
            {
                Output.WriteLine("Switching to {0}",
                    symbols[symbolIndex].SymbolName);

                ChartCommands.CommandLine(".csy name=" +
                    symbols[symbolIndex].SymbolName);
            }
        }

        //> Switching to @CD#
        //> Switching to @EU#
        //> Switching to @ES#
        //> Switching to @NQ#
        //> Switching to @M6A#
        //> Switching to @M6E#

        protected override void CalcBar() { }
    }
}

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