#include #include "../../shared/SimpleLogFile.h" #include "../misc_framework/StandardPlaceholders.h" #include "../data_framework/GenericTosData.h" #include "../data_framework/GenericL1Data.h" #include "../data_framework/IntradaySma.h" #include "../data_framework/RSI.h" #include "../data_framework/LinearRegression.h" #include "../nasdaq_vf/StockTwitsDataNode.h" #include "../../fast_alert_search/FieldLists.h" #include "AlertMatricies.h" ///////////////////////////////////////////////////////////////////// // AlertInputs ///////////////////////////////////////////////////////////////////// DataNodeArgument AlertInputs::doReplacements(DataNodeArgument factory) const { return factory.replace(symbolPlaceholder, _symbol); } ///////////////////////////////////////////////////////////////////// // AlertBackgroundData // // This fills in the values for the standard filters that are common // to all alerts. Examples include the price and volume of the stock // at the time of the alert. // // This class does not ever notify a listner. It could, but that's // now how it's used. Another class is listening for an alert, and // it reads from here at that time. ///////////////////////////////////////////////////////////////////// class AlertBackgroundData : public DataNode { private: GenericTosDataNode *_tosData; GenericL1DataNode *_l1Data; StockTwitsDataNode *_stockTwitsData; GenericDataNode *_relVolData, *_mostRecentCloseData, *_expectedOpenData, *_todaysLowData, *_rsi1Data, *_rsi2Data, *_rsi5Data, *_rsi15Data, *_rsi60Data, *_up1Data, *_up2Data, *_up5Data, *_up10Data, *_up15Data, *_up30Data, *_distanceFromNbboData, *_todaysHighData, *_timedMovement1Data, *_timedMovement2Data, *_timedMovement5Data, *_timedMovement10Data, *_timedMovement15Data, *_timedMovement30Data, *_timedMovement60Data, *_timedMovement120Data, *_volumeChange1Data, *_volumeChange2Data, *_volumeChange5Data, *_volumeChange10Data, *_volumeChange15Data, *_volumeChange30Data, *_sma_2_8_Data, *_sma_2_20_Data, *_sma_2_200_Data, *_sma_5_8_Data, *_sma_5_20_Data, *_sma_5_200_Data, *_sma_15_8_Data, *_sma_15_20_Data, *_sma_15_130_Data, *_sma_15_200_Data, *_qqqq5Data, *_qqqq10Data, *_qqqq15Data, *_qqqq30Data, *_qqqqDData, *_spy5Data, *_spy10Data, *_spy15Data, *_spy30Data, *_spyDData, *_dia5Data, *_dia10Data, *_dia15Data, *_dia30Data, *_diaDData, *_intraDayRange30Data, *_intraDayRange60Data, *_intraDayRange120Data, *_sma_60_8_Data, *_sma_60_20_Data, *_sma_60_200_Data, *_intraDayRange2Data, *_intraDayRange5Data, *_intraDayRange15Data, *_up60Data, *_std_5_20_Data, *_std_15_20_Data, *_std_60_20_Data, *_putVolumeData, *_callVolumeData, *_vwapData, *_intraDayPositionInRange5Data, *_intraDayPositionInRange15Data, *_intraDayPositionInRange30Data, *_intraDayPositionInRange60Data, *_bidData, *_askData, *_sma_2_5_Data, *_sma_5_5_Data, *_sma_15_5_Data, *_postMarketVolumeData, *_preMarketHighData, *_preMarketLowData, *_limitUpData, *_limitDownData, *_sma_2_10_Data, *_sma_5_10_Data, *_sma_15_10_Data, *_sma_60_10_Data; void findMarketData(GenericDataNode *&node, DataNodeArgument const &factory, DataNodeArgument const &symbol); void findMarketData(GenericDataNode *&node, const char *name, DataNodeArgument const &symbol); AlertBackgroundData(DataNodeArgument const &args); friend class DataNode; public: void fillIn(RecordBuilder &event) const; static DataNodeLink *find(AlertBackgroundData *&node, std::string const &symbol); }; void AlertBackgroundData::findMarketData(GenericDataNode *&node, DataNodeArgument const &factory, DataNodeArgument const &symbol) { DataNodeArgument f1 = factory.replace(symbolPlaceholder, symbol); addAutoLink(f1.getValue< GenericDataNodeFactory >().find(node)); } void AlertBackgroundData::findMarketData(GenericDataNode *&node, const char *name, DataNodeArgument const &symbol) { const DataNodeArgument factory = GenericDataNodeFactory::findFactory(name); findMarketData(node, factory, symbol); } AlertBackgroundData::AlertBackgroundData(DataNodeArgument const &args) { const std::string symbol = args.getStringValue(); addAutoLink(GenericL1DataNode::find(_l1Data, symbol)); addAutoLink(GenericTosDataNode::find(_tosData, symbol)); addAutoLink(StockTwitsDataNode::find(_stockTwitsData, symbol)); findMarketData(_relVolData, "RelVol", args); findMarketData(_distanceFromNbboData, "DistanceFromNbbo", args); findMarketData(_todaysHighData, "HighPrice", args); findMarketData(_todaysLowData, "LowPrice", args); findMarketData(_timedMovement1Data, "TimedMovement1", args); findMarketData(_timedMovement2Data, "TimedMovement2", args); findMarketData(_timedMovement5Data, "TimedMovement5", args); findMarketData(_timedMovement10Data, "TimedMovement10", args); findMarketData(_timedMovement15Data, "TimedMovement15", args); findMarketData(_timedMovement30Data, "TimedMovement30", args); findMarketData(_timedMovement60Data, "TimedMovement60", args); findMarketData(_timedMovement120Data, "TimedMovement120", args); findMarketData(_volumeChange1Data, "VolumeChange1", args); findMarketData(_volumeChange2Data, "VolumeChange2", args); findMarketData(_volumeChange5Data, "VolumeChange5", args); findMarketData(_volumeChange10Data, "VolumeChange10", args); findMarketData(_volumeChange15Data, "VolumeChange15", args); findMarketData(_volumeChange30Data, "VolumeChange30", args); findMarketData(_postMarketVolumeData, "PostMarketVolume", args); findMarketData(_qqqq5Data, "QQQQ5", args); findMarketData(_qqqq10Data, "QQQQ10", args); findMarketData(_qqqq15Data, "QQQQ15", args); findMarketData(_qqqq30Data, "QQQQ30", args); findMarketData(_qqqqDData, "QQQQD", args); findMarketData(_spy5Data, "SPY5", args); findMarketData(_spy10Data, "SPY10", args); findMarketData(_spy15Data, "SPY15", args); findMarketData(_spy30Data, "SPY30", args); findMarketData(_spyDData, "SPYD", args); findMarketData(_dia5Data, "DIA5", args); findMarketData(_dia10Data, "DIA10", args); findMarketData(_dia15Data, "DIA15", args); findMarketData(_dia30Data, "DIA30", args); findMarketData(_diaDData, "DIAD", args); findMarketData(_intraDayRange2Data, "IntraDayRange2", args); findMarketData(_intraDayRange5Data, "IntraDayRange5", args); findMarketData(_intraDayRange15Data, "IntraDayRange15", args); findMarketData(_intraDayRange30Data, "IntraDayRange30", args); findMarketData(_intraDayRange60Data, "IntraDayRange60", args); findMarketData(_intraDayRange120Data, "IntraDayRange120", args); findMarketData(_up1Data, "UpCandles1", args); findMarketData(_up2Data, "UpCandles2", args); findMarketData(_up5Data, "UpCandles5", args); findMarketData(_up10Data, "UpCandles10", args); findMarketData(_up15Data, "UpCandles15", args); findMarketData(_up30Data, "UpCandles30", args); findMarketData(_up60Data, "UpCandles60", args); findMarketData(_mostRecentCloseData, "MostRecentClose", args); findMarketData(_sma_2_5_Data, createIntradaySmaFactory(2, 5, false), args); findMarketData(_sma_2_8_Data, createIntradaySmaFactory(2, 8, false), args); findMarketData(_sma_2_10_Data, createIntradaySmaFactory(2, 10, false), args); findMarketData(_sma_2_20_Data, createIntradaySmaFactory(2, 20, false), args); findMarketData(_sma_2_200_Data, createIntradaySmaFactory(2, 200, false), args); findMarketData(_sma_5_5_Data, createIntradaySmaFactory(5, 5, false), args); findMarketData(_sma_5_8_Data, createIntradaySmaFactory(5, 8, false), args); findMarketData(_sma_5_10_Data, createIntradaySmaFactory(5, 10, false), args); findMarketData(_sma_5_20_Data, createIntradaySmaFactory(5, 20, false), args); findMarketData(_sma_5_200_Data, createIntradaySmaFactory(5, 200, false), args); findMarketData(_sma_15_5_Data, createIntradaySmaFactory(15, 5, false), args); findMarketData(_sma_15_8_Data, createIntradaySmaFactory(15, 8, false), args); findMarketData(_sma_15_10_Data, createIntradaySmaFactory(15, 10, false), args); findMarketData(_sma_15_20_Data, createIntradaySmaFactory(15, 20, false), args); findMarketData(_sma_15_130_Data, createIntradaySmaFactory(15, 130, false), args); findMarketData(_sma_15_200_Data, createIntradaySmaFactory(15, 200, false), args); findMarketData(_sma_60_8_Data, createIntradaySmaFactory(60, 8, false), args); findMarketData(_sma_60_10_Data, createIntradaySmaFactory(60, 10, false), args); findMarketData(_sma_60_20_Data, createIntradaySmaFactory(60, 20, false), args); findMarketData(_sma_60_200_Data, createIntradaySmaFactory(60, 200, false), args); findMarketData(_std_5_20_Data, LinearRegressionBase::stdDevFactory(5, 20), args); findMarketData(_std_15_20_Data, LinearRegressionBase::stdDevFactory(15, 20), args); findMarketData(_std_60_20_Data, LinearRegressionBase::stdDevFactory(60, 20), args); findMarketData(_rsi1Data, createRsiFactory(1), args); findMarketData(_rsi2Data, createRsiFactory(2), args); findMarketData(_rsi5Data, createRsiFactory(5), args); findMarketData(_rsi15Data, createRsiFactory(15), args); findMarketData(_rsi60Data, createRsiFactory(60), args); findMarketData(_expectedOpenData, "ExpectedOpen", args); findMarketData(_vwapData, "VWAP", args); findMarketData(_intraDayPositionInRange5Data, "IntraDayPositionInRange5", args); findMarketData(_intraDayPositionInRange15Data, "IntraDayPositionInRange15", args); findMarketData(_intraDayPositionInRange30Data, "IntraDayPositionInRange30", args); findMarketData(_intraDayPositionInRange60Data, "IntraDayPositionInRange60", args); findMarketData(_preMarketHighData, "PreMarketHighFilter", args); findMarketData(_preMarketLowData, "PreMarketLowFilter", args); DataNodeArgument factory = GenericDataNodeFactory::findFactory("PutVolume"); factory = factory.replace(symbolPlaceholder, symbol); addAutoLink(factory.getValue< GenericDataNodeFactory >() .find(this, 0, _putVolumeData)); factory = GenericDataNodeFactory::findFactory("CallVolume"); factory = factory.replace(symbolPlaceholder, symbol); addAutoLink(factory.getValue< GenericDataNodeFactory >() .find(this, 0, _callVolumeData)); findMarketData(_limitUpData, "LimitUp", args); findMarketData(_limitDownData, "LimitDown", args); } static void fillInInteger(RecordBuilder &recordBuilder, FieldId fieldId, GenericDataNode *source) { bool valid; DataNode::Integer value; source->getInteger(valid, value); if (valid) recordBuilder.append(fieldId, value); } static void fillInDouble(RecordBuilder &recordBuilder, FieldId fieldId, GenericDataNode *source) { bool valid; double value; source->getDouble(valid, value); if (valid) recordBuilder.append(fieldId, value); } void AlertBackgroundData::fillIn(RecordBuilder &event) const { if (_tosData->getValid()) { const double price = _tosData->getLast().price; if (price) event.append(MainFields::price, price); const Integer volume = _tosData->getLast().volume; if (volume > 0) event.append(MainFields::tvol, volume); const double todaysClose = _tosData->getLast().todaysClose; if (todaysClose > 0.0) event.append(MainFields::last, todaysClose); } if (_l1Data->getValid()) { const double askPrice = _l1Data->getCurrent().askPrice; const double bidPrice = _l1Data->getCurrent().bidPrice; if ((askPrice > 0) && (bidPrice > 0)) { event.append(MainFields::spread, askPrice - bidPrice); event.append(MainFields::bid, bidPrice); event.append(MainFields::ask, askPrice); } if (_l1Data->getCurrent().bidSize > 0) event.append(MainFields::bid_size, _l1Data->getCurrent().bidSize); if (_l1Data->getCurrent().askSize > 0) event.append(MainFields::ask_size, _l1Data->getCurrent().askSize); // Let the database compute the total options volume and the put call // ratio. That used to be done here. fillInInteger(event, MainFields::put_v, _putVolumeData); fillInInteger(event, MainFields::call_v, _callVolumeData); } bool stockTwitsValid; double stockTwitsValue; _stockTwitsData->getRelativeVolume(stockTwitsValid, stockTwitsValue); if (stockTwitsValid) // I originally thought it would make more sense for this to be a ratio. // But when designing the icons, it made more sense to be a %, so I // changed the number to match the icon. event.append(MainFields::social_rv, stockTwitsValue*100); fillInDouble(event, MainFields::relvol, _relVolData); fillInDouble(event, MainFields::distance_from_nbbo, _distanceFromNbboData); fillInDouble(event, MainFields::t_high, _todaysHighData); fillInDouble(event, MainFields::t_low, _todaysLowData); fillInDouble(event, MainFields::p_up_1, _timedMovement1Data); fillInDouble(event, MainFields::p_up_2, _timedMovement2Data); fillInDouble(event, MainFields::p_up_5, _timedMovement5Data); fillInDouble(event, MainFields::p_up_10, _timedMovement10Data); fillInDouble(event, MainFields::p_up_15, _timedMovement15Data); fillInDouble(event, MainFields::p_up_30, _timedMovement30Data); fillInDouble(event, MainFields::p_up_60, _timedMovement60Data); fillInDouble(event, MainFields::p_up_120, _timedMovement120Data); fillInInteger(event, MainFields::v_up_1, _volumeChange1Data); fillInInteger(event, MainFields::v_up_2, _volumeChange2Data); fillInInteger(event, MainFields::v_up_5, _volumeChange5Data); fillInInteger(event, MainFields::v_up_10, _volumeChange10Data); fillInInteger(event, MainFields::v_up_15, _volumeChange15Data); fillInInteger(event, MainFields::v_up_30, _volumeChange30Data); fillInInteger(event, MainFields::pm_volume, _postMarketVolumeData); fillInDouble(event, MainFields::qqqq_5, _qqqq5Data); fillInDouble(event, MainFields::qqqq_10, _qqqq10Data); fillInDouble(event, MainFields::qqqq_15, _qqqq15Data); fillInDouble(event, MainFields::qqqq_30, _qqqq30Data); fillInDouble(event, MainFields::qqqq_d, _qqqqDData); fillInDouble(event, MainFields::spy_5, _spy5Data); fillInDouble(event, MainFields::spy_10, _spy10Data); fillInDouble(event, MainFields::spy_15, _spy15Data); fillInDouble(event, MainFields::spy_30, _spy30Data); fillInDouble(event, MainFields::spy_d, _spyDData); fillInDouble(event, MainFields::dia_5, _dia5Data); fillInDouble(event, MainFields::dia_10, _dia10Data); fillInDouble(event, MainFields::dia_15, _dia15Data); fillInDouble(event, MainFields::dia_30, _dia30Data); fillInDouble(event, MainFields::dia_d, _diaDData); fillInDouble(event, MainFields::range_2, _intraDayRange2Data); fillInDouble(event, MainFields::range_5, _intraDayRange5Data); fillInDouble(event, MainFields::range_15, _intraDayRange15Data); fillInDouble(event, MainFields::range_30, _intraDayRange30Data); fillInDouble(event, MainFields::range_60, _intraDayRange60Data); fillInDouble(event, MainFields::range_120, _intraDayRange120Data); fillInInteger(event, MainFields::up_1, _up1Data); fillInInteger(event, MainFields::up_2, _up2Data); fillInInteger(event, MainFields::up_5, _up5Data); fillInInteger(event, MainFields::up_10, _up10Data); fillInInteger(event, MainFields::up_15, _up15Data); fillInInteger(event, MainFields::up_30, _up30Data); fillInInteger(event, MainFields::up_60, _up60Data); fillInDouble(event, MainFields::most_recent_close, _mostRecentCloseData); fillInDouble(event, MainFields::sma_2_8, _sma_2_8_Data); fillInDouble(event, MainFields::sma_2_10, _sma_2_10_Data); fillInDouble(event, MainFields::sma_2_20, _sma_2_20_Data); fillInDouble(event, MainFields::sma_2_200, _sma_2_200_Data); fillInDouble(event, MainFields::sma_5_8, _sma_5_8_Data); fillInDouble(event, MainFields::sma_5_10, _sma_5_10_Data); fillInDouble(event, MainFields::sma_5_20, _sma_5_20_Data); fillInDouble(event, MainFields::sma_5_200, _sma_5_200_Data); fillInDouble(event, MainFields::sma_15_8, _sma_15_8_Data); fillInDouble(event, MainFields::sma_15_10, _sma_15_10_Data); fillInDouble(event, MainFields::sma_15_20, _sma_15_20_Data); fillInDouble(event, MainFields::sma_15_130, _sma_15_130_Data); fillInDouble(event, MainFields::sma_15_200, _sma_15_200_Data); fillInDouble(event, MainFields::sma_60_8, _sma_60_8_Data); fillInDouble(event, MainFields::sma_60_10, _sma_60_10_Data); fillInDouble(event, MainFields::sma_60_20, _sma_60_20_Data); fillInDouble(event, MainFields::sma_60_200, _sma_60_200_Data); fillInDouble(event, MainFields::std_5_20, _std_5_20_Data); fillInDouble(event, MainFields::std_15_20, _std_15_20_Data); fillInDouble(event, MainFields::std_60_20, _std_60_20_Data); fillInDouble(event, MainFields::sma_2_5, _sma_2_5_Data); fillInDouble(event, MainFields::sma_5_5, _sma_5_5_Data); fillInDouble(event, MainFields::sma_15_5, _sma_15_5_Data); fillInDouble(event, MainFields::rsi_1, _rsi1Data); fillInDouble(event, MainFields::rsi_2, _rsi2Data); fillInDouble(event, MainFields::rsi_5, _rsi5Data); fillInDouble(event, MainFields::rsi_15, _rsi15Data); fillInDouble(event, MainFields::rsi_60, _rsi60Data); fillInDouble(event, MainFields::expected_open, _expectedOpenData); fillInDouble(event, MainFields::vwap, _vwapData); fillInDouble(event, MainFields::prange_5, _intraDayPositionInRange5Data); fillInDouble(event, MainFields::prange_15, _intraDayPositionInRange15Data); fillInDouble(event, MainFields::prange_30, _intraDayPositionInRange30Data); fillInDouble(event, MainFields::prange_60, _intraDayPositionInRange60Data); fillInDouble(event, MainFields::high_pre, _preMarketHighData); fillInDouble(event, MainFields::low_pre, _preMarketLowData); fillInDouble(event, MainFields::limit_up, _limitUpData); fillInDouble(event, MainFields::limit_down, _limitDownData); /* To test this: ./a.out -i database_RO=marvin -i database_RW=drama -i fake_alerts=1 telnet chuck-liddell 1234 command=debug_tos&symbol=TEST&price=10.0&exchange=NASD&volume=1000000&sale_condition=SC&set=1 command=debug_l1&symbol=TEST&bid_price=100&ask_price=199&bid_size=8888&ask_size=88888&set=1 command=debug_tos&symbol=TEST&high=10&low=9.99&set=1 command=debug_tos&symbol=TEST&high=11&low=9.99&set=1 command=debug_tos&symbol=TEST&high=11&low=9.9&set=1 command=debug_tos&symbol=TEST&high=12&low=9.9&set=1 command=debug_tos&symbol=TEST&high=12&low=9.8&set=1 command=debug_tos&symbol=TEST&high=13&low=9.8&set=1 command=debug_tos&symbol=TEST&high=13&low=9.7&set=1 */ } DataNodeLink *AlertBackgroundData::find(AlertBackgroundData *&node, std::string const &symbol) { return findHelper(NULL, 0, node, symbol); } ///////////////////////////////////////////////////////////////////// // AlertMatrix ///////////////////////////////////////////////////////////////////// AlertMatrix::AlertMatrix(AlertMatrixListener *listener) : _externalListener(listener) { } AlertMatrix::~AlertMatrix() { for (std::vector< Listener * >::iterator it = _listeners.begin(); it != _listeners.end(); it++) { delete *it; } } void AlertMatrix::add(AlertCategory const &category) { for (std::vector< AlertInputs >::const_iterator it = _inputsList.begin(); it != _inputsList.end(); it++) { add(category, *it); } _categoryList.push_back(category); } void AlertMatrix::add(AlertInputs const &inputs) { for (std::vector< AlertCategory >::const_iterator it = _categoryList.begin(); it != _categoryList.end(); it++) { add(*it, inputs); } _inputsList.push_back(inputs); } void AlertMatrix::add(AlertCategory const &category, AlertInputs const &inputs) { _listeners.push_back(new Listener(category, inputs, _externalListener)); } ///////////////////////////////////////////////////////////////////// // AlertMatrix::Listener ///////////////////////////////////////////////////////////////////// void AlertMatrix::Listener::onWakeup(int msgId) { if (_blackListData->isBlackListed()) { return; } //sendToLogFile(TclList()<isAlert = _category.isAlert(); event->recordBuilder.append(MainFields::timestamp, DataNodeManager::getDefault()->getSubmitTime()); event->recordBuilder.append(MainFields::symbol, _inputs.getSymbol()); if (_category.isAlert()) { event->recordBuilder.append(MainFields::alert_type, _category.alertType); const std::string message = _alertNode->getMsg(); event->recordBuilder.append(MainFields::description, message); if (message.empty()) { // This would typically mean that some alert class requested data, then // forgot to override the onWakeup() method. I looked at several ways of // catching this. As long as you enable core dumps, this assertion will // give you the most information. TclList msg; msg<<__FILE__<<__LINE__<<__FUNCTION__ <<"event->data.msg.empty()" <<"alertType"<<_category.alertType <<"symbol"<<_inputs.getSymbol(); sendToLogFile(msg); assert(false && "event->data.msg.empty()"); } event->recordBuilder.append(MainFields::alt_description, _alertNode->getAltMsg()); event->recordBuilder.append(MainFields::num, _alertNode->getCount()); bool qualityValid; double qualityValue; _alertNode->getQuality(qualityValid, qualityValue); if (qualityValid) event->recordBuilder.append(MainFields::quality, qualityValue); } _backgroundData->fillIn(event->recordBuilder); _externalListener->onAlert(event); } AlertMatrix::Listener::Listener(AlertCategory const &category, AlertInputs const &inputs, AlertMatrixListener *externalListener) : _category(category), _inputs(inputs), _externalListener(externalListener), _alertNode(NULL), _alertLink(NULL), _backgroundData(NULL), _backgroundLink(NULL), _blackListData(NULL), _blackListLink(NULL) { GenericDataNode *node; _alertLink = inputs.doReplacements(category.factory) .getValue< GenericDataNodeFactory >() .find(this, 0, node); _alertNode = dynamic_cast< Alert * >(node); _backgroundLink = AlertBackgroundData::find(_backgroundData, _inputs.getSymbol()); _blackListLink = BlackList::find(_blackListData, _inputs.getSymbol()); } AlertMatrix::Listener::~Listener() { _alertLink->release(); if (_backgroundLink) { _backgroundLink->release(); } if (_blackListLink) { _blackListLink->release(); } }