Situation
You want to know how to submit limit orders in a single-instrument trading strategy based on the mouse click location.
Programming example
using System; using System.Drawing; using System.Linq; using PowerLanguage.Function; using ATCenterProxy.interop; using System.Windows.Forms; // Added namespace PowerLanguage.Strategy { [MouseEvents(true), IOGMode(IOGMode.Enabled)] public class Example_LimitOrdersMouseClick : SignalObject { private IOrderPriced enterLong; private IOrderMarket exitLong; private double enterLongPrice; public Example_LimitOrdersMouseClick(object _ctx) : base(_ctx) { } protected override void Create() { enterLong = OrderCreator.Limit( new SOrderParameters(Contracts.Default, EOrderAction.Buy)); exitLong = OrderCreator.MarketNextBar( new SOrderParameters(Contracts.Default, EOrderAction.Sell)); } protected override void CalcBar() { if (StrategyInfo.MarketPosition == 0) { if (enterLongPrice > 0) enterLong.Send(enterLongPrice); } } protected override void OnMouseEvent(MouseClickArgs arg) { if ((arg.buttons != MouseButtons.Left) || (arg.keys == Keys.None)) this.CalcBar(); if (arg.keys == Keys.Control) { if (arg.point.Price > Bars.Close[0]) return; enterLongPrice = arg.point.Price; Output.WriteLine("{0} - Submitting enter long order @ {1}", DateTime.Now.ToString("HH:mm:ss"), enterLongPrice.ToString("F5")); } else if (arg.keys == Keys.Shift) { enterLongPrice = 0; Output.WriteLine("{0} - Submitting exit long market order.", DateTime.Now.ToString("HH:mm:ss")); exitLong.Send(); } else if (arg.keys == (Keys.Control | Keys.Shift)) { enterLongPrice = 0; Output.WriteLine("{0} - Cancel limit long order.", DateTime.Now.ToString("HH:mm:ss")); } } } }
Output of the programming example
Applied to a chart, the example strategy looks like:

With the following orders in the Order and Position Tracker (click for a larger version):

And the resulting output in the Output Window:
06:51:04 - Submitting enter long order @ 1,31591 06:51:25 - Cancel limit long order. 06:51:33 - Submitting enter long order @ 1,31597 06:57:09 - Cancel limit long order. 06:57:16 - Submitting enter long order @ 1,31612 07:06:28 - Submitting exit long market order.
Working with mouse clicks in MultiCharts .NET
Programmatically controlling MultiCharts .NET can be done with mouse clicks, and these can be used for reading and writing text files, turning a script on or off, or to submit market orders with mouse clicks.
See working with mouse events and combining mouse clicks with keyboard keys to learn more.
There are two requirements in working with mouse clicks:
- The
MouseEvents
attribute needs to be set to true; - The
OnMouseEvent()
method, responsible for programmatically processing mouse clicks (see PowerLanguage .NET Help, n.d.), needs to be implemented.
One way to use mouse clicks is to submit limit orders based on the mouse click location.
Trading strategy limit orders in MultiCharts .NET
Adding managed limited orders to a trading strategy has three requirements (MultiCharts, 2013):
- An
IOrderPriced
object needs to be declared (see line 13 in the example); - The order object needs to be instantiated (i.e., created) in the
Create()
override method with theOrderCreator.Limit()
method (lines 22-23); - And the order needs to be submitted with its
Send()
method (line 34).
Unfilled managed limit orders need to be resubmitted on every price update: otherwise, the order will be cancelled (e.g., see MultiCharts Wiki, February 2012).
Left mouse clicks are processed as follows in the example: a click with the Control key submits a buy (enter long) market order, a click with Shift sends a sell (exit long) market order, and a click with both Control and Shift cancels the pending limit order.
P.S. See submitting market orders with a mouse click to learn about market orders and mouse clicks.
MultiCharts .NET programming example
First, the System.Windows.Forms
namespace is added for easy referencing the Keys
and MouseButtons
enumerations:
using System.Windows.Forms; // Added
Then two MultiCharts .NET class attributes are added:
[MouseEvents(true), IOGMode(IOGMode.Enabled)]
Setting MouseEvents
to true enables the processing of mouse clicks, while setting IOGMode
to enabled turns the executing of orders intra-bar on.
Declaring and instantiating order objects in MultiCharts .NET
Next two order objects and a double variable are declared:
private IOrderPriced enterLong; private IOrderMarket exitLong; private double enterLongPrice;
Two types of orders are used here: an IOrderPriced
limit order and an IOrderMarket
market order. The enterLongPrice
variable is used to keep track of the limit order price.
In the Create()
method the orders are created:
protected override void Create() { enterLong = OrderCreator.Limit( new SOrderParameters(Contracts.Default, EOrderAction.Buy)); exitLong = OrderCreator.MarketNextBar( new SOrderParameters(Contracts.Default, EOrderAction.Sell)); }
The OrderCreator.Limit()
method creates the enter long limit order, while the exit market order is created with OrderCreator.MarketNextBar()
. Both have the trade size from the setting in the ‘Strategy Properties’ window (Contracts.Default
), and are a buy (EOrderAction.Buy
) and sell (EOrderAction.Sell
) order, respectively.
When intra-bar order generation is turned on, MarketNextBar()
orders are actually send on the next tick (see MultiCharts Wiki, August 2012).
Submitting limit orders in MultiCharts .NET
Next is the CalcBar()
override method:
protected override void CalcBar() { if (StrategyInfo.MarketPosition == 0) { if (enterLongPrice > 0) enterLong.Send(enterLongPrice); } }
The first if statement verifies if the strategy's current market position is flat, in which case the StrategyInfo.MarketPosition
property returns 0 (MultiCharts, 2013). The nested if statement (lines 33-34) then verifies whether the enterLongPrice
variable is greater than zero.
When both expressions evaluate to true, the limit order is submitted by calling its Send()
method (line 34). The limit price is specified by passing in the enterLongPrice
variable in the Send()
method.
Processing mouse clicks in MultiCharts .NET
The OnMouseEvent()
method starts with the following if statement:
if ((arg.buttons != MouseButtons.Left) || (arg.keys == Keys.None)) this.CalcBar();
By the way, mouse click information is accessible through members of the MouseClickArgs
struct variable, named here arg
(see line 28). For example, arg.buttons
can be used to determine which mouse button was pressed.
Two conditions are evaluated here: whether the arg.buttons
enumerator is unequal to a left mouse click (MouseButtons.Left
) and if the mouse click was not accompanied by a keyboard key (Keys.None
). When either of these are true, tested with the conditional-OR (||
) operator, the OnMouseEvent()
method is exited prematurely by calling the CalcBar()
override method.
To learn more about triggering a recalculation of the CalcBar()
method, see recalculating a script with a mouse click. For more on return
, see how to ‘stop’ a script from calculating.
This way only left mouse clicks with a keyboard key are processed in the remainder of the method. The CalcBar()
method is called with the this
keyword, which refers to the current instance of the object (Liberty & MacDonald, 2009), so that any open limit order is resubmitted again and not cancelled (see line 34).
Using mouse clicks for determining the limit order price
The next part of the OnMouseEvent()
method determines the limit price:
if (arg.keys == Keys.Control) { if (arg.point.Price > Bars.Close[0]) return; enterLongPrice = arg.point.Price; Output.WriteLine("{0} - Submitting enter long order @ {1}", DateTime.Now.ToString("HH:mm:ss"), enterLongPrice.ToString("F5")); }
When the mouse click was accompanied with the Control key (line 43), the nested if statement (lines 45-46) verifies if the price of the mouse click (arg.point.Price
) was above the current price (Bars.Close[0]
). The return
keyword, which immediately exits the current method (Stellman & Greene, 2010), is called for prices that would lead to an immediately triggered price limit order.
When the mouse click location is below the current price, the mouse click price (arg.point.Price
) is sorted in the enterLongPrice
variable. This variable is subsequently used in the CalcBar()
method (lines 33-34).
A message is also printed to the Output Window (lines 50-52): with substitution parameters the current computer DateTime is formatted to a string and enterLongPrice
is displayed with five decimals (ToString("F5")
).
Submitting a exit long market order in MultiCharts .NET
Then an else-if statement checks if the Shift key was pressed:
else if (arg.keys == Keys.Shift) { enterLongPrice = 0; Output.WriteLine("{0} - Submitting exit long market order.", DateTime.Now.ToString("HH:mm:ss")); exitLong.Send(); }
The enterLongPrice
variable is set to 0 (line 56) when the conditional expression of the if statement (line 54) evaluates to true. Since a value of 0 will not resubmit the limit order (see lines 33-34), a pending limit order will be cancelled. This is because unfilled managed orders are cancelled as soon as they are not resubmitted anymore (e.g., see MultiCharts Wiki, February 2012).
Next, information is printed to the Output Window (lines 58-59) and the exitLong
market order is submitted to close the position (line 61).
Cancel pending limit orders in MultiCharts .NET
The last part of the OnMouseEvent()
method causes limit orders to be cancelled:
else if (arg.keys == (Keys.Control | Keys.Shift)) { enterLongPrice = 0; Output.WriteLine("{0} - Cancel limit long order.", DateTime.Now.ToString("HH:mm:ss")); }
The else-if statement uses the bitwise-OR operator (|
) to evaluate whether both the Control and Shift key were pressed. Since Keys
is an enumeration with the Flags
attribute (which allows for combining several enumerators; Albahari & Albahari, 2012), this OR operator is needed for this Keys
combination.
When both Control and Shift were pressed during the mouse click, the enterLongPrice
variable is set to 0. That stops sending the limit order (see the if statement in the CalcBar()
method; lines 33-34). Lastly, an informational message is printed to the Output Window (lines 67-68).
Key points
- Processing mouse clicks requires adding the
MouseEvents
attribute and implementing theOnMouseEvent()
method; - Limit orders need to be declared as an
IOrderPriced
object and subsequently created in theCreate()
method with theOrderCreator.Limit()
method. Submitting a limit order is done by calling itsSend()
method; - Managed limit orders remain active when resubmitted on every
CalcBar()
calculation: to cancel pending limit orders, stop submitting them; - The variable that contains the
MouseClickArgs
struct data in theOnMouseEvent()
method has several members with mouse click information, such as mouse button (buttons
), keyboard key (keys
), and price location of the click (point.Price
).
Albahari, J. & Albahari, B. (2012). C# 5.0 in a Nutshell: The Definitive Reference (5th edition). Sebastopol, CA: O’Reilly Media.
Liberty, J. & MacDonald, B. (2009). Learning C# 3.0: Master the Fundamentals of C# 3.0. Sebastopol, CA: O’Reilly Media.
MultiCharts (2013). MultiCharts .NET Programming Guide (version 1.0). Retrieved from http://www.multicharts.com/downloads/MultiCharts.NET-ProgrammingGuide-v1.0.pdf
MultiCharts Wiki (2012, February 19). Buy. Retrieved on August 27, 2014, from http://www.multicharts.com/trading-software/index.php/Buy
MultiCharts Wiki (2012, August 31). IntraBarOrderGeneration. Retrieved on August 23, 2014, from http://www.multicharts.com/trading-software/index.php/IntraBarOrderGeneration
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.
Visit Kodify.net for more helpful coding articles.