#include "../misc_framework/GenericDataNodes.h" #include "StandardCandles.h" #include "RSI.h" //////////////////////////////////////////////////////////////////// // RSI //////////////////////////////////////////////////////////////////// class RSI : public GenericDataNode { private: StandardCandles *_candleData; int _barCount; bool _valid; double _averageGains; double _averageLosses; double _value; StandardCandles::Epoch _previousEpoch; void reset(); double computeRsi() const; static double computeRsi(double averageGains, double averageLosses); void prepairInitial(int firstBar, int lastBar); void prepairNext(int bar); void updateEmas(double &averageGains, double &averageLosses, StandardCandles::SingleCandle const &older, StandardCandles::SingleCandle const &newer) const; void updateRsi(); void generateRsi(); void verifyCurrent(); void onWakeup(int msgId); RSI(DataNodeArgument const &args); friend class GenericDataNodeFactory; public: void getDouble(bool &valid, double &value) const; }; void RSI::reset() { _value = 0; if (_valid) { _valid = false; } } double RSI::computeRsi() const { if (!_valid) return -1; if (_candleData->isCurrentCandleValid()) { double averageGains = _averageGains; double averageLosses = _averageLosses; StandardCandles::CandleArray const &candles = _candleData->getHistory(); updateEmas(averageGains, averageLosses, candles[_candleData->historicalCandleCount()-1], _candleData->getCurrentCandle()); return computeRsi(averageGains, averageLosses); } else // It doesn't really matter why the current candle is not valid. We could // have just converted the current candle into a historical candle. Or // this could be a slowly trading stock. Either way, the result should // be the same. If we made the current candle a clone of the last candle, // then we'd add one last gain of 0. And that doesn't change the result // for an RSI. return computeRsi(_averageGains, _averageLosses); } double RSI::computeRsi(double averageGains, double averageLosses) { if (averageLosses <= 0.0) if (averageGains <= 0.0) // 0 / 0 -- take the limit of n/n as n->0 return 50; else // a / 0 -- a very large number. If you take a limit, you can easily // remove this singularity from the equation as a whole. return 100; else return 100 - 100 / (1 + averageGains / averageLosses); } // This starts by comparting firstBar to firstBar+1, and ends by comparing // lastBar-1 to lastBar. void RSI::prepairInitial(int firstBar, int lastBar) { StandardCandles::CandleArray const &candles = _candleData->getHistory(); _averageGains = 0.0; _averageLosses = 0.0; for (int i = firstBar; i < lastBar; i++) { const double gain = candles[i+1].close - candles[i].close; if (gain >= 0) _averageGains += gain; else _averageLosses -= gain; } _averageGains /= (lastBar - firstBar); _averageLosses /= (lastBar - firstBar); } // This compares bar-1 to bar. void RSI::prepairNext(int bar) { StandardCandles::CandleArray const &candles = _candleData->getHistory(); updateEmas(_averageGains, _averageLosses, candles[bar - 1], candles[bar]); } void RSI::updateEmas(double &averageGains, double &averageLosses, StandardCandles::SingleCandle const &older, StandardCandles::SingleCandle const &newer) const { const double gain = newer.close - older.close; averageGains *= _barCount - 1; averageLosses *= _barCount - 1; if (gain >= 0.0) averageGains += gain; else averageLosses -= gain; averageGains /= _barCount; averageLosses /= _barCount; } void RSI::updateRsi() { // Add one new candle, assuming we successfully generated the RSI last time. prepairNext(_candleData->historicalCandleCount()-1); _valid = true; } void RSI::generateRsi() { // Create the RSI from scratch. int firstBar = _candleData->historicalCandleCount() - _barCount - 1; assert(firstBar >= 0); /* The formula is rather strange, and somewhat non-deterministic. When * you first start, you do a simple moving average of a function of the * first N terms. As you go forward, you do an exponential moving * average. So starting the computation at a different place can give * you a different result. For an N bar RSI, we have to start N-1 bars * back to have enought data. But we try to start N bars further back * than that, so the results will be similar to someone who started * way, way back. */ firstBar = std::max(firstBar - _barCount * 3, 0); int lastBar = firstBar + _barCount; prepairInitial(firstBar, lastBar); while (true) { lastBar++; if (lastBar >= _candleData->historicalCandleCount()) break; prepairNext(lastBar); } _valid = true; } void RSI::verifyCurrent() { if (_previousEpoch != _candleData->getEpoch()) { _previousEpoch = _candleData->getEpoch(); switch (_candleData->getLastTransition()) { case StandardCandles::ltNew: reset(); break; case StandardCandles::ltReset: if (_candleData->historicalCandleCount() <= _barCount) reset(); else generateRsi(); break; case StandardCandles::ltNewCandle: if (_candleData->historicalCandleCount() <= _barCount) // A 14 bar RSI requires at least 15 candles. The first step is to // take the difference between the closing price of each // consecutive pair of bars. 14 refers to the number of // differences. reset(); else if (_valid) updateRsi(); else generateRsi(); break; case StandardCandles::ltUpdateCurrentCandle: // Do nothing. We don't care if the current candle is updating. // we only look at the end of the candle. break; } } } void RSI::onWakeup(int msgId) { /* We have to check at least once every time the underlying data changes, * because we like to build the bars one at a time. Otherwise it's * possible that we could miss one. */ verifyCurrent(); } void RSI::getDouble(bool &valid, double &value) const { const_cast< RSI *>(this)->verifyCurrent(); valid = _valid; value = computeRsi(); } RSI::RSI(DataNodeArgument const &args) : _valid(false), _averageGains(0), _averageLosses(0), _value(0), _previousEpoch(0) { 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(this, 0, _candleData, symbol, minutesPerBar, strict)); } //////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////// // This returns a generic data node factory wrapped in a DataNodeArgument // to get the memory management. DataNodeArgument createRsiFactory(int minutes, int bars, bool strict) { return GenericDataNodeFactory::create< RSI > (argList(symbolPlaceholderObject, minutes, bars, strict)); } //void initializeRSI() //{ // GenericDataNodeFactory::storeFactory("RSI15", createRsiFactory()); //}