#include "CurrentCandles.h" void CurrentCandles::onWakeup(int msgId) { // Something could have changed and been detected earlier, in a call to // getHistory(). We should look for that, first. optionallyNotifyListeners(); makeCurrent(); optionallyNotifyListeners(); } void CurrentCandles::optionallyNotifyListeners() { if (_somethingChanged) { _freezeUpdates = true; notifyListeners(); _freezeUpdates = false; _somethingChanged = false; } } void CurrentCandles::makeCurrent() { if (_freezeUpdates) return; bool recentChange = false; if (_baseEpoch != _candleData->getEpoch()) { // Check candle data. _baseEpoch = _candleData->getEpoch(); if (_candleData->getLastTransition() != StandardCandles::ltNewCandle) { _history = _candleData->getHistory(); _lastTransition = ltReset; recentChange = true; } else { if (historySize() == _candleData->historicalCandleCount()) { // Replace the candle we built ourself with the official // source. Presumably this is the common case. I'm not even // certain that the other cases could happen. const int last = _history.size() - 1; if (_history[last] != _candleData->getHistory()[last]) { // This test is not strictly necessary. This will prevent // us from sending extra notifications when nothing changes. // This is particularlly helpful because this is removing the // case where a lot of candles are updating at the same time. // I suspect that most of the time we will not have to notify // at the end of a candle. _history[last] == _candleData->getHistory()[last]; recentChange = true; _lastTransition = ltUpdateCurrentCandle; } } else if (historySize() == _candleData->historicalCandleCount() - 1) { // Add a new candle to the end. _history.push_back(_candleData->getHistory()[_history.size()]); recentChange = true; _lastTransition = ltNewCandle; } else { // Lengths don't match up in any sensible way. _history = _candleData->getHistory(); recentChange = true; _lastTransition = ltReset; } } if (_candleData->isCurrentCandleValid()) { // This could only happen when we are first created. Otherwise, // new candle events always clear the initial candle. At this // point we're going to assume it's safe not to check the TOS data. // Strictly speaking, that's nott 100% safe. If someone created this // object in response to a TOS event, there could be a descrepancy. _history.push_back(_candleData->getCurrentCandle()); } } else { // This was not called in the constructor or in response to an event from // the underlying candles. So this might come from a TOS event, and // someone might have woken us up before the underlying candles had a // chance to update the current candle. So we have to look for that case // ourselves. if (_candleData->isActive()) { // New candles are reasonable at this time. bool needToCheckLastPrint = // The last print should be part of the current candle. _tosData->getValid() && (_tosData->getLast().time >= _candleData->getCurrentCandleStartTime()); bool currentCandleIsValid = false; SingleCandle current; if (_candleData->isCurrentCandleValid()) { currentCandleIsValid = true; // Start from the current candle according to the underlying // candles. current = _candleData->getCurrentCandle(); if (needToCheckLastPrint) { // And make sure it includes the last print. const double price = _tosData->getLast().price; if (price != current.close) { current.close = price; current.high = std::max(current.high, price); current.low = std::min(current.low, price); current.volume += _tosData->getLast().size; } } } else if (needToCheckLastPrint) { // Start a new candle including only this print. currentCandleIsValid = true; current.close = _tosData->getLast().price; current.high = current.close; current.low = current.close; current.open = current.close; current.volume = _tosData->getLast().size; } if (currentCandleIsValid) { if (historySize() > _candleData->historicalCandleCount()) { // We have already stored a value for the current candle. // See if it has changed. Check here because a lot of the // data nodes using this one will have complicated formulas // and we don't want to wake them any more than we have to. // Note: This was a good idea, but it doesn't work that // well because almost every print will change the volume. SingleCandle &lastInList = _history[_history.size() - 1]; if (lastInList != current) { lastInList = current; recentChange = true; _lastTransition = ltUpdateCurrentCandle; } } else { // Add the current candle to the end of the list. _history.push_back(current); recentChange = true; _lastTransition = ltNewCandle; } } } } if (recentChange) { _selfEpoch++; if (_somethingChanged) { // There was already a change in progress that has not been reported // to everyone. This will get caught leter, because _selfEpoch went // up by more than one at a time, so the result will be correct. // But this hurts performance, and we hope to avoid it. // TODO report this to the log. } else { // Call notifyListeners() soon. Anyone who asks will see the change // because the epoch changed. But we don't want to call // notifyListeners() at this time. Calling notifyListeners() inside // of a request for data is not the way things are normally done, and // it could cause problems. _somethingChanged = true; } } } CurrentCandles::CurrentCandles(DataNodeArgument const &args) : _selfEpoch(1), _lastTransition(ltReset), _baseEpoch(0), _freezeUpdates(false) { DataNodeArgumentVector const &argList = args.getListValue(); assert(argList.size() == 2); // symbol, minutesPerBar std::string const &symbol = argList[0].getStringValue(); const int minutesPerBar = argList[1].getIntValue(); addAutoLink(StandardCandles::find(this, wCandleData, _candleData, symbol, minutesPerBar)); addAutoLink(GenericTosDataNode::find(this, wTosData, _tosData, symbol)); makeCurrent(); _somethingChanged = false; } DataNodeLink *CurrentCandles::find(DataNodeListener *listener, int msgId, CurrentCandles *&node, std::string const &symbol, int minutesPerBar) { return findHelper(listener, msgId, node, argList(symbol, minutesPerBar)); } CurrentCandles::CandleArray const & CurrentCandles::getHistory(Epoch &epoch, LastTransition &lastTransition) { makeCurrent(); if (epoch == _selfEpoch) { lastTransition = ltNone; } else if (epoch != _selfEpoch - 1) { // Ideally this shouldn't happen. Anyone who's really watching for the // transitions will watch every update. But let's make this interface // simple, consistent and complete. lastTransition = ltReset; epoch = _selfEpoch; } else { // This person has been watching and we know the last thing that he // saw. This is the ideal case, were he can just make small changes. lastTransition = _lastTransition; epoch = _selfEpoch; } return _history; }