Order Entry / Market Access API
Start sending orders and receiving order updates callbacks in these 4 steps:
- Initialize a callback
listenerobject registerApplicationwith the listenerinita trading sessionstartnanoconda threads
Tip
Please refer to the Sample Application for demonstration. The sample application is the best starting point for your own algo.
Callbacks
Callback Listener
As market data arrives from the exchange it gets normalized and appropriate callback is raised. All our nanoconda market data objects are simple structs and you can directly access any field.
In order to receive callback updates user must implement a listener class and inherit nanoconda::listener
struct myListener : nanoconda::listener
{
void onorderack(const nanoconda::order* order) { };
void onorderreject(const nanoconda::order* order) { };
void oncancelack(const nanoconda::order* order) { };
void oncancelreject(const nanoconda::order* order) { };
void onmodifyack(const nanoconda::order* order) { };
void onmodifyreject(const nanoconda::order* order) { };
void onfill(const nanoconda::order* order) { };
void onout(const nanoconda::order* order) { };
void onorderstatus(const nanoconda::order* order) { };
/*
...
*/
}
Order Struct
struct order
{
unsigned long long symbolId;
unsigned long long exchangeSecurityId;
unsigned long long orderId;
unsigned long long exchangetime;
unsigned long long seqno;
unsigned long long updatetime;
unsigned long long writeseqno;
unsigned long long exchangeOrderId;
unsigned long long customId;
long long price;
long long triggerPrice;
long long priceLast;
long long balance;
unsigned int quantity;
unsigned int quantityDisplay;
unsigned int quantityLast;
unsigned int quantityFilled;
orderStatus status;
orderType type;
char side;
unsigned short exchangeReason;
unsigned short reservedS;
order()
{
reset();
}
void reset();
void setSymbol(const char* str);
const char* getSymbol();
long long priceAverage();
static inline unsigned short size()
{
return 128;
}
};
Order Acknowledged
Order is acknowledted by the exchanged. orderId will match the one returned in newOrder() and exchangeOrderId will be populated by exchange order Id.
Order Filled
Whenever an order got fills onfill() callback will be called, regardless of quantity of the fill.
It is users responsibility to handle partial fills, however the API provides all information needed for each order to identify them.
Important order Fields:
priceLast- price of the (last) fill.quantityLast- quantity of the last fill (current callback).quantityFilled- total cumulative filled quantity.quantity- Original order quantity.
If quantityFilled is below quantity it means that the order was only filled partially.
Order Cancel Acknowledged
Order was successfully cancelled.
Order Modify Acknowledged
Order was successfully modified.
Order Removed
Whenever an order is removed by the exchange (without user request), onout is called.
Order Status
Returned to notify the algo about order status. Used at the start of the program if there are any orders outstanding on the exchange (cancel on disconnect is disabled).
Order Rejected
Order was rejected by the exchange
Order Cancel Rejected
Order Cancel request was rejected.
Order Modify Rejected
Order Modification request was rejected.API functuins
All Order Entry functions are performed on the dmasession object which is returned form dmasession::init() call.
Note
API Calls return nanoconda::reasoncode to notify the user if the call was successful or if there was an error. It is users responsibility to handle any errors returned. Please refer to Reason Codes for explanation of each code and to Sample Application for an example on how to handle the codes.
struct dmasession
{
static dmasession* init(const char* accountName, reasoncode& reason, short cpu = -1);
reasoncode newOrder(order* o);
reasoncode cancelOrder(order* o);
reasoncode modifyOrder(order* o);
reasoncode cancelAllOrders(unsigned long long symbolId = 0);
reasoncode flatten(unsigned long long symbolId = 0);
reasoncode initUIManager(uilistener* uil);
reasoncode setuidata(void* src, unsigned short len=16000) const;
reasoncode setalgoreport(algoreport* report) const;
risk* getRiskLimits();
long long sessionPnl();
long long accountBalance();
long long maxDrawdown();
reasoncode getSecurityStats(unsigned long long symbolId, securitystats* stats);
/*
...
*/
};
New Order
After successfull completion orderId will be filled in immediately after the call in your order object.
Required Fields: 1. symbolId 2. type 3. side 4. quantity 5. price (if not a market order) 6. triggerPrice (for stop orders)
custimId and customData fields can be used to supply any user data that will be copied and returned back for callbacks for the relevant order.
OrderType WARM
The API provides a special order type called WARM that is used to warm the sending cache after idle periods. This helps ensure minimal latency when trading resumes.
Key Characteristics
WARMorders do not leave the server- They are used only for warming the cache
- Each
WARMorder is internally immediately confirmed and canceled viaonOutcallback - No messages are sent to the exchange
Recommended Usage
Submit a WARM order:
- After any period of inactivity longer than 1 minute
- Before sending the next actual order
- Anytime you want to prevent cold start latency
This helps maintain a hot session and avoids latency spikes caused by cache eviction or idle paths.
Cancel Order
Required Fields:
- orderId
- symbolId
- side
Modify Order
Required Fields:
- orderId
- symbolId
- side
- type
- quantity
- price (if not a market order)
- triggerPrice (for stop orders)
Cancel All Orders
If symbolId is provided then only orders for the specified symbol will be cancelled. Otherwise all orders will be cancelled.
Flatten Account
Flatten account will close all open positions (send Market orders opposing account net position for each instrument ) and cancell all orders.
Get Risk Limits
It is often useful for an application to retrieve the state of account and risk limits.
It can be done via the following API call:
returns a pointer to nanoconda::risk object which you can iterate over to get account summary per risked instrument. symbolcount is the total number of available instruments.
struct securityrisk
{
char symbol[16];
long long capitalreq;
long long capitaluse;
int maxpositions;
int clipsize;
int livelots;
int liveorders;
int netopenpositions;
int fee;
int fillslong;
int fillsshort;
};
struct risk
{
unsigned short symbolcount;
unsigned short reserved1;
unsigned short reserved2;
unsigned short reserved3;
securityrisk risklimits[];
unsigned short size();
};
Session P&L, Maximum Drawdown and Account Balance
It may be useful for the algorithm to have real-time access to the session's Profit and Loss (P&L), as well as the Maximum Drawdown and Account Balance (if configured).
These values can be retrieved using the following API calls:
Both functions return absolute values in Nanoconda price units.
Info
Values are represented using the same format as price fields — scaled by a factor of 10^5 from the original value.
Security Statistics and P&L
It may be useful for the algorithm to have real-time access to statistics and Profit and Loss (P&L) for a specific security.
These values are abailable through the following API call:
The caller must provide a symbolId and a pre-allocated nanoconda::securitystats object, which the API will populate with the corresponding data.
struct securitystats
{
unsigned long long symbolId;
long long realizedPnl;
long long currentAvgPrice;
unsigned int livelotslong;
unsigned int liveorderslong;
unsigned int livelotsshort;
unsigned int liveordersshort;
int netopenpositions;
int reserved1;
unsigned int fillslong;
unsigned int fillsshort;
long long reserved2;
};
Sample Application
Here is a simple bracket algorithm that shows most of the often used features (can also be found in src/bracketorder.cpp).
Tip
Code is annotated, you can click on (1) icon to view additional information.
- You found on a sample annotation, now look for the same in the code below!
/* Nanoconda 2025 | A simple Bracket Order Example | bracketorder.cpp | g++ bracketorder.cpp -lnanoconda
* If market moves up 4 ticks from the any low point we open a new LONG position for 10 contracts
* When the opening order is filled we send a 8 tick wide bracket (STOP Loss and a LIMIT for profits)
* If LIMIT order is partially executed we MODIFY stop loss to only cover remaining quantity
* If any of the STOP Loss order or LIMIT order is fully filled we cancel the other order
* This program is for llustrative purposes only, please add your own logic to handle empty callbacks
* such as order Rejections, Acks etc.
*/
#include "nanoconda.h"
#include <cstring>
#include <iostream>
using namespace nanoconda;
struct myListener : nanoconda::listener
{
nanoconda::order openOrder;
nanoconda::order stopLossOrder;
nanoconda::order limitOrder;
unsigned long long symbolId;
long long referencePrice;
long long tickSize;
bool bracketLaunched;
nanoconda::dmasession* tradingSession;
myListener() // (1) initialize starting states
{
symbolId=0;
referencePrice = 0;
tickSize = 0;
tradingSession = nullptr;
bracketLaunched = false;
/* Open Market Order Buy 10 contracts at market, price not required for MKT */
openOrder.side='B';
openOrder.quantity=10;
openOrder.type=nanoconda::orderType::MKT;
/* Stop Loss Order Sell 10 contracts, price unknown at initialization */
stopLossOrder.side='S';
stopLossOrder.quantity=10;
stopLossOrder.type = nanoconda::orderType::STP;
/* Limit Order Sell 10 contracts, price unknown at initialization */
limitOrder.side='S';
limitOrder.quantity=10;
limitOrder.type = nanoconda::orderType::LMT;
}
void onsecurity(const nanoconda::security* s) {
if ((this->symbolId == 0) && !strcmp(s->symbol,"ESM5")) // (2) make sure we process the desired security
{
this->symbolId = s->symbolId;
this->openOrder.symbolId = s->symbolId;
this->stopLossOrder.symbolId = s->symbolId;
this->limitOrder.symbolId = s->symbolId;
this->tickSize = s->tickSize;
/* alternative symbol setting
*
openOrder.setSymbol("ESM5");
stopLossOrder.setSymbol("ESM5");
limitOrder.setSymbol("ESM5");
*/
}
};
void onbook(const nanoconda::book* b) {
if((symbolId ==0) || (tickSize == 0) || (tradingSession == nullptr)) return; //initialization checks
if (referencePrice == 0)
{
referencePrice = b->buys[0].price; //(3) set the very first price to best bid
}
if (referencePrice > 0 && !bracketLaunched)
{
if (b->buys[0].price < referencePrice) referencePrice = b->buys[0].price; // When market moves down, so does our reference price
if (b->buys[0].price >= referencePrice + 4 * tickSize) // We find a market impulse of 4 ticks up from market low
{
bracketLaunched = true;
nanoconda::reasoncode rcode = tradingSession->newOrder(&openOrder); // submit new market order (BUY 10 Contracts at MARKET)
if (rcode == nanoconda::SUCCESS) // verify successful submission
{
std::cout << "Sent new BUY order with ID " << openOrder.orderId << std::endl;
}
else
{
std::cerr << "newOrder() did not succeed, error code " << rcode << std::endl;
}
}
}
};
void onfill(const nanoconda::order* order) {
std::cout << "Order ID " << order->orderId <<" received a fill " << std::endl;
if (order->orderId == openOrder.orderId && order->quantityFilled >= openOrder.quantity) // Fill orderId matched opening order
{
stopLossOrder.price = order->priceLast - 8 * tickSize;
stopLossOrder.triggerPrice = order->priceLast - 8 * tickSize;
nanoconda::reasoncode rcode = tradingSession->newOrder(&stopLossOrder); //market order (BUY 10 Contracts at MARKET)
if (rcode == nanoconda::SUCCESS)
{
std::cout << "Sent new SELL order STOP Loss with ID " << stopLossOrder.orderId << std::endl;
}
else
{
std::cerr << "newOrder() did not succeed for STOP Loss, error code " << rcode << std::endl;
}
limitOrder.price = order->priceLast + 8 * tickSize;
limitOrder.triggerPrice = order->priceLast + 8 * tickSize;
rcode = tradingSession->newOrder(&limitOrder); //market order (BUY 10 Contracts at MARKET)
if (rcode == nanoconda::SUCCESS)
{
std::cout << "Sent new SELL order LIMIT with ID " << limitOrder.orderId << std::endl;
}
else
{
std::cerr << "newOrder() did not succeed for LIMIT, error code " << rcode << std::endl;
}
}
if (order->orderId == limitOrder.orderId && order->quantityFilled < limitOrder.quantity) // Partial fill of LIMIT order
{
unsigned short originalQty = stopLossOrder.quantity;
stopLossOrder.quantity = limitOrder.quantity - order->quantityFilled; // set new quantity to remaining open positions
nanoconda::reasoncode rcode = tradingSession->modifyOrder(&stopLossOrder); // modify STOP Loss order quantity
if (rcode == nanoconda::SUCCESS)
{
std::cout << "Modification for STOP Loss submitted succesfully, Order ID " << stopLossOrder.orderId << " New quantity: " << stopLossOrder.quantity << std::endl;
}
else
{
std::cerr << "modifyOrder() did not succeed for Stop Loss, error code " << rcode << "Order ID: " << stopLossOrder.orderId << std::endl;
stopLossOrder.quantity = originalQty;
}
}
if (order->orderId == stopLossOrder.orderId && order->quantityFilled >= stopLossOrder.quantity) // Stop Loss order got fully filled
{
nanoconda::reasoncode rcode = tradingSession->cancelOrder(&limitOrder); // cancel LIMIT order
if (rcode == nanoconda::SUCCESS)
{
std::cout << "Cancellation submitted succesfully for LIMIT order with ID " << limitOrder.orderId << std::endl;
}
else
{
std::cerr << "cancelOrder() did not succeed for LIMIT, error code " << rcode << "Order ID: " << limitOrder.orderId << std::endl;
}
}
if (order->orderId == limitOrder.orderId && order->quantityFilled >= limitOrder.quantity) // Limit order got fully filled
{
nanoconda::reasoncode rcode = tradingSession->cancelOrder(&stopLossOrder); // cancel STOP Loss order
if (rcode == nanoconda::SUCCESS)
{
std::cout << "Cancellation submitted succesfully for LIMIT order with ID " << stopLossOrder.orderId << std::endl;
}
else
{
std::cerr << "cancelOrder() did not succeed for LIMIT, error code " << rcode << "Order ID: " << stopLossOrder.orderId << std::endl;
}
}
};
void onorderack(const nanoconda::order* order) {
std::cout << "Order ID: " << order->orderId <<" Acknowledged, Exchange ID: " << order->exchangeOrderId << std::endl;
};
void onorderreject(const nanoconda::order* order) {
std::cout << "Order ID " << order->orderId <<" Rejected " << std::endl;
};
void oncancelack(const nanoconda::order* order) { };
void oncancelreject(const nanoconda::order* order) { };
void onmodifyack(const nanoconda::order* order) { };
void onmodifyreject(const nanoconda::order* order) { };
void onout(const nanoconda::order* order) { };
void onorderstatus(const nanoconda::order* order) { };
void ontrade(const nanoconda::trade* t) { };
};
int main(int argc, char *argv[])
{
myListener* myApplication = new myListener();
nanoconda::logger* mylogger = new nanoconda::logger("myLoggerFile.log");
nanoconda::registerApplication("username1","password", myApplication); // Register your application and listener
nanoconda::subscribe("ESM5", "XCME", 3); // (4) subscribe for data updates for ESM5
nanoconda::reasoncode rcode;
myApplication->tradingSession = nanoconda::dmasession::init("useraccount1", rcode); // (5) start a new trading session and save it
if(rcode != nanoconda::SUCCESS)
{
fprintf(stderr, "DMA Session INIT Failed with error %s\n",reasonToStr(rcode));
return 1;
}
nanoconda::start(); // (6) start nanoconda threads
bool running = true;
while(running)
{
}
nanoconda::stop();
return 0;
}
-
Initialize orders starting state.
From nanoconda.henum orderType : char { EMPTY = '0', MKT = 'M', LMT = 'L', STP = 'S', SLMT = 'T', FOK = 'K', WARM = 'W', }; enum orderStatus : unsigned short { INVALID = 0, Q_NEW = 10, Q_CANCEL = 20, Q_MODIFY = 30, Q_CANCEL_ALL = 90, Q_FLATTEN= 99, PENDING_ACK = 15, PENDING_CANCEL_ACK = 25, PENDING_MODIFY_ACK = 35, R_ACK = 110, R_CANCEL_ACK = 120, R_MODIFY = 130, R_ORDER_REJECT = 210, R_CANCEL_REJECT = 220, R_MODIFY_REJECT = 230, R_EXECUTED = 300, R_EXECUTED_PARTIAL = 315, R_EXECUTION_CORRECTED = 330, R_EXECUTION_CANCELED = 390, R_OUT = 510 }; ... struct order { unsigned long long symbolId; unsigned long long exchangeSecurityId; unsigned long long orderId; unsigned long long exchangetime; unsigned long long updatetime; unsigned long long exchangeOrderId; unsigned long long execId; unsigned long long customId; long long price; long long triggerPrice; long long priceLast; long long balance; unsigned int quantity; unsigned int quantityDisplay; unsigned int quantityLast; unsigned int quantityFilled; unsigned int quantityMinimum; unsigned int reservedI; orderStatus status; unsigned short exchangeReason; orderType type; char side; unsigned short reservedS; order() { reset(); } void reset(); void setSymbol(const char* str); const char* getSymbol(); long long priceAverage(); static inline unsigned short size() { return 128; } }; -
This is an example of printing information about a product.
onsecurity()will be called upon subscription and then if there is a change in security status from the exchange.From nanoconda.henum securityStatus : unsigned short { HALT = 0, CLOSE = 200, POST_CLOSE = 220, PRE_OPEN = 120, OPEN = 100 }; //... struct security { char symbol[32]; unsigned long long symbolId; unsigned long long exchangeSecurityId; unsigned long long tickSize; unsigned long long multiplier; unsigned long long underlyingId; long long lowLimitBand; long long highLimitBand; securityStatus status; char entCode[6]; char highPrecisionPrice; char reserved[7]; long long settlementPrice; int openInterest; int clearedVolume; long long reservedL2; static inline unsigned short size() { return 128; } }; - Using Bid for tracking markets moves up.
From nanoconda.h
struct pricelevel { long long price; unsigned int qty; unsigned int orders; static inline unsigned short size() { return 16; } }; //... struct book { unsigned long long symbolId; unsigned long long exchangetime; unsigned long long receivetime; unsigned long long symbolseqno; unsigned long long writetime; unsigned long long writeseqno; unsigned long long reserved[2]; pricelevel buys[10]; pricelevel sells[10]; static inline unsigned short size() { return 384; } }; - Add subscription to
ESM5Futures withXCMEentitlement code, your credentials must be provisioned for this entitlement. -
nanoconda::dmasessionobject is used to submit trading requests, there could be more than one trading session.nanoconda::dmasessionstruct dmasession { enum type { INVALID_SESSION = 0, TRADING = 100, SUPERVISOR = 200 }; type sessionType(); unsigned long long sessionID(); static dmasession* init(const char* accountName, reasoncode& reason, short cpu = -1); reasoncode newOrder(order* o); reasoncode cancelOrder(order* o); reasoncode modifyOrder(order* o); reasoncode cancelAllOrders(unsigned long long symbolId = 0); reasoncode flatten(unsigned long long symbolId = 0); reasoncode initUIManager(uilistener* uil); reasoncode setuidata(void* src, unsigned short len=16000) const; reasoncode setalgoreport(algoreport* report) const; risk* getRiskLimits(); private: dmasession(); void* implementation; }; -
Once
start()is called your application will start receiving callbacks for the subscribed instruments. Method Signature:
reasoncode subscribe(const char* symbol, const char* entitlement, short cpu = -1);
Refer to threading for more information on how to control nanoconda threads.
Compile your application