#include #include #include "../../shared/SimpleLogFile.h" #include "../misc_framework/GenericDataNodes.h" #include "../data_framework/RSI.h" #include "../data_framework/GenericTosData.h" #include "../data_framework/StandardCandles.h" #include "../data_framework/SimpleMarketData.h" #include "TiqMisc.h" #include "CCI.h" #include "ReportAlertsThread.h" #include "ChrisPavelic.h" ///////////////////////////////////////////////////////////////////// // Global ///////////////////////////////////////////////////////////////////// static void add(std::string &destination, std::string const &name, std::string const &value) { if (!destination.empty()) destination += '&'; destination += name; destination += '='; destination += value; } static void add(std::string &destination, std::string const &name, double value) { add(destination, name, formatPrice(value)); } static void add(std::string &destination, std::string const &name, int value) { add(destination, name, ntoa(value)); } static void add(std::string &destination, std::string const &name, int64_t value) { add(destination, name, ntoa(value)); } ///////////////////////////////////////////////////////////////////// // ChrisPavelicInitialSpike // // Once a day some minimum set of criteria must be met. This is a boolean // that can be set but never cleared. Each moring it starts as false. // // This will notify the listener when the change is made. ///////////////////////////////////////////////////////////////////// class ChrisPavelicInitialSpike : public DataNode { private: StandardCandles *_candleData; GenericDataNode *_cciData; GenericDataNode *_rsiData; GenericTosDataNode *_tosData; double _previousClose; bool _found; bool _lastValuesValid; double _lastCCI; double _lastRSI; double _initialPrice; void saveOldValues(); enum { wCandle }; void checkForSpike(); void onCandle(); void onWakeup(int msgId); ChrisPavelicInitialSpike(DataNodeArgument const &args); friend class DataNode; public: static DataNodeLink *find(DataNodeListener *listener, int msgId, ChrisPavelicInitialSpike *&node, std::string const &symbol) { return findHelper(listener, msgId, node, symbol); } bool found() const { return _found; } // Meaningful if and only if found() returns true. double initialPrice() const { return _initialPrice; } }; ChrisPavelicInitialSpike::ChrisPavelicInitialSpike (DataNodeArgument const &args) : _found(false), _initialPrice(0) { const std::string symbol = args.getStringValue(); DataNodeArgument factory; factory = createRsiFactory(5, 14).replace(symbolPlaceholder, symbol); addAutoLink(factory.getValue< GenericDataNodeFactory >().find(_rsiData)); factory = createCciFactory(5, 20).replace(symbolPlaceholder, symbol); addAutoLink(factory.getValue< GenericDataNodeFactory >().find(_cciData)); addAutoLink(StandardCandles::find(this, wCandle, _candleData, symbol, 5, true)); addAutoLink(GenericTosDataNode::find(_tosData, symbol)); _previousClose = getPreviousClose(symbol); saveOldValues(); } void ChrisPavelicInitialSpike::onWakeup(int msgId) { if (_found) return; switch (msgId) { case wCandle: onCandle(); break; } } void ChrisPavelicInitialSpike::checkForSpike() { if (!_tosData->getValid()) return; StandardCandles::SingleCandle candle; if (_candleData->isUpdating()) { // The end of the candle. Recent requests from Chris say that we should // only check for a spike at this time. StandardCandles::CandleArray const &candles = _candleData->getHistory(); if (candles.empty()) return; candle = *candles.rbegin(); } else { // Not the end of a candle. Originally we called this function on every // print. if (!_candleData->isActive()) // Outside market hours. return; if (!_candleData->isCurrentCandleValid()) return; candle = _candleData->getCurrentCandle(); } if (candle.volume <= 10000) return; if (candle.high - candle.low <= 0.50) return; const TosData last = _tosData->getLast(); if (last.price <= _previousClose + 1.00) return; bool valid; double rsi; _rsiData->getDouble(valid, rsi); if (!valid) return; if (rsi <= 70) return; if (rsi <= _lastRSI + 15) return; double cci; _cciData->getDouble(valid, cci); if (!valid) return; if (cci <= 100) return; if (cci <= _lastCCI + 100) return; _found = true; _initialPrice = last.price; notifyListeners(); } void ChrisPavelicInitialSpike::saveOldValues() { _rsiData->getDouble(_lastValuesValid, _lastRSI); if (_lastValuesValid) _cciData->getDouble(_lastValuesValid, _lastCCI); } void ChrisPavelicInitialSpike::onCandle() { if (_candleData->isUpdating()) { checkForSpike(); saveOldValues(); } } ///////////////////////////////////////////////////////////////////// // ChrisPavelicCurrentState // // These values can change at any time. We pass on the notification from // the ChrisPavelicInitialSpike. We don't notify the user at any other // time. ///////////////////////////////////////////////////////////////////// class ChrisPavelicCurrentState : public DataNode { private: const std::string _symbol; ChrisPavelicInitialSpike *_initialSpike; GenericDataNode *_cci5Data; GenericDataNode *_rsi5Data; GenericDataNode *_cci2Data; GenericDataNode *_rsi2Data; GenericTosDataNode *_tosData; double _previousClose; void onWakeup(int msgId); ChrisPavelicCurrentState(DataNodeArgument const &args); friend class DataNode; public: static DataNodeLink *find(DataNodeListener *listener, int msgId, ChrisPavelicCurrentState *&node, std::string const &symbol) { return findHelper(listener, msgId, node, symbol); } bool triggered() const; double upFromOpen() const; void addStatus(std::string &destination, std::string const &suffix) const; }; void ChrisPavelicCurrentState::onWakeup(int msgId) { std::string status; addStatus(status, ""); TclList msg; msg<found()"<<_initialSpike->found() <<"triggered()"<getValid()) return 0; const TosData last = _tosData->getLast(); return last.price - last.open; } void ChrisPavelicCurrentState::addStatus(std::string &destination, std::string const &suffix) const { add(destination, "symbol" + suffix, _symbol); if (_previousClose) add(destination, "previous_close" + suffix, _previousClose); if (_tosData->getValid()) { const TosData last = _tosData->getLast(); add(destination, "price" + suffix, last.price); add(destination, "open" + suffix, last.open); add(destination, "volume" + suffix, last.volume); } bool valid; double value; _rsi5Data->getDouble(valid, value); if (valid) add(destination, "rsi_5" + suffix, value); _cci5Data->getDouble(valid, value); if (valid) add(destination, "cci_5" + suffix, value); _rsi2Data->getDouble(valid, value); if (valid) add(destination, "rsi_2" + suffix, value); _cci2Data->getDouble(valid, value); if (valid) add(destination, "cci_2" + suffix, value); if (_initialSpike->found()) add(destination, "initial_price" + suffix, _initialSpike->initialPrice()); } bool ChrisPavelicCurrentState::triggered() const { if (!_initialSpike->found()) return false; if (!_tosData->getValid()) return false; const TosData last = _tosData->getLast(); if (last.price <= _previousClose + 1.00) return false; bool valid; double value; _rsi2Data->getDouble(valid, value); if (!valid) return false; if (value <= 60) return false; _cci2Data->getDouble(valid, value); if (!valid) return false; if (value <= 100) return false; return true; } ChrisPavelicCurrentState::ChrisPavelicCurrentState (DataNodeArgument const &args) : _symbol(args.getStringValue()) { DataNodeArgument factory; factory = createRsiFactory(5, 14).replace(symbolPlaceholder, _symbol); addAutoLink(factory.getValue< GenericDataNodeFactory >().find(_rsi5Data)); factory = createCciFactory(5, 20).replace(symbolPlaceholder, _symbol); addAutoLink(factory.getValue< GenericDataNodeFactory >().find(_cci5Data)); factory = createRsiFactory(2, 28).replace(symbolPlaceholder, _symbol); addAutoLink(factory.getValue< GenericDataNodeFactory >().find(_rsi2Data)); factory = createCciFactory(2, 20).replace(symbolPlaceholder, _symbol); addAutoLink(factory.getValue< GenericDataNodeFactory >().find(_cci2Data)); addAutoLink(GenericTosDataNode::find(_tosData, _symbol)); addAutoLink(ChrisPavelicInitialSpike::find(this, 0, _initialSpike, _symbol)); _previousClose = getPreviousClose(_symbol); TclList msg; msg<getDouble(valid, value); msg<<"rsi5"; if (valid) msg<getDouble(valid, value); msg<<"cci5"; if (valid) msg<getDouble(valid, value); msg<<"rsi2"; if (valid) msg<getDouble(valid, value); msg<<"cci2"; if (valid) msg< _all; std::set< ChrisPavelicCurrentState * > _primed; TimerThread::Helper _timer; void showList(); void onBroadcast(BroadcastMessage &message, int msgId); void onWakeup(int msgId); ChrisPavelicSortedList(DataNodeArgument const &args); friend class DataNode; public: static DataNodeLink *find(std::string const &fileName) { ChrisPavelicSortedList *node; return findHelper(NULL, 0, node, fileName); } }; void ChrisPavelicSortedList::onWakeup(int msgId) { _primed.insert(_all[msgId]); TclList msg; msg<getTimerThread()) { const std::string filename = args.getStringValue(); std::ifstream stream(filename.c_str(), std::ios_base::in); while(stream) { std::string symbol; std::getline(stream, symbol); symbol = strtoupper(trim(symbol, " \r")); if (!symbol.empty()) { ChrisPavelicCurrentState *node; addAutoLink(ChrisPavelicCurrentState::find(this, _all.size(), node, symbol)); _all.push_back(node); } } ReportAlertsThread::getInstance()->setCacheable("ChrisPavelic-SortedList"); registerForBroadcast(_timer, 0); getManager()->getTimerThread().requestPeriodicBroadcast(_timer, 10000); } void ChrisPavelicSortedList::showList() { std::set< std::pair< double, ChrisPavelicCurrentState * > > sorted; for (auto it = _primed.cbegin(); it != _primed.end(); it++) { ChrisPavelicCurrentState *const item = *it; if (item->triggered()) sorted.insert({item->upFromOpen(), item}); } std::string body; int row = 0; for (auto it = sorted.rbegin(); it != sorted.rend(); it++, row++) { ChrisPavelicCurrentState *const item = it->second; const std::string suffix = "_" + ntoa(row); item->addStatus(body, suffix); } ReportAlertsThread::getInstance() ->reportAlert("ChrisPavelic-SortedList", body); } void ChrisPavelicSortedList::onBroadcast(BroadcastMessage &message, int msgId) { // Timer! showList(); } ///////////////////////////////////////////////////////////////////// // ChrisPavelicAlert ///////////////////////////////////////////////////////////////////// class ChrisPavelicAlert : public DataNode { private: ChrisPavelicCurrentState *_currentState; GenericTosDataNode *_tosData; double _lastHigh; double _nextInterestingHigh; double _lastInterestingHigh; time_t _lastInterestingHighTime; int _newHighCount; void onWakeup(int msgId); ChrisPavelicAlert(DataNodeArgument const &args); friend class DataNode; public: static DataNodeLink *find(std::string const &symbol) { ChrisPavelicAlert *node; return findHelper(NULL, 0, node, symbol); } }; ChrisPavelicAlert::ChrisPavelicAlert(DataNodeArgument const &args) : _lastHigh(0), _nextInterestingHigh(0), _lastInterestingHigh(0), _lastInterestingHighTime(0), _newHighCount(-1) { const std::string symbol = args.getStringValue(); addAutoLink(GenericTosDataNode::find(this, 0, _tosData, symbol)); addAutoLink(ChrisPavelicCurrentState::find(NULL, 0, _currentState, symbol)); } void ChrisPavelicAlert::onWakeup(int msgId) { if (!_tosData->getValid()) return; const TosData last = _tosData->getLast(); bool newHigh = false; if (last.high > _lastHigh) { _lastHigh = last.high; _newHighCount++; newHigh = true; } else if (last.high < _lastHigh) // This happens. Usually a bad print was corrected. Often something // really far out, so if you didn't reset this, you wouldn't see anything // higher all day. _lastHigh = last.high; if (!newHigh) return; if (!_currentState->triggered()) return; std::string body; _currentState->addStatus(body, ""); add(body, "new_high_count", _newHighCount); add(body, "time", formatTime(time(NULL))); ReportAlertsThread::getInstance() ->reportAlert("ChrisPavelic-High", body); double nextInterestingHigh = _nextInterestingHigh; static const double PADDING = 0.00001; if (nextInterestingHigh == 0) { if (last.open == 0) return; else // We have to be up at least $1.00 from the open or nothing will // trigger. The next cutoff is 1.25 above the open. We only get // here before the first such alert. As soon as we have one of these // alerts, _nextInterestingHigh will be set according to the price of // that alert. nextInterestingHigh = last.open + 1.25 - PADDING; } if (last.high >= nextInterestingHigh) { double lastInterestingHigh = _lastInterestingHigh; if (lastInterestingHigh == 0) lastInterestingHigh = last.open; add(body, "price_move", last.high - lastInterestingHigh); const time_t now = time(NULL); if (_lastInterestingHighTime) add(body, "move_time", now - _lastInterestingHighTime); add(body, "time", formatTime(now)); ReportAlertsThread::getInstance() ->reportAlert("ChrisPavelic-High25", body); _lastInterestingHigh = last.high; _lastInterestingHighTime = now; // Go up to the next multiple of $0.25. _nextInterestingHigh = (std::trunc(_lastInterestingHigh * 4) + 1) / 4 - PADDING; } } ///////////////////////////////////////////////////////////////////// // Global ///////////////////////////////////////////////////////////////////// static std::vector< DataNodeLink * > cleanup; void initChrisPavelic() { const std::string file = "data/ChrisPavelic_symbols.txt"; cleanup.push_back(ChrisPavelicSortedList::find(file)); std::ifstream stream(file.c_str(), std::ios_base::in); while(stream) { std::string symbol; std::getline(stream, symbol); symbol = strtoupper(trim(symbol, " \r")); if (!symbol.empty()) { cleanup.push_back(ChrisPavelicAlert::find(symbol)); } } }