#include "../../shared/SimpleLogFile.h" #include "VolumeBlockMinAndMax.h" //////////////////////////////////////////////////////////////////// // VolumeBlockMinAndMax::MinAndMaxAccumulator //////////////////////////////////////////////////////////////////// void VolumeBlockMinAndMax::MinAndMaxAccumulator::reset() { _blockCount = 0; _initialDirection = vbdUnknown; _currentDirection = vbdUnknown; _recentBlocks.clear(); _turningPoints.clear(); } VolumeBlockMinAndMax::MinAndMaxAccumulator::MinAndMaxAccumulator() { reset(); } VolumeBlockMinAndMax::Direction VolumeBlockMinAndMax::MinAndMaxAccumulator::getCurrentDirection() const { switch(_currentDirection) { case vbdPossibleUp: return vbdUp; case vbdPossibleDown: return vbdDown; default: return _currentDirection; } } void VolumeBlockMinAndMax::MinAndMaxAccumulator::addRecentBlock (VolumeBlock const &block) { // Consecutive duplicates are ignored. Others are appended. bool needToAdd; if (_recentBlocks.empty()) needToAdd = true; else { VolumeBlock const &previous = _recentBlocks[_recentBlocks.size() - 1].block; needToAdd = (block.low != previous.low) || (block.high != previous.high); } if (needToAdd) { RecentBlock r; r.block = block; r.index = _blockCount; _recentBlocks.push_back(r); } } void VolumeBlockMinAndMax::MinAndMaxAccumulator::setRecentBlock (VolumeBlock const &block) { _recentBlocks.clear(); RecentBlock r; r.block = block; r.index = _blockCount; _recentBlocks.push_back(r); } void VolumeBlockMinAndMax::MinAndMaxAccumulator::addTurningPoint (VolumeBlock const &block, int recentBlockIndex, Orientation orientation) { TurningPoint tp; if (orientation == vboHigh) tp.price = _recentBlocks[recentBlockIndex].block.high; else tp.price = _recentBlocks[recentBlockIndex].block.low; tp.index = _recentBlocks[recentBlockIndex].index; tp.orientation = orientation; _turningPoints.push_back(tp); } void VolumeBlockMinAndMax::MinAndMaxAccumulator::addBlock (VolumeBlock const &block) { switch (_currentDirection) { case vbdUnknown: for (int previousBlockIndex = _recentBlocks.size() - 1; previousBlockIndex >= 0; previousBlockIndex--) if ((block.high > _recentBlocks[previousBlockIndex].block.high) && (block.low > _recentBlocks[previousBlockIndex].block.low)) { _initialDirection = vbdUp; break; } else if ((block.high < _recentBlocks[previousBlockIndex].block.high) && (block.low < _recentBlocks[previousBlockIndex].block.low)) { _initialDirection = vbdDown; break; } if (_initialDirection == vbdUnknown) addRecentBlock(block); else { _currentDirection = _initialDirection; setRecentBlock(block); } break; case vbdUp: assert(_recentBlocks.size() == 1); if (block.high >= _recentBlocks[0].block.high) setRecentBlock(block); else if (block.low < _recentBlocks[0].block.low) { addTurningPoint(block, 0, vboHigh); _currentDirection = vbdDown; setRecentBlock(block); } else { _currentDirection = vbdPossibleDown; addRecentBlock(block); } break; case vbdDown: assert(_recentBlocks.size() == 1); if (block.low <= _recentBlocks[0].block.low) setRecentBlock(block); else if (block.high > _recentBlocks[0].block.high) { addTurningPoint(block, 0, vboLow); _currentDirection = vbdUp; setRecentBlock(block); } else { _currentDirection = vbdPossibleUp; addRecentBlock(block); } break; case vbdPossibleDown: assert(!_recentBlocks.empty()); if (block.high >= _recentBlocks[0].block.high) { _currentDirection = vbdUp; setRecentBlock(block); } else for (int previousBlockIndex = _recentBlocks.size() - 1; previousBlockIndex >= 0; previousBlockIndex--) if ((block.high > _recentBlocks[previousBlockIndex].block.high) && (block.low > _recentBlocks[previousBlockIndex].block.low)) { addTurningPoint(block, 0, vboHigh); addTurningPoint(block, 0, vboLow); _currentDirection = vbdUp; setRecentBlock(block); break; } else if ((block.high < _recentBlocks[previousBlockIndex].block.high) && (block.low < _recentBlocks[previousBlockIndex].block.low)) { addTurningPoint(block, 0, vboHigh); _currentDirection = vbdDown; setRecentBlock(block); break; } if ((_currentDirection == vbdPossibleUp) || (_currentDirection == vbdPossibleDown)) addRecentBlock(block); else setRecentBlock(block); break; case vbdPossibleUp: assert(!_recentBlocks.empty()); if (block.low <= _recentBlocks[0].block.low) { _currentDirection = vbdDown; setRecentBlock(block); } else for (int previousBlockIndex = _recentBlocks.size() - 1; previousBlockIndex >= 0; previousBlockIndex--) if ((block.high < _recentBlocks[previousBlockIndex].block.high) && (block.low < _recentBlocks[previousBlockIndex].block.low)) { addTurningPoint(block, 0, vboLow); addTurningPoint(block, 0, vboHigh); _currentDirection = vbdDown; setRecentBlock(block); break; } else if ((block.high > _recentBlocks[previousBlockIndex].block.high) && (block.low > _recentBlocks[previousBlockIndex].block.low)) { addTurningPoint(block, 0, vboLow); _currentDirection = vbdUp; setRecentBlock(block); break; } if ((_currentDirection == vbdPossibleUp) || (_currentDirection == vbdPossibleDown)) addRecentBlock(block); else setRecentBlock(block); break; } _blockCount++; } //////////////////////////////////////////////////////////////////// // VolumeBlockMinAndMax //////////////////////////////////////////////////////////////////// void VolumeBlockMinAndMax::onWakeup(int msgId) { const int blockCount = _barData->getBlockCount(); if (blockCount == 0) _highsAndLows.reset(); else while (_highsAndLows.getBlockCount() < blockCount) _highsAndLows.addBlock(_barData-> getBlocks()[_highsAndLows.getBlockCount()]); notifyListeners(); } VolumeBlockMinAndMax::VolumeBlockMinAndMax(DataNodeArgument const &args) { std::string const &symbol = args.getStringValue(); addAutoLink(NormalVolumeBreakBars::find(this, 0, _barData, symbol)); onWakeup(0); checkInvariants(symbol); } DataNodeLink *VolumeBlockMinAndMax::find(DataNodeListener *listener, int msgId, VolumeBlockMinAndMax *&node, std::string const &symbol) { return findHelper(listener, msgId, node, symbol); } inline std::string oString(VolumeBlockMinAndMax::Orientation o) { if (o == VolumeBlockMinAndMax::vboHigh) return "vboHigh"; else return "vboLow"; } void VolumeBlockMinAndMax::checkInvariants(std::string const &symbol) { TclList problems; TurningPoints const &turningPoints = _highsAndLows.getTurningPoints(); for (TurningPoints::size_type i = 1; i < turningPoints.size(); i++) { TurningPoint const &previous = turningPoints[i-1]; TurningPoint const ¤t = turningPoints[i]; if (current.orientation == previous.orientation) { TclList problem; problem<<"Same orientation"<= previous.price))) { TclList problem; problem<<"Wrong direction" <price<index<orientation); // items<