Situation
You want to alternate through a list of charts with a mouse click.

Programming example

using System;
using System.Drawing;
using System.Linq;
using PowerLanguage.Function;
using System.Collections.Generic;   // Added
using System.Windows.Forms;         // Added

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

        List chartList = new List()
        {
            ".csy name=EUR/USD, df=LMAX, res=10 min",
            ".csy name=EUR/GBP, df=LMAX, res=30 min",
            ".csy name=USD/JPY, df=LMAX, res=15 min",
            ".csy name=EUR/AUD, df=LMAX, res=30 min",
            ".csy name=AUD/CAD, df=LMAX, res=45 min"
        };

        protected override void StartCalc()
        {
            Output.WriteLine("Current symbol is {0}, with {1} {2} resolution.",
                Bars.Info.Name,
                Bars.Info.Resolution.Size,
                Bars.Info.Resolution.Type);
        }
        
        protected override void CalcBar() { }

        protected override void OnMouseEvent(MouseClickArgs arg)
        {
            int currentIndex = 0, indexNextSymbol;

            if ((arg.buttons == MouseButtons.Left) && (arg.keys == Keys.Control))
            {
                Output.WriteLine("Received a {0} click + {1} key.",
                    arg.buttons,
                    arg.keys);

                for (int i = 0; i < chartList.Count; i++)
                {
                    if (chartList[i].Contains(Bars.Info.Name))
                    {
                        currentIndex = i;
                        break;
                    }
                }

                if (currentIndex == (chartList.Count - 1))
                    indexNextSymbol = 0;
                else
                    indexNextSymbol = currentIndex + 1;

                ChartCommands.CommandLine(chartList[indexNextSymbol]);
            }
        }
    }
}

Output of the programming example

The following output in the Output Window is generated:

Current symbol is EUR/USD, with 10 Minute resolution.
Received a Left click + Control key.
Current symbol is EUR/GBP, with 30 Minute resolution.
Received a Left click + Control key.
Current symbol is USD/JPY, with 15 Minute resolution.
Received a Left click + Control key.
Current symbol is EUR/AUD, with 30 Minute resolution.
Received a Left click + Control key.
Current symbol is AUD/CAD, with 45 Minute resolution.
Received a Left click + Control key.
Current symbol is EUR/USD, with 10 Minute resolution.

Working with mouse events in MultiCharts .NET

Mouse clicks provide a way to programmatically control MultiCharts .NET. They can be used, for example, to submit limit orders based on mouse click location, send market orders, reload price data, and turn a script on or ‘off’.

See working with mouse clicks and combining mouse clicks with keyboard keys to learn more about processing mouse clicks in MultiCharts .NET.

There are two requirements in working with mouse events:

  • The MouseEvent attribute needs to be enabled;
  • The OnMouseEvent() method, which contains the code to programmatically deal with mouse clicks (see PowerLanguage .NET Help, n.d.), needs to be implemented.

One way in which mouse clicks can be used is to cycle through trading charts. To do that, Command Line commands need to be used.

Switching to another chart with the Command Line

The Command Line allows manipulating the active chart through textual commands (MultiCharts Wiki, 2014). These commands can be typed in manually or provided programmatically through the ChartCommands.CommandLine() method (MultiCharts Blog, 2013).

Changing the current chart symbol is done with the .csy command. Three of its parameters are used in this article: name (the symbol name to change to), df (defines the data feed), and res, which determines the resolution (MultiCharts Wiki, 2014).

The .csy command is used in the programming example together with mouse clicks to cycle through a list of symbols.

MultiCharts .NET programming example

The example starts with adding two namespaces:

using System.Collections.Generic;   // Added
using System.Windows.Forms;         // Added

The System.Collections.Generic namespace contains the generic List<T> class that is used later on, while the System.Windows.Forms namespace contains the Keys and MouseButtons enumerations.

Two MultiCharts .NET class attributes are also declared:

[MouseEvents(true), SameAsSymbol(true)]

Setting MouseEvents to true enables the processing of mouse clicks on the chart, and with the SameAsSymbol attribute set to true the indicator is plotted on the main price chart.

Creating a generic list in MultiCharts .NET

Then a List<T> is created and populated:

List chartList = new List()
{
    ".csy name=EUR/USD, df=LMAX, res=10 min",
    ".csy name=EUR/GBP, df=LMAX, res=30 min",
    ".csy name=USD/JPY, df=LMAX, res=15 min",
    ".csy name=EUR/AUD, df=LMAX, res=30 min",
    ".csy name=AUD/CAD, df=LMAX, res=45 min"
};

P.S. The <T> at the end of List<T> means it is generic: it works with all types, but after specifying the type, list members are limited to that type (Stellman & Greene, 2010). For example, List<string> creates a list of strings, List<int> an integer list, and List<Asset> a list with Asset objects.

The chartList is a string list that has five elements added between the braces (see lines 17-21). Each of these elements is a Command Line command to change the data series symbol, and will be used later in the example.

Outputting current symbol information in MultiCharts .NET

Symbol information is outputted in the StartCalc() override method:

protected override void StartCalc()
{
    Output.WriteLine("Current symbol is {0}, with {1} {2} resolution.",
        Bars.Info.Name,
        Bars.Info.Resolution.Size,
        Bars.Info.Resolution.Type);
}

For more on symbol and resolution information, see how to get symbol information and working with data series resolution information.

Here a string with substitution parameters is printed to the Output Window with three elements: the symbol name (Bars.Info.Name), the size of the resolution (Bars.Info.Resolution.Size), and the resolution type (Bars.Info.Resolution.Type).

Processing mouse events in MultiCharts .NET

The OnMouseEvent() method is implemented as follows:

protected override void CalcBar() { }

protected override void OnMouseEvent(MouseClickArgs arg)
{
    int currentIndex = 0, indexNextSymbol;

    if ((arg.buttons == MouseButtons.Left) && (arg.keys == Keys.Control))
    {
        Output.WriteLine("Received a {0} click + {1} key.",
            arg.buttons,
            arg.keys);

        for (int i = 0; i < chartList.Count; i++)
        {
            if (chartList[i].Contains(Bars.Info.Name))
            {
                currentIndex = i;
                break;
            }
        }

        if (currentIndex == (chartList.Count - 1))
            indexNextSymbol = 0;
        else
            indexNextSymbol = currentIndex + 1;

        ChartCommands.CommandLine(chartList[indexNextSymbol]);
    }
}

The CalcBar() override method always needs to be added, even when it is empty (see line 32). Not doing so generates the ‘does not implement required abstract member’-error message.

By the way, members of the MouseClickArgs struct variable (named arg here; see line 34) contain mouse click information. For example, arg.keys provides the keyboard key that was pressed during the mouse click.

The method begins with declaring two integers: currentIndex (initialised to zero) and indexNextSymbol.

The first if statement (line 38) evaluates whether the click was done with the left mouse button (MouseButtons.Left) and the Control key (Keys.Control). When both expressions evaluate to true, a message is printed to the Output Window and the substitution parameters contain the values of both enumerators (lines 40-42).

Looping through the list collection

Next is a for loop:

for (int i = 0; i < chartList.Count; i++)
{
    if (chartList[i].Contains(Bars.Info.Name))
    {
        currentIndex = i;
        break;
    }
}

This loop starts iterating at zero and continues as long as i is less then the number of elements in the chartList list, returned by its Count property. Because index numbers of a list are zero-based, while the Count property is not (see Liberty & MacDonald, 2009), this looping statement iterates over the complete list.

In the for loop body, an if statement verifies if the current string element from the list (chartList[i]) contains the current chart symbol (Bars.Info.Name). The Contains() method of a string returns a Boolean value indicating if the specified substring occurs within the string (Microsoft Developer Network [MSDN], n.d.).

When Contains() returns true, the location of the current symbol in the list is stored in the currentIndex variable. The break statement is then called to immediately exit the loop.

Determining the index of the next symbol

Now that the location of the current symbol in the list is known, the next symbol to switch to needs to be determined:

if (currentIndex == (chartList.Count - 1))
    indexNextSymbol = 0;
else
    indexNextSymbol = currentIndex + 1;

ChartCommands.CommandLine(chartList[indexNextSymbol]);

This if-else statement determines the list index for the next symbol. When the current location of the symbol equals the last element in the list (chartList.Count - 1), the next index will need to be the first element (indexNextSymbol = 0). Otherwise, an exception will be triggered later on by requesting an element outside the range of the list. When the location of the symbol in the list is not the last index, the next element in the list is the current one plus 1 (line 56).

Lastly, the ChartCommands.CommandLine() method is called (line 58). In this method the next element from the chartList is passed in, causing the chart to switch to the next symbol.

Key points

  • Processing mouse clicks requires adding the MouseEvent attribute and implementing the OnMouseEvent() method;
  • The .csy Command Line command changes the current symbol. This command can be programmatically executed with the ChartCommands.CommandLine() method.
References

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

Microsoft Developer Network [MSDN] (n.d.). String.Contains Method. Retrieved on August 29, 2014, from http://msdn.microsoft.com/en-us/library/dy85x1sa%28v=vs.110%29.aspx

MultiCharts Blog (2013, May 31). MultiCharts .NET 8.7 Beta 2 – What’s New. Retrieved on August 29, 2014, from http://www.multicharts.com/traders-blog/?p=937

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

PowerLanguage .NET Help (n.d.). Retrieved on May 16, 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.