#include #include #include "../misc_framework/GenericDataNodes.h" #include "../data_framework/StandardCandles.h" #include "CCI.h" // http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:commodity_channel_index_cci /* CCI = (Typical Price - 20-period SMA of TP) / (.015 x Mean Deviation) Typical Price (TP) = (High + Low + Close)/3 Constant = .015 There are four steps to calculating the Mean Deviation. First, subtract the most recent 20-period average of the typical price from each period's typical price. Second, take the absolute values of these numbers. Third, sum the absolute values. Fourth, divide by the total number of periods (20). */ // This will never send notifications. Something could change on every // print. But actually computing the CCI is expensive. So we wait until // someone asks for the value. class CCI : public GenericDataNode { static const double NONE; static const StandardCandles::SingleCandle NO_CANDLE; StandardCandles *_candleData; int _barCount; double _value; StandardCandles::Epoch _previousEpoch; StandardCandles::SingleCandle _previousCurrentCandle; static double typicalPrice(StandardCandles::SingleCandle const &candle) { return (candle.low + candle.high + candle.close) / 3.0; } static bool missing(StandardCandles::SingleCandle const &candle) { return candle.close == NONE; } static bool same(StandardCandles::SingleCandle const &a, StandardCandles::SingleCandle const &b) { return (a.high == b.high) && (a.low == b.low) && (a.close == b.close); } StandardCandles::SingleCandle getCurrentCandle() { if (_candleData->isCurrentCandleValid() && _candleData->shouldUseLive()) return _candleData->getCurrentCandle(); else return NO_CANDLE; } void verifyCurrent(); CCI(DataNodeArgument const &args); friend class GenericDataNodeFactory; public: void getDouble(bool &valid, double &value) const; }; // The rules for comparing NaN values are a little flakey. Comparing // infinities works as expected. const double CCI::NONE = -std::numeric_limits< double >::infinity(); const StandardCandles::SingleCandle CCI::NO_CANDLE = { NONE, NONE, NONE, NONE, 0}; void CCI::getDouble(bool &valid, double &value) const { const_cast< CCI * >(this)->verifyCurrent(); valid = _value != NONE; value = _value; } void CCI::verifyCurrent() { StandardCandles::SingleCandle currentCandle = getCurrentCandle(); if ((_previousEpoch == _candleData->getEpoch()) && same(_previousCurrentCandle, currentCandle)) // Nothing interesting has changed since the last time we computed _value. return; _previousEpoch = _candleData->getEpoch(); _previousCurrentCandle = currentCandle; _value = NONE; // In case we bail out. const int stop = _candleData->historicalCandleCount(); const int first = stop - _barCount + !missing(currentCandle); if (first < 0) return; StandardCandles::CandleArray const &candles = _candleData->getHistory(); std::vector< double > prices; prices.reserve(_barCount); for (int i = first; i < stop; i++) prices.push_back(typicalPrice(candles[i])); if (_candleData->isCurrentCandleValid()) prices.push_back(typicalPrice(_candleData->getCurrentCandle())); assert((int)prices.size() == _barCount); double sum = 0.0; for (auto it = prices.begin(); it != prices.end(); it++) sum += *it; const double average = sum / _barCount; sum = 0.0; for (auto it = prices.begin(); it != prices.end(); it++) sum += std::fabs(*it - average); const double meanDeviation = sum / _barCount; const double lastTypicalPrice = *prices.rbegin(); _value = (lastTypicalPrice - average) / (0.015 * meanDeviation); } CCI::CCI(DataNodeArgument const &args) : _value(NONE), _previousEpoch(0), _previousCurrentCandle(NO_CANDLE) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 4); // Symbol, Minutes/Bar, Bars, Strict std::string const &symbol = argList[0].getStringValue(); const int minutesPerBar = argList[1].getIntValue(); _barCount = argList[2].getIntValue(); const bool strict = argList[3].getBooleanValue(); addAutoLink(StandardCandles::find(NULL, 0, _candleData, symbol, minutesPerBar, strict)); } // This returns a generic data node factory wrapped in a DataNodeArgument // to get the memory management. DataNodeArgument createCciFactory(int minutes, int bars, bool strict) { return GenericDataNodeFactory::create< CCI > (argList(symbolPlaceholderObject, minutes, bars, strict)); }