#include #include #include "../../shared/SimpleLogFile.h" #include "../misc_framework/GenericDataNodes.h" #include "../data_framework/SynchronizedTimers.h" #include "../data_framework/GenericTosData.h" #include "../data_framework/SimpleMarketData.h" #include "../data_framework/TradeDirection.h" #include "ReportAlertsThread.h" #include "TiqMisc.h" #include "ArthurIlyayev.h" class ArthurIlyayevAlert : public DataNode { private: std::string _symbol; TradeDirection _direction; double _previousClose; BarCounter *_barCounterData; GenericTosDataNode *_tosData; bool _primed; double _threashold; void onTos(); void onBarCounter(); static bool goodRange(double high, double low); enum { wTOS, wBarCounter }; void onWakeup(int msgId); ArthurIlyayevAlert(DataNodeArgument const &args); friend class DataNode; public: static DataNodeLink *find(std::string const &symbol, bool up) { ArthurIlyayevAlert *node; return findHelper(NULL, 0, node, argList(symbol, up)); } }; ArthurIlyayevAlert::ArthurIlyayevAlert(DataNodeArgument const &args) : _primed(false) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 2); // Symbol, Long _symbol = argList[0].getStringValue(); _direction = argList[1].getBooleanValue(); _previousClose = getPreviousClose(_symbol); if (_previousClose) { addAutoLink(GenericTosDataNode::find(this, wTOS, _tosData, _symbol)); addAutoLink(BarCounter::find(this, wBarCounter, _barCounterData, 5)); } } bool ArthurIlyayevAlert::goodRange(double high, double low) { // The spec was not specific about what price to use. const double price = (high + low) / 2; const double min = (price < 80.0)?0.10:0.20; const double max = floor(price / 10.0) / 10.0 + 0.09; const double range = high - low; return (range >= min) && (range <= max); } void ArthurIlyayevAlert::onTos() { if (!_tosData->getValid()) return; const double price = _tosData->getLast().price; if (price <= 0) return; if (_direction.moreReportable(price, _threashold)) { _primed = false; std::string info = "symbol="; info += urlEncode(_symbol); info += "&last="; info += urlEncode(formatPrice(price)); info += "&prev_close="; info += urlEncode(formatPrice(_previousClose)); info += "&time="; info += formatTime(time(NULL)); std::string category = _direction.isLong()?"ArthurIlyayev-Up":"ArthurIlyayev-Down"; ReportAlertsThread::getInstance()->reportAlert(category, info); } } void ArthurIlyayevAlert::onBarCounter() { if (_barCounterData->getTimePhase() != BarCounter::tpNotify) // Only update once per candle transition. return; if (_barCounterData->getCurrentBar() != 1) return; // This is the end of the first bar. if (!_tosData->getValid()) return; const double open = _tosData->getLast().open; if (open <= 0) return; const double gap = (open - _previousClose) / _previousClose * 100.0; if (!_direction.upByAtLeast(gap, 0.5)) return; const Integer volume = _tosData->getLast().volume; if (volume < 20000) return; const double high = _tosData->getLast().high; const double low = _tosData->getLast().low; if (!goodRange(high, low)) return; _threashold = _direction.mostReportable(high, low); _primed = true; } void ArthurIlyayevAlert::onWakeup(int msgId) { switch (msgId) { case wTOS: if (_primed) onTos(); break; case wBarCounter: onBarCounter(); break; } } ///////////////////////////////////////////////////////////////////// // Global ///////////////////////////////////////////////////////////////////// static std::vector< DataNodeLink * > cleanup; static void alertsForSymbol(std::string const &symbol) { TclList msg; msg<