#include "../data_framework/SimpleMarketData.h" #include "../data_framework/NormalVolumeBreakBars.h" #include "../alert_framework/AlertBase.h" #include "../data_framework/GenericL1Data.h" #include "SupportAndResistance.h" //////////////////////////////////////////////////////////////////// // SupportOrResistanceAccumulator //////////////////////////////////////////////////////////////////// class SupportOrResistanceAccumulator { private: struct SupportOrResistanceLine { double price; double reserved; int startIndex; }; int _nextIndex; int _direction; double _tickVol; std::vector< SupportOrResistanceLine > _lines; double _previousPreviousPrice; double _previousPrice; public: SupportOrResistanceAccumulator(); void addPoint(double price); bool breaksLine(double price) const; void brokenLineInfo(double currentPrice, double &linePrice, int &startingIndex) const; int getNextIndex() const { return _nextIndex; } double getTickVol() const { return _tickVol; } void reset(); void reset(bool top, double tickVol); }; void SupportOrResistanceAccumulator::reset() { _nextIndex = 0; _previousPreviousPrice = 0.0; _previousPrice = 0.0; _lines.clear(); } void SupportOrResistanceAccumulator::reset(bool top, double tickVol) { _direction = top?1:-1; _tickVol = tickVol; reset(); } SupportOrResistanceAccumulator::SupportOrResistanceAccumulator() : _direction(0), _tickVol(0.0) { reset(); } void SupportOrResistanceAccumulator::addPoint(double price) { for (unsigned int i = 0; i < _lines.size(); i++) if (price * _direction > _lines[i].price * _direction) { _lines.resize(i); break; } if (_nextIndex > 1) { if ((_previousPreviousPrice * _direction < _previousPrice * _direction) && (_previousPrice * _direction > price * _direction)) { // New local high if ((!_lines.empty()) && (_lines.rbegin()->reserved * _direction <= _previousPrice * _direction)) // In the shadow of the previous line. _lines.rbegin()->reserved = _previousPrice - _tickVol * _direction; else { // Create a new line. SupportOrResistanceLine line; line.price = _previousPrice; line.reserved = _previousPrice - _tickVol * _direction; line.startIndex = _nextIndex - 1; _lines.push_back(line); } } } _nextIndex++; _previousPreviousPrice = _previousPrice; _previousPrice = price; } bool SupportOrResistanceAccumulator::breaksLine(double price) const { if (_lines.empty()) return false; return price * _direction > _lines.rbegin()->price * _direction; } void SupportOrResistanceAccumulator::brokenLineInfo(double currentPrice, double &linePrice, int &startingIndex) const { linePrice = 0.0; startingIndex = 0; for (std::vector< SupportOrResistanceLine >::const_iterator it = _lines.begin(); it != _lines.end(); it++) { if (currentPrice * _direction > it->price * _direction) { linePrice = it->price; startingIndex = it->startIndex; break; } } } //////////////////////////////////////////////////////////////////// // SupportOrResistance // // This data node notifies its listeners exactly once every time // the underlying volume bar data node updates, regardless of any // values in the input or output of this data node. // It does not notify its listeners at any other time. //////////////////////////////////////////////////////////////////// class SupportOrResistance : public DataNode { private: bool _top; double _volatility; NormalVolumeBreakBars *_barData; SupportOrResistanceAccumulator _lines; bool _brokeLine; double _brokenLinePrice; time_t _brokenLineStartTime; Integer _brokenLineVolume; double _brokenLineQuality; double getPrice(VolumeBlock const &bar); void updateCurrentState(); void onWakeup(int msgId); SupportOrResistance(DataNodeArgument const &args); friend class DataNode; public: static DataNodeLink *find(DataNodeListener *listener, int msgId, SupportOrResistance *&node, std::string const &symbol, bool top); bool getBrokeLine() { return _brokeLine; } double getBrokenLinePrice() { return _brokenLinePrice; } time_t getBrokenLineStartTime() { return _brokenLineStartTime; } Integer getBrokenLineVolume() { return _brokenLineVolume; } double getBrokenLineQuality() { return _brokenLineQuality; } void testPrice(double price, bool &breaksALine, double &linePrice, double &lineQuality, Integer &lineVolume, time_t &lineStartTime); }; inline double SupportOrResistance::getPrice(VolumeBlock const &bar) { return _top?bar.high:bar.low; } void SupportOrResistance::testPrice (double price, bool &breaksALine, double &linePrice, double &lineQuality, Integer &lineVolume, time_t &lineStartTime) { if (_lines.breaksLine(price)) { breaksALine = true; int startingIndex; _lines.brokenLineInfo(price, linePrice, startingIndex); lineQuality = (_lines.getNextIndex() - startingIndex + 1) / 4.0; lineVolume = (_lines.getNextIndex() - startingIndex + 1) * _barData->getGroupBy(); lineStartTime = _barData->getBlocks()[startingIndex].startTime; } else { breaksALine = false; linePrice = 0.0; lineQuality = 0.0; lineVolume = 0; lineStartTime = 0; } } void SupportOrResistance::updateCurrentState() { _brokeLine = false; _brokenLinePrice = 0.0; VolumeBlocks const &bars = _barData->getBlocks(); if (_lines.getNextIndex() > (int)bars.size()) { // This is part of the contract with the NormalVolumeBreakBars. If the // data shrinks, it will go to 0. That makes the code much simpler. assert(bars.empty()); _lines.reset(); } else { for (unsigned int i = _lines.getNextIndex(); i < bars.size(); i++) { const double price = getPrice(bars[i]); if (_lines.breaksLine(price)) { _brokeLine = true; int lineStartingIndex; _lines.brokenLineInfo(price, _brokenLinePrice, lineStartingIndex); _brokenLineStartTime = bars[lineStartingIndex].startTime; _brokenLineVolume = (_lines.getNextIndex() - lineStartingIndex + 1) * _barData->getGroupBy(); _brokenLineQuality = (_lines.getNextIndex() - lineStartingIndex + 1) / 4.0; } _lines.addPoint(price); } } } void SupportOrResistance::onWakeup(int msgId) { // newBarData updateCurrentState(); notifyListeners(); } SupportOrResistance::SupportOrResistance(DataNodeArgument const &args) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 2); // Symbol, top. const std::string symbol = argList[0].getStringValue(); _top = argList[1].getBooleanValue(); _volatility = getTickVolatility(symbol); if (_volatility >= MIN_VOLATILITY) { _lines.reset(_top, _volatility); addAutoLink(NormalVolumeBreakBars::find(this, 0, _barData, symbol)); updateCurrentState(); } } DataNodeLink *SupportOrResistance::find(DataNodeListener *listener, int msgId, SupportOrResistance *&node, std::string const &symbol, bool top) { return findHelper(listener, msgId, node, argList(symbol, top)); } //////////////////////////////////////////////////////////////////// // BrokeSupportOrResistanceConfirmed //////////////////////////////////////////////////////////////////// class BrokeSupportOrResistanceConfirmed : public Alert { private: bool _top; SupportOrResistance *_lines; void onWakeup(int msgId); BrokeSupportOrResistanceConfirmed(DataNodeArgument const &args); friend class GenericDataNodeFactory; }; void BrokeSupportOrResistanceConfirmed::onWakeup(int msgId) { if (_lines->getBrokeLine() && (_lines->getBrokenLineQuality() >= 8)) { const std::string price = formatPrice(_lines->getBrokenLinePrice()); std::string msg = _top?"above resistance":"below support"; msg = "Crossed " + msg + ", " + price + ", confirmed by volume. Time: " + durationString(_lines->getBrokenLineStartTime(), getSubmitTime()) + ". Volume: " + addCommas(_lines->getBrokenLineVolume()); std::string altMsg = "p="; altMsg += price; altMsg += "&d="; altMsg += ntoa(getSubmitTime() - _lines->getBrokenLineStartTime()); altMsg += "&v="; altMsg += ntoa(_lines->getBrokenLineVolume()); report(msg, altMsg, _lines->getBrokenLineQuality()); } } BrokeSupportOrResistanceConfirmed::BrokeSupportOrResistanceConfirmed (DataNodeArgument const &args) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 2); // Symbol, top. const std::string symbol = argList[0].getStringValue(); _top = argList[1].getBooleanValue(); addAutoLink(SupportOrResistance::find(this, 0, _lines, symbol, _top)); } //////////////////////////////////////////////////////////////////// // BrokeSupportOrResistance //////////////////////////////////////////////////////////////////// class BrokeSupportOrResistance : public Alert { private: SupportOrResistance *_lines; GenericL1DataNode *_l1Data; bool _top; double _extremePriceSeen; double _extremeLineBroken; bool _extremePriceIsValid; bool _extremeLineIsValid; bool _lastPriceWasValid; void resetPrices(); bool moreReportable(double newValue, double oldValue); bool moreReportable(double newValue, bool newValid, double oldValue, bool oldValid); void newL1Data(); enum { wSupportOrResistance, wL1Data }; void onWakeup(int msgId); void getCurrentPrice(bool &valid, double &price); BrokeSupportOrResistance(DataNodeArgument const &args); friend class GenericDataNodeFactory; }; void BrokeSupportOrResistance::resetPrices() { getCurrentPrice(_extremePriceIsValid, _extremePriceSeen); if (_extremePriceIsValid) { _lastPriceWasValid = true; double lineQuality; Integer lineVolume; time_t lineStartTime; _lines->testPrice(_extremePriceSeen, _extremeLineIsValid, _extremeLineBroken, lineQuality, lineVolume, lineStartTime); } else { _lastPriceWasValid = false; _extremeLineIsValid = false; _extremeLineBroken = 0.0; } } bool BrokeSupportOrResistance::moreReportable(double newValue, double oldValue) { if (_top) return newValue > oldValue; else return newValue < oldValue; } bool BrokeSupportOrResistance::moreReportable(double newValue, bool newValid, double oldValue, bool oldValid) { if (newValid) if (oldValid) return moreReportable(newValue, oldValue); else return true; else return false; } void BrokeSupportOrResistance::newL1Data() { if (!_extremePriceIsValid) // We just started running, or for some other reason we have no data. resetPrices(); else { bool newPriceValid; double newPrice; getCurrentPrice(newPriceValid, newPrice); if (newPriceValid) { if (moreReportable(newPrice, _extremePriceSeen)) { _extremePriceSeen = newPrice; double newLine; bool newLineValid; double quality; Integer volume; time_t startTime; _lines->testPrice(newPrice, newLineValid, newLine, quality, volume, startTime); if (moreReportable(newLine, newLineValid, _extremeLineBroken, _extremeLineIsValid)) { _extremeLineBroken = newLine; _extremeLineIsValid = true; if (_lastPriceWasValid) { // If the datafeed is temporarily down, we can't report // an alert immediately on the upstroke. Otherwise, // if we are down for a while, we are likely to spew // out a lot of alerts all at once when we come up. const std::string price = formatPrice(_extremeLineBroken); std::string msg = "Crossed "; if (_top) msg += "above resistance"; else msg += "below support"; msg += ", "; msg += price; msg += ". Time: "; msg += durationString(startTime, getSubmitTime()); msg += ". Volume: "; msg += addCommas(volume); msg += '.'; std::string altMsg = "p="; altMsg += price; altMsg += "&d="; altMsg += ntoa(getSubmitTime() - startTime); altMsg += "&v="; altMsg += ntoa(volume); report(msg, altMsg, quality); } } } } _lastPriceWasValid = newPriceValid; } } void BrokeSupportOrResistance::onWakeup(int msgId) { switch (msgId) { case wSupportOrResistance: resetPrices(); break; case wL1Data: newL1Data(); break; } } void BrokeSupportOrResistance::getCurrentPrice(bool &valid, double &price) { valid = false; if (_l1Data->getValid()) { L1Data const ¤t = _l1Data->getCurrent(); if ((current.bidPrice > 0) && (current.askPrice > 0)) { valid = true; if (_top) price = std::min(current.bidPrice, current.askPrice); else price = std::max(current.bidPrice, current.askPrice); } } } BrokeSupportOrResistance::BrokeSupportOrResistance (DataNodeArgument const &args) : _extremePriceSeen(0.0), _extremeLineBroken(0.0), _extremePriceIsValid(false), _extremeLineIsValid(false), _lastPriceWasValid(false) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 2); // Symbol, top. const std::string symbol = argList[0].getStringValue(); _top = argList[1].getBooleanValue(); addAutoLink(SupportOrResistance::find(this, wSupportOrResistance, _lines, symbol, _top)); addAutoLink(GenericL1DataNode::find(this, wL1Data, _l1Data, symbol)); newL1Data(); } //////////////////////////////////////////////////////////////////// // Initialization //////////////////////////////////////////////////////////////////// void initializeSupportAndResistance() { GenericDataNodeFactory::sf< BrokeSupportOrResistanceConfirmed > ("CrossedAboveResistanceConfirmed", symbolPlaceholderObject, true); GenericDataNodeFactory::sf< BrokeSupportOrResistanceConfirmed > ("CrossedBelowSupportConfirmed", symbolPlaceholderObject, false); GenericDataNodeFactory::sf< BrokeSupportOrResistance > ("CrossedAboveResistance", symbolPlaceholderObject, true); GenericDataNodeFactory::sf< BrokeSupportOrResistance > ("CrossedBelowSupport", symbolPlaceholderObject, false); }