Order Management
Placing and managing orders is an integral part of every trading system. With Trading System API an order is more than a simple message. Orders are implemented as reference counted objects with a lifetime that persists beyond the order being filled, cancelled or rejected. It is thus possible to access an order's properties even when the order is no longer 'active'. Developers only interact with objects of class order_ref. These objects represent a handle on the underlying order object. The underlying order is only destroyed once the last order_ref object goes out of scope or is explicitly instructed to releases the order it references.
Creating Orders
Orders are created via member functions of class instrument such as instrument::buy() and instrument::sell().
instrument sp500;
void on_bar_close(void) {
if (buy_signal)
{
sp500.buy(1); // Order is sent immediately
}
if (sell_signal)
{
sp500.sell(1);
}
}
By default class instrument's order functions will transmit an order immediately and return a corresponding order_ref object. In a simulated environment market orders are filled in their entirety immediately, but in a live environment many things can go wrong and it is a good idea to retain an handle on all orders so order status can be queried at all time.
instrument sp500;
order_ref entry_order, exit_order;
void on_bar_close(void) {
if (buy_signal)
{
entry_order = sp500.buy(1); // Retain a reference to new order
}
if (sell_signal)
{
exit_order = sp500.sell(1);
}
}
Order Identification
Orders are identified in several ways.
Order-ID Type |
Function |
Description |
native |
order_ref::id() |
This is the ID assigned by the strategy itself. It look something like: 1000000000055 where the 1 in front is the ID of the strategy's instrument object which generated it, and the back number is the actual order number. This allows fast routing of confirmations back to the originating instrument. |
external |
order_ref::external_id() |
This is the ID as assigned by the platform session. Your broker/platform probably has a way to reset this sequence if required. |
permanent |
order_ref::permanent_id() |
A permanent ID which persists across platform sessions, also returned by your broker/platform. |
Order Status
An order's status can be checked by invoking order_ref::status().
order_ref o = mkt.buy(10);
order::status s = o.status();
An order's status can be any of the following:
State |
Description |
order::created |
The order has been created but has not been transmitted, and can still be modified. |
order::pending_submit |
The order has been transmitted. It is has not yet been confirmed as submitted but we must assume that is has been. |
order::pre_submitted |
The order is held by the platform until elected for submission. This can happen when the order is submitted outside trading hours, or when a particular order type is simulated by the platform. |
order::submitted |
The order is confirmed working at the exchange. It may already be filled. |
order::pending_cancel |
A cancellation request has been sent, but the order is not yet confirmed cancelled. In this state the order may already be filled. |
order::pending_cancel_replace |
A request to cancel-replace the order has been made, but no confirmation has been received. |
order::paused |
The exchange has notified that the order is paused, due to exchange issues. |
order::filled |
The order has been filled in its entirety. |
order::cancelled |
The order is confirmed cancelled and is no longer active. |
order::rejected |
The order has been rejected. |
These are alternate functions for checking order status:
order_ref::is_filled()
order_ref::is_sent()
order_ref::is_active()
order_ref::is_partially_filled()
order_ref::is_cancelled()
order_ref::is_pending_submit()
order_ref::is_pre_submitted()
order_ref::is_acknowledged()
order_ref::is_submitted()
order_ref::is_pending_cancel()
order_ref::is_cancelled()
Order 'Active' Status
An order is considered active if it's status is not any of: order::created, order::filled, order::cancelled or order::rejected. Use the order_ref::is_active() member to check if an order is active. Active orders are orders that can still cause financial loss if not handled properly. It is thus important not to lose knowledge of such orders. To prevent the loss of handles a safeguard has been built into class order_ref:
Safeguard: You cannot assign, using operator=(), an order_ref object to an existing order_ref object if the existing object still references an active order! Attempting such an assignment would cause an exception to be thrown. |
order_ref my_order;
//. . .
my_order = sp500.buy_limit(...);
assert(my_order.is_active());
my_order = sp500.sell_limit(...); // exception here! Assignment to 'active' order not allowed.
By disallowing the second assignment the strategy prevents you from losing the handle on your active limit order. Needless to say, it's important to catch such errors during the strategy testing phase. Such errors should not occur during live strategy deployment.
On occasions it makes sense to replace a reference. For the assignment to succeed the existing reference must first be released using order_ref::release() like so:
order_ref my_order;
//. . .
my_order = sp500.buy_limit(...);
my_order.release();
my_order = sp500.sell_limit(...); // success!
Verifying an Order Reference is 'Defined'
The default order_ref constructor leaves the object in an undefined state. Before you call any members of a given order_ref object, you must check if the object is in a defined state by calling order_ref::defined() or order_ref::is_defined().
order_ref my_order;
//. . .
if(my_order.is_defined()) // check if order reference is defined
{
my_order.cancel();
}
class order_ref also defines an operator bool(). This makes it possible to skip calling order_ref::is_defined() and simply write:
if(my_order) // uses operator bool() to see if 'defined'
{
my_order.cancel();
}
order_ref::is_active() can be invoked even if the object is in an undefined state! In such a case the function simply returns false. So rather than write: if(my_order) // check if object is 'defined' { if(my_order.is_active()){ my_order.cancel(); } } you can simply write: if(my_order.is_active()) // return false if order_ref is undefined { my_order.cancel(); }
|
Creating and Transmitting Orders
Orders can be transmitted immediately or transmission can be delayed to allow setting various order properties. Every order creation function like for example:
order_ref o;
//. . .
o = buy_limit(...);
has a counterpart with a postfix '_order' which delays transmission and for which transmission needs to be explicit like:
order_ref o;
//. . .
o = buy_limit_order(...);
o.time_in_force(order::day); // amend order
o.send(); // explicitly send order
The full set of class instrument's order creation functions is:
order_ref buy(quantity_t quantity);
order_ref buy_order(quantity_t quantity); // '_order' postfix means delayed transmission.
order_ref sell(quantity_t quantity);
order_ref sell_order(quantity_t quantity);
order_ref buy_stop(quantity_t quantity, double stop_price);
order_ref buy_stop_order(quantity_t quantity, double stop_price);
order_ref sell_stop(quantity_t quantity, double stop_price);
order_ref sell_stop_order(quantity_t quantity, double stop_price);
order_ref buy_stop_limit(quantity_t quantity, double stop_price, double price_limit);
order_ref buy_stop_limit_order(quantity_t quantity, double stop_price, double price_limit);
order_ref sell_stop_limit(quantity_t quantity, double stop_price, double price_limit);
order_ref sell_stop_limit_order(quantity_t quantity, double stop_price, double limit_price);
order_ref buy_limit(quantity_t quantity, double limit_price);
order_ref buy_limit_order(quantity_t quantity, double limit_price);
order_ref sell_limit(quantity_t quantity, double limit_price);
order_ref sell_limit_order(quantity_t quantity, double limit_price);
Order 'Properties' Maintained by the Instrument Object
The order creation functions shown above have simple declaration. These functions don't require more parameters as they are members of class instrument which keeps track of all relevant order related properties such as :
•symbol
•expiry
•exchange,
•broker,
•currency,
•tick-size
•tick-value
•etc.
Most of these properties are required for live deployment only at which time the information is automatically retrieved from the server. For strategy simulations, the information required is limited.
class order_ref Members
Orders are normally manipulated via order_ref objects. It is possible to access the internally managed order objects as well, but there is usually little reason to do so. Every member of class order has a corresponding member in class order_ref. The following function name list is an overview of order_ref class members. For a full listing with documentation, please consult the API's Reference Manual.
send(); cancel(); update_price(); update_quantity(); release() get() print() symbol() exchange() id() instrument_id() tag() time_in_force() expiry_time_point() price() fixed_fill_price(); group_id() is_in_group() quantity()
|
remaining() size() min_fill_quantity() action() type() aux_price() is_stop_limit() exec_interval() create_bar_ordinal() age_in_bars() age() defined() operator bool() is_filled() is_sent() active() is_active() is_partially_filled()
|
cancelled() is_cancelled() marked_for_deletion() status() prev_status() filled_quantity() last_fill_timestamp() create_timestamp() is_pending_submit(); is_pre_submitted(); is_acknowledged(); is_submitted(); is_pending_cancel(); is_cancelled(); update_status(); external_id() permanent_id() |
Minimum Tick and Price Rounding
Transaction prices for exchange traded securities are restricted to increments of 'tick-size'. For regular shares this tick-size is usually 1/100 of a currency unit. For futures contracts this value varies. The reason why the tick size is important is because limit and stop orders submit one or more prices, such as the limit price, the stop price, or the stop limit price. When these prices are submitted to the trading platform / broker, they must be valid prices, that is they most be multiples of the minimum tick, or else they will be rejected.
During live deployment with the Trading System API - Server, the tick-size will be set automatically (provided the security was set up properly in the server) for each instrument and all stop and limit prices will be rounded automatically to the nearest multiple.
Automatic rounding is not enabled for simulations. This is because simulations often involve heavily normalized, back adjusted data which no longer corresponds to a traded price. For heavily split and divided adjusted data series, the minimum tick value would also have to be adjusted for the simulation to be meaningful.
Some order functions define a price parameter, such as the 'limit-price' or 'stop-price'.
When you pass a price argument to the instrument::limit_order() function, the given price will be automatically rounded to a multiple of the instrument's min-tick value. This tick-size value can be set via the instrument::tick() member. This same member also defines a flag parameter that enables or disables automatic rounding.
instrument sp500;
// . . .
void on_start(void) override
{
sp500.connect("emini.daily");
bool auto_round = true;
sp500.tick(0.1, 25.0, auto_round); // set tick-size, tick-value, and auto-rounding
}
The above call to tick sets the tick-size to 0.1 of an (index) point, with a value of 25.0 dollars per tick. To disable auto-rounding pass 'false' as third argument.
Order Related Members of Class Instrument
Under normal circumstances orders will be managed via instances of class order_ref which features a large set of useful member. But occasionally, it may come in handy to get a list of all internally managed orders, or cancel an order based on its order-id. Please consult the API's Reference Manual for more details.
void instrument::cancel_all_orders(void);
void instrument::cancel_order(identifier_t id);
bool instrument::order_exists(identifier_t id)const;
void instrument::get_orders(
std::vector<order>& v, bool active_only = true)const;
std::vector<order_ref> instrument::orders(void)const;
std::vector<order_ref> instrument::active_orders(void)const;
bool instrument::has_active_orders(void)const;
void instrument::get_active_orders(
std::vector<order_ref>&, order::type, order::action) const;
order_ref instrument::get_order(identifier_t id) const;
order* instrument::get_order_ptr(identifier_t id) const;
size_t instrument::active_order_count(void) const;
size_t instrument::order_count() const;
Order Related Strategy Members
The following functions provide access to orders via the order-ID.
void strategy::cancel_order(identifier_t order_id, identifier_t instrument_id);
order_ref strategy::get_order_ref(identifier_t order_id) const;
order* strategy::order_ptr(identifier_t order_id) const; // Access to order via ID
Iterating through all Strategy Orders
It is possible to iterate through all strategy orders by iterating through all strategy instrument members via strategy::instruments() and then fetch all orders from each instrument using instrument::orders() and instrument::active_orders().
The number of orders owned by all instruments in the strategy is returned by:
size_t strategy::active_order_count(void) const; // Number of active orders
size_t strategy::order_count(void) const; // Number of all orders
Tutorial 303 shows how this is done. Please see the on_bar_close() member in the tutorial project for a full code listing:
// access all instruments
std::vector<instrument*> all_instruments = instruments();
// access all orders in all instruments
for(auto* i_ptr : all_instruments)
{
// all orders
std::vector<tsa::order_ref> all_orders = i_ptr->orders();
for (auto o : all_orders) {
//o.print(std::cout);
}
//active orders only
std::vector<tsa::order_ref> active_orders = i_ptr->active_orders();
for (auto o : active_orders) {
//o.print(std::cout);
}
}