Market Data API
Start receiving market data callbacks in these 4 steps:
- Initialize a callback
listenerobject registerApplicationwith the listenersubscribeto desired instrumentsstartnanoconda 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 onsecurity(const nanoconda::security* s) { };
void onbook(const nanoconda::book* b) { };
void ontrade(const nanoconda::trade* t) { };
void onbookorder(const bookorder* o) { };
/*
...
*/
}
Security Information
onsecurity() callback is called one time on any subscription and then for any update from the exchange to the security, usch as a trading status change.
struct security
{
char symbol[24];
char reservedC1[8];
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;
unsigned char segmentId;
securityType type;
unsigned char legs;
char reserved[4];
long long settlementPrice;
int openInterest;
int clearedVolume;
long long reservedL2;
static inline unsigned short size()
{
return 128;
}
};
Book Snapshot
Book updates are provided on any book update from the exchange. Snapshot of the top 10 levels is provided. Price level 0 is the top level for both sells and buys
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;
}
};
Book Order Update
When enabled, individual order updates are provided through the onbookorder() callback. This feature enables Level 3 (L3) processing by the algorithm, such as constructing the full order book or identifying order-level changes between two onbook() callbacks.
enum orderupdatetype : unsigned char
{
OU_FILL=0,
OU_ADD=1,
OU_DEL=2,
OU_UPD=3
};
struct bookorder
{
unsigned long long symbolId;
unsigned long long orderId;
unsigned long long transactiontime;
unsigned long long exchangetime;
long long price;
unsigned int quantity;
orderupdatetype updatetype;
char side;
unsigned char lastEvent;
unsigned char aggressing;
unsigned long long receivetime;
unsigned long long writetime;
bookorder()
{
reset();
}
void reset();
static inline unsigned short size()
{
return 64;
}
};
Trade Event
struct trade
{
unsigned long long symbolId;
unsigned long long exchangetime;
unsigned long long receivetime;
unsigned long long symbolseqno;
unsigned long long writetime;
unsigned long long transactiontime;
char aggressor;
unsigned char lastEvent;
char reserved[2];
unsigned int numOrders;
long long low;
long long high;
pricelevel bestbid;
pricelevel bestask;
long long lastPrice;
long long vwap;
unsigned int lastSize;
unsigned int totalVol;
long long open;
static inline unsigned short size()
{
return 136;
}
};
Additionally, several API calls are available to the user.
API functions
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.
List Available Symbols
If A list of available symbols is required, you can call listSymbols(), which will trigger onsecurity() callbacks for each of available security
Subscribe To A Symbol
Call subscribe() to subscribe to market data updates for a specific symbol
reasoncode subscribe(const char* symbol, const char* entitlement, short cpu = -1);
//Example: nanoconda::subscribe("ESM5", "XCME", 3);
Subscribe To An Underlying
Call subscribeUnderlying() to subscribe to market data updates for all available symbols for an underlying.
reasoncode subscribeUnderlying(const char* symbol, const char* entitlement, short cpu = -1);
//Example: nanoconda::subscribeUnderlying("NQ", "XCME", 3);
Helper Functions
Convert price to double
Get Symbol ID from a Symbol Name
Get Symbol Name As String
Get Security Object
Get Spread Security Legs
To identify the individual legs of a complex instrument, use the getSecurityLegs API and provide the spread's symbolId.
struct leginfo
{
unsigned long long symbolId;
unsigned char ratio;
char side;
unsigned short reservedUS;
unsigned int multiplier;
};
struct securitylegs
{
enum : unsigned short { MAX_SPREAD_LEGS = 48 };
unsigned int legCount;
unsigned int reserved;
leginfo legs[MAX_SPREAD_LEGS];
};
nanoconda::securitylegs legs;
nanoconda::reasoncode rcode = nanoconda::getSecurityLegs(symbolId, &legs);
if(rcode == SUCCESS)
{
for (int i=0; i< legs.legCount;i++)
{
printf("Leg %d: symbolId %llu symbol %s ratio %d side %c multiplier %d\n",i, legs.legs[i].symbolId, nanoconda::getSymbolName(legs.legs[i].symbolId), legs.legs[i].ratio, legs.legs[i].side, legs.legs[i].multiplier);
}
}
else
{
printf("getSecurityLegs for %llu returned code %u %s\n", symbolId, rcode, reasonToStr(rcode));
}
Sample Application
Here is a basic program to subscribe to common Index Futues on CME (can also be found in src/marketdata.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 | marketdata.cpp | g++ marketdata.cpp -lnanoconda */
#include "nanoconda.h"
#include <iostream>
using namespace nanoconda;
struct myListener : nanoconda::listener
{
void onsecurity(const nanoconda::security* s) { // (6) callback with security information
printf("myListener::onsecurity %s| symbolId: %llu ExchangeId: %llu TickSize: %f ContractMultiplier: %llu Trading Status: %u\n", s->symbol, s->symbolId, s->exchangeSecurityId, nacoPx(s->tickSize), s->multiplier, s->status);
if(s->type == nanoconda::TP_SPREAD)
{
nanoconda::securitylegs legs;
nanoconda::reasoncode rcode = nanoconda::getSecurityLegs(s->symbolId, &legs);
if(rcode == SUCCESS)
{
for (int i=0; i< legs.legCount;i++)
{
printf("Leg %d: symbolId %llu symbol %s ratio %d side %c multiplier %d\n",i, legs.legs[i].symbolId, nanoconda::getSymbolName(legs.legs[i].symbolId), legs.legs[i].ratio, legs.legs[i].side, legs.legs[i].multiplier);
}
}
else
{
printf("getSecurityLegs for %llu returned code %u %s\n", s->symbolId, rcode, reasonToStr(rcode));
}
}
};
void onbook(const nanoconda::book* b) {// (7) callback for a book update
printf("myListener::onbook %s| [%u,%.4f | %.4f,%u] time: %llu\n", nanoconda::getSymbolName(b->symbolId), b->buys[0].qty, nacoPx(b->buys[0].price), nacoPx(b->sells[0].price), b->sells[0].qty, b->exchangetime);
};
void ontrade(const nanoconda::trade* t) {// (8) callback for a new trade
printf("myListener::ontrade %s, %.4f|%d\n",nanoconda::getSymbolName(t->symbolId), nacoPx(t->lastPrice), t->lastSize);
};
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) { };
};
int main(int argc, char *argv[])
{
myListener* mdApp = new myListener();
nanoconda::logger* mylogger = new nanoconda::logger("myLoggerFile.log");
nanoconda::reasoncode rcode;
rcode = nanoconda::registerApplication("username1","password", mdApp); //(1) register your identity and Callback Listener
if(rcode != nanoconda::SUCCESS) return rcode; // Exit program with the error code
rcode = nanoconda::subscribe("ESM5", "XCME", 3); //(2) subscribe to ESM5 Futures on cpu #3
if(rcode != nanoconda::SUCCESS) return rcode; // Exit program with the error code
rcode = nanoconda::subscribeUnderlying("NQ", "XCME", 3); //(3) subscribe to ESM5 Futures on cpu #3
if(rcode != nanoconda::SUCCESS) return rcode; // Exit program with the error code
nanoconda::start(); //(4) start callback threads to start receiving updates
bool running = true;
while(running) //(5) keep your main application thread alive until running becomes false or you kill the process
{
}
nanoconda::stop(); //cleanup resources
return 0;
}
- Register your application with your listener and credentials, which are needed to identify your application. They will be provided by support@nanoconda.com.
- Add subscription to
ESM5Futures withXCMEentitlement code, your credentials must be provisioned for this entitlement.
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. - Add subscription to
NQunderlying chain. This call will subscribe to all NQ Futures, regardless of expiration. - Once
start()is called your application will start receiving callbacks for the subscribed instruments. -
Keep your main thread running for application to not be terminated, you can add your own logic to set terminate the loop, for example use SIGINT to set
running = false. -
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; unsigned char segmentId; char reserved[6]; long long settlementPrice; int openInterest; int clearedVolume; long long reservedL2; static inline unsigned short size() { return 128; } }; - This is an example of printing the top level of a
onbook()update. You have access to top 10 levels. This callback will be raised on any book update.From nanoconda.hstruct 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 transactiontime; unsigned char lastEvent; char reserved[7]; pricelevel buys[10]; pricelevel sells[10]; pricelevel buysImplied[2]; pricelevel sellsImplied[2]; static inline unsigned short size() { return 400; } }; - This is an example of printing information for the last
tradeupdate.ontrade()callback will be raised for every trade.From nanoconda.hstruct pricelevel { long long price; unsigned int qty; unsigned int orders; static inline unsigned short size() { return 16; } }; //... struct trade { unsigned long long symbolId; unsigned long long exchangetime; unsigned long long receivetime; unsigned long long symbolseqno; unsigned long long writetime; unsigned long long transactiontime; char aggressor; unsigned char lastEvent; char reserved[6]; long long low; long long high; pricelevel bestbid; pricelevel bestask; long long lastPrice; long long vwap; unsigned int lastSize; unsigned int totalVol; long long open; static inline unsigned short size() { return 136; } };
Compile your application