/** * Method invoked when we stop processing the last level in a message. We * invoke this method after the last entry for the level gets processed. * The subscription may be destroyed from this callback. */ void onBookAtomicEndBook ( MamdaSubscription* subscription, MamdaBookAtomicListener& listener) { /** * When level recap/delta is received, it is stored in a MamdaOrderBookPriceLevel. * If no entry deltas/recaps are received, then the price level should be applied * to the book. * The entry delta/recap callback will mark the price level with an UNKNOWN side to * show that it does not need to be applied to the book */ if(mReusablePriceLevel.getSide() != MamdaOrderBookPriceLevel::MAMDA_BOOK_SIDE_UNKNOWN) { if (gExampleLogLevel == EXAMPLE_LOG_LEVEL_NORMAL && mOrderBook.getQuality() == MAMA_QUALITY_OK) { prettyPrint(subscription, mReusablePriceLevel); } applyLevel(mReusablePriceLevel); mReusablePriceLevel.setSide(MamdaOrderBookPriceLevel::MAMDA_BOOK_SIDE_UNKNOWN); } if(mOrderBook.getQuality() == MAMA_QUALITY_OK && gExampleLogLevel == EXAMPLE_LOG_LEVEL_NORMAL) { prettyPrint(mOrderBook); } cout<< "BOOK END\n"; }
/** * Method invoked when an order book delta is reported. */ void onBookAtomicLevelDelta ( MamdaSubscription* subscription, MamdaBookAtomicListener& listener, const MamaMsg& msg, const MamdaBookAtomicLevel& level) { /** * The level should only be processed when entires are not received * i.e. for level only based feeds * If an entry was received on the previous level, then the level will * have been marked with an UNKNOWN side and should not be applied to * the book */ if(mReusablePriceLevel.getSide() != MamdaOrderBookPriceLevel::MAMDA_BOOK_SIDE_UNKNOWN) { if (gExampleLogLevel == EXAMPLE_LOG_LEVEL_NORMAL && mOrderBook.getQuality() == MAMA_QUALITY_OK) { prettyPrint(subscription, mReusablePriceLevel); } applyLevel(mReusablePriceLevel); } /** * Store the current level */ storeLevel(level); }
/* * Helper function to print a MamdaOrderBookPriceLevel */ void prettyPrint( MamdaSubscription* subscription, const MamdaOrderBookPriceLevel& level) { // Print Entry Level Info const char* symbol = subscription->getSymbol (); const char* time = level.getTime().getTimeAsString(); double price = level.getPrice (); char side = level.getSide(); char action = level.getAction (); double size = level.getSize (); cout << "LEVEL " << symbol << " " << time << " " << action << " " << price << " " << side << " " << size << endl; }
void prettyPrint (const MamdaOrderBookBasicDelta& delta) { char timeStr[32]; MamdaOrderBookEntry* entry = delta.getEntry(); MamdaOrderBookPriceLevel* level = delta.getPriceLevel (); mama_quantity_t size = delta.getPlDeltaSize (); MamdaOrderBookPriceLevel::Action action = delta.getPlDeltaAction(); if (MamdaOrderBookPriceLevel::MAMDA_BOOK_LEVEL_LIMIT == level->getOrderType()) { double price = level->getPrice (); printf (" %7g %7.*f %c \n", size, mPrecision, price, action); } else { printf (" %7g MARKET %c \n",size, action); } }
/** * Method invoked when a full refresh of the order book for the * security is available. The reason for the invocation may be * any of the following: * - Initial image. * - Recap update (e.g., after server fault tolerant event or data * quality event.) * - After stale status removed. */ void onBookAtomicLevelEntryRecap ( MamdaSubscription* subscription, MamdaBookAtomicListener& listener, const MamaMsg& msg, const MamdaBookAtomicLevelEntry& levelEntry) { applyEntry(levelEntry); /** * An entry has been processed on the level so mark the level with * an unknown side so that it will not be applied to book */ mReusablePriceLevel.setSide (MamdaOrderBookPriceLevel::MAMDA_BOOK_SIDE_UNKNOWN); }
/** * Helper function to store a MamdaBookAtomicLevel in * the resuabale MamdaOrderBookPriceLevel */ void storeLevel(const MamdaBookAtomicLevel& level) { mReusablePriceLevel.clear(); mReusablePriceLevel.setPrice (level.getPriceLevelPrice()); mReusablePriceLevel.setSide ((MamdaOrderBookPriceLevel::Side)level.getPriceLevelSide()); mReusablePriceLevel.setTime (level.getPriceLevelTime()); /** * As per the MAMDA Developers Guide, the following three accessors on a MamdaBookAtomicLevel * object should not be used for V5 entry updates. Here, these calls are used and * the resulting MamdaOrderBookPriceLevel is only used when the callbacks received * indicate that the update was not a V5 entry update. */ mReusablePriceLevel.setSize (level.getPriceLevelSize()); mReusablePriceLevel.setAction ((MamdaOrderBookPriceLevel::Action) level.getPriceLevelAction()); mReusablePriceLevel.setNumEntries ((mama_u32_t)level.getPriceLevelNumEntries()); }
/** * Method invoked when an order book delta is reported. */ void onBookAtomicLevelEntryDelta ( MamdaSubscription* subscription, MamdaBookAtomicListener& listener, const MamaMsg& msg, const MamdaBookAtomicLevelEntry& levelEntry) { if (gExampleLogLevel == EXAMPLE_LOG_LEVEL_NORMAL && mOrderBook.getQuality() == MAMA_QUALITY_OK) { prettyPrint (subscription, levelEntry); } applyEntry (levelEntry); /** * An entry has been processed on the level so mark the level with * an unknown side so that it will not be applied to book */ mReusablePriceLevel.setSide (MamdaOrderBookPriceLevel::MAMDA_BOOK_SIDE_UNKNOWN); }
/** * Method invoked when a full refresh of the order book for the * security is available. The reason for the invocation may be * any of the following: * - Initial image. * - Recap update (e.g., after server fault tolerant event or data * quality event.) * - After stale status removed. */ void onBookAtomicLevelRecap ( MamdaSubscription* subscription, MamdaBookAtomicListener& listener, const MamaMsg& msg, const MamdaBookAtomicLevel& level) { /** * The level should only be processed when entires are not received * i.e. for level only based feeds * If an entry was received on the previous level, then the level will * have been marked with an UNKNOWN side and should not be applied to * the book */ if(mReusablePriceLevel.getSide() != MamdaOrderBookPriceLevel::MAMDA_BOOK_SIDE_UNKNOWN) { applyLevel (mReusablePriceLevel); } /** * Store the current level */ storeLevel(level); }
void MamdaQuoteToBookListenerImpl::handleQuote (MamdaSubscription* subscription, const MamaMsg& msg) { checkQuoteCount (subscription, msg); MamdaOrderBookPriceLevel* level = NULL; if (mQuoteCache.mGotBidSize || mQuoteCache.mGotBidPrice) { // get current level MamdaOrderBook::bidIterator bidIter = mFullBook->bidBegin(); if (bidIter == mFullBook->bidEnd()) level = NULL; else level = *bidIter; if (level == NULL) { if (mQuoteCache.mBidSize == (mama_quantity_t)0 || mQuoteCache.mBidPrice.isZero()) { if (mQuoteCache.mBidSize == (mama_quantity_t)0 && mQuoteCache.mBidPrice.isZero()) { mama_log (MAMA_LOG_LEVEL_WARN, "MamdaQuoteToBookListener: Got bid update, but price and size are 0\n"); } } else { addLevel (level, mQuoteCache.mBidPrice.getValue(), mQuoteCache.mBidSize, MamdaOrderBookPriceLevel::MAMDA_BOOK_SIDE_BID, mQuoteCache.mQuoteTime); } } else { if ((mQuoteCache.mBidSize == 0) || (mQuoteCache.mBidPrice.isZero())) { deleteLevel (level, MamdaOrderBookPriceLevel::MAMDA_BOOK_SIDE_BID, mQuoteCache.mQuoteTime); } else if (mQuoteCache.mBidPrice == level->getPrice()) { updateLevel (level, mQuoteCache.mBidSize, mQuoteCache.mBidSize - level->getSize(), MamdaOrderBookPriceLevel::MAMDA_BOOK_SIDE_BID, mQuoteCache.mQuoteTime); } else { deleteLevel (level, MamdaOrderBookPriceLevel::MAMDA_BOOK_SIDE_BID, mQuoteCache.mQuoteTime); addLevel (level, mQuoteCache.mBidPrice.getValue(), mQuoteCache.mBidSize, MamdaOrderBookPriceLevel::MAMDA_BOOK_SIDE_BID, mQuoteCache.mQuoteTime); } } } if (mQuoteCache.mGotAskSize || mQuoteCache.mGotAskPrice) { // get current level MamdaOrderBook::askIterator askIter = mFullBook->askBegin(); if (askIter == mFullBook->askEnd()) level = NULL; else level = *askIter; if (level == NULL) { if (mQuoteCache.mAskSize == (mama_quantity_t)0 || mQuoteCache.mAskPrice.isZero()) { if (mQuoteCache.mAskSize == (mama_quantity_t)0 && mQuoteCache.mAskPrice.isZero()) { mama_log (MAMA_LOG_LEVEL_WARN, "MamdaQuoteToBookListener: Got ask update, but price and size are 0\n"); } } else { addLevel (level, mQuoteCache.mAskPrice.getValue(), mQuoteCache.mAskSize, MamdaOrderBookPriceLevel::MAMDA_BOOK_SIDE_ASK, mQuoteCache.mQuoteTime); } } else { if ((mQuoteCache.mAskSize == 0) || (mQuoteCache.mAskPrice.isZero())) { deleteLevel (level, MamdaOrderBookPriceLevel::MAMDA_BOOK_SIDE_ASK, mQuoteCache.mQuoteTime); } else if (mQuoteCache.mAskPrice == level->getPrice()) { updateLevel (level, mQuoteCache.mAskSize, mQuoteCache.mAskSize - level->getSize(), MamdaOrderBookPriceLevel::MAMDA_BOOK_SIDE_ASK, mQuoteCache.mQuoteTime); } else { deleteLevel (level, MamdaOrderBookPriceLevel::MAMDA_BOOK_SIDE_ASK, mQuoteCache.mQuoteTime); addLevel (level, mQuoteCache.mAskPrice.getValue(), mQuoteCache.mAskSize, MamdaOrderBookPriceLevel::MAMDA_BOOK_SIDE_ASK, mQuoteCache.mQuoteTime); } } } if (mUpdateInconsistentBook || mFullBook->getIsConsistent()) { invokeDeltaHandlers (subscription, &msg); } }
void MamdaOrderBookPriceLevel::setAsDifference ( const MamdaOrderBookPriceLevel& lhs, const MamdaOrderBookPriceLevel& rhs) { assert (lhs.getPrice() == rhs.getPrice()); MamdaOrderBookPriceLevel::iterator lhsEnd = end(); MamdaOrderBookPriceLevel::iterator lhsIter = begin(); MamdaOrderBookPriceLevel::iterator rhsEnd = end(); MamdaOrderBookPriceLevel::iterator rhsIter = begin(); while ((lhsIter != lhsEnd) && (rhsIter != rhsEnd)) { const char* lhsId = NULL; const char* rhsId = NULL; mama_quantity_t lhsSize = 0.0; mama_quantity_t rhsSize = 0.0; if (lhsIter != lhsEnd) { const MamdaOrderBookEntry* entry = *lhsIter; lhsId = entry->getId(); lhsSize = entry->getSize(); } if (rhsIter != rhsEnd) { const MamdaOrderBookEntry* entry = *rhsIter; rhsId = entry->getId(); rhsSize = entry->getSize(); } if (lhsId && rhsId) { if (strcmp(lhsId,rhsId)==0) { // Same ID, maybe different size. if (mama_isQuantityEqual (lhsSize, rhsSize)) { MamdaOrderBookEntry* updateEntry = new MamdaOrderBookEntry (**rhsIter); updateEntry->setAction( MamdaOrderBookEntry::MAMDA_BOOK_ACTION_UPDATE); addEntry (updateEntry); } ++lhsIter; ++rhsIter; continue; } else { // Different ID (either something exists on the LHS // and not on RHS or vice versa). MamdaOrderBookPriceLevel::const_iterator rhsFound = findEntryAfter (rhsIter, lhsId); if (rhsFound != rhsEnd) { // The ID from the LHS was found on the RHS, so // there must have been additional entries on the // RHS, which we now need to add. do { MamdaOrderBookEntry* entry = new MamdaOrderBookEntry(**rhsIter); addEntry (entry); ++rhsIter; } while (rhsIter != rhsFound); } else { // The ID from the LHS was not present on the RHS, // so add the LHS entry. Note: it would probably // be faster to iterate over the LHS side rather // than begin the loop again. MamdaOrderBookEntry* entry = new MamdaOrderBookEntry(**lhsIter); addEntry (entry); ++lhsIter; } } } } setPrice (rhs.getPrice()); setSizeChange (rhs.getSize() - lhs.getSize()); setSize (rhs.getSize()); setNumEntries (rhs.getNumEntries()); setAction (MamdaOrderBookPriceLevel::MAMDA_BOOK_ACTION_UPDATE); setTime (rhs.getTime()); setSide (rhs.getSide()); }
void BookPublisher::processOrder () { MamdaOrderBookPriceLevel* level = NULL; MamdaOrderBookEntry* entry = NULL; order thisOrder = orderArray[mOrderCount]; mBookTime.setToNow(); if (mProcessEntries) { switch (thisOrder.entAction) { case ENTDELETE: { level = mBook->getLevelAtPrice (thisOrder.price, thisOrder.side); if (level) entry = level->findEntry (thisOrder.entId); if (entry) mBook->deleteEntry (entry, mBookTime, NULL); break; } case ENTADD: { mBook->addEntry (thisOrder.entId, thisOrder.entSize, thisOrder.price, thisOrder.side, mBookTime, NULL, NULL); break; } case ENTUPDATE: { entry = level->findEntry (thisOrder.entId); mBook->updateEntry (entry, thisOrder.entSize, mBookTime, NULL); break; } } } else { level = mBook->getLevelAtPrice(thisOrder.price, thisOrder.side); if (level) { level->setSizeChange (thisOrder.sizeChange); level->setPrice (thisOrder.price); level->setSize (thisOrder.volume); level->setNumEntries (thisOrder.numEntries); level->setTime (mBookTime); level->setAction (thisOrder.plAction); } else { level = new MamdaOrderBookPriceLevel(); level->setSide (thisOrder.side); level->setSizeChange (thisOrder.sizeChange); level->setPrice (thisOrder.price); level->setSize (thisOrder.volume); level->setNumEntries (thisOrder.numEntries); level->setTime (mBookTime); level->setAction (thisOrder.plAction); } switch (thisOrder.plAction) { case PLDELETE: mBook->deleteLevel(*level); break; case PLADD: mBook->addLevel(*level); break; case PLUPDATE: mBook->updateLevel(*level); break; } } mOrderCount++; }
/** * Helper function to apply a MamdaBookAtomicLevelEntry to * a MamdaOrderBook */ void applyEntry (const MamdaBookAtomicLevelEntry& levelEntry) { MamdaOrderBookEntry::Action entryAction; if(mOrderBook.getQuality() == MAMA_QUALITY_OK) { entryAction = (MamdaOrderBookEntry::Action) levelEntry.getPriceLevelEntryAction(); switch(entryAction) { case MamdaOrderBookEntry::MAMDA_BOOK_ACTION_UPDATE : try { //get the price level by price mLevelPtr = mOrderBook.findLevel( levelEntry.getPriceLevelPrice(), (MamdaOrderBookPriceLevel::Side) levelEntry.getPriceLevelSide()); if(mLevelPtr != NULL) { //get the entry by id mEntryPtr = mLevelPtr->findEntry(levelEntry.getPriceLevelEntryId()); if(mEntryPtr != NULL) { mOrderBook.updateEntry( mEntryPtr, levelEntry.getPriceLevelEntrySize(), levelEntry.getPriceLevelEntryTime(), (MamdaOrderBookBasicDelta*) NULL); break; } } /* * intentional fall through to add the entry if * the entry or level cannot be found to update it */ } catch ( MamdaOrderBookInvalidEntry &e) { cout<< "atomicbookbuilder: could not update entry.\n"; cout << "Caught MamdaOrderBookInvalidEntry [" << e.what() << "]\n"; } catch (MamdaOrderBookException &e) { cout<< "atomicbookbuilder: could not update entry.\n"; cout<< "Caught MamdaOrderBookException [" << e.what() << "[\n"; } case MamdaOrderBookEntry::MAMDA_BOOK_ACTION_ADD : mEntryPtr = mOrderBook.addEntry( levelEntry.getPriceLevelEntryId(), levelEntry.getPriceLevelEntrySize(), levelEntry.getPriceLevelPrice(), (MamdaOrderBookPriceLevel::Side) levelEntry.getPriceLevelSide(), levelEntry.getPriceLevelEntryTime(), (const MamaSourceDerivative*) NULL, (MamdaOrderBookBasicDelta*) NULL); mEntryPtr->setReason( (MamdaOrderBookTypes::Reason)levelEntry.getPriceLevelEntryReason()); break; case MamdaOrderBookEntry::MAMDA_BOOK_ACTION_DELETE : try { //get the price level by price mLevelPtr = mOrderBook.findLevel( levelEntry.getPriceLevelPrice(), (MamdaOrderBookPriceLevel::Side) levelEntry.getPriceLevelSide()); if(mLevelPtr != NULL) { //get the entry by id mEntryPtr = mLevelPtr->findEntry(levelEntry.getPriceLevelEntryId()); if(mEntryPtr != NULL) mOrderBook.deleteEntry( mEntryPtr, levelEntry.getPriceLevelEntryTime(), (MamdaOrderBookBasicDelta*) NULL); } } catch ( MamdaOrderBookInvalidEntry &e) { cout<< "atomicbookbuilder: could not delete entry.\n"; cout << "Caught MamdaOrderBookInvalidEntry [" << e.what() << "]\n"; } catch (MamdaOrderBookException &e) { cout<< "atomicbookbuilder: could not delete entry.\n"; cout<< "Caught MamdaOrderBookException [" << e.what() << "[\n"; } break; default: cout << "atomicbookbuilder: Unknown entry action [" << (char)entryAction << "]\n"; break; } mEntryPtr = NULL; mLevelPtr = NULL; } }
/** * Helper function to apply a MamdaOrderBookPriceLevel to * a MamdaOrderBook */ void applyLevel (MamdaOrderBookPriceLevel& level) { if(mOrderBook.getQuality() == MAMA_QUALITY_OK) { switch(level.getAction()) { case MamdaOrderBookPriceLevel::MAMDA_BOOK_ACTION_UPDATE : try { /* * When in the order book the only Action which makes sense is * ADD */ level.setAction(MamdaOrderBookPriceLevel::MAMDA_BOOK_ACTION_ADD); mOrderBook.updateLevel(level); } catch ( MamdaOrderBookException &e) { //Exception is thrown when updating level which does not exist //level will be added so handled internally } break; case MamdaOrderBookPriceLevel::MAMDA_BOOK_ACTION_ADD : try { mLevelPtr = new MamdaOrderBookPriceLevel(level); /* * When in the order book the only Action which makes sense is * ADD */ mLevelPtr->setAction(MamdaOrderBookPriceLevel::MAMDA_BOOK_ACTION_ADD); mOrderBook.addLevel(*mLevelPtr); //ownership of ptr passed to MamdaOrderBook } catch ( MamdaOrderBookException &e) { //Exception is thrown if adding a level already in book //handled internally by updating level delete mLevelPtr; } break; case MamdaOrderBookPriceLevel::MAMDA_BOOK_ACTION_DELETE : try { mOrderBook.deleteLevel(mReusablePriceLevel); } catch (MamdaOrderBookException &e) { //Thrown if the level cannot be found in the book //No need for handling as level is deleted } break; default: cout << "atomicbookbuilder: Unknown price level [" << level.getAction() << "]\n"; break; } mLevelPtr = NULL; } }