#include "../alert_framework/AlertBase.h" #include "../data_framework/VolumeBlockMinAndMax.h" #include "../data_framework/GenericTosData.h" #include "FibBuySell.h" //////////////////////////////////////////////////////////////////// // FibBuySellSignal //////////////////////////////////////////////////////////////////// class FibBuySellSignal : public Alert { private: GenericTosDataNode *_tosData; VolumeBlockMinAndMax *_turningData; double _pullback; std::string _pullbackString; enum { wTos, wTurning }; void onTos(); void onWakeup(int msgId); protected: struct FibSupportAndResistance { int firstIndex; double firstPoint; double secondPoint; double thirdMustPass; }; typedef std::vector< FibSupportAndResistance > GoalSet; double getPullback() const { return _pullback; } std::string const &getPullbackString() const { return _pullbackString; } VolumeBlockMinAndMax *getTurningData() const { return _turningData; } VolumeBlockMinAndMax::TurningPoints _adjustedTurningPoints; GoalSet _lookingFor; void deleteGoal(int index); virtual void newPrint(double price) =0; virtual void newVolumeBlock() =0; FibBuySellSignal(DataNodeArgument const &args); }; FibBuySellSignal::FibBuySellSignal(DataNodeArgument const &args) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 3); // Symbol, Pullback, Pullback string std::string const &symbol = argList[0].getStringValue(); _pullback = argList[1].getDoubleValue(); _pullbackString = argList[2].getStringValue(); addAutoLink(GenericTosDataNode::find(this, wTos, _tosData, symbol)); addAutoLink(VolumeBlockMinAndMax::find(this, wTurning, _turningData, symbol)); } void FibBuySellSignal::onTos() { if (_tosData->getValid()) { TosData const &last = _tosData->getLast(); if (last.newPrint && (last.price != last.open)) // Don't let the opening print be the cause of this alert. We were // getting way too many alerts right at the open in the first few // seconds. Presumably these were real alerts, not a bug, but this // should at least spread out the alerts a little bit. Presumably // most of these will still fire soon. newPrint(_tosData->getLast().price); } } void FibBuySellSignal::onWakeup(int msgId) { switch (msgId) { case wTos: onTos(); break; case wTurning: newVolumeBlock(); break; } } void FibBuySellSignal::deleteGoal(int index) { _lookingFor.erase(_lookingFor.begin() + index); } //////////////////////////////////////////////////////////////////// // FibBuySignal //////////////////////////////////////////////////////////////////// class FibBuySignal : public FibBuySellSignal { protected: virtual void newPrint(double price); virtual void newVolumeBlock(); private: void addPossibleTop(int startingIndex); void findGoals(); FibBuySignal(DataNodeArgument const &args); friend class GenericDataNodeFactory; }; void FibBuySignal::newPrint(double price) { VolumeBlocks const &allBlocks = getTurningData()->getAllBlocks(); for (int i = _lookingFor.size() - 1; i >= 0; i--) { FibSupportAndResistance const &possible = _lookingFor[i]; if (price > possible.secondPoint) deleteGoal(i); else if (price <= possible.thirdMustPass) { if ((i == 0) || (price > _lookingFor[i - 1].thirdMustPass) || (possible.secondPoint != _lookingFor[i - 1].secondPoint)) { const std::string firstPoint = formatPrice(possible.firstPoint); const std::string secondPoint = formatPrice(possible.secondPoint); std::string msg = "Fibonacci buy signal. Retraced "; msg += getPullbackString(); msg += " from resistance ("; msg += secondPoint; msg += ") back to support ("; msg += firstPoint; msg += "). Started "; msg += durationString(allBlocks[possible.firstIndex].startTime, getSubmitTime()); msg += " ago."; std::string altMsg = "fp="; altMsg += firstPoint; altMsg += "&sp="; altMsg += secondPoint; altMsg += "&d="; altMsg += ntoa(getSubmitTime() - allBlocks[possible.firstIndex].startTime); report(msg, altMsg, (allBlocks.size() - possible.firstIndex) / 4.0); } deleteGoal(i); } } } void FibBuySignal::newVolumeBlock() { _lookingFor.clear(); _adjustedTurningPoints = getTurningData()->getTurningPoints(); if (!_adjustedTurningPoints.empty()) { VolumeBlockMinAndMax::TurningPoint const &last = *_adjustedTurningPoints.rbegin(); if (last.orientation == VolumeBlockMinAndMax::vboLow) addPossibleTop(last.index); findGoals(); } } void FibBuySignal::addPossibleTop(int startingIndex) { VolumeBlocks const &allBlocks = getTurningData()->getAllBlocks(); double highestPrice = allBlocks[startingIndex].high; int highestPriceIndex = startingIndex; for (unsigned int i = startingIndex + 1; i < allBlocks.size(); i++) if (allBlocks[i].high >= highestPrice) { highestPrice = allBlocks[i].high; highestPriceIndex = i; } VolumeBlockMinAndMax::TurningPoint next; next.price = highestPrice; next.index = highestPriceIndex; next.orientation = VolumeBlockMinAndMax::vboHigh; _adjustedTurningPoints.push_back(next); } void FibBuySignal::findGoals() { _lookingFor.clear(); _lookingFor.reserve(_adjustedTurningPoints.size() / 2); VolumeBlocks const &allBlocks = getTurningData()->getAllBlocks(); int possiblePivotIndex = _adjustedTurningPoints.size() - 1; int pivotIndex = possiblePivotIndex; double lowestAfterPivot = allBlocks[_adjustedTurningPoints[possiblePivotIndex].index].low; for (unsigned int i = _adjustedTurningPoints[possiblePivotIndex].index + 1; i < allBlocks.size(); i++) lowestAfterPivot = std::min(lowestAfterPivot, allBlocks[i].low); while (possiblePivotIndex > 0) { int possibleStartIndex = possiblePivotIndex - 1; if (_adjustedTurningPoints[possiblePivotIndex].price > _adjustedTurningPoints[pivotIndex].price) { for (int i = possiblePivotIndex; i < pivotIndex; i++) lowestAfterPivot = std::min(lowestAfterPivot, _adjustedTurningPoints[i].price); pivotIndex = possiblePivotIndex; } bool valid = true; for (int i = possibleStartIndex + 1; i < pivotIndex; i++) if (_adjustedTurningPoints[possibleStartIndex].price > _adjustedTurningPoints[i].price) { valid = false; break; } if (valid) { FibSupportAndResistance nextGoal; nextGoal.firstIndex = _adjustedTurningPoints[possibleStartIndex].index; nextGoal.firstPoint = _adjustedTurningPoints[possibleStartIndex].price; nextGoal.secondPoint = _adjustedTurningPoints[pivotIndex].price; nextGoal.thirdMustPass = nextGoal.secondPoint - (nextGoal.secondPoint - nextGoal.firstPoint) * getPullback(); if (lowestAfterPivot > nextGoal.thirdMustPass) _lookingFor.push_back(nextGoal); } possiblePivotIndex -= 2; } } FibBuySignal::FibBuySignal(DataNodeArgument const &args) : FibBuySellSignal(args) { newVolumeBlock(); } //////////////////////////////////////////////////////////////////// // FibSellSignal //////////////////////////////////////////////////////////////////// class FibSellSignal : public FibBuySellSignal { protected: virtual void newPrint(double price); virtual void newVolumeBlock(); private: void addPossibleBottom(int startingIndex); void findGoals(); FibSellSignal(DataNodeArgument const &args); friend class GenericDataNodeFactory; }; void FibSellSignal::newPrint(double price) { VolumeBlocks const &allBlocks = getTurningData()->getAllBlocks(); for (int i = _lookingFor.size() - 1; i >= 0; i--) { FibSupportAndResistance const &possible = _lookingFor[i]; if (price < possible.secondPoint) deleteGoal(i); else if (price >= possible.thirdMustPass) { if ((i == 0) || (price < _lookingFor[i - 1].thirdMustPass) || (possible.secondPoint != _lookingFor[i - 1].secondPoint)) { const std::string firstPoint = formatPrice(possible.firstPoint); const std::string secondPoint = formatPrice(possible.secondPoint); std::string msg = "Fibonacci sell signal. Retraced "; msg += getPullbackString(); msg += " from support ("; msg += secondPoint; msg += ") back to resistance ("; msg += firstPoint; msg += "). Started "; msg += durationString(allBlocks[possible.firstIndex].startTime, getSubmitTime()); msg += " ago."; std::string altMsg = "fp="; altMsg += firstPoint; altMsg += "&sp="; altMsg += secondPoint; altMsg += "&d="; altMsg += ntoa(getSubmitTime() - allBlocks[possible.firstIndex].startTime); report(msg, altMsg, (allBlocks.size() - possible.firstIndex) / 4.0); } deleteGoal(i); } } } void FibSellSignal::newVolumeBlock() { _lookingFor.clear(); _adjustedTurningPoints = getTurningData()->getTurningPoints(); if (!_adjustedTurningPoints.empty()) { VolumeBlockMinAndMax::TurningPoint const &last = *_adjustedTurningPoints.rbegin(); if (last.orientation == VolumeBlockMinAndMax::vboHigh) // We appear to be going down. But maybe we're going up. addPossibleBottom(last.index); findGoals(); } } void FibSellSignal::addPossibleBottom(int startingIndex) { VolumeBlocks const &allBlocks = getTurningData()->getAllBlocks(); double lowestPrice = allBlocks[startingIndex].low; int lowestPriceIndex = startingIndex; for (unsigned int i = startingIndex + 1; i < allBlocks.size(); i++) if (allBlocks[i].low <= lowestPrice) { lowestPrice = allBlocks[i].low; lowestPriceIndex = i; } VolumeBlockMinAndMax::TurningPoint next; next.price = lowestPrice; next.index = lowestPriceIndex; next.orientation = VolumeBlockMinAndMax::vboLow; _adjustedTurningPoints.push_back(next); } void FibSellSignal::findGoals() { _lookingFor.clear(); _lookingFor.reserve(_adjustedTurningPoints.size() / 2); VolumeBlocks const &allBlocks = getTurningData()->getAllBlocks(); int possiblePivotIndex = _adjustedTurningPoints.size() - 1; int pivotIndex = possiblePivotIndex; double highestAfterPivot = allBlocks[_adjustedTurningPoints[possiblePivotIndex].index].high; for (unsigned int i = _adjustedTurningPoints[possiblePivotIndex].index + 1; i < allBlocks.size(); i++) highestAfterPivot = std::max(highestAfterPivot, allBlocks[i].high); while (possiblePivotIndex > 0) { int possibleStartIndex = possiblePivotIndex - 1; if (_adjustedTurningPoints[possiblePivotIndex].price < _adjustedTurningPoints[pivotIndex].price) { for (int i = possiblePivotIndex; i < pivotIndex; i++) highestAfterPivot = std::max(highestAfterPivot, _adjustedTurningPoints[i].price); pivotIndex = possiblePivotIndex; } bool valid = true; for (int i = possibleStartIndex + 1; i < pivotIndex; i++) if (_adjustedTurningPoints[possibleStartIndex].price < _adjustedTurningPoints[i].price) { valid = false; break; } if (valid) { FibSupportAndResistance nextGoal; nextGoal.firstIndex = _adjustedTurningPoints[possibleStartIndex].index; nextGoal.firstPoint = _adjustedTurningPoints[possibleStartIndex].price; nextGoal.secondPoint = _adjustedTurningPoints[pivotIndex].price; nextGoal.thirdMustPass = nextGoal.secondPoint + (nextGoal.firstPoint - nextGoal.secondPoint) * getPullback(); if (highestAfterPivot < nextGoal.thirdMustPass) _lookingFor.push_back(nextGoal); } possiblePivotIndex -= 2; } } FibSellSignal::FibSellSignal(DataNodeArgument const &args) : FibBuySellSignal(args) { newVolumeBlock(); } //////////////////////////////////////////////////////////////////// // Initialization //////////////////////////////////////////////////////////////////// void initializeFibBuySell() { GenericDataNodeFactory::sf< FibBuySignal > ("FibBuy38", symbolPlaceholderObject, 0.381966, "38.1966%"); GenericDataNodeFactory::sf< FibBuySignal > ("FibBuy50", symbolPlaceholderObject, 0.5, "50%"); GenericDataNodeFactory::sf< FibBuySignal > ("FibBuy62", symbolPlaceholderObject, 0.618034, "61.8034%"); GenericDataNodeFactory::sf< FibBuySignal > ("FibBuy79", symbolPlaceholderObject, 0.786151, "78.6151%"); GenericDataNodeFactory::sf< FibSellSignal > ("FibSell38", symbolPlaceholderObject, 0.381966, "38.1966%"); GenericDataNodeFactory::sf< FibSellSignal > ("FibSell50", symbolPlaceholderObject, 0.5, "50%"); GenericDataNodeFactory::sf< FibSellSignal > ("FibSell62", symbolPlaceholderObject, 0.618034, "61.8034%"); GenericDataNodeFactory::sf< FibSellSignal > ("FibSell79", symbolPlaceholderObject, 0.786151, "78.6151%"); }