#include #include "../../shared/SimpleLogFile.h" #include "../misc_framework/GenericDataNodes.h" #include "../data_framework/GenericTosData.h" #include "../data_framework/StandardCandles.h" #include "../data_framework/SimpleMarketData.h" #include "../data_framework/IntradayStochastic.h" #include "ReportAlertsThread.h" #include "TiqMisc.h" #include "MiscAlerts.h" ///////////////////////////////////////////////////////////////////// // ConsolidationSet // // This comes from Rick Boensch via Jamie. // // Original request: // Min Price = .50 // Max Price = 200 // Minimum Volume Today = 200,000 // Relative Vol = 2 Minimum // // The Stock Must trade atleast once per minute (In case you're wondering // about this one, it's to try and avoid the stocks that simply move without // trading)** // // 10 Consecutive 2 Minute candles where the range of each candle is equal to // or less than .003 x Stock Price // // A column that Indicates Actual Rel Vol Value // // Displaying of Symbols in Window, whenever condition ceases to exist, // symbol disappears. // // I'll take care of the min/max price in the overnight processing. // // Add this to TIQ ini to see the results: // // [Alert Window 6] // subscription=GLOBAL-consolidation_set // title=Consolidation // display=set // extra=Rel Vol // ///////////////////////////////////////////////////////////////////// class ConsolidationSet : public DataNode { private: const std::string _symbol; GenericTosDataNode *_tosData; StandardCandles *_1MinCandles; StandardCandles *_2MinCandles; GenericDataNode *_relVolData; double _previousClose; time_t _blackoutEnds; bool live() { return _blackoutEnds; } void onWakeup(int msgId); void checkState(bool &shouldDisplay, double &relVol); ConsolidationSet(DataNodeArgument const &args); friend class DataNode; public: static DataNodeLink *find(std::string const &symbol); }; ConsolidationSet::ConsolidationSet(DataNodeArgument const &args) : _symbol(args.getStringValue()), _previousClose(getPreviousClose(_symbol)), _blackoutEnds(0) { addAutoLink(GenericTosDataNode::find(this, 0, _tosData, _symbol)); addAutoLink(StandardCandles::find(this, 0, _1MinCandles, _symbol, 1)); addAutoLink(StandardCandles::find(NULL, 0, _2MinCandles, _symbol, 2)); const DataNodeArgument factory = GenericDataNodeFactory::findFactory("RelVol"); addAutoLink(GenericDataNodeFactory::replaceAndFind(factory, NULL, 0, _relVolData, symbolPlaceholder, _symbol)); } void ConsolidationSet::checkState(bool &shouldDisplay, double &relVol) { shouldDisplay = _tosData->getValid(); if (!shouldDisplay) return; if (_tosData->getLast().volume < 200000) { shouldDisplay = false; return; } _relVolData->getDouble(shouldDisplay, relVol); if (!shouldDisplay) return; if (relVol < 2) { shouldDisplay = false; return; } if (_1MinCandles->historicalCandleCount() < 20) // The stock must trade at least once per minute. // The requirements were a bit vauge on this one. I'm assuming that we // need 20 minutes worth of full one minute candles. That generally // matches the last 10 2 minute candles that we're looking at a little // later. { shouldDisplay = false; return; } const double maxSize = _tosData->getLast().price *0.003; int historyCount; if (_2MinCandles->isCurrentCandleValid()) { StandardCandles::SingleCandle const &candle = _2MinCandles->getCurrentCandle(); shouldDisplay = candle.high - candle.low <= maxSize; if (!shouldDisplay) return; // Current partial candle, plus 9 more. historyCount = 9; } else // Look at 10 historical candles. historyCount = 10; if (_2MinCandles->historicalCandleCount() < historyCount) { shouldDisplay = false; return; } StandardCandles::CandleArray const &candles = _2MinCandles->getHistory(); for (StandardCandles::CandleArray::const_reverse_iterator it = candles.rbegin(); historyCount; historyCount--, it++) { StandardCandles::SingleCandle const &candle = *it; shouldDisplay = candle.high - candle.low <= maxSize; if (!shouldDisplay) return; } } void ConsolidationSet::onWakeup(int msgId) { // This could come from any event. Relative volume changes continuously with // time, so arguably we could respond to timer events here. static const std::string category = "GLOBAL-consolidation_set"; bool shouldDisplay; double relVol; checkState(shouldDisplay, relVol); if (live() && !shouldDisplay) { // Send delete message. _blackoutEnds = 0; std::string info = "symbol="; info += urlEncode(_symbol); info += "&action=delete"; ReportAlertsThread::getInstance()->reportAlert(category, info); } else { const time_t now = time(NULL); if (shouldDisplay && ((!live()) || (now > _blackoutEnds))) { // Send add message. _blackoutEnds = now + 4; std::string info = "symbol="; info += urlEncode(_symbol); info += "&relvol="; info += ntoa(relVol); // Repeat with this code to so we can reuse some decoding routines // on the client side. info += "&dist_from_bb="; info += ntoa(relVol); // Last and Previous Close were not part of the written requirements. // But they may be useful, and by adding them it's easier to reuse // some client code. if (_tosData->getValid() && _tosData->getLast().price) { info += "&last="; info += urlEncode(formatPrice(_tosData->getLast().price)); } if (_previousClose) { info += "&prev_close="; info += urlEncode(formatPrice(_previousClose)); } info += "&action=add"; ReportAlertsThread::getInstance()->reportAlert(category, info); } } } DataNodeLink *ConsolidationSet::find(std::string const &symbol) { ConsolidationSet *node; return findHelper(NULL, 0, node, symbol); } ///////////////////////////////////////////////////////////////////// // StochasticTest // // We're going to report only the stocks which are very overbought // or oversold. These numbers, 95 and 5, come from some other // rick/jamie requirements. For now we're limiting ourselves to // these stocks so we can report every change in these stocks. ///////////////////////////////////////////////////////////////////// class StochasticTest : public DataNode { std::string _symbol; std::string _category; bool _overbought; GenericTosDataNode *_tosData; IntradayStochastic *_stocData; double _previousClose; bool _active; void onWakeup(int msgId); StochasticTest(DataNodeArgument const &args); friend class DataNode; public: static DataNodeLink *find(std::string const &symbol, bool overbought, int minutes, std::string const &category); }; void StochasticTest::onWakeup(int msgId) { bool valid; double pK, pD; _stocData->get(valid, pK, pD); bool currentlyActive; if (!valid) currentlyActive = false; else { if (_overbought) currentlyActive = pD > 95.0; else currentlyActive = pD < 5.0; //currentlyActive=true; // temp test } if (currentlyActive) { std::string info = "symbol="; info += urlEncode(_symbol); if (_tosData->getValid() && _tosData->getLast().price) { info += "&last="; info += urlEncode(formatPrice(_tosData->getLast().price)); } //if (_prevClose) //{ // info += "&prev_close="; // info += urlEncode(formatPrice(_prevClose)); //} info += "&action=add"; info += "&dist_from_bb="; info += formatPrice(pD); info += "&pD="; info += formatPrice(pD); info += "&pK="; info += formatPrice(pK); ReportAlertsThread::getInstance()->reportAlert(_category, info); } else if (_active) { std::string info = "symbol="; info += urlEncode(_symbol); info += "&action=delete"; ReportAlertsThread::getInstance()->reportAlert(_category, info); } _active = currentlyActive; } StochasticTest::StochasticTest(DataNodeArgument const &args) : _active(false) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 4); // Symbol, Overbought, Minutes, Category _symbol = argList[0].getStringValue(); _overbought = argList[1].getBooleanValue(); const int minutes = argList[2].getIntValue(); _category = argList[3].getStringValue(); _previousClose = getPreviousClose(_symbol); addAutoLink(GenericTosDataNode::find(_tosData, _symbol)); addAutoLink(IntradayStochastic::findFast(this, 0, _stocData, _symbol, minutes, 14, 3)); } DataNodeLink *StochasticTest::find(std::string const &symbol, bool overbought, int minutes, std::string const &category) { StochasticTest *node; return findHelper(NULL, 0, node, argList(symbol, overbought, minutes, category)); } ///////////////////////////////////////////////////////////////////// // MissingVolumeTest // // Question: Does the daily volume number increase only with // individual prints, or is there other data involved? If volume // consistanly goes up, that could mean that the odd lots, which are // not reported as prints, are part of the daily volume number. // // Answer: Volume does not appear to go up because of these missing // prints. We tested this with Realtick and DTN. Each one had a // few places during the day where the volume number was not as // expected, but it was usually off by a number that was larger than // 100 and was divisible by 100. ///////////////////////////////////////////////////////////////////// class MissingVolumeTest : public DataNode { std::string _symbol; GenericTosDataNode *_tosData; Integer _lastVolume; void onWakeup(int msgId); MissingVolumeTest(DataNodeArgument const &args); friend class DataNode; public: static DataNodeLink *find(std::string const &symbol); }; void MissingVolumeTest::onWakeup(int msgId) { if (!_tosData->getValid()) _lastVolume = 0; else { if (_tosData->getLast().newPrint && (_lastVolume + _tosData->getLast().size != _tosData->getLast().volume)) { TclList msg; msg<<__FILE__<<__LINE__<<__FUNCTION__ <<"MissingVolumeTest" <<"_lastVolume"<<_lastVolume <<"size"<<_tosData->getLast().size <<"volume"<<_tosData->getLast().volume; sendToLogFile(msg); } _lastVolume = _tosData->getLast().volume; } } MissingVolumeTest::MissingVolumeTest(DataNodeArgument const &args) : _symbol(args.getStringValue()), _lastVolume(0) { addAutoLink(GenericTosDataNode::find(this, 0, _tosData, _symbol)); } DataNodeLink *MissingVolumeTest::find(std::string const &symbol) { MissingVolumeTest *node; return findHelper(NULL, 0, node, symbol); } ///////////////////////////////////////////////////////////////////// // Global ///////////////////////////////////////////////////////////////////// static std::vector< DataNodeLink * > cleanup; static void alertsForSymbol(std::string const &symbol) { TclList msg; msg<<__FILE__<<__LINE__<<__FUNCTION__<<"ConsolidationSet"<