#include "NxCorePython.h"
#include "robin_hood.h"
#ifndef MAX_PATH
#define MAX_PATH 260
#endif

thread_local NxCoreClass NxCore;
int __stdcall OnNxCoreCallbackSetReverseLookup(const NxCoreSystem* pNxCoreSys, const NxCoreMessage* pNxCoreMsg);
inline void PopulateNxDate(NxDate nxdate, NxDateObject* nxdateObject) {
    memcpy(&nxdateObject->NDays, &nxdate.NDays, 12);
}
inline void PopulateNxTime(NxTime nxtime, NxTimeObject* nxtimeObject) {
    memcpy(&nxtimeObject->MsOfDay, &nxtime.MsOfDay, 12);
}
inline void UpdateUserData(NxCoreSystem* pNxCoreSys, NxCoreMessage* pNxCoreMsg, NxCoreSystemObject* pNxCoreSysObject, NxCoreMessageObject* pNxCoreMsgObject) {
    pNxCoreSys->UserData = pNxCoreSysObject->UserData;
    if (pNxCoreMsgObject->coreHeader->pnxStringSymbol)
        memcpy(pNxCoreMsg->coreHeader.pnxStringSymbol, &pNxCoreMsgObject->coreHeader->pnxStringSymbol->UserData1, 8);
    if (pNxCoreMsgObject->coreHeader->pnxOptionHdr) {
        if (pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsDateAndStrike)
            memcpy(pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsDateAndStrike, &pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsDateAndStrike->UserData1, 8);
        if (pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsUnderlying)
            memcpy(pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsUnderlying, &pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsUnderlying->UserData1, 8);
        if (pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsSeriesChain)
            memcpy(pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsSeriesChain, &pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsSeriesChain->UserData1, 8);
        if (pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsSpecialSettle)
            memcpy(pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsSpecialSettle, &pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsSpecialSettle->UserData1, 8);
    }
}
struct OptionLookupResult {
    PyObject* symbolObject;
    NxString* pExpectedUnderlyingNxString;
    PyObject* underlyingObject;
    NxString* pExpectedSeriesChainNxString;
    PyObject* seriesChainObject;
    NxString* pExpectedSpecialSettleNxString;
    PyObject* specialSettleObject;
};

class ThreadData {
public:
#ifdef USE_VECTOR_FUNCTION
    vectorcallfunc NxCoreVectorCallbackPython = NULL;
#endif
    PyObject* NxCoreCallbackPython = NULL;
    NxCoreSystemObject* m_pNxCoreSysObject = nullptr;
    NxCoreMessageObject* m_pNxCoreMsgObject = nullptr;
    NxCore3ExtObject* m_pnxCore3Ext = nullptr;
    NxStringObject* m_pnxStringMarketMaker;
    NxStringObject* m_pnxStringCategory;
    NxStringObject* m_pnxStringSymbol;
    NxStringObject* m_pnxsDateAndStrike;
    NxStringObject* m_pnxsUnderlying;
    NxStringObject* m_pnxsSeriesChain;
    NxStringObject* m_pnxsSpecialSettle;
    NxStringObject* m_pnxsSymbolOld;
    NxStringObject* m_pnxsDateAndStrikeOld;
    NxStringObject* m_pnxsUnderlyingOld;
    NxStringObject* m_pnxsSeriesChainOld;
    NxStringObject* m_pnxsSpecialSettleOld;
    NxStringObject* m_pnxStringMarketMakerState[512];
    NxOptionHdrObject* m_pNxCoreOptionHdrObject = nullptr;//we will use this as needed
    NxOptionHdrObject* m_pNxCoreOptionHdrOld = nullptr;
    NxOptionHdrObject* m_pNxCoreOptionHdrSpin;
    NxCoreSystemObject* m_pNxCoreSysSpin;
    NxCoreMessageObject* m_pNxCoreMsgSpin;
    NxStringObject* m_pnxsSymbolSpin;
    NxStringObject* m_pnxsDateAndStrikeSpin;
    NxStringObject* m_pnxsUnderlyingSpin;
    NxStringObject* m_pnxsSeriesChainSpin;
    NxStringObject* m_pnxsSpecialSettleSpin;
    PyObject* m_pCallbackTupleSpin = nullptr;
    PyObject* m_pCallbackTuple = nullptr;
    int m_controlFlags;
    char m_symbolFilterEnable;
    robin_hood::unordered_flat_map<std::string, size_t> m_symbolFilterLookup;
    robin_hood::unordered_flat_map<std::string, size_t> m_symbolExcludeLookup;
    std::string m_StartSymbol = "";
    std::string m_EndSymbol = "";
    int m_FilterListedExg = 0;

    robin_hood::unordered_flat_map<size_t, PyObject*> m_DefinedStringLookup[256];
    OptionLookupResult* m_OptionLookupAtom[65536];
    robin_hood::unordered_flat_map<size_t, OptionLookupResult*> m_OptionLookup;
    PyObject** m_SymbolLookupAtom[256];
    robin_hood::unordered_flat_map<size_t, PyObject*> m_SymbolLookup[256];
    robin_hood::unordered_flat_map<std::string, size_t> m_SymbolNxStringLookup[256];
    PyObject* m_DateAndStrikeLookup[65536];
    robin_hood::unordered_flat_map<size_t, PyObject*> m_MMLookup;




    inline PyObject* GetjStringDateAndStrike(NxString* pNxString) {
        PyObject* stringObject;
        unsigned short key;
        if (pNxString) {
            //only 2 char Strings. convert string to key.
            if (pNxString->String[0])
                key = ((unsigned short)pNxString->String[1] << 8) | pNxString->String[0];
            else
                key = 0;
            if (m_DateAndStrikeLookup[key]) {
                stringObject = m_DateAndStrikeLookup[key];
            }
            else {
                char DateAndStrike[3];
                strncpy(DateAndStrike, pNxString->String, 2);
                DateAndStrike[2] = 0;
                stringObject = Py_BuildValue("s", DateAndStrike);
                m_DateAndStrikeLookup[key] = stringObject;
            }
        }
        else
            stringObject = nullptr;
        return stringObject;
    }
    inline PyObject* GetStringObject(NxString* pNxString, unsigned char exg) {
        PyObject* stringObject;
        if (!m_SymbolLookupAtom[exg]) {
            m_SymbolLookupAtom[exg] = new PyObject * [65536];
            if (m_SymbolLookupAtom[exg])
                memset(m_SymbolLookupAtom[exg], 0, 65536 * sizeof(PyObject*));
            else
                return nullptr;
        }
        if (pNxString) {
            if (pNxString->Atom != NxATOM_MAX_16BIT) {
                stringObject = m_SymbolLookupAtom[exg][pNxString->Atom];
                if (stringObject)
                    return stringObject;
            }
            else {
                auto iter = m_SymbolLookup[exg].find((size_t)pNxString->String);
                if (iter != m_SymbolLookup[exg].end())
                    return iter->second;
            }
            stringObject = Py_BuildValue("s", pNxString->String);
            if (pNxString->Atom != NxATOM_MAX_16BIT)
                m_SymbolLookupAtom[exg][pNxString->Atom] = stringObject;
            else
                m_SymbolLookup[exg][(size_t)pNxString->String] = stringObject;
            m_SymbolNxStringLookup[exg][std::string(pNxString->String)] = (size_t)pNxString;
        }
        else
            stringObject = nullptr;
        return stringObject;
    }
    inline OptionLookupResult* GetLookupOption(NxString* pNxStringSymbol, NxOptionHdr* pnxOptionHdr) {
        NxString* pNxStringUnderlying = pnxOptionHdr->pnxsUnderlying;
        NxString* pNxStringSeriesChain = pnxOptionHdr->pnxsSeriesChain;
        NxString* pNxStringSpecialSettle = pnxOptionHdr->pnxsSpecialSettle;
        OptionLookupResult* pResult;
        if (pNxStringSymbol) {
            if ((pNxStringSymbol->Atom != NxATOM_MAX_16BIT && (pResult = m_OptionLookupAtom[pNxStringSymbol->Atom])) || (pNxStringSymbol->Atom == NxATOM_MAX_16BIT && (pResult = m_OptionLookup[(size_t)pNxStringSymbol->String]))) {
                if (pResult->pExpectedUnderlyingNxString != pNxStringUnderlying) {
                    if (pResult->underlyingObject)
                        Py_XDECREF(pResult->underlyingObject);
                    if (pNxStringUnderlying) {
                        pResult->underlyingObject = Py_BuildValue("s", pNxStringUnderlying->String);
                        pResult->pExpectedUnderlyingNxString = pNxStringUnderlying;
                    }
                    else {
                        pResult->underlyingObject = nullptr;
                        pResult->pExpectedUnderlyingNxString = nullptr;
                    }

                }
                if (pResult->pExpectedSeriesChainNxString != pNxStringSeriesChain) {
                    if (pResult->seriesChainObject)
                        Py_XDECREF(pResult->seriesChainObject);
                    if (pNxStringSeriesChain) {
                        pResult->seriesChainObject = Py_BuildValue("s", pNxStringSeriesChain->String);
                        pResult->pExpectedSeriesChainNxString = pNxStringSeriesChain;
                    }
                    else {
                        pResult->seriesChainObject = nullptr;
                        pResult->pExpectedSeriesChainNxString = nullptr;
                    }

                }
                if (pResult->pExpectedSpecialSettleNxString != pNxStringSpecialSettle) {
                    if (pResult->specialSettleObject)
                        Py_XDECREF(pResult->specialSettleObject);
                    if (pNxStringSpecialSettle) {
                        pResult->specialSettleObject = Py_BuildValue("s", pNxStringSpecialSettle->String);
                        pResult->pExpectedSpecialSettleNxString = pNxStringSpecialSettle;
                    }
                    else {
                        pResult->specialSettleObject = nullptr;
                        pResult->pExpectedSpecialSettleNxString = nullptr;
                    }
                }
            }
            else {
                pResult = new OptionLookupResult;
                if (pResult) {
                    memset(pResult, 0, sizeof(OptionLookupResult));
                    pResult->symbolObject = Py_BuildValue("s", pNxStringSymbol->String);
                    if (pNxStringUnderlying) {
                        pResult->underlyingObject = Py_BuildValue("s", pNxStringUnderlying->String);
                        pResult->pExpectedUnderlyingNxString = pNxStringUnderlying;
                    }
                    if (pNxStringSeriesChain) {
                        pResult->seriesChainObject = Py_BuildValue("s", pNxStringSeriesChain->String);
                        pResult->pExpectedSeriesChainNxString = pNxStringSeriesChain;
                    }
                    if (pNxStringSpecialSettle) {
                        pResult->specialSettleObject = Py_BuildValue("s", pNxStringSpecialSettle->String);
                        pResult->pExpectedSpecialSettleNxString = pNxStringSpecialSettle;
                    }
                    if (pNxStringSymbol->Atom != NxATOM_MAX_16BIT)
                        m_OptionLookupAtom[pNxStringSymbol->Atom] = pResult;
                    else
                        m_OptionLookup[(size_t)pNxStringSymbol->String] = pResult;
                }
            }
            return pResult;
        }
        return nullptr;
    }
    inline NxString* GetNxStringSymbol(const char* symbol, unsigned char exg) {
        if (symbol && exg < 256) {
            NxString* pNxString = (NxString*)m_SymbolNxStringLookup[exg][symbol];
            return pNxString;
        }
        return nullptr;
    }
    inline PyObject* GetStringMM(NxString* pNxString) {
        PyObject* jString;
        if (pNxString) {
            if (m_MMLookup[(size_t)pNxString->String]) {
                jString = m_MMLookup[(size_t)pNxString->String];
            }
            else {
                jString = Py_BuildValue("s", pNxString->String);
                m_MMLookup[(size_t)pNxString->String] = jString;
            }
        }
        else
            jString = nullptr;
        return jString;
    }
    inline void populateCoreHdr(const NxCoreMessage* pNxCoreMsg, NxCoreMessageObject* pNxCoreMsgObject) {

        if (pNxCoreMsg->coreHeader.pnxOptionHdr) {
            OptionLookupResult* pOption;
            pNxCoreMsgObject->coreHeader->pnxOptionHdr = m_pNxCoreOptionHdrObject;
            memcpy(&pNxCoreMsgObject->coreHeader->pnxOptionHdr->exgUnderlying, &pNxCoreMsg->coreHeader.pnxOptionHdr->exgUnderlying, 16);
            PopulateNxDate(pNxCoreMsg->coreHeader.pnxOptionHdr->nxExpirationDate, pNxCoreMsgObject->coreHeader->pnxOptionHdr->nxExpirationDate);

            if ((pOption = GetLookupOption(pNxCoreMsg->coreHeader.pnxStringSymbol, pNxCoreMsg->coreHeader.pnxOptionHdr))) {
                if (pOption->symbolObject) {
                    pNxCoreMsgObject->coreHeader->pnxStringSymbol = m_pnxStringSymbol;
                    memcpy(&pNxCoreMsgObject->coreHeader->pnxStringSymbol->UserData1, pNxCoreMsg->coreHeader.pnxStringSymbol, 10);
                    pNxCoreMsgObject->coreHeader->pnxStringSymbol->String = pOption->symbolObject;
                }
                else
                    pNxCoreMsgObject->coreHeader->pnxStringSymbol = nullptr;
                if (pOption->underlyingObject) {
                    pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsUnderlying = m_pnxsUnderlying;
                    memcpy(&pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsUnderlying->UserData1, pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsUnderlying, 10);
                    pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsUnderlying->String = pOption->underlyingObject;
                }
                else
                    pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsUnderlying = nullptr;
                if (pOption->seriesChainObject) {
                    pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsSeriesChain = m_pnxsSeriesChain;
                    memcpy(&pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsSeriesChain->UserData1, pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsSeriesChain, 10);
                    pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsSeriesChain->String = pOption->seriesChainObject;
                }
                else
                    pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsSeriesChain = nullptr;
                if (pOption->specialSettleObject) {
                    pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsSpecialSettle = m_pnxsSpecialSettle;
                    memcpy(&pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsSpecialSettle->UserData1, pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsSpecialSettle, 10);
                    pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsSpecialSettle->String = pOption->specialSettleObject;
                }
                else
                    pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsSpecialSettle = nullptr;
                PyObject* DateAndStrinkeString = GetjStringDateAndStrike(pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsDateAndStrike);
                if (DateAndStrinkeString) {
                    pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsDateAndStrike = m_pnxsDateAndStrike;
                    memcpy(&pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsDateAndStrike->UserData1, pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsDateAndStrike, 10);
                    pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsDateAndStrike->String = DateAndStrinkeString;
                }
                else
                    pNxCoreMsgObject->coreHeader->pnxOptionHdr->pnxsDateAndStrike = nullptr;
            }
            else
                pNxCoreMsgObject->coreHeader->pnxOptionHdr = nullptr;
        }
        else {
            pNxCoreMsgObject->coreHeader->pnxOptionHdr = nullptr;
            if (pNxCoreMsg->coreHeader.pnxStringSymbol) {
                pNxCoreMsgObject->coreHeader->pnxStringSymbol = m_pnxStringSymbol;
                memcpy(&pNxCoreMsgObject->coreHeader->pnxStringSymbol->UserData1, pNxCoreMsg->coreHeader.pnxStringSymbol, 10);
                pNxCoreMsgObject->coreHeader->pnxStringSymbol->String = GetStringObject(pNxCoreMsg->coreHeader.pnxStringSymbol, (unsigned char)pNxCoreMsg->coreHeader.ListedExg);
            }
            else
                pNxCoreMsgObject->coreHeader->pnxStringSymbol = nullptr;
        }
        memcpy(&pNxCoreMsgObject->coreHeader->ListedExg, &pNxCoreMsg->coreHeader.ListedExg, 8);
        PopulateNxDate(pNxCoreMsg->coreHeader.nxSessionDate, pNxCoreMsgObject->coreHeader->nxSessionDate);
        PopulateNxTime(pNxCoreMsg->coreHeader.nxExgTimestamp, pNxCoreMsgObject->coreHeader->nxExgTimestamp);
    }


    void SetSymbolFilterUserData(const NxCoreMessage* pNxCoreMsg) {
        size_t exg;
        if (m_symbolFilterEnable == 1) {
            exg = m_symbolFilterLookup[pNxCoreMsg->coreHeader.pnxStringSymbol->String];
            if (exg == 256 || exg == pNxCoreMsg->coreHeader.ListedExg) {
                pNxCoreMsg->coreHeader.pnxStringSymbol->UserData2 = 1;
            }
            else
                pNxCoreMsg->coreHeader.pnxStringSymbol->UserData2 = 0;
        }
        else if (m_symbolFilterEnable == 2) {
            exg = m_symbolExcludeLookup[pNxCoreMsg->coreHeader.pnxStringSymbol->String];
            if (exg == 256 || exg == pNxCoreMsg->coreHeader.ListedExg) {
                pNxCoreMsg->coreHeader.pnxStringSymbol->UserData2 = 0;
            }
            else
                pNxCoreMsg->coreHeader.pnxStringSymbol->UserData2 = 1;
        }
        else if (m_symbolFilterEnable == 3) {
            if (m_FilterListedExg && m_FilterListedExg != pNxCoreMsg->coreHeader.ListedExg) {
                pNxCoreMsg->coreHeader.pnxStringSymbol->UserData2 = 0;
                return;
            }
            for (int i = 0; i < m_StartSymbol.size(); i++) {
                if (i >= strlen(pNxCoreMsg->coreHeader.pnxStringSymbol->String) || m_StartSymbol.at(i) > pNxCoreMsg->coreHeader.pnxStringSymbol->String[i]) {
                    pNxCoreMsg->coreHeader.pnxStringSymbol->UserData2 = 0;
                    return;
                }
                if (m_StartSymbol.at(i) < pNxCoreMsg->coreHeader.pnxStringSymbol->String[i])// found charecter greater than starting string so done checking
                    break;
            }
            if (m_EndSymbol.size()) {// no end string means include all remaining symbol
                for (int i = 0; i < strlen(pNxCoreMsg->coreHeader.pnxStringSymbol->String); i++) {
                    if (i >= m_EndSymbol.size() || m_EndSymbol.at(i) < pNxCoreMsg->coreHeader.pnxStringSymbol->String[i]) {
                        pNxCoreMsg->coreHeader.pnxStringSymbol->UserData2 = 0;
                        return;
                    }
                    if (m_EndSymbol.at(i) > pNxCoreMsg->coreHeader.pnxStringSymbol->String[i])// found charecter less than ending string so done checking
                        break;
                }
            }
            pNxCoreMsg->coreHeader.pnxStringSymbol->UserData2 = 1;
        }
    }

    int __stdcall SetReverseLookup(const NxCoreSystem* pNxCoreSys, const NxCoreMessage* pNxCoreMsg) {
        if (m_symbolFilterEnable)
            SetSymbolFilterUserData(pNxCoreMsg);
        if (pNxCoreMsg->coreHeader.pnxOptionHdr)
            m_SymbolNxStringLookup[10][std::string(pNxCoreMsg->coreHeader.pnxStringSymbol->String) + std::to_string(pNxCoreMsg->coreHeader.pnxOptionHdr->nxExpirationDate.NDays).append(1, pNxCoreMsg->coreHeader.pnxOptionHdr->PutCall == 0 ? 'C' : 'P') + std::to_string(pNxCoreMsg->coreHeader.pnxOptionHdr->strikePrice)] = (size_t)pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsDateAndStrike;
        else
            m_SymbolNxStringLookup[pNxCoreMsg->coreHeader.ListedExg][std::string(pNxCoreMsg->coreHeader.pnxStringSymbol->String)] = (size_t)pNxCoreMsg->coreHeader.pnxStringSymbol;
        return NxCALLBACKRETURN_CONTINUE;
    }

    int __stdcall Callback(const NxCoreSystem* pNxCoreSys, const NxCoreMessage* pNxCoreMsg)
    {
        int retCode = NxCALLBACKRETURN_CONTINUE;

        switch (pNxCoreMsg->MessageType) {
        case NxMSG_STATUS:
            if (pNxCoreSys->Status == NxCORESTATUS_LOADED_STATE) {
                NxCore.SpinSymbols(NxSPIN_ALL_EXGS, NxSPIN_NON_OPTIONS, 0, OnNxCoreCallbackSetReverseLookup, 0);
                NxCore.SpinSymbols(NxSPIN_ALL_EXGS, NxSPIN_OPTIONS, 0, OnNxCoreCallbackSetReverseLookup, 0);
            }
            break;
        case NxMSG_EXGQUOTE:
            if (m_symbolFilterEnable && pNxCoreMsg->coreHeader.pnxStringSymbol->UserData2 != 1)
                return NxCALLBACKRETURN_CONTINUE;
            if (m_controlFlags & NxCF_EXCLUDE_QUOTES)
                return NxCALLBACKRETURN_CONTINUE;
            memcpy(&m_pNxCoreMsgObject->coreData->ExgQuote->coreQuote->AskSize, &pNxCoreMsg->coreData.ExgQuote.coreQuote, 36);
            memcpy(&m_pNxCoreMsgObject->coreData->ExgQuote->BestAskPrice, &pNxCoreMsg->coreData.ExgQuote.BestAskPrice, 44);
            break;
        case NxMSG_MMQUOTE:
            if (m_symbolFilterEnable && pNxCoreMsg->coreHeader.pnxStringSymbol->UserData2 != 1)
                return NxCALLBACKRETURN_CONTINUE;
            if (m_controlFlags & NxCF_EXCLUDE_QUOTES2)
                return NxCALLBACKRETURN_CONTINUE;
            memcpy(&m_pNxCoreMsgObject->coreData->MMQuote->coreQuote->AskSize, &pNxCoreMsg->coreData.MMQuote.coreQuote, 36);
            memcpy(&m_pNxCoreMsgObject->coreData->MMQuote->MarketMakerType, &pNxCoreMsg->coreData.MMQuote.MarketMakerType, 2);
            if (pNxCoreMsg->coreData.MMQuote.pnxStringMarketMaker) {
                m_pNxCoreMsgObject->coreData->MMQuote->pnxStringMarketMaker = m_pnxStringMarketMaker;
                memcpy(&m_pNxCoreMsgObject->coreData->MMQuote->pnxStringMarketMaker->UserData1, pNxCoreMsg->coreData.MMQuote.pnxStringMarketMaker, 10);
                m_pNxCoreMsgObject->coreData->MMQuote->pnxStringMarketMaker->String = GetStringMM(pNxCoreMsg->coreData.MMQuote.pnxStringMarketMaker);
            }
            else
                m_pNxCoreMsgObject->coreData->MMQuote->pnxStringMarketMaker = nullptr;
            break;
        case NxMSG_TRADE:
            if (m_symbolFilterEnable && pNxCoreMsg->coreHeader.pnxStringSymbol->UserData2 != 1)
                return NxCALLBACKRETURN_CONTINUE;
            memcpy(&m_pNxCoreMsgObject->coreData->Trade->nxAnalysis->FilterThreshold, &pNxCoreMsg->coreData.Trade.nxAnalysis, 24);
            memcpy(&m_pNxCoreMsgObject->coreData->Trade->Price, &pNxCoreMsg->coreData.Trade.Price, 22);
            memcpy(&m_pNxCoreMsgObject->coreData->Trade->TotalVolume, &pNxCoreMsg->coreData.Trade.TotalVolume, 36);
            for (int i = 0; i < 4; i++)
                PyList_SetItem(m_pNxCoreMsgObject->coreData->Trade->ExtTradeConditions, i, PyLong_FromLong(pNxCoreMsg->coreData.Trade.ExtTradeConditions[i]));
            break;
        case NxMSG_SYMBOLCHANGE:
            if (m_symbolFilterEnable)
                SetSymbolFilterUserData(pNxCoreMsg);
            if (m_symbolFilterEnable && pNxCoreMsg->coreHeader.pnxStringSymbol->UserData2 != 1)
                return NxCALLBACKRETURN_CONTINUE;
            if (pNxCoreMsg->coreData.SymbolChange.pnxsSymbolOld) {
                m_pNxCoreMsgObject->coreData->SymbolChange->pnxsSymbolOld = m_pnxsSymbolOld;
                if (m_pNxCoreMsgObject->coreData->SymbolChange->pnxsSymbolOld->String)
                    Py_XDECREF(m_pNxCoreMsgObject->coreData->SymbolChange->pnxsSymbolOld->String);
                m_pNxCoreMsgObject->coreData->SymbolChange->pnxsSymbolOld->String = Py_BuildValue("s", pNxCoreMsg->coreData.SymbolChange.pnxsSymbolOld->String);
            }
            else
                m_pNxCoreMsgObject->coreData->SymbolChange->pnxsSymbolOld = NULL;
            if (pNxCoreMsg->coreData.SymbolChange.pnxOptionHdrOld) {
                m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld = m_pNxCoreOptionHdrOld;
                if (pNxCoreMsg->coreData.SymbolChange.pnxOptionHdrOld->pnxsDateAndStrike) {
                    m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsDateAndStrike = m_pnxsDateAndStrikeOld;
                    if (m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsDateAndStrike->String)
                        Py_XDECREF(m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsDateAndStrike->String);
                    m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsDateAndStrike->String = Py_BuildValue("s", pNxCoreMsg->coreData.SymbolChange.pnxOptionHdrOld->pnxsDateAndStrike->String);
                }
                else
                    m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsDateAndStrike = NULL;
                if (pNxCoreMsg->coreData.SymbolChange.pnxOptionHdrOld->pnxsSeriesChain) {
                    m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsSeriesChain = m_pnxsSeriesChainOld;
                    if (m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsSeriesChain->String)
                        Py_XDECREF(m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsSeriesChain->String);
                    m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsSeriesChain->String = Py_BuildValue("s", pNxCoreMsg->coreData.SymbolChange.pnxOptionHdrOld->pnxsSeriesChain->String);
                }
                else
                    m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsSeriesChain = NULL;
                if (pNxCoreMsg->coreData.SymbolChange.pnxOptionHdrOld->pnxsSpecialSettle) {
                    m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsSpecialSettle = m_pnxsSpecialSettleOld;
                    if (m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsSpecialSettle->String)
                        Py_XDECREF(m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsSpecialSettle->String);
                    m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsSpecialSettle->String = Py_BuildValue("s", pNxCoreMsg->coreData.SymbolChange.pnxOptionHdrOld->pnxsSpecialSettle->String);
                }
                else
                    m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsSpecialSettle = NULL;
                if (pNxCoreMsg->coreData.SymbolChange.pnxOptionHdrOld->pnxsUnderlying) {
                    m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsUnderlying = m_pnxsUnderlyingOld;
                    if (m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsUnderlying->String)
                        Py_XDECREF(m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsUnderlying->String);
                    m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsUnderlying->String = Py_BuildValue("s", pNxCoreMsg->coreData.SymbolChange.pnxOptionHdrOld->pnxsUnderlying->String);
                }
                else
                    m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsUnderlying = NULL;
            }
            else
                m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld = NULL;
            if (pNxCoreMsg->coreHeader.pnxOptionHdr)
                m_SymbolNxStringLookup[10][std::string(pNxCoreMsg->coreHeader.pnxStringSymbol->String) + std::to_string(pNxCoreMsg->coreHeader.pnxOptionHdr->nxExpirationDate.NDays).append(1, pNxCoreMsg->coreHeader.pnxOptionHdr->PutCall == 0 ? 'C' : 'P') + std::to_string(pNxCoreMsg->coreHeader.pnxOptionHdr->strikePrice)] = (size_t)pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsDateAndStrike;
            memcpy(&m_pNxCoreMsgObject->coreData->SymbolChange->Status, &pNxCoreMsg->coreData.SymbolChange.Status, 2);
            m_pNxCoreMsgObject->coreData->SymbolChange->ListedExgOld = pNxCoreMsg->coreData.SymbolChange.ListedExgOld;
            if (pNxCoreMsg->coreData.SymbolChange.pnxsSymbolOld)
                memcpy(&m_pNxCoreMsgObject->coreData->SymbolChange->pnxsSymbolOld->UserData1, &pNxCoreMsg->coreData.SymbolChange.pnxsSymbolOld->UserData1, 10);
            if (pNxCoreMsg->coreData.SymbolChange.pnxOptionHdrOld) {
                memcpy(&m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->exgUnderlying, &pNxCoreMsg->coreData.SymbolChange.pnxOptionHdrOld->exgUnderlying, 16);
                if (pNxCoreMsg->coreData.SymbolChange.pnxOptionHdrOld->pnxsDateAndStrike)
                    memcpy(&m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsDateAndStrike->UserData1, &pNxCoreMsg->coreData.SymbolChange.pnxOptionHdrOld->pnxsDateAndStrike->UserData1, 10);
                if (pNxCoreMsg->coreData.SymbolChange.pnxOptionHdrOld->pnxsSeriesChain)
                    memcpy(&m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsSeriesChain->UserData1, &pNxCoreMsg->coreData.SymbolChange.pnxOptionHdrOld->pnxsSeriesChain->UserData1, 10);
                if (pNxCoreMsg->coreData.SymbolChange.pnxOptionHdrOld->pnxsSpecialSettle)
                    memcpy(&m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsSpecialSettle->UserData1, &pNxCoreMsg->coreData.SymbolChange.pnxOptionHdrOld->pnxsSpecialSettle->UserData1, 10);
                if (pNxCoreMsg->coreData.SymbolChange.pnxOptionHdrOld->pnxsUnderlying)
                    memcpy(&m_pNxCoreMsgObject->coreData->SymbolChange->pnxOptionHdrOld->pnxsUnderlying->UserData1, &pNxCoreMsg->coreData.SymbolChange.pnxOptionHdrOld->pnxsUnderlying->UserData1, 10);
            }
            break;
        case NxMSG_CATEGORY:
            if (m_symbolFilterEnable && pNxCoreMsg->coreHeader.pnxStringSymbol->UserData2 != 1)
                return NxCALLBACKRETURN_CONTINUE;
            m_pNxCoreMsgObject->coreData->Category->pnxStringCategory = m_pnxStringCategory;
            if (m_pNxCoreMsgObject->coreData->Category->pnxStringCategory->String)
                Py_XDECREF(m_pNxCoreMsgObject->coreData->Category->pnxStringCategory->String);
            m_pNxCoreMsgObject->coreData->Category->pnxStringCategory->String = Py_BuildValue("s", pNxCoreMsg->coreData.Category.pnxStringCategory->String);
            m_pNxCoreMsgObject->coreData->Category->NFields = pNxCoreMsg->coreData.Category.NFields;
            memcpy(&m_pNxCoreMsgObject->coreData->Category->pnxStringCategory->UserData1, &pNxCoreMsg->coreData.Category.pnxStringCategory->UserData1, 10);
            for (int i = 0; i < pNxCoreMsg->coreData.Category.NFields; i++) {
                NxCategoryFieldObject* pField = (NxCategoryFieldObject*)PyList_GetItem(m_pNxCoreMsgObject->coreData->Category->pnxFields, i);
                memcpy(&pField->FieldName, &pNxCoreMsg->coreData.Category.pnxFields[i].FieldName, 2 * sizeof(char*) + 2);
                if (!pNxCoreMsg->coreData.Category.pnxFields[i].Set)
                    continue;
                switch (pNxCoreMsg->coreData.Category.pnxFields[i].FieldType) {
                case NxCFT_64BIT:pField->data->i64Bit = pNxCoreMsg->coreData.Category.pnxFields[i].data.i64Bit; break;
                case NxCFT_32BIT:pField->data->i32Bit = pNxCoreMsg->coreData.Category.pnxFields[i].data.i32Bit; break;
                case NxCFT_STRINGZ:pField->data->StringZ = pNxCoreMsg->coreData.Category.pnxFields[i].data.StringZ; break;
                case NxCFT_DOUBLE:pField->data->Double = pNxCoreMsg->coreData.Category.pnxFields[i].data.Double; break;
                case NxCFT_PRICE:
                    memcpy(&pField->data->nxPrice->Price, &pNxCoreMsg->coreData.Category.pnxFields[i].data.nxPrice.Price, 5);
                    break;
                case NxCFT_DATE:PopulateNxDate(pNxCoreMsg->coreData.Category.pnxFields[i].data.nxDate, pField->data->nxDate); break;
                case NxCFT_TIME:PopulateNxTime(pNxCoreMsg->coreData.Category.pnxFields[i].data.nxTime, pField->data->nxTime); break;
                case NxCFT_NxSTRING:
                    if (pField->data->pnxString->String)
                        Py_XDECREF(pField->data->pnxString->String);
                    pField->data->pnxString->String = Py_BuildValue("s", pNxCoreMsg->coreData.Category.pnxFields[i].data.pnxString->String);
                    memcpy(&pField->data->pnxString->UserData1, &pNxCoreMsg->coreData.Category.pnxFields[i].data.pnxString->UserData1, 10);
                    break;
                case NxCFT_STRING_IDX:
                case NxCFT_STRING_MAP:
                    memcpy(&pField->data->stringTableItem->ixTable, &pNxCoreMsg->coreData.Category.pnxFields[i].data.stringTableItem.ixTable, 8);
                    break;
                }
            }
            break;
        case NxMSG_SYMBOLSPIN:
            if (m_symbolFilterEnable)
                SetSymbolFilterUserData(pNxCoreMsg);
            if (m_symbolFilterEnable && pNxCoreMsg->coreHeader.pnxStringSymbol->UserData2 != 1)
                return NxCALLBACKRETURN_CONTINUE;
            memcpy(&m_pNxCoreMsgObject->coreData->SymbolSpin->SpinID, &pNxCoreMsg->coreData.SymbolSpin.SpinID, 20);
            if (pNxCoreMsg->coreHeader.pnxOptionHdr)
                m_SymbolNxStringLookup[10][std::string(pNxCoreMsg->coreHeader.pnxStringSymbol->String) + std::to_string(pNxCoreMsg->coreHeader.pnxOptionHdr->nxExpirationDate.NDays).append(1, pNxCoreMsg->coreHeader.pnxOptionHdr->PutCall == 0 ? 'C' : 'P') + std::to_string(pNxCoreMsg->coreHeader.pnxOptionHdr->strikePrice)] = (size_t)pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsDateAndStrike;
            break;
        default:return NxCALLBACKRETURN_CONTINUE;
        }

        memcpy(&m_pNxCoreSysObject->UserData, &pNxCoreSys->UserData, 8);
        PopulateNxDate(pNxCoreSys->nxDate, m_pNxCoreSysObject->nxDate);
        PopulateNxTime(pNxCoreSys->nxTime, m_pNxCoreSysObject->nxTime);
        memcpy(&m_pNxCoreSysObject->ClockUpdateInterval, &pNxCoreSys->ClockUpdateInterval, 12 + sizeof(void*) * 5);
        m_pNxCoreMsgObject->MessageType = pNxCoreMsg->MessageType;
        if (pNxCoreMsg->pnxCore3Ext) {
            m_pNxCoreMsgObject->pnxCore3Ext = m_pnxCore3Ext;
            memcpy(&m_pNxCoreMsgObject->pnxCore3Ext->MicrosOfDaySRC, pNxCoreMsg->pnxCore3Ext, 40);
        }
        else
            m_pNxCoreMsgObject->pnxCore3Ext = nullptr;

        populateCoreHdr(pNxCoreMsg, m_pNxCoreMsgObject);
        PyObject* result;
#ifdef USE_VECTOR_FUNCTION
        PyObject* args[2];
        args[0] = (PyObject*)m_pNxCoreSysObject;
        args[1] = (PyObject*)m_pNxCoreMsgObject;
        if (NxCoreVectorCallbackPython)
            result = NxCoreVectorCallbackPython(NxCoreCallbackPython, args, 2, NULL);
        else
            result = PyObject_CallObject(NxCoreCallbackPython, m_pCallbackTuple);
#else
        result = PyObject_CallObject(NxCoreCallbackPython, m_pCallbackTuple);
#endif
        if (result != NULL) {
            retCode = PyLong_AsLong(result);
            Py_DECREF(result);
        }
        else if (PyErr_Occurred()) {
            if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt))//Let keyboard interrupts stop callback
                return NxCALLBACKRETURN_STOP;
            else
                PyErr_Print();
        }
        UpdateUserData((NxCoreSystem*)pNxCoreSys, (NxCoreMessage*)pNxCoreMsg, m_pNxCoreSysObject, m_pNxCoreMsgObject);
        return retCode;
    }
};
thread_local ThreadData td;
int __stdcall OnNxCoreCallbackSetReverseLookup(const NxCoreSystem* pNxCoreSys, const NxCoreMessage* pNxCoreMsg) {
    return td.SetReverseLookup(pNxCoreSys, pNxCoreMsg);
}
int __stdcall OnNxCoreCallback(const NxCoreSystem* pNxCoreSys, const NxCoreMessage* pNxCoreMsg) {
    return td.Callback(pNxCoreSys, pNxCoreMsg);
}

PyObject* NxCorePython_LoadNxCore(PyObject* self, PyObject* args, PyObject* kwargs) {
    /* Shared references that do not need Py_DECREF before returning. */
    const char* StringNxCoreLib;

    /* Parse positional and keyword arguments */
    char arg1[] = "StringNxCoreLib";
    static char* keywords[] = { arg1, NULL };
    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", keywords, &StringNxCoreLib)) {
        PyErr_SetString(PyExc_TypeError, "invalid parameters");
        return NULL;
    }

    return PyLong_FromLong(NxCore.LoadNxCore(StringNxCoreLib));
}

PyObject* NxCorePython_ProcessTape(PyObject* self, PyObject* args, PyObject* kwargs) {
    /* Shared references that do not need Py_DECREF before returning. */
    ThreadData* pTD = &td;
    const char* filename;
    long long pMemoryAddr;
    int UserData;
    PyObject* callbackFunction;

    /* Parse positional and keyword arguments */
    char arg1[] = "filename";
    char arg2[] = "pMemoryAddr";
    char arg3[] = "controlFlags";
    char arg4[] = "UserData";
    char arg5[] = "callbackFunction";
    static char* keywords[] = { arg1, arg2, arg3, arg4, arg5, NULL };
    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sliiO:set_callback", keywords, &filename, &pMemoryAddr, &pTD->m_controlFlags, &UserData, &callbackFunction)) {
        PyErr_SetString(PyExc_TypeError, "invalid parameters");
        return NULL;
    }
    if (!PyCallable_Check(callbackFunction)) {
        PyErr_SetString(PyExc_TypeError, "parameter must be callable");
        return NULL;
    }
    Py_XINCREF(callbackFunction);
#ifdef USE_VECTOR_FUNCTION
    pTD->NxCoreVectorCallbackPython= PyVectorcall_Function(callbackFunction);
#endif
    Py_XDECREF(pTD->NxCoreCallbackPython);
    pTD->NxCoreCallbackPython = callbackFunction;
    pTD->m_pNxCoreSysObject = (NxCoreSystemObject*)PyObject_CallObject((PyObject*)&NxCoreSystemType, NULL);
    pTD->m_pNxCoreMsgObject = (NxCoreMessageObject*)PyObject_CallObject((PyObject*)&NxCoreMessageType, NULL);
    pTD->m_pNxCoreOptionHdrObject = (NxOptionHdrObject*)PyObject_CallObject((PyObject*)&NxOptionHdrType, NULL);
    pTD->m_pnxCore3Ext = (NxCore3ExtObject*)PyObject_CallObject((PyObject*)&NxCore3ExtType, NULL);
    pTD->m_pnxStringMarketMaker = (NxStringObject*)PyObject_CallObject((PyObject*)&NxStringType, NULL);
    pTD->m_pnxStringCategory = (NxStringObject*)PyObject_CallObject((PyObject*)&NxStringType, NULL);
    pTD->m_pnxStringSymbol = (NxStringObject*)PyObject_CallObject((PyObject*)&NxStringType, NULL);
    pTD->m_pnxsDateAndStrike = (NxStringObject*)PyObject_CallObject((PyObject*)&NxStringType, NULL);
    pTD->m_pnxsUnderlying = (NxStringObject*)PyObject_CallObject((PyObject*)&NxStringType, NULL);
    pTD->m_pnxsSeriesChain = (NxStringObject*)PyObject_CallObject((PyObject*)&NxStringType, NULL);
    pTD->m_pnxsSpecialSettle = (NxStringObject*)PyObject_CallObject((PyObject*)&NxStringType, NULL);
    pTD->m_pnxsSymbolOld = (NxStringObject*)PyObject_CallObject((PyObject*)&NxStringType, NULL);
    pTD->m_pnxsDateAndStrikeOld = (NxStringObject*)PyObject_CallObject((PyObject*)&NxStringType, NULL);
    pTD->m_pnxsUnderlyingOld = (NxStringObject*)PyObject_CallObject((PyObject*)&NxStringType, NULL);
    pTD->m_pnxsSeriesChainOld = (NxStringObject*)PyObject_CallObject((PyObject*)&NxStringType, NULL);
    pTD->m_pnxsSpecialSettleOld = (NxStringObject*)PyObject_CallObject((PyObject*)&NxStringType, NULL);
    for(int i =0; i<512; i++)
        pTD->m_pnxStringMarketMakerState[i] = (NxStringObject*)PyObject_CallObject((PyObject*)&NxStringType, NULL);
    pTD->m_pNxCoreOptionHdrOld = (NxOptionHdrObject*)PyObject_CallObject((PyObject*)&NxOptionHdrType, NULL);
    pTD->m_pNxCoreSysSpin = (NxCoreSystemObject*)PyObject_CallObject((PyObject*)&NxCoreSystemType, NULL);
    pTD->m_pNxCoreMsgSpin = (NxCoreMessageObject*)PyObject_CallObject((PyObject*)&NxCoreMessageType, NULL);
    pTD->m_pNxCoreOptionHdrSpin = (NxOptionHdrObject*)PyObject_CallObject((PyObject*)&NxOptionHdrType, NULL);
    pTD->m_pnxsSymbolSpin = (NxStringObject*)PyObject_CallObject((PyObject*)&NxStringType, NULL);
    pTD->m_pnxsDateAndStrikeSpin = (NxStringObject*)PyObject_CallObject((PyObject*)&NxStringType, NULL);
    pTD->m_pnxsUnderlyingSpin = (NxStringObject*)PyObject_CallObject((PyObject*)&NxStringType, NULL);
    pTD->m_pnxsSeriesChainSpin = (NxStringObject*)PyObject_CallObject((PyObject*)&NxStringType, NULL);
    pTD->m_pnxsSpecialSettleSpin = (NxStringObject*)PyObject_CallObject((PyObject*)&NxStringType, NULL);
    pTD->m_pCallbackTupleSpin = Py_BuildValue("(O,O)", pTD->m_pNxCoreSysSpin, pTD->m_pNxCoreMsgSpin);
    pTD->m_pCallbackTuple = Py_BuildValue("(O,O)", pTD->m_pNxCoreSysObject, pTD->m_pNxCoreMsgObject);
    int retcode = NxCore.ProcessTape(filename, (char*)pMemoryAddr, pTD->m_controlFlags, UserData, OnNxCoreCallback);
    Py_XDECREF(pTD->m_pNxCoreSysObject);
    Py_XDECREF(pTD->m_pNxCoreMsgObject);
    Py_XDECREF(pTD->m_pNxCoreOptionHdrObject);
    Py_XDECREF(pTD->m_pCallbackTuple);
    Py_XDECREF(pTD->m_pnxStringMarketMaker);
    Py_XDECREF(pTD->m_pnxStringCategory);
    Py_XDECREF(pTD->m_pnxStringSymbol);
    Py_XDECREF(pTD->m_pnxsDateAndStrike);
    Py_XDECREF(pTD->m_pnxsUnderlying);
    Py_XDECREF(pTD->m_pnxsSeriesChain);
    Py_XDECREF(pTD->m_pnxsSpecialSettle);
    Py_XDECREF(pTD->m_pnxsSymbolOld);
    Py_XDECREF(pTD->m_pnxsDateAndStrikeOld);
    Py_XDECREF(pTD->m_pnxsUnderlyingOld);
    Py_XDECREF(pTD->m_pnxsSeriesChainOld);
    Py_XDECREF(pTD->m_pnxsSpecialSettleOld);
    for (int i = 0; i < 512; i++)
        Py_XDECREF(pTD->m_pnxStringMarketMakerState[i]);
    Py_XDECREF(pTD->m_pNxCoreOptionHdrOld);

    Py_XDECREF(pTD->m_pNxCoreSysSpin);
    Py_XDECREF(pTD->m_pNxCoreMsgSpin);
    Py_XDECREF(pTD->m_pNxCoreOptionHdrSpin);
    Py_XDECREF(pTD->m_pnxsSymbolSpin);
    Py_XDECREF(pTD->m_pnxsDateAndStrikeSpin);
    Py_XDECREF(pTD->m_pnxsUnderlyingSpin);
    Py_XDECREF(pTD->m_pnxsSeriesChainSpin);
    Py_XDECREF(pTD->m_pnxsSpecialSettleSpin);
    Py_XDECREF(pTD->m_pCallbackTupleSpin);
    for (int i = 0; i < 256; i++) {
        for (auto it = pTD->m_DefinedStringLookup[i].begin(); it != pTD->m_DefinedStringLookup[i].end(); ++it) {
            if (it->second)
                Py_XDECREF(it->second);
        }
        pTD->m_DefinedStringLookup[i].clear();
    }
    if (PyErr_Occurred())
        return NULL;
    return PyLong_FromLong(retcode);
}

PyObject* NxCorePython_UnloadNxCore(PyObject* self) {
    NxCore.UnloadNxCore();
    Py_RETURN_NONE;
}

PyObject* NxCorePython_APIVersion(PyObject* self) {
    return PyLong_FromLong(NxCore.APIVersion());
}

PyObject* NxCorePython_SaveState(PyObject* self, PyObject* args) {
    const char* StringPath;
    int controlFlags=0;
    if (!PyArg_ParseTuple(args, "si", &StringPath,&controlFlags)) {
        PyErr_SetString(PyExc_TypeError, "invalid parameters");
        return NULL;
    }

    return PyLong_FromLong(NxCore.SaveState(StringPath, controlFlags));
}

PyObject* NxCorePython_GetDefinedString(PyObject* self, PyObject* args) {
    ThreadData* pTD = &td;
    int ixTable = 0,ixString=0;
    if (!PyArg_ParseTuple(args, "ii", &ixTable, &ixString)) {
        PyErr_SetString(PyExc_TypeError, "invalid parameters");
        return NULL;
    }

    PyObject* oString;
    if ((oString = pTD->m_DefinedStringLookup[ixTable][ixString])) {
        Py_XINCREF(oString);
        return oString;
    }
    const char* pTableItem = NxCore.GetDefinedString(ixTable, ixString);
    if (pTableItem == nullptr)
        Py_RETURN_NONE;

    oString = Py_BuildValue("s", pTableItem);
    Py_XINCREF(oString);
    pTD->m_DefinedStringLookup[ixTable][ixString] = oString;
    return oString;
}

PyObject* NxCorePython_ExcludeOpraExch(PyObject* self, PyObject* args) {
    int removeID = 0;
    if (!PyArg_ParseTuple(args, "i", &removeID)) {
        PyErr_SetString(PyExc_TypeError, "invalid parameters");
        return NULL;
    }
    return PyLong_FromLong(NxCore.ExcludeOpraExch(removeID));
}

PyObject* NxCorePython_GetSymbolAtom32(PyObject* self) {

    return PyLong_FromLong(NxCore.GetSymbolAtom32());
}

PyObject* NxCorePython_GetTapeName(PyObject* self, PyObject* args) {
    char tapename[MAX_PATH];
    if (NxCore.GetTapeName(tapename, MAX_PATH) >= 0)
        return Py_BuildValue("s", tapename);
    Py_RETURN_NONE;
}

PyObject* NxCorePython_PriceConvert(PyObject* self, PyObject* args) {
    int lPrice, PriceType, PriceTypeNew;

    if (!PyArg_ParseTuple(args, "iii", &lPrice, &PriceType, &PriceTypeNew)) {
        PyErr_SetString(PyExc_TypeError, "invalid parameters");
        return NULL;
    }
    return PyLong_FromLong(NxCore.PriceConvert(lPrice, PriceType, PriceTypeNew));
}

PyObject* NxCorePython_PriceFormat(PyObject* self, PyObject* args) {
    int lPrice, PriceType, expandWidth=0;
    bool bCommas=false;
    char buff[MAX_PATH];
    if (!PyArg_ParseTuple(args, "ii|ip", &lPrice, &PriceType, &expandWidth, &bCommas)) {
        PyErr_SetString(PyExc_TypeError, "invalid parameters");
        return NULL;
    }
    if (NxCore.PriceFormat(buff, lPrice, PriceType, expandWidth, bCommas) < 1)
        Py_RETURN_NONE;
    return Py_BuildValue("s", buff);
}

PyObject* NxCorePython_PriceToDouble(PyObject* self, PyObject* args) {
    int lPrice, PriceType;
    if (!PyArg_ParseTuple(args, "ii", &lPrice, &PriceType)) {
        PyErr_SetString(PyExc_TypeError, "invalid parameters");
        return NULL;
    }
    return PyFloat_FromDouble(NxCore.PriceToDouble(lPrice, PriceType));
}

PyObject* NxCorePython_ProcessReturnValue(PyObject* self, PyObject* args) {
    int retval;
    if (!PyArg_ParseTuple(args, "i", &retval)) {
        PyErr_SetString(PyExc_TypeError, "invalid parameters");
        return NULL;
    }
    NxCore.ProcessReturnValue(retval);
    Py_RETURN_NONE;
}


thread_local PyObject* OnUserListTapesPython;
thread_local PyObject* OnUserListAPIDLLsPython;
int __stdcall OnUserListTapes(void* pUserParam, const NxCoreTapeFile* pNxTF)
{
    NxCoreTapeFileObject* pTapeFile=(NxCoreTapeFileObject*)PyObject_CallObject((PyObject*)&NxCoreTapeFileType, NULL);
    memcpy(&pTapeFile->FileAttributes,&pNxTF->FileAttributes,12);
    PopulateNxDate(pNxTF->TapeDate, pTapeFile->TapeDate);
    pTapeFile->PathnameStrZ=Py_BuildValue("s", pNxTF->PathnameStrZ);
    pTapeFile->PermSet = Py_BuildValue("s", pNxTF->PermSet);
    PyObject_CallObject(OnUserListTapesPython, Py_BuildValue("(O)", pTapeFile));
    Py_XDECREF(pTapeFile);
    if (PyErr_Occurred())
        PyErr_Print();
    return 0;
}

PyObject* NxCorePython_ListTapes(PyObject* self, PyObject* args) {
    int controlFlags;
    PyObject* callbackFunction;
    if (!PyArg_ParseTuple(args, "iO", &controlFlags, &callbackFunction)) {
        PyErr_SetString(PyExc_TypeError, "invalid parameters");
        return NULL;
    }
    if (!PyCallable_Check(callbackFunction)) {
        PyErr_SetString(PyExc_TypeError, "parameter must be callable");
        return NULL;
    }
    Py_XINCREF(callbackFunction);
    Py_XDECREF(OnUserListTapesPython);
    OnUserListTapesPython = callbackFunction;
    return PyLong_FromLong(NxCore.ListTapes(controlFlags, OnUserListTapes, nullptr));
}

int __stdcall OnUserlistDLL(void* pUserParam, const NxCoreAPIDLLFile* pNxDF)
{
    NxCoreAPIDLLFileObject* pAPIFile = (NxCoreAPIDLLFileObject*)PyObject_CallObject((PyObject*)&NxCoreAPIDLLFileType, NULL);
    memcpy(&pAPIFile->FileAttributes, &pNxDF->FileAttributes, 12);
    PopulateNxDate(pNxDF->BuildDate, pAPIFile->BuildDate);
    PopulateNxTime(pNxDF->BuildTime, pAPIFile->BuildTime);
    pAPIFile->PathnameStrZ = Py_BuildValue("s", pNxDF->PathnameStrZ);
    memcpy(&pAPIFile->verMajor, &pNxDF->verMajor, 4);
    PyObject_CallObject(OnUserListAPIDLLsPython, Py_BuildValue("(O)", pAPIFile));
    Py_XDECREF(pAPIFile);
    if (PyErr_Occurred())
        PyErr_Print();
    return  0;
}

PyObject* NxCorePython_ListAPIDLLs(PyObject* self, PyObject* args) {
    int controlFlags;
    PyObject* callbackFunction;
    if (!PyArg_ParseTuple(args, "iO", &controlFlags, &callbackFunction)) {
        PyErr_SetString(PyExc_TypeError, "invalid parameters");
        return NULL;
    }
    if (!PyCallable_Check(callbackFunction)) {
        PyErr_SetString(PyExc_TypeError, "parameter must be callable");
        return NULL;
    }
    Py_XINCREF(callbackFunction);
    Py_XDECREF(OnUserListAPIDLLsPython);
    OnUserListAPIDLLsPython = callbackFunction;
    return PyLong_FromLong(NxCore.ListAPIDLLs(controlFlags, OnUserlistDLL, nullptr));
}

thread_local PyObject* OnUserSymbolSpinPython = NULL;
int __stdcall OnUserSymbolSpin(const NxCoreSystem* pNxCoreSys, const NxCoreMessage* pNxCoreMsg)
{
    ThreadData* pTD = &td;
    if (pTD->m_symbolFilterEnable && pNxCoreMsg->coreHeader.pnxStringSymbol->UserData2 != 1)
        return NxCALLBACKRETURN_CONTINUE;
    int retCode = NxCALLBACKRETURN_CONTINUE;
    if (pNxCoreMsg->coreHeader.pnxOptionHdr) {
        pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr = pTD->m_pNxCoreOptionHdrSpin;
        memcpy(&pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr->exgUnderlying, &pNxCoreMsg->coreHeader.pnxOptionHdr->exgUnderlying, 16);
        PopulateNxDate(pNxCoreMsg->coreHeader.pnxOptionHdr->nxExpirationDate, pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr->nxExpirationDate);

        if (pNxCoreMsg->coreHeader.pnxStringSymbol) {
            pTD->m_pNxCoreMsgSpin->coreHeader->pnxStringSymbol = pTD->m_pnxsSymbolSpin;
            memcpy(&pTD->m_pNxCoreMsgSpin->coreHeader->pnxStringSymbol->UserData1, pNxCoreMsg->coreHeader.pnxStringSymbol, 10);
            pTD->m_pNxCoreMsgSpin->coreHeader->pnxStringSymbol->String = Py_BuildValue("s", pNxCoreMsg->coreHeader.pnxStringSymbol->String);
            pTD->m_SymbolNxStringLookup[10][std::string(pNxCoreMsg->coreHeader.pnxStringSymbol->String) + std::to_string(pNxCoreMsg->coreHeader.pnxOptionHdr->nxExpirationDate.NDays).append(1, pNxCoreMsg->coreHeader.pnxOptionHdr->PutCall == 0 ? 'C' : 'P') + std::to_string(pNxCoreMsg->coreHeader.pnxOptionHdr->strikePrice)] = (size_t)pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsDateAndStrike;
        }
        else
            pTD->m_pNxCoreMsgSpin->coreHeader->pnxStringSymbol = nullptr;
        if (pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsUnderlying) {
            pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr->pnxsUnderlying = pTD->m_pnxsUnderlyingSpin;
            memcpy(&pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr->pnxsUnderlying->UserData1, pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsUnderlying, 10);
            pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr->pnxsUnderlying->String = Py_BuildValue("s", pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsUnderlying->String);
        }
        else
            pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr->pnxsUnderlying = nullptr;
        if (pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsSeriesChain) {
            pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr->pnxsSeriesChain = pTD->m_pnxsSeriesChainSpin;
            memcpy(&pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr->pnxsSeriesChain->UserData1, pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsSeriesChain, 10);
            pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr->pnxsSeriesChain->String = Py_BuildValue("s", pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsSeriesChain->String);
        }
        else
            pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr->pnxsSeriesChain = nullptr;
        if (pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsSpecialSettle) {
            pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr->pnxsSpecialSettle = pTD->m_pnxsSpecialSettleSpin;
            memcpy(&pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr->pnxsSpecialSettle->UserData1, pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsSpecialSettle, 10);
            pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr->pnxsSpecialSettle->String = Py_BuildValue("s", pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsSpecialSettle->String);
        }
        else
            pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr->pnxsSpecialSettle = nullptr;

        if (pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsDateAndStrike) {
            pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr->pnxsDateAndStrike = pTD->m_pnxsDateAndStrikeSpin;
            memcpy(&pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr->pnxsDateAndStrike->UserData1, pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsDateAndStrike, 10);
            char DateAndStrike[3];
            strncpy(DateAndStrike, pNxCoreMsg->coreHeader.pnxOptionHdr->pnxsDateAndStrike->String, 2);
            DateAndStrike[2] = 0;
            pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr->pnxsDateAndStrike->String = Py_BuildValue("s", DateAndStrike);
        }
        else
            pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr->pnxsDateAndStrike = nullptr;
    }
    else {
        pTD->m_pNxCoreMsgSpin->coreHeader->pnxOptionHdr = nullptr;
        if (pNxCoreMsg->coreHeader.pnxStringSymbol) {
            pTD->m_pNxCoreMsgSpin->coreHeader->pnxStringSymbol = pTD->m_pnxsSymbolSpin;
            memcpy(&pTD->m_pNxCoreMsgSpin->coreHeader->pnxStringSymbol->UserData1, pNxCoreMsg->coreHeader.pnxStringSymbol, 10);
            pTD->m_pNxCoreMsgSpin->coreHeader->pnxStringSymbol->String = Py_BuildValue("s", pNxCoreMsg->coreHeader.pnxStringSymbol->String);
            pTD->m_SymbolNxStringLookup[pNxCoreMsg->coreHeader.ListedExg][std::string(pNxCoreMsg->coreHeader.pnxStringSymbol->String)] = (size_t)pNxCoreMsg->coreHeader.pnxStringSymbol;
        }
        else
            pTD->m_pNxCoreMsgSpin->coreHeader->pnxStringSymbol = nullptr;
    }
    memcpy(&pTD->m_pNxCoreMsgSpin->coreHeader->ListedExg, &pNxCoreMsg->coreHeader.ListedExg, 8);
    PopulateNxDate(pNxCoreMsg->coreHeader.nxSessionDate, pTD->m_pNxCoreMsgSpin->coreHeader->nxSessionDate);
    PopulateNxTime(pNxCoreMsg->coreHeader.nxExgTimestamp, pTD->m_pNxCoreMsgSpin->coreHeader->nxExgTimestamp);
    memcpy(&pTD->m_pNxCoreMsgSpin->coreData->SymbolSpin->SpinID, &pNxCoreMsg->coreData.SymbolSpin.SpinID, 20);

    memcpy(&pTD->m_pNxCoreSysSpin->UserData, &pNxCoreSys->UserData, 8);
    PopulateNxDate(pNxCoreSys->nxDate, pTD->m_pNxCoreSysSpin->nxDate);
    PopulateNxTime(pNxCoreSys->nxTime, pTD->m_pNxCoreSysSpin->nxTime);
    memcpy(&pTD->m_pNxCoreSysSpin->ClockUpdateInterval, &pNxCoreSys->ClockUpdateInterval, 12 + sizeof(void*) * 5);
    pTD->m_pNxCoreMsgSpin->MessageType = pNxCoreMsg->MessageType;
    pTD->m_pNxCoreMsgSpin->pnxCore3Ext = nullptr;

    PyObject* result = PyObject_CallObject(OnUserSymbolSpinPython, pTD->m_pCallbackTupleSpin);
    UpdateUserData((NxCoreSystem*)pNxCoreSys, (NxCoreMessage*)pNxCoreMsg, pTD->m_pNxCoreSysSpin, pTD->m_pNxCoreMsgSpin);
    if (result != NULL) {
        retCode = PyLong_AsLong(result);
        Py_DECREF(result);
    }
    else if (PyErr_Occurred())
        PyErr_Print();
    return retCode;
}
PyObject* NxCorePython_SpinSymbols(PyObject* self, PyObject* args) {
    int ListedExg, controlFlags, spinID, tempUserData;
    PyObject* callbackFunction;
    if (!PyArg_ParseTuple(args,  "iiiOi",  &ListedExg, &controlFlags, &spinID, &callbackFunction,&tempUserData)) {
        PyErr_SetString(PyExc_TypeError, "invalid parameters");
        return NULL;
    }
    if (!PyCallable_Check(callbackFunction)) {
        PyErr_SetString(PyExc_TypeError, "parameter must be callable");
        return NULL;
    }
    Py_XINCREF(callbackFunction);
    Py_XDECREF(OnUserSymbolSpinPython);
    OnUserSymbolSpinPython = callbackFunction;
    return PyLong_FromLong(NxCore.SpinSymbols(ListedExg, controlFlags, spinID, OnUserSymbolSpin, tempUserData));
}

PyObject* NxCorePython_StateGetExgQuotes(PyObject* self, PyObject* args) {
    int listedExg, strikeNDays = 0, strikePrice = 0, PutCall = 2;
    const char * symbol;
    char szSymbolCustom[256];
    NxString* pnxString;
    NxCoreStateExgQuotes StateExgQuotes;
    if (!PyArg_ParseTuple(args, "is|iii", &listedExg,&symbol, &strikeNDays, &strikePrice, &PutCall)) {
        PyErr_SetString(PyExc_TypeError, "invalid parameters");
        return NULL;
    }
    if (strikeNDays && strikePrice && PutCall != 2) {
#ifdef _WIN32
        if (sprintf_s(szSymbolCustom, "%s%d%c%d", symbol, strikeNDays, PutCall == 0 ? 'C' : 'P', strikePrice) > 0)
#else
        if (sprintf(szSymbolCustom, "%s%d%c%d", symbol, strikeNDays, PutCall == 0 ? 'C' : 'P', strikePrice) > 0)
#endif
            symbol = szSymbolCustom;
    }
    pnxString=td.GetNxStringSymbol(symbol, listedExg);
    if(!pnxString)
        Py_RETURN_NONE;
    if(NxCore.StateGetExgQuotes(&StateExgQuotes, pnxString))
        Py_RETURN_NONE;
    NxCoreStateExgQuotesObject* pExgQuotes = (NxCoreStateExgQuotesObject*) PyObject_CallObject((PyObject*)&NxCoreStateExgQuotesType, NULL);
    if(!pExgQuotes)
        Py_RETURN_NONE;
    memcpy(&pExgQuotes->StateQuoteCount, &StateExgQuotes.StateQuoteCount, 8);
    for (int i = 0; i < StateExgQuotes.StateQuoteCount; i++) {
        NxCoreStateExgQuoteObject* pQuote =(NxCoreStateExgQuoteObject*) PyList_GetItem(pExgQuotes->StateExgQuotes, i);
        memcpy(&pQuote->AskPrice, &StateExgQuotes.StateExgQuotes[i].AskPrice, 19);
    }
    return (PyObject*)pExgQuotes;
}

PyObject* NxCorePython_StateGetMMQuotes(PyObject* self, PyObject* args) {
    ThreadData* pTD = &td;
    int reportingExg, listedExg, strikeNDays = 0, strikePrice = 0, PutCall = 2;
    const char* symbol;
    char szSymbolCustom[256];
    NxString* pnxString;
    NxCoreStateMMQuotes StateMMQuotes;
    if (!PyArg_ParseTuple(args, "iis|iii", &reportingExg, &listedExg, &symbol, &strikeNDays, &strikePrice, &PutCall)) {
        PyErr_SetString(PyExc_TypeError, "invalid parameters");
        return NULL;
    }
    if (strikeNDays && strikePrice && PutCall != 2) {
#ifdef _WIN32
        if (sprintf_s(szSymbolCustom, "%s%d%c%d", symbol, strikeNDays, PutCall == 0 ? 'C' : 'P', strikePrice) > 0)
#else
        if (sprintf(szSymbolCustom, "%s%d%c%d", symbol, strikeNDays, PutCall == 0 ? 'C' : 'P', strikePrice) > 0)
#endif
            symbol = szSymbolCustom;
    }
    pnxString = pTD->GetNxStringSymbol(symbol, listedExg);
    if (!pnxString)
        Py_RETURN_NONE;
    if (NxCore.StateGetMMQuotes(reportingExg,&StateMMQuotes, pnxString))
        Py_RETURN_NONE;
    NxCoreStateMMQuotesObject* pMMQuotes = (NxCoreStateMMQuotesObject*)PyObject_CallObject((PyObject*)&NxCoreStateMMQuotesType, NULL);
    if (!pMMQuotes)
        Py_RETURN_NONE;
    memcpy(&pMMQuotes->StateQuoteCount, &StateMMQuotes.StateQuoteCount, 8);
    for (int i = 0; i < StateMMQuotes.StateQuoteCount; i++) {
        NxCoreStateMMQuoteObject* pQuote = (NxCoreStateMMQuoteObject*)PyList_GetItem(pMMQuotes->StateMMQuotes, i);
        memcpy(&pQuote->AskPrice, &StateMMQuotes.StateMMQuotes[i].AskPrice, 16);
        pQuote->pnxStringMarketMaker = pTD->m_pnxStringMarketMakerState[i];
        pQuote->pnxStringMarketMaker->String=pTD->GetStringMM(StateMMQuotes.StateMMQuotes[i].pnxStringMarketMaker);
        memcpy(&pQuote->MarketMakerType, &StateMMQuotes.StateMMQuotes[i].MarketMakerType, 2);
        memcpy(&pQuote->pnxStringMarketMaker->UserData1, &StateMMQuotes.StateMMQuotes[i].pnxStringMarketMaker->UserData1, 10);
    }
    return (PyObject*)pMMQuotes;
}

PyObject* NxCorePython_StateGetLastTrade(PyObject* self, PyObject* args) {
    int listedExg, strikeNDays=0, strikePrice=0, PutCall=2;
    const char* symbol;
    char szSymbolCustom[256];
    NxString* pnxString;
    NxCoreStateTrade StateTrade;
    if (!PyArg_ParseTuple(args, "is|iii", &listedExg, &symbol, &strikeNDays, &strikePrice, &PutCall)) {
        PyErr_SetString(PyExc_TypeError, "invalid parameters");
        return NULL;
    }
    if (strikeNDays && strikePrice && PutCall != 2) {
#ifdef _WIN32
        if (sprintf_s(szSymbolCustom, "%s%d%c%d", symbol, strikeNDays, PutCall == 0 ? 'C' : 'P', strikePrice) > 0)
#else
        if (sprintf(szSymbolCustom, "%s%d%c%d", symbol, strikeNDays, PutCall == 0 ? 'C' : 'P', strikePrice) > 0)
#endif
            symbol = szSymbolCustom;
    }
    pnxString = td.GetNxStringSymbol(symbol, listedExg);
    if (!pnxString)
        Py_RETURN_NONE;
    if (NxCore.StateGetLastTrade(&StateTrade, pnxString))
        Py_RETURN_NONE;
    NxCoreStateTradeObject* pTrade = (NxCoreStateTradeObject*)PyObject_CallObject((PyObject*)&NxCoreStateTradeType, NULL);
    if (!pTrade)
        Py_RETURN_NONE;
    
    memcpy(&pTrade->TotalVolume, &StateTrade.TotalVolume, 48);
    return (PyObject*)pTrade;
}
PyObject* NxCorePython_DateFromNDays(PyObject* self, PyObject* args) {
    PyObject* oGeneric;
    if (!PyArg_ParseTuple(args, "O", &oGeneric)) {
        Py_RETURN_FALSE;
    }
    if (!PyObject_IsInstance(oGeneric, (PyObject*)&NxDateType))
        Py_RETURN_FALSE;
    NxDateObject* oDate = (NxDateObject*)oGeneric;
    NxCore.DateFromNDays((NxDate*)&oDate->NDays);
    Py_RETURN_TRUE;
}
PyObject* NxCorePython_DateFromYMD(PyObject* self, PyObject* args) {
    PyObject* oGeneric;
    if (!PyArg_ParseTuple(args, "O", &oGeneric)) {
        Py_RETURN_FALSE;
    }
    if (!PyObject_IsInstance(oGeneric, (PyObject*)&NxDateType))
        Py_RETURN_FALSE;
    NxDateObject* oDate = (NxDateObject*)oGeneric;
    NxCore.DateFromYMD((NxDate*)&oDate->NDays);
    Py_RETURN_TRUE;
}

PyObject* NxCorePython_FilterSymbols(PyObject* self, PyObject* args) {
ThreadData* pTD = &td;
    pTD->m_symbolFilterLookup.clear();
    pTD->m_symbolFilterEnable = 0;
    PyObject* SymbolList;
    PyObject* item, * pyString, * exgObject;
    if (!PyArg_ParseTuple(args, "O", &SymbolList)) {
        Py_RETURN_FALSE;
    }
    if (PyUnicode_Check(SymbolList)) {
        pTD->m_symbolFilterLookup[PyUnicode_AsUTF8(SymbolList)] = 256;
    }
    else if (PyTuple_Check(SymbolList)) {
        if (PyTuple_Size(SymbolList) == 1) {
            pyString = PyTuple_GetItem(SymbolList, 0);
            if (PyUnicode_Check(pyString)) {
                pTD->m_symbolFilterLookup[PyUnicode_AsUTF8(pyString)] = 256;
            }
            else
                Py_RETURN_FALSE;
        }
        else if (PyTuple_Size(SymbolList) == 2) {
            exgObject = PyTuple_GetItem(SymbolList, 1);
            pyString = PyTuple_GetItem(SymbolList, 0);
            if (PyUnicode_Check(pyString) && PyLong_Check(exgObject)) {
                pTD->m_symbolFilterLookup[PyUnicode_AsUTF8(pyString)] = PyLong_AsLong(exgObject);
            }
            else
                Py_RETURN_FALSE;
        }
        else
            Py_RETURN_FALSE;
    }
    else if (PyList_Check(SymbolList)) {// check that object is a list
        size_t len = PyList_Size(SymbolList);
        for (int i = 0; i < len; i++) {
            item = PyList_GetItem(SymbolList, i);
            if (PyTuple_Check(item)) {
                if (PyTuple_Size(item) == 1) {
                    pyString = PyTuple_GetItem(item, 0);
                    if (PyUnicode_Check(pyString))
                        pTD->m_symbolFilterLookup[PyUnicode_AsUTF8(pyString)] = 256;
                    else
                        Py_RETURN_FALSE;
                }
                else if (PyTuple_Size(item) == 2) {
                    exgObject = PyTuple_GetItem(item, 1);
                    pyString = PyTuple_GetItem(item, 0);
                    if (PyUnicode_Check(pyString) && PyLong_Check(exgObject))
                        pTD->m_symbolFilterLookup[PyUnicode_AsUTF8(pyString)] = PyLong_AsLong(exgObject);
                    else
                        Py_RETURN_FALSE;
                }
            }
            else if (PyUnicode_Check(item)) {
                pTD->m_symbolFilterLookup[PyUnicode_AsUTF8(item)] = 256;
            }
            else
                Py_RETURN_FALSE;
        }
    }
    else
        Py_RETURN_FALSE;
    pTD->m_symbolFilterEnable = 1;
    Py_RETURN_TRUE;
}

PyObject* NxCorePython_ExcludeSymbols(PyObject* self, PyObject* args) {
    ThreadData* pTD = &td;
    pTD->m_symbolExcludeLookup.clear();
    pTD->m_symbolFilterEnable = 0;
    PyObject* SymbolList;
    PyObject* item, * pyString, * exgObject;
    if (!PyArg_ParseTuple(args, "O", &SymbolList)) {
        Py_RETURN_FALSE;
    }
    if (PyUnicode_Check(SymbolList)) {
        pTD->m_symbolExcludeLookup[PyUnicode_AsUTF8(SymbolList)] = 256;
    }
    else if (PyTuple_Check(SymbolList)) {
        if (PyTuple_Size(SymbolList) == 1) {
            pyString = PyTuple_GetItem(SymbolList, 0);
            if (PyUnicode_Check(pyString)) {
                pTD->m_symbolExcludeLookup[PyUnicode_AsUTF8(pyString)] = 256;
            }
            else
                Py_RETURN_FALSE;
        }
        else if (PyTuple_Size(SymbolList) == 2) {
            exgObject = PyTuple_GetItem(SymbolList, 1);
            pyString = PyTuple_GetItem(SymbolList, 0);
            if (PyUnicode_Check(pyString) && PyLong_Check(exgObject)) {
                pTD->m_symbolExcludeLookup[PyUnicode_AsUTF8(pyString)] = PyLong_AsLong(exgObject);
            }
            else
                Py_RETURN_FALSE;
        }
        else
            Py_RETURN_FALSE;
    }
    else if (PyList_Check(SymbolList)) {// check that object is a list
        size_t len = PyList_Size(SymbolList);
        for (int i = 0; i < len; i++) {
            item = PyList_GetItem(SymbolList, i);
            if (PyTuple_Check(item)) {
                if (PyTuple_Size(item) == 1) {
                    pyString = PyTuple_GetItem(item, 0);
                    if (PyUnicode_Check(pyString))
                        pTD->m_symbolExcludeLookup[PyUnicode_AsUTF8(pyString)] = 256;
                    else
                        Py_RETURN_FALSE;
                }
                else if (PyTuple_Size(item) == 2) {
                    exgObject = PyTuple_GetItem(item, 1);
                    pyString = PyTuple_GetItem(item, 0);
                    if (PyUnicode_Check(pyString) && PyLong_Check(exgObject))
                        pTD->m_symbolExcludeLookup[PyUnicode_AsUTF8(pyString)] = PyLong_AsLong(exgObject);
                    else
                        Py_RETURN_FALSE;
                }
            }
            else if (PyUnicode_Check(item)) {
                pTD->m_symbolExcludeLookup[PyUnicode_AsUTF8(item)] = 256;
            }
            else
                Py_RETURN_FALSE;
        }
    }
    else
        Py_RETURN_FALSE;
    pTD->m_symbolFilterEnable = 2;
    Py_RETURN_TRUE;
}

PyObject* NxCorePython_FilterSymbolsRange(PyObject* self, PyObject* args) {
    ThreadData* pTD = &td;
    pTD->m_symbolFilterEnable = 0;
    const char* pszStartSymbol;
    const char* pszEndSymbol;
    int listedExg=0;
    if (!PyArg_ParseTuple(args, "ss|i", &pszStartSymbol,&pszEndSymbol,&listedExg)) {
        Py_RETURN_FALSE;
    }
    pTD->m_StartSymbol = std::string(pszStartSymbol);
    pTD->m_EndSymbol = std::string(pszEndSymbol);
    pTD->m_FilterListedExg = listedExg;
    pTD->m_symbolFilterEnable = 3;
    Py_RETURN_TRUE;
}

PyObject* NxCorePython_GetMajorVersion(PyObject* self, PyObject* args) {
    int APIVersion;
    if (!PyArg_ParseTuple(args, "i", &APIVersion)) {
        Py_RETURN_FALSE;
    }
    return PyLong_FromLong((APIVersion) >> 24);
}
PyObject* NxCorePython_GetMinorVersion(PyObject* self, PyObject* args) {
    int APIVersion;
    if (!PyArg_ParseTuple(args, "i", &APIVersion)) {
        Py_RETURN_FALSE;
    }
    return PyLong_FromLong((APIVersion >> 16) & 0x000000ff);
}
PyObject* NxCorePython_GetBuildVersion(PyObject* self, PyObject* args) {
    int APIVersion;
    if (!PyArg_ParseTuple(args, "i", &APIVersion)) {
        Py_RETURN_FALSE;
    }
    return PyLong_FromLong(APIVersion & 0x0000ffff);
}

PyObject* NxCorePython_RemovePermission(PyObject* self, PyObject* args) {
    int permissionID;
    if (!PyArg_ParseTuple(args, "i", &permissionID)) {
        Py_RETURN_FALSE;
    }
    return PyLong_FromLong(NxCore.ExcludeOpraExch(NxPERMID_REMOVE | permissionID));
}

PyDoc_STRVAR(NxCorePython_StateGetLastTrade_doc, "StateGetLastTrade(ListedExg, Symbol[, Expiration_NDays, StrikePrice, PutOrCall]) -> NxCoreStateTrade\n\
Returns the current Memory State of a Symbol's Open, High, Low, Last, Volume Data.\nCan only be called from inside your NxCoreCallback function.");
PyDoc_STRVAR(NxCorePython_StateGetMMQuotes_doc, "StateGetMMQuotes(ReportingExg, ListedExg, Symbol[, Expiration_NDays, StrikePrice, PutOrCall]) -> NxCoreStateMMQuotes\n\
Returns the current Memory State of a Symbol's Market Marker Quotes.\nCan only be called from inside your NxCoreCallback function.");
PyDoc_STRVAR(NxCorePython_StateGetExgQuotes_doc, "StateGetExgQuotes(ListedExg, Symbol[, Expiration_NDays, StrikePrice, PutOrCall]) -> NxCoreStateExgQuotes\n\
Returns the current Memory State of a Symbol's Exchange Quotes.\nCan only be called from inside your NxCoreCallback function.");
PyDoc_STRVAR(NxCorePython_SpinSymbols_doc, "SpinSymbols(ListedExg, controlFlags, spinID, SpinCallback, tempUserData)\n\
Starts a SymbolSpin which iterates over each symbol in each of the requested Listed Exchanges.\n\
ListedExg:    Specify the ListedExchange that you wish to iterate or use NxSPIN_ALL_EXGS(-1) to iterate all ListedExchanges\n\
controlFlags: NxSPIN_NON_OPTIONS(0) for regular symbols or NxSPIN_OPTIONS(1) for options. To iterate both symbols and options, call this function twice, passing 0 and 1 for this parameter.\n\
spinID:       Assigned to the NxCoreMessage.coreData.SymbolSpin.SpinID and is reserved for your use.\n\
SpinCallback: Callback function to be used during symbol spin. Parameters are NxCoreSystem and NxCoreMessage.\n\
tempUserData: Temporarily assigned to NxCoreSystem.UserData during callback. NxCoreSystem.UserData can be changed during the callback");
PyDoc_STRVAR(NxCorePython_FilterSymbols_doc, "FilterSymbols([Symbol1,Symbol2,Symbol3,...])\nFilterSymbols([(Symbol1,ListedExg1),(Symbol2,ListedExg2),(Symbol3,ListedExg3),...])\n\
Only sends selected symbols to callback, improving performance. Must be called prior to ProcessTape.\n\
Arguements can either be a list of symbol strings. or a list of tuples of a symbol string and listed exchange. Symbols should be in NxCore format");
PyDoc_STRVAR(NxCorePython_ExcludeSymbols_doc, "ExcludeSymbols([Symbol1,Symbol2,Symbol3,...])\nFilterSymbols([(Symbol1,ListedExg1),(Symbol2,ListedExg2),(Symbol3,ListedExg3),...])\n\
Blocks selected symbols from being sent to callback, improving performance. Must be called prior to ProcessTape.\n\
Arguements can either be a list of symbol strings. or a list of tuples of a symbol string and listed exchange. Symbols should be in NxCore format");
PyDoc_STRVAR(NxCorePython_FilterSymbolsRange_doc, "FilterSymbolsRange(StartSymbol,EndSymbol[,ListedExg])\n\
Sets alphabetical range of symbols to send to callback, improving performance. Can only be called once per thread. Must be called prior to ProcessTape.\n\
StartSymbol: First symbol in range to process. Can be an empty string for no starting symbol.\n\
EndSymbol: Last symbol in range to process. Can be an empty string for no ending symbol.\n\
ListedExg: Only send specified listed exchange to callback. Default all listed exchanges.");
PyDoc_STRVAR(NxCorePython_ListAPIDLLs_doc, "ListAPIDLLs(controlFlags, apiCallback)\n\
Searches computer for NxCoreAPI .dll files on windows or .so files on linux\
controlFlags: should be 0. reserved for future use.\n\
apiCallback:  Callback function. Parameter is NxCoreAPIDLLFile.");
PyDoc_STRVAR(NxCorePython_ListTapes_doc, "ListTapes(controlFlags, apiCallback)\n\
Searches computer for NxCore tape files\
controlFlags: NxLTF_SEARCH_LOCAL(0) to search local drives. NxLTF_SEARCH_NETWORK(1) to search local and network drives. NxLTF_SEARCH_REMOVEABLE(2) to search local and removable drives\n\
apiCallback:  Callback function. Parameter is NxCoreTapeFile.");
PyDoc_STRVAR(NxCorePython_ProcessReturnValue_doc, "ProcessReturnValue(returnValue)\n\
Prints string corresponding to the return value from processTape()");
PyDoc_STRVAR(NxCorePython_PriceFormat_doc, "PriceFormat(Price, PriceType[, expandWidth[, bCommas]]) -> str\n\
Returns string of price.\n\
Price:       Price to format.\n\
PriceType:   PriceType that Price is using.\n\
expandWidth: Minimum length of string. will space pad. default 0.\n\
bCommas:     Use comma to separate thousands.");
PyDoc_STRVAR(NxCorePython_PriceConvert_doc, "PriceConvert(Price, PriceType, PriceTypeNew) -> int\n\
Returns a price converted from one price type to another.\n\
Price:        Price to be converted\n\
PriceType:    Original PriceType.\n\
PriceTypeNew: Price Type to convert iPrice into.");
PyDoc_STRVAR(NxCorePython_PriceToFloat_doc, "PriceToFloat(Price, PriceType) -> float\n\
Returns floating point price from integer Price and integer PriceType. Alias of PriceToDouble. Implented to match Python floating point type name.");
PyDoc_STRVAR(NxCorePython_PriceToDouble_doc, "PriceToDouble(Price, PriceType) -> float\n\
Returns floating point price from integer Price and integer PriceType..");
PyDoc_STRVAR(NxCorePython_GetTapeName_doc, "GetTapeName() -> str\n\
Returns the full path filename of the NxCore tape being processed. Returns empty string if processing real-time. Returns path to tape if processing state file.");
PyDoc_STRVAR(NxCorePython_GetSymbolAtom32_doc, "GetSymbolAtom32() -> int\n\
Returns the full atom when the value in the NxString is NxATOM_MAX_16BIT(65535)");
PyDoc_STRVAR(NxCorePython_ExcludeOpraExch_doc, "ExcludeOpraExch(removeKey)\n\
Removes a permission ID from being processed from the tape. This can significantly improve performance.\n\
removeKey: This key is a bitwise OR combination of a control flag and a permission ID. \n\
           The control flag to remove a permission ID is NxPERMID_REMOVE(0x00100000).\n\
           A list of permission IDs can be found in the NxST_PERMID string table.");
PyDoc_STRVAR(NxCorePython_GetDefinedString_doc, "GetDefinedString(ixTable,ixString) -> str\n\
Converts a string index into string.\n\
NOTE: These strings are a part of the tape, not the dll.\n\
NOTE: Defined strings are loaded after NxCORESTATUS_INITIALIZING status callback.\n\
ixTable:  Identifier of table. For possible values see: http://nxcoreapi.com/doc/reference_Tables.html \n\
ixString: A zero-based number that indexes into one of the String Tables.");
PyDoc_STRVAR(NxCorePython_SaveState_doc, "SaveState(stateFilename,controlFlags)\n\
Creates a NxCore Tape \"Bookmark\" that ProcessTape can use as a starting point. Returns 0 on success or -12 if state is currently being saved.\n\
stateFilename: Filename of state file to create.\n\
controlFlags:  NxSAVESTATE_GRADUALLY(0) Save completes in multiple passes - NxCore writes out memory block to file in 1MB increments.\n\
               NxSAVESTATE_ONEPASS(1) Save completes in one pass - NxCore writes out entire memory block to file at one time.\n\
               NxSAVESTATE_CANCEL(2) Abort a scheduled Save State -- can be done at NxCSSAVESTATE_CAPTURE time ");
PyDoc_STRVAR(NxCorePython_APIVersion_doc, "APIVersion() -> int \n\
Returns the NxCore API version expressed as a 4 byte integer. GetMajorVersion, GetMinorVersion,and GetBuildVersion functions can be used to compute the major, minor, and build values. This is not the version of the Python wrapper.");
PyDoc_STRVAR(NxCorePython_UnLoadNxCore_doc, "UnloadNxCore()\n\
Unloads the NxCore Library.");
PyDoc_STRVAR(NxCorePython_LoadNxCore_doc, "LoadNxCore(NxCoreFilename) -> int\n\
Loads the NxCore dynamic library. Returns version of NxCoreAPI if successful, 0 on failure");
PyDoc_STRVAR(NxCorePython_DateFromNDays_doc, "DateFromNDays(NxDate) -> boolean\n\
Using a populated NDays member, populates all other fields.\n\
This function modifies the object passed in. Returns True on success or False on Failure");
PyDoc_STRVAR(NxCorePython_DateFromYMD_doc, "DateFromYMD(NxDate) -> boolean\n\
Using populated Year, Month, and Day members, populates all other fields.\n\
This function modifies the object passed in. Returns True on success or False on Failure");
PyDoc_STRVAR(NxCorePython_ProcessTape_doc, "ProcessTape(filename,baseMemory,controlFlags,userData,callbackFunction)\n\
Starts processing NxCore data. Returns 0 after successfully completing a day.\n\
filename:         The full path filename name of the NxCore Tape to process. Alternatively an empty string(\"\") will process the real-time stream.\n\
baseMemory:       Specifies the base memory address for NxCore to process data. It is recommended to use 0.\n\
controlFlags:     bitwise flags to control processing of tape.\n\
                  NxCF_EXCLUDE_QUOTES(0x00000001) Exclude Exchange Quotes\n\
                  NxCF_EXCLUDE_QUOTES2(0x00000002) Exclude Market Maker Quotes\n\
                  NxCF_EXCLUDE_OPRA(0x00000004) Exclude all options from OPRA\n\
                  NxCF_EXCLUDE_QUOTES(0x04000000)  If set, memory is allocated (if possible) at the address specified by the 2nd parameter to ProcessTape (baseMemory)\n\
                  NxCF_EXCLUDE_QUOTES(0x08000000) Exclude CRC checking\n\
userData:         The value of user data is copied to NxCoreSystem.UserData and is available to reference or change during your callback function.\n\
callbackFunction: The callback function that will be called for all messages when processing the NxCore Tape.\n\
                  Parameters are NxCoreSystem and NxCoreMessage.");
PyDoc_STRVAR(NxCorePython_GetMajorVersion_doc, "GetMajorVersion(version) -> int\n\
Return the major version from 4 byte version");
PyDoc_STRVAR(NxCorePython_GetMinorVersion_doc, "GetMinorVersion(version) -> int\n\
Return the minor version from 4 byte version");
PyDoc_STRVAR(NxCorePython_GetBuildVersion_doc, "GetBuildVersion(version) -> int\n\
Return the build version from 4 byte version");
PyDoc_STRVAR(NxCorePython_RemovePermission_doc, "RemovePermission(permissionID) -> int\n\
Removes a permission ID from being processed from the tape. This can significantly improve performance.\n\
permissionID: permission ID to be removed.\n\
              A list of permission IDs can be found in the NxST_PERMID string table.");

/*
 * List of functions to add to NxCorePython in exec_NxCorePython().
 */
static PyMethodDef NxCorePython_functions[] = {
    { "StateGetLastTrade", (PyCFunction)NxCorePython_StateGetLastTrade, METH_VARARGS , NxCorePython_StateGetLastTrade_doc },
    { "StateGetMMQuotes", (PyCFunction)NxCorePython_StateGetMMQuotes, METH_VARARGS , NxCorePython_StateGetMMQuotes_doc },
    { "StateGetExgQuotes", (PyCFunction)NxCorePython_StateGetExgQuotes, METH_VARARGS , NxCorePython_StateGetExgQuotes_doc },
    { "SpinSymbols", (PyCFunction)NxCorePython_SpinSymbols, METH_VARARGS , NxCorePython_SpinSymbols_doc },
    { "ListAPIDLLs", (PyCFunction)NxCorePython_ListAPIDLLs, METH_VARARGS , NxCorePython_ListAPIDLLs_doc },
    { "ListTapes", (PyCFunction)NxCorePython_ListTapes, METH_VARARGS , NxCorePython_ListTapes_doc },
    { "ProcessReturnValue", (PyCFunction)NxCorePython_ProcessReturnValue, METH_VARARGS , NxCorePython_ProcessReturnValue_doc },
    { "PriceFormat", (PyCFunction)NxCorePython_PriceFormat, METH_VARARGS , NxCorePython_PriceFormat_doc },
    { "PriceConvert", (PyCFunction)NxCorePython_PriceConvert, METH_VARARGS , NxCorePython_PriceConvert_doc },
    { "PriceToDouble", (PyCFunction)NxCorePython_PriceToDouble, METH_VARARGS , NxCorePython_PriceToDouble_doc },
    { "PriceToFloat", (PyCFunction)NxCorePython_PriceToDouble, METH_VARARGS , NxCorePython_PriceToFloat_doc },
    { "GetTapeName", (PyCFunction)NxCorePython_GetTapeName, METH_NOARGS , NxCorePython_GetTapeName_doc },
    { "GetSymbolAtom32", (PyCFunction)NxCorePython_GetSymbolAtom32, METH_NOARGS , NxCorePython_GetSymbolAtom32_doc },
    { "ExcludeOpraExch", (PyCFunction)NxCorePython_ExcludeOpraExch, METH_VARARGS , NxCorePython_ExcludeOpraExch_doc },
    { "GetDefinedString", (PyCFunction)NxCorePython_GetDefinedString, METH_VARARGS , NxCorePython_GetDefinedString_doc },
    { "SaveState", (PyCFunction)NxCorePython_SaveState, METH_VARARGS , NxCorePython_SaveState_doc },
    { "APIVersion", (PyCFunction)NxCorePython_APIVersion, METH_NOARGS, NxCorePython_APIVersion_doc },
    { "UnloadNxCore", (PyCFunction)NxCorePython_UnloadNxCore, METH_NOARGS, NxCorePython_UnLoadNxCore_doc },
    { "LoadNxCore", (PyCFunction)NxCorePython_LoadNxCore, METH_VARARGS | METH_KEYWORDS, NxCorePython_LoadNxCore_doc },
    { "ProcessTape", (PyCFunction)NxCorePython_ProcessTape, METH_VARARGS | METH_KEYWORDS, NxCorePython_ProcessTape_doc },
    { "DateFromNDays", (PyCFunction)NxCorePython_DateFromNDays, METH_VARARGS, NxCorePython_DateFromNDays_doc },
    { "DateFromYMD", (PyCFunction)NxCorePython_DateFromYMD, METH_VARARGS, NxCorePython_DateFromYMD_doc },
    { "FilterSymbols", (PyCFunction)NxCorePython_FilterSymbols, METH_VARARGS , NxCorePython_FilterSymbols_doc },
    { "ExcludeSymbols", (PyCFunction)NxCorePython_ExcludeSymbols, METH_VARARGS , NxCorePython_ExcludeSymbols_doc },
    { "FilterSymbolsRange", (PyCFunction)NxCorePython_FilterSymbolsRange, METH_VARARGS , NxCorePython_FilterSymbolsRange_doc },
    { "GetMajorVersion", (PyCFunction)NxCorePython_GetMajorVersion, METH_VARARGS , NxCorePython_GetMajorVersion_doc },
    { "GetMinorVersion", (PyCFunction)NxCorePython_GetMinorVersion, METH_VARARGS , NxCorePython_GetMinorVersion_doc },
    { "GetBuildVersion", (PyCFunction)NxCorePython_GetBuildVersion, METH_VARARGS , NxCorePython_GetBuildVersion_doc },
    { "RemovePermission", (PyCFunction)NxCorePython_RemovePermission, METH_VARARGS , NxCorePython_RemovePermission_doc },
    { NULL, NULL, 0, NULL } /* marks end of array */
};

/*
 * Initialize NxCorePython. May be called multiple times, so avoid
 * using static state.
 */
int exec_NxCorePython(PyObject *module) {
    PyModule_AddFunctions(module, NxCorePython_functions);
    if (PyType_Ready(&NxDateType) < 0)
        return 1;
    if (PyType_Ready(&NxTimeType) < 0)
        return 1;
    if (PyType_Ready(&NxPriceType) < 0)
        return 1;
    if (PyType_Ready(&StringTableItemType) < 0)
        return 1;
    if (PyType_Ready(&NxCoreSystemType) < 0)
        return 1;
    if (PyType_Ready(&NxCoreDataType) < 0)
        return 1;
    if (PyType_Ready(&NxStringType) < 0)
        return 1;
    if (PyType_Ready(&NxOptionHdrType) < 0)
        return 1;
    if (PyType_Ready(&NxCoreHeaderType) < 0)
        return 1;
    if (PyType_Ready(&NxCore3ExtType) < 0)
        return 1;
    if (PyType_Ready(&NxCoreMessageType) < 0)
        return 1;
    if (PyType_Ready(&NxCoreQuoteType) < 0)
        return 1;
    if (PyType_Ready(&NxCoreExgQuoteType) < 0)
        return 1;
    if (PyType_Ready(&NxCoreMMQuoteType) < 0)
        return 1;
    if (PyType_Ready(&NxCTAnalysisType) < 0)
        return 1;
    if (PyType_Ready(&NxCoreTradeType) < 0)
        return 1;
    if (PyType_Ready(&NxCoreSymbolChangeType) < 0)
        return 1;
    if (PyType_Ready(&NxCoreSymbolSpinType) < 0)
        return 1;
    if (PyType_Ready(&NxCoreCategoryType) < 0)
        return 1;
    if (PyType_Ready(&NxCoreCategoryDataType) < 0)
        return 1;
    if (PyType_Ready(&NxCategoryFieldType) < 0)
        return 1;
    if (PyType_Ready(&DefinesType) < 0)
        return 1;
    if (PyType_Ready(&NxCoreStateExgQuoteType) < 0)
        return 1;
    if (PyType_Ready(&NxCoreStateMMQuoteType) < 0)
        return 1;
    if (PyType_Ready(&NxCoreStateExgQuotesType) < 0)
        return 1;
    if (PyType_Ready(&NxCoreStateMMQuotesType) < 0)
        return 1;
    if (PyType_Ready(&NxCoreStateTradeType) < 0)
        return 1;
    if (PyType_Ready(&NxCoreTapeFileType) < 0)
        return 1;
    if (PyType_Ready(&NxCoreAPIDLLFileType) < 0)
        return 1;
    PyModule_AddObject(module, "NxDate", (PyObject*)&NxDateType);
    PyModule_AddObject(module, "NxTime", (PyObject*)&NxTimeType);
    PyModule_AddObject(module, "NxPrice", (PyObject*)&NxPriceType);
    PyModule_AddObject(module, "StringTableItem", (PyObject*)&StringTableItemType);
    PyModule_AddObject(module, "NxCoreSystem", (PyObject*)&NxCoreSystemType);
    PyModule_AddObject(module, "NxCoreData", (PyObject*)&NxCoreDataType);
    PyModule_AddObject(module, "NxString", (PyObject*)&NxStringType);
    PyModule_AddObject(module, "NxOptionHdr", (PyObject*)&NxOptionHdrType);
    PyModule_AddObject(module, "NxCoreHeader", (PyObject*)&NxCoreHeaderType);
    PyModule_AddObject(module, "NxCore3Ext", (PyObject*)&NxCore3ExtType);
    PyModule_AddObject(module, "NxCoreMessage", (PyObject*)&NxCoreMessageType);
    PyModule_AddObject(module, "NxCoreQuote", (PyObject*)&NxCoreQuoteType);
    PyModule_AddObject(module, "NxCoreExgQuote", (PyObject*)&NxCoreExgQuoteType);
    PyModule_AddObject(module, "NxCoreMMQuote", (PyObject*)&NxCoreMMQuoteType);
    PyModule_AddObject(module, "NxCTAnalysis", (PyObject*)&NxCTAnalysisType);
    PyModule_AddObject(module, "NxCoreTrade", (PyObject*)&NxCoreTradeType);
    PyModule_AddObject(module, "NxCoreSymbolChange", (PyObject*)&NxCoreSymbolChangeType);
    PyModule_AddObject(module, "NxCoreSymbolSpin", (PyObject*)&NxCoreSymbolSpinType);
    PyModule_AddObject(module, "NxCoreCategory", (PyObject*)&NxCoreCategoryType);
    PyModule_AddObject(module, "NxCoreCategoryData", (PyObject*)&NxCoreCategoryDataType);
    PyModule_AddObject(module, "NxCategoryField", (PyObject*)&NxCategoryFieldType);
    PyModule_AddObject(module, "NxCoreStateExgQuote", (PyObject*)&NxCoreStateExgQuoteType);
    PyModule_AddObject(module, "NxCoreStateMMQuote", (PyObject*)&NxCoreStateMMQuoteType);
    PyModule_AddObject(module, "NxCoreStateExgQuotes", (PyObject*)&NxCoreStateExgQuotesType);
    PyModule_AddObject(module, "NxCoreStateMMQuotes", (PyObject*)&NxCoreStateMMQuotesType);
    PyModule_AddObject(module, "NxCoreStateTrade", (PyObject*)&NxCoreStateTradeType);
    PyModule_AddObject(module, "NxCoreTapeFile", (PyObject*)&NxCoreTapeFileType);
    PyModule_AddObject(module, "NxCoreAPIDLLFile", (PyObject*)&NxCoreAPIDLLFileType);
    PyModule_AddStringConstant(module, "__author__", "Nanex LLC");
    PyModule_AddIntConstant(module, "year", 2026);
    AddDefineObjects(module);

    return 0; /* success */
}

/*
 * Documentation for NxCorePython.
 */
PyDoc_STRVAR(NxCorePython_doc, "The NxCore Python module");


static PyModuleDef_Slot NxCorePython_slots[] = {
    { Py_mod_exec,(void*) exec_NxCorePython },
    { 0, NULL }
};

static PyModuleDef NxCorePython_def = {
    PyModuleDef_HEAD_INIT,
    "NxCorePython",
    NxCorePython_doc,
    0,              /* m_size */
    NULL,           /* m_methods */
    NxCorePython_slots,
    NULL,           /* m_traverse */
    NULL,           /* m_clear */
    NULL,           /* m_free */
};
PyMODINIT_FUNC PyInit_NxCore() {
    return PyModuleDef_Init(&NxCorePython_def);
}
