API Documentation

Language: C++ Java Python C C#
Basic: Intro/Trade Quote Category Status Symbol Options
Detailed: Trade Quote

Introduction to Options

This example should use the demo.DO.nx2 tape instead of demo.XU.nx2. Messages for option contracts are very similar to messages for equities. The primary difference is the addition of pnxOptionHdr to the coreHeader object. The major challenge for options processing is the number of messages. In addition to parsing options messages, this guide will also cover some recommended performance improvements. The following code is for Windows. The method for starting threads in C varies by operating system

Code

#ifdef _WIN32
#include "windows.h"
#else
#include "pthread.h"
#endif

#include "NxCoreAPI_Wrapper_C.h"

#ifdef _WIN32
__declspec(thread) bool g_successfulExclude = false;
#else
_Thread_local bool g_successfulExclude = false;
#endif

char* pszFilename;

int OnNxCoreCallback(const NxCoreSystem* pNxCoreSys, const NxCoreMessage* pNxCoreMsg) {
    if (pNxCoreMsg->MessageType == NxMSG_STATUS && !g_successfulExclude) {
        int optionPermissions[4] = { 2, 20, 21, 22 };
        for (int i = 0; i < 4; i++) {
            int permission = optionPermissions[i];
            if (pNxCoreSys->UserData != permission && pfExcludeOpraExch(NxPERMID_REMOVE | permission) != 0)
                return NxCALLBACKRETURN_CONTINUE;
        }
        g_successfulExclude = true;
    }

    if (pNxCoreMsg->MessageType == NxMSG_TRADE) {
        const NxCoreHeader* pHeader = &pNxCoreMsg->coreHeader;
        NxOptionHdr* pnxOptionHdr = &pHeader->pnxOptionHdr;
        if (pnxOptionHdr != 0) {
            char* symbol = pHeader->pnxStringSymbol->String;
            const NxTime* pTimestamp = &pHeader->nxExgTimestamp;
            const NxDate* pExpireDate = &pnxOptionHdr->nxExpirationDate;
            double strikePrice = pfNxCorePriceToDouble(pnxOptionHdr->strikePrice, 7);
            char* contractType;
            if (pnxOptionHdr->PutCall == 1)
                contractType = "Put";
            else
                contractType = "Call";

            const NxCoreTrade* pTrade = &pNxCoreMsg->coreData.Trade;
            double price = pfNxCorePriceToDouble(pTrade->Price, pTrade->PriceType);
            int size = pTrade->Size;

            printf("Trade for %s %s expiring %d%02d%02d with strike $%.2f at %02d:%02d:%02d for %d shares at $%.2f\n",
                symbol, contractType, pExpireDate->Year, pExpireDate->Month, pExpireDate->Day, strikePrice,
                pTimestamp->Hour, pTimestamp->Minute, pTimestamp->Second, size,price);
        }
    }
    return NxCALLBACKRETURN_CONTINUE;
}
void thProcess(size_t permission) {
    int returnValue = pfNxCoreProcessTape(pszFilename, NULL, NxCF_EXCLUDE_CRC_CHECK, permission, OnNxCoreCallback);
    processReturnValue(returnValue);
}
int main(int argc, char* argv[]) {
    if (argc < 3)
        return 1;
    if (!LoadNxCore(argv[1])){
        printf("loading library failed\n");
        return 2;
    }
    pszFilename = argv[2];

#ifdef _WIN32
    HANDLE threads[4];
    threads[0] = CreateThread(NULL, 0, thProcess, 2, 0, 0);
    threads[1] = CreateThread(NULL, 0, thProcess, 20, 0, 0);
    threads[2] = CreateThread(NULL, 0, thProcess, 21, 0, 0);
    threads[3] = CreateThread(NULL, 0, thProcess, 22, 0, 0);

    WaitForMultipleObjects(4, threads, TRUE, INFINITE);

    for (int i = 0; i<4; i++)
        CloseHandle(threads[i]);
#else
    pthread_t threads[4];
    pthread_create(&threads[0],0,thProcess,2);
    pthread_create(&threads[1],0,thProcess,20);
    pthread_create(&threads[2],0,thProcess,21);
    pthread_create(&threads[3],0,thProcess,22);

    for (int i = 0; i<4; i++)
        pthread_join(threads[i],NULL);
#endif

    return 0;
}

Splitting data

One way of improving performance is limiting what part of the options feed is processed on a thread. Nanex splits the option feed into 4 permissions 2, 20, 21, and 22 each corresponding to different ranges of option roots. The function pfExcludeOpraExch is used to remove a permission from processing. By removing all option permissions except for the one assigned to threadPermission, each thread will only process a quarter of the data.

if (pNxCoreMsg->MessageType == NxMSG_STATUS && !g_successfulExclude) {
    int optionPermissions[4] = { 2, 20, 21, 22 };
    for (int i = 0; i < 4; i++) {
        int permission = optionPermissions[i];
        if (pNxCoreSys->UserData != permission 
            && pfExcludeOpraExch(NxPERMID_REMOVE | permission) != 0)
            return NxCALLBACKRETURN_CONTINUE;
    }
    g_successfulExclude = true;
}

Option Header

Option contract information is contained within pnxOptionHdr in the coreHeader object. For this example we will get the expiration date, strike price, and put/call. The strike price always has a price type of 7.

const NxCoreHeader* pHeader = &pNxCoreMsg->coreHeader;
NxOptionHdr* pnxOptionHdr = &pHeader->pnxOptionHdr;
if (pnxOptionHdr != 0) {
    char* symbol = pHeader->pnxStringSymbol->String;
    const NxTime* pTimestamp = &pHeader->nxExgTimestamp;
    const NxDate* pExpireDate = &pnxOptionHdr->nxExpirationDate;
    double strikePrice = pfNxCorePriceToDouble(
        pnxOptionHdr->strikePrice, 7);
    char* contractType;
    if (pnxOptionHdr->PutCall == 1)
        contractType = "Put";
    else
        contractType = "Call";

    const NxCoreTrade* pTrade = &pNxCoreMsg->coreData.Trade;
    double price = pfNxCorePriceToDouble(pTrade->Price, pTrade->PriceType);
    int size = pTrade->Size;
    ...
}

Process Tape Flags

To improve performance we are going to use a control flag in the 3rd argument of pfNxCoreProcessTape. NxCF_EXCLUDE_CRC_CHECK disables frequent CRC calculations. CRC checks will still be performed, but not as frequently.

int returnValue = pfNxCoreProcessTape(
    pszFilename,
    NULL,
    NxCF_EXCLUDE_CRC_CHECK,
    permission,
    OnNxCoreCallback);

Launching Threads Windows

On the main thread we create an array of thread identifiers and create 4 threads passing the 4 different option permission value. Then, we wait for every thread to finish. The first codeblock is for Windows and the second code block is for Linux and MacOS. Windows has the additional step of cleaning up thread handles after they are complete.

#ifdef _WIN32
HANDLE threads[4];
threads[0] = CreateThread(NULL, 0, thProcess, 2, 0, 0);
threads[1] = CreateThread(NULL, 0, thProcess, 20, 0, 0);
threads[2] = CreateThread(NULL, 0, thProcess, 21, 0, 0);
threads[3] = CreateThread(NULL, 0, thProcess, 22, 0, 0);

WaitForMultipleObjects(4, threads, TRUE, INFINITE);

for (int i = 0; i<4; i++)
    CloseHandle(threads[i]);
#else
pthread_t threads[4];
pthread_create(&threads[0],0,thProcess,2);
pthread_create(&threads[1],0,thProcess,20);
pthread_create(&threads[2],0,thProcess,21);
pthread_create(&threads[3],0,thProcess,22);

for (int i = 0; i<4; i++)
    pthread_join(threads[i],NULL);
#endif