#include #include #include "../data_framework/StandardCandles.h" #include "../alert_framework/AlertBase.h" #include "../data_framework/GenericTosData.h" #include "../data_framework/SynchronizedTimers.h" #include "../data_framework/IntradaySma.h" #include "../data_framework/TradeDirection.h" #include "../../shared/SimpleLogFile.h" //////////////////////////////////////////////////////////////////// // StandardCandleChart // // For many of the candle alerts we look at a standard chart with 40 // candles. This seems to be implied in the definition of "big" and // "small" and other vague words used in the defintions associated with // these patterns. As a bonus, we weed out all of the stocks which // don't trade much because they won't have 40 consecutive candles! // Additionally, some of the alerts below take advantage of the fact // that they can look back a small fixed number of candles because they // know that we must have at least 40 candles. // // Because this is so standard, we make this a seperate data node, and // we avoid recomputing a lot of work each time. // // This is a source of a lot of these formulas: // http://www.candlestickforum.com/PPF/Parameters/16_332_/candlestick.asp // // This provides additional interesting information: // http://www.graspr.com/videos/32-How-to-Trade-the-Morning-Evening-Star-Candlestick-Pattern-1 //////////////////////////////////////////////////////////////////// class StandardCandleChart : public DataNode { private: double _chartHeight; StandardCandles *_candleData; void onWakeup(int msgId); StandardCandleChart(DataNodeArgument const &args); friend class DataNode; public: static DataNodeLink *find(DataNodeListener *listener, int msgId, StandardCandleChart *&node, std::string const &symbol, int minutesPerBar) { return findHelper(listener, msgId, node, argList(symbol, minutesPerBar)); } static DataNodeLink *find(DataNodeListener *listener, int msgId, StandardCandleChart *&node, DataNodeArgument const &args) { return findHelper(listener, msgId, node, args); } StandardCandles::CandleArray const &getCandles() const { return _candleData->getHistory(); } double getChartHeight() const { return _chartHeight; } int getMinutesPerBar() const { return _candleData->getMinutesPerBar(); } bool chartHeightValid() const { return _chartHeight > 0.0; } // Note: The Delphi version had a property called NewValidData. That is // moot now. The data will always be new in a callback. Because we do our // initialization all in one thread, we finish it before there are any // listeners. So just call chartHeightValid() if you need to have valid // data. // If you watch TOS data and the chart, you should probably look at this. bool isActive() const { return _candleData->isActive(); } static const unsigned int WIDTH = 40; }; void StandardCandleChart::onWakeup(int msgId) { // Do not check the epoch here. That only applies to filters, and other // places where we have multiple paths to the same data. Our users will // only request data from us when we call them. StandardCandles::CandleArray const &candles = _candleData->getHistory(); if (candles.size() < WIDTH) _chartHeight = 0.0; else { double chartTop = std::numeric_limits< double >::min(); double chartBottom = std::numeric_limits< double >::max(); for (unsigned int i = candles.size() - WIDTH; i < candles.size(); i++) { chartTop = std::max(chartTop, candles[i].high); chartBottom = std::min(chartBottom, candles[i].low); } _chartHeight = chartTop - chartBottom; } //TclList debug; //debug<<__FILE__<<__FUNCTION__<<__LINE__ // <<"_chartHeight"<<_chartHeight // <<"getMinutesPerBar()"<getLastTransition() // <<"historicalCandleCount()"<<_candleData->historicalCandleCount() // <<"getLastCandleStartTime()" // <getLastCandleStartTime()); //sendToLogFile(debug); notifyListeners(); } StandardCandleChart::StandardCandleChart(DataNodeArgument const &args) { addAutoLink(StandardCandles::find(this, 0, _candleData, args)); onWakeup(0); } //////////////////////////////////////////////////////////////////// // Doji //////////////////////////////////////////////////////////////////// class Doji : Alert { private: StandardCandleChart *_candleData; void onWakeup(int msgId); Doji(DataNodeArgument const &args); friend class GenericDataNodeFactory; }; Doji::Doji(DataNodeArgument const &args) { // Args should be symbol, minutesPerBar. addAutoLink(StandardCandleChart::find(this, 0, _candleData, args)); } void Doji::onWakeup(int msgId) { if (_candleData->chartHeightValid()) { StandardCandles::CandleArray const &array = _candleData->getCandles(); StandardCandles::SingleCandle const &last = array[array.size() - 1]; static const double MIN_HEIGHT = 0.09; const double interestingHeight = _candleData->getChartHeight() * MIN_HEIGHT; if ((last.open == last.close) && ((last.high - last.low) > interestingHeight)) { std::string msg; std::string altMsg; if (last.open == last.high) { msg = "Dragonfly Doji"; altMsg = "st=d"; // st is subtype } else if (last.open == last.low) { msg = "Gravestone Doji"; altMsg = "st=g"; } else if (((last.high - last.open) > interestingHeight) && ((last.open - last.low) > interestingHeight)) { msg = "Long-Legged Doji"; altMsg = "st=l"; } else // altMsg = "" msg = "Doji"; report(ntoa(_candleData->getMinutesPerBar()) + " minute " + msg, altMsg); } //else //{ // std::string msg = "Not a Doji. Open="; // msg += formatPrice(last.open, false); // msg += ", Close="; // msg += formatPrice(last.close, false); // msg += ", Height="; // msg += formatPrice(last.high - last.low); // msg += ", interestingHeight="; // msg += formatPrice(interestingHeight); // report(msg, 99); //} } } /* test code * * telnet chuck-liddell 1234 * Then the following about 45 times. * command=debug_standard_candles&symbol=TEST&minutes_per_bar=5&action=single&candles=40:50:30:40:10 * command=debug_standard_candles&symbol=TEST&minutes_per_bar=5&action=single&candles=50:50:30:50:10 * command=debug_standard_candles&symbol=TEST&minutes_per_bar=5&action=single&candles=30:50:30:30:10 * command=debug_standard_candles&symbol=TEST&minutes_per_bar=5&action=single&candles=31:50:30:31:10 */ //////////////////////////////////////////////////////////////////// // TraditionalChannelBreak //////////////////////////////////////////////////////////////////// class TraditionalChannelBreak : Alert { private: StandardCandleChart *_candleData; GenericTosDataNode *_tosData; int _lastReportLevel; int _previousLastReportLevel; bool _up; bool _currentlyConsolidated; double _basePrice; double _levelSize; enum { wCandleData, wTosData }; void newCandleData(); void newTosData(); void onWakeup(int msgId); TraditionalChannelBreak(DataNodeArgument const &args); friend class GenericDataNodeFactory; }; TraditionalChannelBreak::TraditionalChannelBreak (DataNodeArgument const &args) : _lastReportLevel(0), _previousLastReportLevel(0), _currentlyConsolidated(false), _basePrice(0.0), _levelSize(0.0) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 3); std::string const &symbol = argList[0].getStringValue(); int minutesPerBar = argList[1].getIntValue(); _up = argList[2].getBooleanValue(); addAutoLink(StandardCandleChart::find(this, wCandleData, _candleData, symbol, minutesPerBar)); addAutoLink(GenericTosDataNode::find(this, wTosData, _tosData, symbol)); newCandleData(); } void TraditionalChannelBreak::newCandleData() { _previousLastReportLevel = _lastReportLevel; _lastReportLevel = 0; _currentlyConsolidated = false; if (_candleData->chartHeightValid()) { StandardCandles::CandleArray const &candles = _candleData->getCandles(); _levelSize = _candleData->getChartHeight() * 0.09; unsigned int i = 1; double top = std::numeric_limits< double >::min(); double bottom = std::numeric_limits< double >::max(); while (true) { if (i > StandardCandleChart::WIDTH) break; StandardCandles::SingleCandle const &candle = candles[candles.size() - i]; double potentialTop = std::max(top, std::max(candle.open, candle.close)); double potentialBottom = std::min(bottom, std::min(candle.open, candle.close)); if ((potentialTop - potentialBottom) > _levelSize) break; top = potentialTop; bottom = potentialBottom; i++; } i--; // I is now the number of candles in the consolidation. _currentlyConsolidated = i >= 7; if (_up) _basePrice = top; else _basePrice = bottom; } else _currentlyConsolidated = false; } void TraditionalChannelBreak::newTosData() { if (!_currentlyConsolidated) return; if (_levelSize <= 0) return; if (!_tosData->getValid()) return; if (_tosData->getLast().formT) return; if (!_tosData->getLast().updatesLast) return; if (!_tosData->getLast().newPrint) return; double quality; if (_up) quality = _tosData->getLast().price - _basePrice; else quality = _basePrice - _tosData->getLast().price; // This will not throw an exception. If the number is too big, we will get // the smallest possible integer. (0xffffffff) const int level = (int)ceil(quality / _levelSize); if (level > _lastReportLevel) { if (level > _previousLastReportLevel) { std::string msg = ntoa(_candleData->getMinutesPerBar()); msg += _up?" minute consolidation breakout" :" minute consolidation breakdown"; if (level == 2) msg += " with follow through"; else if (level > 2) msg += " with significant follow through"; // Level might have some value beside the simple message. const std::string altMsg = "l=" + ntoa(level); report(msg, altMsg, quality); } _lastReportLevel = level; } } void TraditionalChannelBreak::onWakeup(int msgId) { switch (msgId) { case wCandleData: newCandleData(); break; case wTosData: newTosData(); break; } } //////////////////////////////////////////////////////////////////// // HammerOrHangingMan // // Definition of a hammer: // // Scale: Start with the standard 40 candles of history. The difference // between the highest high and the lowest low on the chart is the range for // the chart. // // Shape: Look at the last candle. There is no top wick. The size of the // bottom wick must be at least twice as big as the body. The size of the body // must be at least 2% of the range of the chart. (A Doji cannot be a // hammer.) // // Downtrend: Draw a box around the previous 9 candles. The bottom of that // box is the lowest low of those candles. The top is the highest high. Draw // a line separating the bottom quarter of the box from the top three quarters. // The top of the hammer must be below that line. // // Quality: // A = The length of the bottom wick of the last candle. // B = The bottom of the second to last candle minus the top of last candle. // This is the gap down, and it might be negative. // This is not our standard defintion of gap, but the low of the // previous, minus the high of the current. // C = The range for the chart // Quality = 100 * (A + (B / 2)) / C // Listed as percent. // // Definition of a hanging man: // // Scale and Shape: Same as hammer. // // Uptrend: Draw a box around the previous 9 candles. The bottom of that // box is the lowest low of those candles. The top is the highest high. Draw // a line separating the top quarter of the box from the bottom three quarters. // The bottom of the body of the last candle must be above that line. // // Quality: // A = The length of the bottom wick of the last candle. // B = The bottom of the body of the last candle minus the high of the second // to last candle. This is a distorted defintion of the gap up. // C = The range for the chart // Quality = 67 * (A + (B / 2)) / C // In the hammer A and B are competing, so we can never get above // 100%. In this pattern they can overlap, so we have to scale // the result back. // Listed as percent. // // In both cases quality cannot be negative. That makes the output and the // help less confusing for the user. // // Note: Some people have asked for more leniency in the definition of a // hammer. Some people say that you can have a small wick at the top. I've // had trouble encorporating that into the defintion. Most of the time the // body of the hammer is so small, than a wick of one penny would be // significant. If I allowed that, then there would be a lot more hammers. //////////////////////////////////////////////////////////////////// class HammerOrHangingMan : public Alert { private: StandardCandleChart *_candleData; bool _isHammer; double getCutoff(StandardCandles::CandleArray const &candles, double position); void onWakeup(int msgId); HammerOrHangingMan(DataNodeArgument const &args); friend class GenericDataNodeFactory; }; double HammerOrHangingMan::getCutoff (StandardCandles::CandleArray const &candles, double position) { // Precondition: We know that we have at least 10 candles. const int length = candles.size(); double highest = candles[length - 10].high; double lowest = candles[length - 10].low; for (int i = length - 9; i <= length - 2; i++) { highest = std::max(highest, candles[i].high); lowest = std::min(lowest, candles[i].low); } return (highest - lowest) * position + lowest; } void HammerOrHangingMan::onWakeup(int msgId) { if (!_candleData->chartHeightValid()) return; StandardCandles::CandleArray const &candles = _candleData->getCandles(); StandardCandles::SingleCandle const &lastCandle = candles[candles.size() - 1]; StandardCandles::SingleCandle const &previousCandle = candles[candles.size() - 2]; double lastBodyTop; double lastBodyBottom; if (lastCandle.open > lastCandle.close) { lastBodyTop = lastCandle.open; lastBodyBottom = lastCandle.close; } else { lastBodyTop = lastCandle.close; lastBodyBottom = lastCandle.open; } bool valid = // There is no top wick. (lastBodyTop == lastCandle.high) && // The size of the bottom wick must be at least twice as big as the body. ((lastBodyTop - lastBodyBottom) * 2 <= lastBodyBottom - lastCandle.low) && // The size of the body must be at least 2% of the range of the chart. (lastBodyTop - lastBodyBottom >= _candleData->getChartHeight() * 0.02); if (valid) { if (_isHammer) { const double gap = previousCandle.low - lastBodyTop; const double quality = (gap/2 + (lastBodyBottom - lastCandle.low)) / _candleData->getChartHeight() * 100.0; valid = quality > 0; if (valid) valid = lastCandle.high < getCutoff(candles, 0.25); if (valid) report("Hammer", quality); } else { const double gap = lastBodyBottom - previousCandle.high; const double quality = (gap/2 + (lastBodyBottom - lastCandle.low)) / _candleData->getChartHeight() * 100.0 * 2.0 / 3.0; valid = quality > 0; if (valid) valid = lastCandle.high > getCutoff(candles, 0.75); if (valid) report("Hanging Man", quality); } } } HammerOrHangingMan::HammerOrHangingMan(DataNodeArgument const &args) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 3); std::string const &symbol = argList[0].getStringValue(); const int minutesPerBar = argList[1].getIntValue(); _isHammer = argList[2].getBooleanValue(); addAutoLink(StandardCandleChart::find(this, 0, _candleData, symbol, minutesPerBar)); } //////////////////////////////////////////////////////////////////// // Engulfing // // Engulfing means that the top of the body of the last candle is higher // than the top of the body of the previous candle and the bottom of the // body of the last candle is lower than the body of the bottom of the // previous candle. // // Bullish engulfing additionally requires that the last candle's close be // higher than its open, and the previous candle's close be lower than its // open. (Last candle is white or green, previous candle is black or red.) // Bearish engulfing is exactly the opposite. // // Quality: // http://www.streetauthority.com/terms/engulfing-candles.asp // "The power of the engulfing candle is increased by two factors -- the // size of the candle and the volume on the day it occurs." //////////////////////////////////////////////////////////////////// class Engulfing : public Alert { private: StandardCandleChart *_candleData; bool _up; void onWakeup(int msgId); Engulfing(DataNodeArgument const &args); friend class GenericDataNodeFactory; }; void Engulfing::onWakeup(int msgId) { static const double MIN_FIRST_HEIGHT_RATIO = 0.025; static const double MIN_DIFFERENCE_RATIO = 0.01; if (!_candleData->chartHeightValid()) return; StandardCandles::CandleArray const &candles = _candleData->getCandles(); StandardCandles::SingleCandle const &lastCandle = candles[candles.size() - 1]; StandardCandles::SingleCandle const &previousCandle = candles[candles.size() - 2]; double lastBodyTop, lastBodyBottom, previousBodyTop, previousBodyBottom; if (_up) { lastBodyTop = lastCandle.close; lastBodyBottom = lastCandle.open; previousBodyTop = previousCandle.open; previousBodyBottom = previousCandle.close; } else { lastBodyTop = lastCandle.open; lastBodyBottom = lastCandle.close; previousBodyTop = previousCandle.close; previousBodyBottom = previousCandle.open; } const double minFirstHeight = _candleData->getChartHeight() * MIN_FIRST_HEIGHT_RATIO; const double minDifference = _candleData->getChartHeight() * MIN_DIFFERENCE_RATIO; if ((previousBodyTop - previousBodyBottom > minFirstHeight) && (lastBodyTop - previousBodyTop > minDifference) && (previousBodyBottom - lastBodyBottom > minDifference)) { double quality = (lastBodyTop - lastBodyBottom) / _candleData->getChartHeight() * 100.0; Integer recentVolume = 0; for (unsigned int i = candles.size() - 10; i < candles.size() - 1; i++) recentVolume += candles[i].volume; if (recentVolume > 0) quality = std::min(100.0, quality * 0.5 + lastCandle.volume * 100.0 / recentVolume); report(_up?"Bullish Engulfing":"Bearish Engulfing", quality); } } Engulfing::Engulfing(DataNodeArgument const &args) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 3); // Symbol, Minutes / Bar, Up std::string const &symbol = argList[0].getStringValue(); const int minutesPerBar = argList[1].getIntValue(); _up = argList[2].getBooleanValue(); addAutoLink(StandardCandleChart::find(this, 0, _candleData, symbol, minutesPerBar)); } //////////////////////////////////////////////////////////////////// // DarkCloudCoverOrPeircing // // Dark cloud cover is a white candle followed by a black candle. The // white candle should have a large body. We will say a minumum of 4% // of the height of the chart. The open of the second candle must // be higher than the high of the previous candle. The close must be // in the lower half of the body of the candle. Being closer to the bottom // is better. // // I add the case that the top of the body of the second candle must be // at least 0.5% over the top of the body of the first, to match the sample // charts better. I also added the requirement that the bottom of the // last candle body must be above the bottom of the previous candle body, to // match the charts. // // Quality: // // http://www.streetauthority.com/terms/darkcloudcover.asp : The larger the penetration of the previous candle (that is, the closer this candle is to being a bearish engulfing), the more powerful the signal. // It also says that the larger the first candle is, the better. // // http://candlestickforum.com/PPF/Parameters/16_498_/candlestick.asp : // Talks about those two, plus the volume of the last two candles should be // above average, plus the size of the gap is helpful. // // We use the size of the body of the first candle directly. This is worth 25% // // We use the size of the body of the second candle for two purposes. A bigger // candle may mean a bigger gap, or a bigger penetration. As either of those // grows, so does the candle. This is worth 25% // // Finally we comare the last two candle's volume to the last 20. If the // average of the last two is the same and the average of the previous 18, // then that's 50%. But it's not bounded, so we have to limit it ourselves. // // Dark cloud cover is negative. The chart was going up, but now it's going // down. // // Peircing pattern. Same thing but up side down. //////////////////////////////////////////////////////////////////// class DarkCloudCoverOrPeircing : public Alert { private: StandardCandleChart *_candleData; bool _up; void onWakeup(int msgId); DarkCloudCoverOrPeircing(DataNodeArgument const &args); friend class GenericDataNodeFactory; }; void DarkCloudCoverOrPeircing::onWakeup(int msgId) { if (!_candleData->chartHeightValid()) return; StandardCandles::CandleArray const &candles = _candleData->getCandles(); StandardCandles::SingleCandle const &lastCandle = candles[candles.size() - 1]; StandardCandles::SingleCandle const &previousCandle = candles[candles.size() - 2]; double lastBodyTop, lastBodyBottom, previousCandleHigh, previousBodyTop, previousBodyBottom; if (_up) { // Flip the image up side down so that we can use the Dark Cloud // Covering definition from above. lastBodyTop = -lastCandle.open; lastBodyBottom = -lastCandle.close; previousBodyTop = -previousCandle.close; previousBodyBottom = -previousCandle.open; previousCandleHigh = -previousCandle.low; } else { // This is really a Dark Cloud Covering. lastBodyTop = lastCandle.open; lastBodyBottom = lastCandle.close; previousBodyTop = previousCandle.close; previousBodyBottom = previousCandle.open; previousCandleHigh = previousCandle.high; } const double previousBodyMiddle = (previousBodyTop + previousBodyBottom) / 2.0; if ((previousBodyTop - previousBodyBottom > _candleData->getChartHeight() * 0.04) && (lastBodyTop > previousCandleHigh) && (lastBodyTop - previousBodyTop > _candleData->getChartHeight() * 0.005) && (previousBodyMiddle > lastBodyBottom) && (lastBodyBottom > previousBodyBottom)) { double quality = ((lastBodyTop - lastBodyBottom) + (previousBodyTop - previousBodyBottom)) / _candleData->getChartHeight() * 50.0; Integer recentVolume = 0; for (unsigned int i = candles.size() - 20; i < candles.size() - 1; i++) recentVolume += candles[i].volume; if (recentVolume > 0) quality = std::min(100.0, quality * 0.5 + (lastCandle.volume + previousCandle.volume) * 100.0 / recentVolume); report(_up?"Piercing Pattern":"Dark Cloud Cover", quality); } } DarkCloudCoverOrPeircing::DarkCloudCoverOrPeircing (DataNodeArgument const &args) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 3); // Symbol, Minutes / Bar, Up std::string const &symbol = argList[0].getStringValue(); const int minutesPerBar = argList[1].getIntValue(); _up = argList[2].getBooleanValue(); addAutoLink(StandardCandleChart::find(this, 0, _candleData, symbol, minutesPerBar)); } //////////////////////////////////////////////////////////////////// // ToppingTail // // The definition of a topping tail seems to be a bit vauge. // https://www.shadowtrader.net/index.aspx?pg=glossary.html has some // specifics, but that seems to be contradicted by some of the pictures // in http://www.billnewell.net/core_trading_velez.pdf. The second // document seems especially vague and self contradictory, but it was // written by Oliver Valez, and we are creating this alert in large part // for Oliver. The definition below the best I could get from that // document. I took the spirit of the words, and stretched out limits // to match the worst of the pictures. // // For a topping tail we need the last bar to be at least 50% as big (from // high to low) as the previous bar. The three bars before the last bar need // to be green, close above open. The top wick (the "tail") of the last // candle has to be at least 0.6 times the size of the body of the last // candle, and at least 1/3 of the entire size of the last candle. // // Here were my initial thoughts on quality. Unfortunately all the stuff // that sounds good would have gotten rid of a lot // of the examples in that paper. It would be nice to see each candle // above the previous one. (Higher highs and higher lows.) It would // be nice to see the bars each at least 5% of the size of the chart. // The top tail of the last candle should be as long as possible, // compared to the chart as a whole. The body of the last candle should // be small compared to the chart as a whole. Having more than 3 // consecutive green candles before is good; more is better. If the // second to last candle as a good tail that's good. More in a row is // better. // // After viewing some a lot of examples from the alert server, I came // up with simpler quality standards. The wick of the last bar should // be as big as possible. The three previous bars should be as big as // possible. This is simple. It makes the pattern obvious on the // chart, so it looks good. And it gets rid of some of the random // stuff. We look at the entire candle for the first three, not just // the wick or the body. // // A Bottoming Tail is exactly the same as a Topping Tail, but the chart // is flipped over. //////////////////////////////////////////////////////////////////// class ToppingTail : public Alert { private: StandardCandleChart *_candleData; bool _top; bool correctDirection(StandardCandles::SingleCandle const &candle); void onWakeup(int msgId); ToppingTail(DataNodeArgument const &args); friend class GenericDataNodeFactory; }; bool ToppingTail::correctDirection(StandardCandles::SingleCandle const &candle) { if (_top) // We are looking for a green candle. return candle.close > candle.open; else return candle.open > candle.close; } void ToppingTail::onWakeup(int msgId) { if (!_candleData->chartHeightValid()) return; StandardCandles::CandleArray const &candles = _candleData->getCandles(); const int lastIndex = candles.size() - 1; if (!(correctDirection(candles[lastIndex - 1]) && correctDirection(candles[lastIndex - 2]) && correctDirection(candles[lastIndex - 3]))) return; StandardCandles::SingleCandle const &lastCandle = candles[lastIndex]; const double lastBodySize = fabs(lastCandle.close - lastCandle.open); double lastWickSize; if (_top) lastWickSize = lastCandle.high - std::max(lastCandle.open, lastCandle.close); else lastWickSize = std::min(lastCandle.open, lastCandle.close) - lastCandle.low; const double lastCandleSize = lastCandle.high - lastCandle.low; StandardCandles::SingleCandle const &previousCandle = candles[lastIndex - 1]; const double prevCandleSize = previousCandle.high - previousCandle.low; if ((lastCandleSize >= prevCandleSize * 0.5) && (lastWickSize >= lastBodySize * 0.6) && (lastWickSize >= lastCandleSize * 0.3333333)) { // Quality can be anywhere between 0 and 100. Each of the // last four candles contributes 0-25. For the most recent // candle we look at the height of the tail relative to the // height of the entire chart. For the three previous candles // we look at the height of the entire candle, low to high. double quality = lastWickSize + prevCandleSize; quality += candles[lastIndex - 2].high - candles[lastIndex - 2].low; quality += candles[lastIndex - 3].high - candles[lastIndex - 3].low; quality = quality / _candleData->getChartHeight() * 25.0; report(_top?"Topping Tail":"Bottoming Tail", quality); } } ToppingTail::ToppingTail(DataNodeArgument const &args) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 3); // Symbol, Minutes / Bar, Top std::string const &symbol = argList[0].getStringValue(); const int minutesPerBar = argList[1].getIntValue(); _top = argList[2].getBooleanValue(); addAutoLink(StandardCandleChart::find(this, 0, _candleData, symbol, minutesPerBar)); } //////////////////////////////////////////////////////////////////// // NarrowRangeBar // // These defintions come from a private memo from Oliver Velez at VCM: // // Narrow Range Buy Bar Scan: // Three Green Bars (close above open) or more followed by a bar that is // smaller (from high to low) than 25% of the average range of the past // 5 bars. // // Narrow Range Sell Bar Scan: // The same applies in reverse. See above // // I'm using the size of the bars as the quality, based on the way // we do it in TToppingTail. //////////////////////////////////////////////////////////////////// class NarrowRangeBar : public Alert { private: StandardCandleChart *_candleData; bool _buy; void onWakeup(int msgId); NarrowRangeBar(DataNodeArgument const &args); friend class GenericDataNodeFactory; }; void NarrowRangeBar::onWakeup(int msgId) { if (!_candleData->chartHeightValid()) return; StandardCandles::CandleArray const &candles = _candleData->getCandles(); const int lastIndex = candles.size() - 1; // 0 is one candle from the end, 1 is two candles from the end, etc. double bodyHeight[3]; for (int i = 0; i < 3; i++) { StandardCandles::SingleCandle const &candle = candles[lastIndex - 1 - i]; if (_buy) // Looking for a green candle. bodyHeight[i] = candle.close - candle.open; else // Looking for a red candle. bodyHeight[i] = candle.open - candle.close; if (bodyHeight[i] <= 0) return; } double previousHeights = 0; for (int i = 1; i <= 5; i++) { StandardCandles::SingleCandle const &candle = candles[lastIndex - i]; previousHeights += candle.high - candle.low; } const double lastHeight = candles[lastIndex].high - candles[lastIndex].low; if (lastHeight * 20 >= previousHeights) return; // Quality can be anywhere between 0 and 100. // The last bar contributes 0-20 based on how small it is. If the // high and low are the same, we get 20. If the size is the maximum // allowed to trigger the alert, we get 0. // Then we look at the body height of the previous three candles. // They should each be red or green depending on the direction of the // alert. If the body height is the entire height of the graph then // we call the candle "very" red or green and we add 10 for that // candle. As the body height goes closer to 0, the contribution to // quality approaches 0. // Finally we look at the total height of the last 5 candles // preceeding the most recent candle. Each of these can contribute // up to 10 each to the quality. If the height of the candle is the // entire height of the chart then the candle makes the full // contribution. const double quality = (previousHeights + bodyHeight[0] + bodyHeight[1] + bodyHeight[2]) * 10 / _candleData->getChartHeight() + 20 - lastHeight / previousHeights * 400; report(_buy?"Narrow Range Buy Bar":"Narrow Range Sell Bar", quality); } NarrowRangeBar::NarrowRangeBar(DataNodeArgument const &args) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 3); // Symbol, Minutes / Bar, Buy std::string const &symbol = argList[0].getStringValue(); const int minutesPerBar = argList[1].getIntValue(); _buy = argList[2].getBooleanValue(); addAutoLink(StandardCandleChart::find(this, 0, _candleData, symbol, minutesPerBar)); } //////////////////////////////////////////////////////////////////// // RisingSma //////////////////////////////////////////////////////////////////// class RisingSma : public Alert { private: StandardCandleChart *_candleData; bool _buy; int _consecutive; void onWakeup(int msgId); RisingSma(DataNodeArgument const &args); friend class GenericDataNodeFactory; }; void RisingSma::onWakeup(int msgId) { int previousConsecutive = _consecutive; // There are multiple ways to fail, and only one way to succeed, so we // assume failure until proven otherwise. _consecutive = 0; if (!_candleData->chartHeightValid()) return; StandardCandles::CandleArray const &candles = _candleData->getCandles(); if (candles.size() < 205) return; const int lastIndex = candles.size() - 1; if (_buy) for (int i = lastIndex - 4; i <= lastIndex; i++) { if (candles[i].close <= candles[i - 8].close) return; if (candles[i].close <= candles[i - 20].close) return; } else for (int i = lastIndex - 4; i <= lastIndex; i++) { if (candles[i].close >= candles[i - 8].close) return; if (candles[i].close >= candles[i - 20].close) return; } if (previousConsecutive == 0) _consecutive = 5; else _consecutive = previousConsecutive + 1; std::string consecutiveStr; switch (_consecutive) { case 5: consecutiveStr = "5"; break; case 8: consecutiveStr = "8"; break; case 13: consecutiveStr = "13"; break; case 21: consecutiveStr = "21"; break; case 34: consecutiveStr = "34"; break; case 55: consecutiveStr = "55"; break; case 89: consecutiveStr = "89"; break; case 144: consecutiveStr = "144"; break; default: return; } double quality = 0.0; for (int i = lastIndex - 4; i <= lastIndex; i++) quality += candles[i].close - candles[i - 200].close; quality = 100.0 - fabs(quality / _candleData->getChartHeight() / 2.0); report((_buy?"8 and 20 period SMA both sloping up. ": "8 and 20 period SMA both sloping down. ") + consecutiveStr + " periods.", "p=" + consecutiveStr, quality); } RisingSma::RisingSma(DataNodeArgument const &args) : _consecutive(0) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 3); // Symbol, Minutes / Bar, Buy std::string const &symbol = argList[0].getStringValue(); const int minutesPerBar = argList[1].getIntValue(); _buy = argList[2].getBooleanValue(); addAutoLink(StandardCandleChart::find(this, 0, _candleData, symbol, minutesPerBar)); } //////////////////////////////////////////////////////////////////// // WideRangeBar // // This is the definition of Wide Range Bar that Oliver publishes: // WRB -- the range of the bar is much greater than the average bar // // This is what he sent us: // A candle where each tail is less than 15% of the total bar, and the // total bar is at least twice the average of the last 20 bars. //////////////////////////////////////////////////////////////////// class WideRangeBar : public Alert { private: StandardCandleChart *_candleData; void onWakeup(int msgId); WideRangeBar(DataNodeArgument const &args); friend class GenericDataNodeFactory; }; void WideRangeBar::onWakeup(int msgId) { if (!_candleData->chartHeightValid()) return; // We only need 21 candles for this alert. StandardCandleChart // assures us that we have 40. StandardCandles::CandleArray const &candles = _candleData->getCandles(); const int lastIndex = candles.size() - 1; StandardCandles::SingleCandle const &lastCandle = candles[lastIndex]; const double lastRange = lastCandle.high - lastCandle.low; if (lastRange <= 0.0) return; const double topWickSize = lastCandle.high - std::max(lastCandle.open, lastCandle.close); if (topWickSize > 0.15 * lastRange) return; const double bottomWickSize = std::min(lastCandle.open, lastCandle.close) - lastCandle.low; if (bottomWickSize > 0.15 * lastRange) return; double averageRange = 0.0; for (int i = lastIndex - 20; i < lastIndex; i++) averageRange += candles[i].high - candles[i].low; averageRange /= 20.0; if (lastRange > averageRange * 2) report("Wide Range Bar", lastRange); } WideRangeBar::WideRangeBar(DataNodeArgument const &args) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 2); // Symbol, Minutes / Bar std::string const &symbol = argList[0].getStringValue(); const int minutesPerBar = argList[1].getIntValue(); addAutoLink(StandardCandleChart::find(this, 0, _candleData, symbol, minutesPerBar)); } //////////////////////////////////////////////////////////////////// // GreenBarReversal //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// // RunningUpNow // // This is intended to be a very short term running alert. It's meant // to be very simple and easy to understand. Because the traditional // channel break works so well, this new alert is based on that. This // alert is basically a channel break without the requirement of a // channel, first. // // We use the same requirements for the size of the move. We start by // looking at the height of a 5 minute chart. But, there is no // guarantee that the 5 minute chart is valid, so we try larger time- // frames after that. We always start with the smallest time-frame // to get a tigher size, closer to a minute chart. // // We reset every minute. The channel breaks reset every 5, 10, 15, or // 30 minutes. We look at the last price at the end of the previous // minute. We don't require a complete candle for the previous minute; // we will look back as far as we need to to find the last print. // // A quick scan of the data shows that only 1/3 of the NASDAQ stocks // have enough 15 minute data to pass the 40 candle rule. That's a // shame, and I'd like to improve that. But I want to make this as // similar as possible to the channel breaks. //////////////////////////////////////////////////////////////////// class RunningUpNow : public Alert { private: static const int CHART_COUNT = 4; StandardCandleChart *_candleData[CHART_COUNT]; GenericTosDataNode *_tosData; SynchronizedTimer *_minuteTimer; bool _up; int _lastReportLevel; int _previousLastReportLevel; bool _basePriceValid; double _basePrice; double _levelSize; enum { rulsUnknown, rulsInvalid, rulsValid } _levelStatus; void updateLevel(double basePrice); void newCandleData(); void newTosData(); void newMinute(); enum { wCandle, wTos, wMinute }; void onWakeup(int msgId); RunningUpNow(DataNodeArgument const &args); friend class GenericDataNodeFactory; }; void RunningUpNow::updateLevel(double basePrice) { if (_levelStatus != rulsUnknown) return; for (int i = 0; i < CHART_COUNT; i++) if (_candleData[i]->chartHeightValid()) { _levelSize = _candleData[i]->getChartHeight() * 0.09; if (_levelSize > 0) { if (basePrice >= 1.00) _levelSize = std::max(_levelSize, 0.02); else _levelSize = std::max(_levelSize, 0.0002); _levelStatus = rulsValid; return; } } _levelStatus = rulsInvalid; } void RunningUpNow::newCandleData() { _levelStatus = rulsUnknown; } void RunningUpNow::newTosData() { if (_basePriceValid && _tosData->getValid()) { TosData const &last = _tosData->getLast(); if (last.newPrint) { updateLevel(last.price); if (_levelStatus == rulsValid) { double quality; if (_up) quality = last.price - _basePrice; else quality = _basePrice - last.price; const int level = (int)(quality / _levelSize); if (level < 0) // Assume we have filtered out late prints in the common // parts of the code, long before we get here. _basePrice = last.price; else if (level > _lastReportLevel) { if (level > _previousLastReportLevel) { std::string msg; switch (level) { case 1: msg = ""; break; case 2: msg = " quickly"; break; case 3: msg = " very quickly"; break; default: msg = " extremely quickly"; } if (_up) msg = "Running up" + msg + ": +"; else msg = "Running down" + msg + ": -"; msg += formatPrice(quality, false); msg += " in less than one minute."; const std::string altMsg = "l=" + ntoa(level); report(msg, altMsg, quality); } _lastReportLevel = level; } } } } } void RunningUpNow::newMinute() { if (_minuteTimer->getTimePhase() == BarCounter::tpUpdate) { _previousLastReportLevel = _lastReportLevel; _lastReportLevel = 0; if (_tosData->getValid()) _basePrice = _tosData->getLast().price; else _basePrice = 0.0; _basePriceValid = _basePrice != 0; } } void RunningUpNow::onWakeup(int msgId) { switch(msgId) { case wCandle: newCandleData(); break; case wTos: newTosData(); break; case wMinute: newMinute(); break; } } RunningUpNow::RunningUpNow(DataNodeArgument const &args) : _lastReportLevel(0), _previousLastReportLevel(0), _basePriceValid(false), _basePrice(0.0), _levelSize(0.0), _levelStatus(rulsUnknown) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 2); // Symbol, more buyers std::string const &symbol = argList[0].getStringValue(); _up = argList[1].getBooleanValue(); addAutoLink(StandardCandleChart::find(this, wCandle, _candleData[0], symbol, 5)); addAutoLink(StandardCandleChart::find(this, wCandle, _candleData[1], symbol, 10)); addAutoLink(StandardCandleChart::find(this, wCandle, _candleData[2], symbol, 15)); addAutoLink(StandardCandleChart::find(this, wCandle, _candleData[3], symbol, 30)); addAutoLink(GenericTosDataNode::find(this, wTos, _tosData, symbol)); addAutoLink(SynchronizedTimer::find(this, wMinute, _minuteTimer)); } //////////////////////////////////////////////////////////////////////// // GreenBarReversal // // This comes from Oliver / VCM. We pieced togather the exact // requirements from multiple places. // // * The alert will fire only at the end of a candle. // * The alert will require at least 3 bars running down, not counting the last bar. // o We will look at both types of running down, and report either. // o The alert specific filter will report the number of bars. // o The message will report the number of bars, and which type of running down // - We do this in part for Google to see different words on this page: http://www.trade-ideas.com/SingleAlertType/PFL75/75%25_pullback_from_lows.html // - And in part because of the number of times people have come back to me with confusion over how we define 3 down bars. // * The flip of the alert will be the red bar reversal (RBR) // * We do not care about the distance from the SMA. // o The user can do this himself with the other filters. // o Core Trader.PDF describes at least three different ways to use this. // * We require the close of the last candle to be above the 20 bar SMA if the SMA is rising, or below the SMA if the SMA is falling. // o This is independent of the direction of the alert. // o We considered several variations on this. // - The high must be above the rising sma or the low must be below the falling sma. // - The same as above, but for all candles in the pattern, not just the last one. // - Above or not too far below the rising sma. // o This seemed like the simplest definition which covered most if not all of the pictures. // // It seems like a lot of this is standard for Oliver's alerts. Perhaps the // wide range bar should have existed in two forms, depending on which // way we were going previously. Topping Tail has some of this built in, // but not all of it, and perhaps it should. We considered making more // of the common stuff into a seperate filter, but that doesn't always make // sense. In particular, you couldn't have an alert for every green or red // bar! // // If we did try to create a filter or library routine for the common stuff, // it would be different from a lot of the filters that we do. Currently, // if a filter looks at candle data, and an alert fires at the end of a // candle, we make sure that the filter updates before the alert does. If an // alert happens in the middle of a candle, the filter looks at the end of the // last complete candle. This filter, in particular the number of consecutive // up bars, would have to be exactly the opposite. In the case of a green // bar reveral, wide range bar, etc., we would want to know the number of // up bars up to and including the next to last one, but not the last one. // For alerts like running up which happen in the middle of a bar, we would // want to know the number of bars running up including the last complete one. // // Oliver's documents divide the strategies into three pieces. // 1) The trigger is a green bar in this case, but the narrow range bar, the // topping tail, the wide range bar, and many others are all other possible // triggers. Reading all the possibilities, the trigger seems almost // arbitrary. // 2) The setup before the trigger includes having several consecutive bars // all moving in the same direction. This is the most important part of // the strategy. // 3) The position of the price relative to the SMA and the direction the SMA // is moving is the final part. We split up the position relative to the // SMA and the direction of the SMA into two parts to better fit into our // structure. // // Brad has requested that we remove the part about being above a rising // SMA or below a falling SMA. //////////////////////////////////////////////////////////////////////// class GreenBarReversal : public Alert { private: // If FGreen is true, we are looking for a chart which was going down, // but turned around and the *last* bar is green. If FGreen is false, // we are looking for the flip of that situation. bool _green; StandardCandleChart *_candleData; void onWakeup(int msgId); GreenBarReversal(DataNodeArgument const &args); friend class GenericDataNodeFactory; }; void GreenBarReversal::onWakeup(int msgId) { if (!_candleData->chartHeightValid()) return; StandardCandles::CandleArray const &candles = _candleData->getCandles(); const int lastIndex = candles.size() - 1; StandardCandles::SingleCandle const &lastCandle = candles[lastIndex]; // Make sure the last bar is the right color. if ((_green && (lastCandle.close <= lastCandle.open)) || ((!_green) && (lastCandle.open <= lastCandle.close))) return; // Check the number of candles before the last one all going in the same // direction. int highLowCount = 0; int i = lastIndex - 1; do { if (_green) { // These should be going down. if (candles[i].high >= candles[i - i].high) break; if (candles[i].low >= candles[i - 1].low) break; } else { // These should be going up. if (candles[i].high <= candles[i - i].high) break; if (candles[i].low <= candles[i - 1].low) break; } highLowCount++; i--; } while (i >= 1); int colorCount = 0; i = lastIndex - 1; do { StandardCandles::SingleCandle const &candle = candles[i]; if ((_green && (candle.close >= candle.open)) || ((!_green) && (candle.close <= candle.open))) break; colorCount++; i--; } while (i >= 0); if ((colorCount < 3) && (highLowCount < 3)) return; // We have an alert! const int quality = std::max(colorCount, highLowCount); std::string description; if (_green) { description = "Green bar after "; description += ntoa(quality); if (colorCount >= highLowCount) { description += " red bars"; if (colorCount == highLowCount) description += " with"; } if (colorCount <= highLowCount) description += " lower highs and lower lows"; } else { description = "Red bar after "; description += ntoa(quality); if (colorCount >= highLowCount) { description += " green bars"; if (colorCount == highLowCount) description += " with"; } if (colorCount <= highLowCount) description += " higher highs and higher lows"; } std::string altMsg = "m="; // Mode = if (highLowCount > colorCount) altMsg += "hl"; // Higher highs and higher lows. or lower highs and lower lows. else if (colorCount > highLowCount) altMsg += "cc"; // Color count. i.e. 4 green bars or 7 red bars. else altMsg += "b"; // Both report(description, altMsg, quality); } GreenBarReversal::GreenBarReversal(DataNodeArgument const &args) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 3); // Symbol, Minutes / Bar, Green std::string const &symbol = argList[0].getStringValue(); const int minutesPerBar = argList[1].getIntValue(); _green = argList[2].getBooleanValue(); addAutoLink(StandardCandleChart::find(this, 0, _candleData, symbol, minutesPerBar)); } //////////////////////////////////////////////////////////////////// // 1, 2, 3 Continuation Play // // The Setup (for a buy) // 1) Bar 1 must be a bullish 20/20 bar. This is the bar that // dictates the direction of the trade. // 2) Bar 2 must stay within the top 1/3 of Bar 1, without trading // above bar 1's high. // 3) The high of bar 2 must be equal or near equal with the high // of bar 1. // 4) Preferably Bar 2's close is below it's open. // // The bullish 20/20 bar is defined by any wide-range period that // as its open price near the low of that period and its close near // the high of that period. // // We call it 20/20 because as a general rule, the open should be // in the lower 20% of the period's range, and the close should be // in the upper 20% of the period's range. // // Bullish 20/20 bars are far more important when they occur after // at least once prior up bar. Several proceeding up bars make the // 20/20 even more significant. // // I'll reuse the definition of "wide-range" from the WideRangeBar // alert. // // Later we added another version of the alert which (typically) // triggers sooner: // From: Paul Lange [mailto:plange@pristine.com] // Sent: Friday, April 30, 2010 4:45 AM // To: Avery Meizner; Brad // Subject: RE: 123 continuation question // // Actually, if it can be done, the signal should come when price on bar three // crossed the 75% mark of range of day of bar 2. Can that be done? That // would alert us to the 'setup' rather than the trigger. //////////////////////////////////////////////////////////////////// class OneTwoThreeContinuation : public Alert { private: TradeDirection _tradeDirection; StandardCandleChart *_candleData; GenericTosDataNode *_tosData; bool _setupOnly; // Fire sooner if this is true. bool _canFireThisPeriod; double _tradeTrigger; // When to buy or sell. double _reportTrigger; // When to signal an alert. double _stopLoss; enum { wCandleData, wTosData }; void onWakeup(int msgId); void onCandleData(); void onTosData(); OneTwoThreeContinuation(DataNodeArgument const &args); friend class GenericDataNodeFactory; }; void OneTwoThreeContinuation::onWakeup(int msgId) { switch (msgId) { case wCandleData: onCandleData(); break; case wTosData: onTosData(); break; } } void OneTwoThreeContinuation::onCandleData() { _canFireThisPeriod = false; if (!_candleData->chartHeightValid()) return; StandardCandles::CandleArray const &candles = _candleData->getCandles(); const int bar1Index = candles.size() - 2; StandardCandles::SingleCandle const &bar1 = candles[bar1Index]; const double bar1Height = bar1.high - bar1.low; if (_tradeDirection.isLong()) { if ((bar1.high - bar1.close) * 5 > bar1Height) // Top wick is too long (or candle is the wrong color). return; if ((bar1.open - bar1.low) * 5 > bar1Height) // Bottom wick is too long (or candle is the wrong color). return; } else { if ((bar1.high - bar1.open) * 5 > bar1Height) // Top wick is too long (or candle is the wrong color). return; if ((bar1.close - bar1.low) * 5 > bar1Height) // Bottom wick is too long (or candle is the wrong color). return; } StandardCandles::SingleCandle const &bar2 = candles[bar1Index + 1]; if (bar2.high - bar2.low > bar1Height / 3.0) return; const double isNear = bar1Height * 0.1; if (_tradeDirection.isLong()) { _tradeTrigger = bar1.high; if (bar2.high > _tradeTrigger) return; if (bar2.high < _tradeTrigger - isNear) return; } else { _tradeTrigger = bar1.low; if (bar2.low < _tradeTrigger) return; if (bar2.low > _tradeTrigger + isNear) return; } double averageBarSize = 0.0; for (int i = bar1Index - 20; i < bar1Index; i++) { StandardCandles::SingleCandle const &candle = candles[i]; averageBarSize += candle.high - candle.low; } averageBarSize /= 20; if (bar1Height < averageBarSize * 2) return; _stopLoss = _tradeDirection.leastReportable(bar2.high, bar2.low); if (_setupOnly) _reportTrigger = (_tradeTrigger - _stopLoss) * 0.75 + _stopLoss; else _reportTrigger = _tradeTrigger; _canFireThisPeriod = true; } void OneTwoThreeContinuation::onTosData() { if (!_canFireThisPeriod) return; if (!_candleData->isActive()) return; if (!_tosData->getValid()) return; if (!_tosData->getLast().newPrint) return; if (!_tradeDirection.moreReportable(_tosData->getLast().price, _reportTrigger)) return; const std::string stopLoss = formatPrice(_stopLoss); std::string altMsg = "sl=" + stopLoss; std::string description = "1-2-3 continuation "; if (_tradeDirection.isLong()) description += "buy"; else description += "sell"; if (_setupOnly) { const std::string tradeTrigger = formatPrice(_tradeTrigger); description += " setup. Enter after crossing "; description += tradeTrigger; description += '.'; altMsg += "&tt="; altMsg += tradeTrigger; } else description += " signal."; description += " Stop loss = "; description += stopLoss; report(description, altMsg); // There is a lot of room to add a quality field. Look at all the // suggestions that were mixed in with the requirements, like more green // candles preceeding a bullish 20/20 bar. _canFireThisPeriod = false; } OneTwoThreeContinuation::OneTwoThreeContinuation(DataNodeArgument const &args) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 4); // Symbol, Minutes / Bar, Green, setup only std::string const &symbol = argList[0].getStringValue(); const int minutesPerBar = argList[1].getIntValue(); _tradeDirection = argList[2].getBooleanValue(); _setupOnly = argList[3].getBooleanValue(); addAutoLink(StandardCandleChart::find(this, wCandleData, _candleData, symbol, minutesPerBar)); addAutoLink(GenericTosDataNode::find(this, wTosData, _tosData, symbol)); onCandleData(); } //////////////////////////////////////////////////////////////////// // Global //////////////////////////////////////////////////////////////////// void initializeSimpleCandles() { GenericDataNodeFactory::sf< Doji >("Doji-5", symbolPlaceholderObject, 5); GenericDataNodeFactory::sf< Doji >("Doji-10", symbolPlaceholderObject, 10); GenericDataNodeFactory::sf< Doji >("Doji-15", symbolPlaceholderObject, 15); GenericDataNodeFactory::sf< Doji >("Doji-30", symbolPlaceholderObject, 30); GenericDataNodeFactory::sf< Doji >("Doji-60", symbolPlaceholderObject, 60); GenericDataNodeFactory::sf< TraditionalChannelBreak > ("TraditionalChannelBreakout-5", symbolPlaceholderObject, 5, true); GenericDataNodeFactory::sf< TraditionalChannelBreak > ("TraditionalChannelBreakdown-5", symbolPlaceholderObject, 5, false); GenericDataNodeFactory::sf< TraditionalChannelBreak > ("TraditionalChannelBreakout-10", symbolPlaceholderObject, 10, true); GenericDataNodeFactory::sf< TraditionalChannelBreak > ("TraditionalChannelBreakdown-10", symbolPlaceholderObject, 10, false); GenericDataNodeFactory::sf< TraditionalChannelBreak > ("TraditionalChannelBreakout-15", symbolPlaceholderObject, 15, true); GenericDataNodeFactory::sf< TraditionalChannelBreak > ("TraditionalChannelBreakdown-15", symbolPlaceholderObject, 15, false); GenericDataNodeFactory::sf< TraditionalChannelBreak > ("TraditionalChannelBreakout-30", symbolPlaceholderObject, 30, true); GenericDataNodeFactory::sf< TraditionalChannelBreak > ("TraditionalChannelBreakdown-30", symbolPlaceholderObject, 30, false); GenericDataNodeFactory::sf< HammerOrHangingMan > ("Hammer-2", symbolPlaceholderObject, 2, true); GenericDataNodeFactory::sf< HammerOrHangingMan > ("Hammer-5", symbolPlaceholderObject, 5, true); GenericDataNodeFactory::sf< HammerOrHangingMan > ("Hammer-10", symbolPlaceholderObject, 10, true); GenericDataNodeFactory::sf< HammerOrHangingMan > ("Hammer-15", symbolPlaceholderObject, 15, true); GenericDataNodeFactory::sf< HammerOrHangingMan > ("Hammer-30", symbolPlaceholderObject, 30, true); GenericDataNodeFactory::sf< HammerOrHangingMan > ("Hammer-60", symbolPlaceholderObject, 60, true); GenericDataNodeFactory::sf< HammerOrHangingMan > ("HangingMan-2", symbolPlaceholderObject, 2, false); GenericDataNodeFactory::sf< HammerOrHangingMan > ("HangingMan-5", symbolPlaceholderObject, 5, false); GenericDataNodeFactory::sf< HammerOrHangingMan > ("HangingMan-10", symbolPlaceholderObject, 10, false); GenericDataNodeFactory::sf< HammerOrHangingMan > ("HangingMan-15", symbolPlaceholderObject, 15, false); GenericDataNodeFactory::sf< HammerOrHangingMan > ("HangingMan-30", symbolPlaceholderObject, 30, false); GenericDataNodeFactory::sf< HammerOrHangingMan > ("HangingMan-60", symbolPlaceholderObject, 60, false); GenericDataNodeFactory::sf< Engulfing > ("BullishEngulfing-5", symbolPlaceholderObject, 5, true); GenericDataNodeFactory::sf< Engulfing > ("BearishEngulfing-5", symbolPlaceholderObject, 5, false); GenericDataNodeFactory::sf< Engulfing > ("BullishEngulfing-10", symbolPlaceholderObject, 10, true); GenericDataNodeFactory::sf< Engulfing > ("BearishEngulfing-10", symbolPlaceholderObject, 10, false); GenericDataNodeFactory::sf< Engulfing > ("BullishEngulfing-15", symbolPlaceholderObject, 15, true); GenericDataNodeFactory::sf< Engulfing > ("BearishEngulfing-15", symbolPlaceholderObject, 15, false); GenericDataNodeFactory::sf< Engulfing > ("BullishEngulfing-30", symbolPlaceholderObject, 30, true); GenericDataNodeFactory::sf< Engulfing > ("BearishEngulfing-30", symbolPlaceholderObject, 30, false); GenericDataNodeFactory::sf< DarkCloudCoverOrPeircing > ("PiercingPattern-5", symbolPlaceholderObject, 5, true); GenericDataNodeFactory::sf< DarkCloudCoverOrPeircing > ("PiercingPattern-10", symbolPlaceholderObject, 10, true); GenericDataNodeFactory::sf< DarkCloudCoverOrPeircing > ("PiercingPattern-15", symbolPlaceholderObject, 15, true); GenericDataNodeFactory::sf< DarkCloudCoverOrPeircing > ("PiercingPattern-30", symbolPlaceholderObject, 30, true); GenericDataNodeFactory::sf< DarkCloudCoverOrPeircing > ("DarkCloudCover-5", symbolPlaceholderObject, 5, false); GenericDataNodeFactory::sf< DarkCloudCoverOrPeircing > ("DarkCloudCover-10", symbolPlaceholderObject, 10, false); GenericDataNodeFactory::sf< DarkCloudCoverOrPeircing > ("DarkCloudCover-15", symbolPlaceholderObject, 15, false); GenericDataNodeFactory::sf< DarkCloudCoverOrPeircing > ("DarkCloudCover-30", symbolPlaceholderObject, 30, false); GenericDataNodeFactory::sf< ToppingTail > ("ToppingTail-2", symbolPlaceholderObject, 2, true); GenericDataNodeFactory::sf< ToppingTail > ("ToppingTail-5", symbolPlaceholderObject, 5, true); GenericDataNodeFactory::sf< ToppingTail > ("ToppingTail-10", symbolPlaceholderObject, 10, true); GenericDataNodeFactory::sf< ToppingTail > ("ToppingTail-15", symbolPlaceholderObject, 15, true); GenericDataNodeFactory::sf< ToppingTail > ("ToppingTail-30", symbolPlaceholderObject, 30, true); GenericDataNodeFactory::sf< ToppingTail > ("ToppingTail-60", symbolPlaceholderObject, 60, true); GenericDataNodeFactory::sf< ToppingTail > ("BottomingTail-2", symbolPlaceholderObject, 2, false); GenericDataNodeFactory::sf< ToppingTail > ("BottomingTail-5", symbolPlaceholderObject, 5, false); GenericDataNodeFactory::sf< ToppingTail > ("BottomingTail-10", symbolPlaceholderObject, 10, false); GenericDataNodeFactory::sf< ToppingTail > ("BottomingTail-15", symbolPlaceholderObject, 15, false); GenericDataNodeFactory::sf< ToppingTail > ("BottomingTail-30", symbolPlaceholderObject, 30, false); GenericDataNodeFactory::sf< ToppingTail > ("BottomingTail-60", symbolPlaceholderObject, 60, false); GenericDataNodeFactory::sf< NarrowRangeBar > ("NarrowRangeBuyBar-5", symbolPlaceholderObject, 5, true); GenericDataNodeFactory::sf< NarrowRangeBar > ("NarrowRangeBuyBar-10", symbolPlaceholderObject, 10, true); GenericDataNodeFactory::sf< NarrowRangeBar > ("NarrowRangeBuyBar-15", symbolPlaceholderObject, 15, true); GenericDataNodeFactory::sf< NarrowRangeBar > ("NarrowRangeBuyBar-30", symbolPlaceholderObject, 30, true); GenericDataNodeFactory::sf< NarrowRangeBar > ("NarrowRangeSellBar-5", symbolPlaceholderObject, 5, false); GenericDataNodeFactory::sf< NarrowRangeBar > ("NarrowRangeSellBar-10", symbolPlaceholderObject, 10, false); GenericDataNodeFactory::sf< NarrowRangeBar > ("NarrowRangeSellBar-15", symbolPlaceholderObject, 15, false); GenericDataNodeFactory::sf< NarrowRangeBar > ("NarrowRangeSellBar-30", symbolPlaceholderObject, 30, false); GenericDataNodeFactory::sf< RisingSma > ("SmaUp-2", symbolPlaceholderObject, 2, true); GenericDataNodeFactory::sf< RisingSma > ("SmaUp-5", symbolPlaceholderObject, 5, true); GenericDataNodeFactory::sf< RisingSma > ("SmaUp-15", symbolPlaceholderObject, 15, true); GenericDataNodeFactory::sf< RisingSma > ("SmaDown-2", symbolPlaceholderObject, 2, false); GenericDataNodeFactory::sf< RisingSma > ("SmaDown-5", symbolPlaceholderObject, 5, false); GenericDataNodeFactory::sf< RisingSma > ("SmaDown-15", symbolPlaceholderObject, 15, false); GenericDataNodeFactory::sf< WideRangeBar > ("WideRangeBar-2", symbolPlaceholderObject, 2); GenericDataNodeFactory::sf< WideRangeBar > ("WideRangeBar-5", symbolPlaceholderObject, 5); GenericDataNodeFactory::sf< WideRangeBar > ("WideRangeBar-15", symbolPlaceholderObject, 15); GenericDataNodeFactory::sf< GreenBarReversal > ("GreenBarReversal-2", symbolPlaceholderObject, 2, true); GenericDataNodeFactory::sf< GreenBarReversal > ("GreenBarReversal-5", symbolPlaceholderObject, 5, true); GenericDataNodeFactory::sf< GreenBarReversal > ("GreenBarReversal-15", symbolPlaceholderObject, 15, true); GenericDataNodeFactory::sf< GreenBarReversal > ("GreenBarReversal-60", symbolPlaceholderObject, 60, true); GenericDataNodeFactory::sf< GreenBarReversal > ("RedBarReversal-2", symbolPlaceholderObject, 2, false); GenericDataNodeFactory::sf< GreenBarReversal > ("RedBarReversal-5", symbolPlaceholderObject, 5, false); GenericDataNodeFactory::sf< GreenBarReversal > ("RedBarReversal-15", symbolPlaceholderObject, 15, false); GenericDataNodeFactory::sf< GreenBarReversal > ("RedBarReversal-60", symbolPlaceholderObject, 60, false); GenericDataNodeFactory::sf< OneTwoThreeContinuation > ("123Up-2", symbolPlaceholderObject, 2, true, false); GenericDataNodeFactory::sf< OneTwoThreeContinuation > ("123Up-5", symbolPlaceholderObject, 5, true, false); GenericDataNodeFactory::sf< OneTwoThreeContinuation > ("123Up-15", symbolPlaceholderObject, 15, true, false); GenericDataNodeFactory::sf< OneTwoThreeContinuation > ("123Up-60", symbolPlaceholderObject, 60, true, false); GenericDataNodeFactory::sf< OneTwoThreeContinuation > ("123Down-2", symbolPlaceholderObject, 2, false, false); GenericDataNodeFactory::sf< OneTwoThreeContinuation > ("123Down-5", symbolPlaceholderObject, 5, false, false); GenericDataNodeFactory::sf< OneTwoThreeContinuation > ("123Down-15", symbolPlaceholderObject, 15, false, false); GenericDataNodeFactory::sf< OneTwoThreeContinuation > ("123Down-60", symbolPlaceholderObject, 60, false, false); GenericDataNodeFactory::sf< OneTwoThreeContinuation > ("123UpSetup-2", symbolPlaceholderObject, 2, true, true); GenericDataNodeFactory::sf< OneTwoThreeContinuation > ("123UpSetup-5", symbolPlaceholderObject, 5, true, true); GenericDataNodeFactory::sf< OneTwoThreeContinuation > ("123UpSetup-15", symbolPlaceholderObject, 15, true, true); GenericDataNodeFactory::sf< OneTwoThreeContinuation > ("123UpSetup-60", symbolPlaceholderObject, 60, true, true); GenericDataNodeFactory::sf< OneTwoThreeContinuation > ("123DownSetup-2", symbolPlaceholderObject, 2, false, true); GenericDataNodeFactory::sf< OneTwoThreeContinuation > ("123DownSetup-5", symbolPlaceholderObject, 5, false, true); GenericDataNodeFactory::sf< OneTwoThreeContinuation > ("123DownSetup-15", symbolPlaceholderObject, 15, false, true); GenericDataNodeFactory::sf< OneTwoThreeContinuation > ("123DownSetup-60", symbolPlaceholderObject, 60, false, true); GenericDataNodeFactory::sf< RunningUpNow > ("RunningUpNow", symbolPlaceholderObject, true); GenericDataNodeFactory::sf< RunningUpNow > ("RunningDownNow", symbolPlaceholderObject, false); }