Skip to content

Market Data API

Start receiving market data callbacks in these 4 steps:

  1. Initialize a callback listener object
  2. registerApplication with the listener
  3. subscribe to desired instruments
  4. start nanoconda 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

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.

void onsecurity(const nanoconda::security* s) { };
nanoconda::security structure
   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

void onbook(const nanoconda::book* b) {  };
nanoconda::book structure
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.

void onbookorder(const bookorder* o) { };
nanoconda::bookorder structure
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

void ontrade(const nanoconda::trade* t) {  };
nanoconda::trade structure
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

   reasoncode listSymbols(const char* entitlement);

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

   double nacoPx(long long px);

Get Symbol ID from a Symbol Name

   unsigned long long getSymbolId(const char* symbol);

Get Symbol Name As String

   const char* getSymbolName(unsigned long long symbolId);

Get Security Object

   security* getSecurity(unsigned long long symbolId);

Get Spread Security Legs

To identify the individual legs of a complex instrument, use the getSecurityLegs API and provide the spread's symbolId.

   reasoncode getSecurityLegs(unsigned long long symbolId, securitylegs* securitylegsstorage);
nanoconda::leginfo and nanoconda::securitylegs
   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];

   };
example
   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.

  1. You found on a sample annotation, now look for the same in the code below!
marketdata.cpp
/* 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;
}
  1. Register your application with your listener and credentials, which are needed to identify your application. They will be provided by support@nanoconda.com.
  2. Add subscription to ESM5 Futures with XCME entitlement 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.
  3. Add subscription to NQ underlying chain. This call will subscribe to all NQ Futures, regardless of expiration.
  4. Once start() is called your application will start receiving callbacks for the subscribed instruments.
  5. 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 .

  6. 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.h
       enum 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;
          }
    
       };
    

  7. 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.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 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;
          }
       };
    
  8. This is an example of printing information for the last trade update. ontrade() callback will be raised for every trade.
    From nanoconda.h
       struct 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

g++ marketdata.cpp -lnanoconda