#include /****************************************************************************/ // s_VolumeLevelAtPrice struct s_VolumeLevelAtPrice { //--- Members ------------------------------------------------------------ int PriceInTicks; // The maximum volume of a trade at price, above or equal to the set // volume level. unsigned int MaxVolume; // The total volume of all trades at price, above or equal to the set // volume level. unsigned int TotalVolume; // The total volume of all bid trades at price, above or equal to the set // volume level. unsigned int BidTradeVolume; // The total volume of all ask trades at price, above or equal to the set // volume level. unsigned int AskTradeVolume; //--- Methods ------------------------------------------------------------ s_VolumeLevelAtPrice(); }; /*==========================================================================*/ inline s_VolumeLevelAtPrice::s_VolumeLevelAtPrice() : PriceInTicks(0) , MaxVolume(0) , TotalVolume(0) , BidTradeVolume(0) , AskTradeVolume(0) { } /****************************************************************************/ // s_VolumeAtPriceV2 struct s_VolumeAtPriceV2 { //--- Members ------------------------------------------------------------ int PriceInTicks; unsigned int Volume; unsigned int BidVolume; unsigned int AskVolume; unsigned int NumberOfTrades; //--- Methods ------------------------------------------------------------ s_VolumeAtPriceV2(); s_VolumeAtPriceV2 ( const unsigned int Volume , const unsigned int BidVolume , const unsigned int AskVolume , const unsigned int NumberOfTrades ); s_VolumeAtPriceV2& operator += (const s_VolumeAtPriceV2& Rhs); s_VolumeAtPriceV2& operator -= (const s_VolumeAtPriceV2& Rhs); bool IsEmpty() const { return PriceInTicks == 0 && Volume == 0; } }; /*==========================================================================*/ inline s_VolumeAtPriceV2::s_VolumeAtPriceV2() : PriceInTicks(0) , Volume(0) , BidVolume(0) , AskVolume(0) , NumberOfTrades(0) { } /*==========================================================================*/ inline s_VolumeAtPriceV2::s_VolumeAtPriceV2 ( const unsigned int Volume , const unsigned int BidVolume , const unsigned int AskVolume , const unsigned int NumberOfTrades ) : PriceInTicks(0) , Volume(Volume) , BidVolume(BidVolume) , AskVolume(AskVolume) , NumberOfTrades(NumberOfTrades) { } /*==========================================================================*/ inline s_VolumeAtPriceV2& s_VolumeAtPriceV2::operator += ( const s_VolumeAtPriceV2& Rhs ) { Volume += Rhs.Volume; BidVolume += Rhs.BidVolume; AskVolume += Rhs.AskVolume; NumberOfTrades += Rhs.NumberOfTrades; return *this; } /*==========================================================================*/ inline s_VolumeAtPriceV2& s_VolumeAtPriceV2::operator -= ( const s_VolumeAtPriceV2& Rhs ) { if (Rhs.Volume > Volume) Volume = 0; else Volume -= Rhs.Volume; if (Rhs.BidVolume > BidVolume) BidVolume = 0; else BidVolume -= Rhs.BidVolume; if (Rhs.AskVolume > AskVolume) AskVolume = 0; else AskVolume -= Rhs.AskVolume; NumberOfTrades -= Rhs.NumberOfTrades; return *this; } /***************************************************************************** // c_VAPContainerBase This is a container class related to bar data. Each bar may contain multiple elements of the given template type for specific prices within the bar. The template elements are stored in a 1-dimensional dynamically allocated array. A 2nd 1-dimensional dynamically allocated array is used to keep track of which elements in the 1st array belong to which bar indexes. ----------------------------------------------------------------------------*/ template class c_VAPContainerBase { //--- Public Methods ----------------------------------------------------- public: c_VAPContainerBase ( const unsigned int InitialAllocationElements = 1024 ); c_VAPContainerBase(const c_VAPContainerBase&) = delete; c_VAPContainerBase(c_VAPContainerBase&& rr_Source); ~c_VAPContainerBase(); c_VAPContainerBase& operator = (const c_VAPContainerBase&) = delete; c_VAPContainerBase& operator = (c_VAPContainerBase&& rr_Right); void Clear(); void ClearFromBarIndexToEnd(const unsigned int BarIndex); unsigned int GetNumberOfBars() const; unsigned int GetSizeAtBarIndex(const unsigned int BarIndex) const; void Swap(c_VAPContainerBase& r_That); bool GetVAPElement ( const int PriceInTicks , const unsigned int BarIndex , t_VolumeAtPrice** p_VAP , const bool AllocateIfNeeded = false ); bool GetVAPElementAtIndex ( const unsigned int BarIndex , int VAPDataIndex , t_VolumeAtPrice** p_VAP , bool ReturnErrorOutOnOfBounds = false ) const; bool GetVAPElementAtIndex (const unsigned int BarIndex , int VAPDataIndex , const t_VolumeAtPrice** p_VAP , bool ReturnErrorOutOnOfBounds = false ) const; bool GetVAPElementForPriceIfExists ( const unsigned int BarIndex , const int PriceInTicks , t_VolumeAtPrice** p_VAP , unsigned int& r_InsertionIndex ); bool GetNextHigherVAPElement ( const unsigned int BarIndex , int& r_PriceInTicks , const t_VolumeAtPrice** p_VAP ) const; bool GetNextLowerVAPElement ( const unsigned int BarIndex , int& r_PriceInTicks , const t_VolumeAtPrice** p_VAP ) const; // This was the original function signature. bool GetHighAndLowPriceTicksForBarIndex ( const unsigned int BarIndex , int& r_HighPriceInTicks , int& r_LowPriceInTicks ) const; // Overloaded function allows for optional return parameters. bool GetHighAndLowPriceTicksForBarIndex ( const unsigned int BarIndex , int* p_HighPriceInTicks , int* p_LowPriceInTicks ) const; bool GetHighAndLowPriceTicksForBarIndexRange ( const unsigned int FirstBarIndex , const unsigned int LastBarIndex , int* p_HighPriceInTicks , int* p_LowPriceInTicks ) const; const t_VolumeAtPrice& GetVAPElementAtPrice ( const unsigned int BarIndex , const int PriceInTicks ) const; bool AddVolumeAtPrice ( const int PriceInTicks , const unsigned int BarIndex , const t_VolumeAtPrice& VolumeAtPrice ); bool SubtractVolumeAtPrice ( const int PriceInTicks , const unsigned int BarIndex , const t_VolumeAtPrice& VolumeAtPrice ); bool SetVolumeAtPrice (const int PriceInTicks , const unsigned int BarIndex , const t_VolumeAtPrice& VolumeAtPrice ); //--- Private Static Methods --------------------------------------------- private: static bool PriceIsLess ( const t_VolumeAtPrice& First , const t_VolumeAtPrice& Second ) ; //--- Private Methods ---------------------------------------------------- private: bool EnsureBarExists ( const unsigned int BarIndex , const bool AllocateIfNeeded ); bool InitialAllocation(); bool AllocateBar(const unsigned int BarIndex); bool AllocateElement(const unsigned int ElementIndex); unsigned int GetFirstDataElementIndexForBar ( const unsigned int BarIndex ) const; bool GetBeginEndIndexesForBarIndex ( const unsigned int BarIndex , unsigned int* p_BeginIndex , unsigned int* p_EndIndex ) const; void GetHighAndLowPriceTicks ( const unsigned int BeginIndex , unsigned int EndIndex , int* p_HighPriceInTicks , int* p_LowPriceInTicks ) const; //--- Private Members ---------------------------------------------------- private: // Note: The order of these private members must not change in order // to preserve compatibility with custom study DLLs that may have // been built with a different version of this code. These private // members are shared between those DLLs and Sierra Chart. // This points to an array with an element for each bar. The value // at each element is the index of the first element in the VAP data // array that contains data for the bar. unsigned int* m_p_BarIndexToFirstElementIndexArray = nullptr; // The number of elements that are currently used in the above array. unsigned int m_NumberOfBars = 0; // The number of elements that are currently allocated for the above // array. The elements allocated is usually greater than the // elements used so that the memory for the array does not need to be // reallocated for every new element. unsigned int m_NumElementsAllocated = 0; // This points to an array of all of the Volume at Price data // elements that are stored in this container. t_VolumeAtPrice* m_p_VAPDataElements = nullptr; // The number of elements that are currently used in the above array. unsigned int m_NumElementsUsed = 0; // The number of elements that are currently allocated for the above // array. unsigned int m_NumBarsAllocated = 0; // This is the number of bars that are initially allocated when the // arrays for the container are initially allocated. const unsigned int m_InitialAllocationElements = 0; // These members are not used any longer but need to remain here // because older compiled studies will alter these values. So they // must remain in the structure. int m_Unused_LastSortedBarIndex = 0; int m_Unused_LastSortedBarSize = 0; }; /*==========================================================================*/ template inline c_VAPContainerBase::c_VAPContainerBase ( const unsigned int InitialAllocationElements ) : m_InitialAllocationElements(InitialAllocationElements) { } /*==========================================================================*/ template inline c_VAPContainerBase::c_VAPContainerBase ( c_VAPContainerBase&& rr_Source ) : c_VAPContainerBase(rr_Source.m_InitialAllocationElements) { Swap(rr_Source); } /*==========================================================================*/ template inline c_VAPContainerBase::~c_VAPContainerBase() { if (m_p_VAPDataElements != nullptr) { free(m_p_VAPDataElements); m_p_VAPDataElements = nullptr; } if (m_p_BarIndexToFirstElementIndexArray != nullptr) { free(m_p_BarIndexToFirstElementIndexArray); m_p_BarIndexToFirstElementIndexArray = nullptr; } } /*==========================================================================*/ template inline c_VAPContainerBase& c_VAPContainerBase::operator = ( c_VAPContainerBase&& rr_Right ) { Swap(rr_Right); return *this; } /*==========================================================================*/ template inline void c_VAPContainerBase::Clear() { // Swapping with a new temporary object will effectively destroy and // re-initialize this object. Swap(c_VAPContainerBase(m_InitialAllocationElements)); } /*==========================================================================*/ template inline void c_VAPContainerBase::ClearFromBarIndexToEnd ( const unsigned int BarIndex ) { if (BarIndex >= m_NumberOfBars) return; // Clear the VAP data array. const unsigned int BeginIndex = m_p_BarIndexToFirstElementIndexArray[BarIndex]; const unsigned int NumElementsToClear = m_NumElementsUsed - BeginIndex; memset ( m_p_VAPDataElements + BeginIndex , 0 , NumElementsToClear * sizeof(t_VolumeAtPrice) ); m_NumElementsUsed = BeginIndex; // Clear the StartingVAPIndexesForBarIndexes array. const unsigned int NumBarsToClear = m_NumberOfBars - BarIndex; memset ( m_p_BarIndexToFirstElementIndexArray + BarIndex , 0 , NumBarsToClear * sizeof(unsigned int) ); m_NumberOfBars = BarIndex; } /*==========================================================================*/ template inline unsigned int c_VAPContainerBase::GetNumberOfBars() const { return m_NumberOfBars; } /*============================================================================ Returns the number of Volume at Price elements for the bar at the given BarIndex. ----------------------------------------------------------------------------*/ template inline unsigned int c_VAPContainerBase::GetSizeAtBarIndex ( const unsigned int BarIndex ) const { unsigned int BeginIndex, EndIndex; if (!GetBeginEndIndexesForBarIndex(BarIndex, &BeginIndex, &EndIndex)) return 0; return EndIndex - BeginIndex; } /*==========================================================================*/ template inline void c_VAPContainerBase::Swap ( c_VAPContainerBase& r_That ) { using std::swap; // Note: not swapping m_InitialAllocationElements. swap(m_p_VAPDataElements, r_That.m_p_VAPDataElements); swap(m_NumElementsAllocated, r_That.m_NumElementsAllocated); swap(m_NumElementsUsed, r_That.m_NumElementsUsed); swap ( m_p_BarIndexToFirstElementIndexArray , r_That.m_p_BarIndexToFirstElementIndexArray ); swap(m_NumBarsAllocated, r_That.m_NumBarsAllocated); swap(m_NumberOfBars, r_That.m_NumberOfBars); } /*============================================================================ This method sets the given p_VAP pointer to point to the element at the given BarIndex and PriceInTicks. If the requested element does not exist, and AllocateIfNeeded is true (default), the element will be automatically allocated. A new element can only be allocated if it is at or beyond the last bar in the container. Returns true unless the requested element could not be created or accessed. ----------------------------------------------------------------------------*/ template inline bool c_VAPContainerBase::GetVAPElement ( const int PriceInTicks , const unsigned int BarIndex , t_VolumeAtPrice** p_VAP , const bool AllocateIfNeeded ) { *p_VAP = nullptr; if (!EnsureBarExists(BarIndex, AllocateIfNeeded)) return false; unsigned int InsertionIndex = -1; if (GetVAPElementForPriceIfExists(BarIndex, PriceInTicks, p_VAP, InsertionIndex)) return true; // The requested element does not exist. if (!AllocateIfNeeded) return false; // We cannot add because we are not at the last bar index segment in // the VAP array. if (BarIndex + 1 < m_NumberOfBars) return false; if (!AllocateElement(++m_NumElementsUsed)) { Clear(); return false; } if (InsertionIndex == -1) { InsertionIndex = m_NumElementsUsed - 1; } else if (InsertionIndex < m_NumElementsUsed - 1)// Not inserting at the end. { memmove(m_p_VAPDataElements + InsertionIndex + 1, m_p_VAPDataElements + InsertionIndex, (m_NumElementsUsed - InsertionIndex - 1)* sizeof(t_VolumeAtPrice)); } t_VolumeAtPrice& VolumeAtPrice = m_p_VAPDataElements[InsertionIndex]; VolumeAtPrice = t_VolumeAtPrice(); VolumeAtPrice.PriceInTicks = PriceInTicks; *p_VAP = &m_p_VAPDataElements[InsertionIndex]; return true; } /*============================================================================ VAPDataIndex is zero based, and is relative to the data for BarIndex. ----------------------------------------------------------------------------*/ template inline bool c_VAPContainerBase::GetVAPElementAtIndex ( const unsigned int BarIndex , int VAPDataIndex , t_VolumeAtPrice** p_VAP , bool ReturnErrorOutOnOfBounds ) const { *p_VAP = nullptr; unsigned int VAPSegmentBeginIndex, VAPSegmentEndIndex; if (!GetBeginEndIndexesForBarIndex ( BarIndex , &VAPSegmentBeginIndex , &VAPSegmentEndIndex ) ) { return false; } const int NumberOfElements = VAPSegmentEndIndex - VAPSegmentBeginIndex; if (VAPDataIndex < 0) { if (ReturnErrorOutOnOfBounds) return false; VAPDataIndex = 0; } if (VAPDataIndex > NumberOfElements - 1) { if (ReturnErrorOutOnOfBounds) return false; VAPDataIndex = NumberOfElements - 1; } *p_VAP = m_p_VAPDataElements + VAPSegmentBeginIndex + VAPDataIndex; return true; } /*============================================================================ VAPDataIndex is zero based, and is relative to the data for BarIndex. ----------------------------------------------------------------------------*/ template inline bool c_VAPContainerBase::GetVAPElementAtIndex (const unsigned int BarIndex , int VAPDataIndex , const t_VolumeAtPrice** p_VAP , bool ReturnErrorOutOnOfBounds ) const { *p_VAP = nullptr; unsigned int VAPSegmentBeginIndex, VAPSegmentEndIndex; if (!GetBeginEndIndexesForBarIndex(BarIndex, &VAPSegmentBeginIndex, &VAPSegmentEndIndex )) { return false; } const int NumberOfElements = VAPSegmentEndIndex - VAPSegmentBeginIndex; if (VAPDataIndex < 0) { if (ReturnErrorOutOnOfBounds) return false; VAPDataIndex = 0; } if (VAPDataIndex > NumberOfElements - 1) { if (ReturnErrorOutOnOfBounds) return false; VAPDataIndex = NumberOfElements - 1; } *p_VAP = m_p_VAPDataElements + VAPSegmentBeginIndex + VAPDataIndex; return true; } /*============================================================================ This function is for getting a specific Volume at Price element to modify. Returns true unless the requested element does not exist. ----------------------------------------------------------------------------*/ template inline bool c_VAPContainerBase::GetVAPElementForPriceIfExists ( const unsigned int BarIndex , const int PriceInTicks , t_VolumeAtPrice** p_VAP , unsigned int& r_InsertionIndex ) { unsigned int BeginIndex = 0, EndIndex = 0; if (!GetBeginEndIndexesForBarIndex(BarIndex, &BeginIndex, &EndIndex)) { return false; } // This is a temporary object only for the purpose of providing an object // of the same type for comparison. t_VolumeAtPrice Target; Target.PriceInTicks = PriceInTicks; t_VolumeAtPrice* p_BeginElement = m_p_VAPDataElements + BeginIndex; t_VolumeAtPrice* p_EndElement = m_p_VAPDataElements + EndIndex; t_VolumeAtPrice* p_FoundElement = std::lower_bound(p_BeginElement, p_EndElement, Target, PriceIsLess); if (p_FoundElement->PriceInTicks == PriceInTicks && p_FoundElement != p_EndElement) { *p_VAP = p_FoundElement; return true; } r_InsertionIndex = static_cast ( p_FoundElement - p_BeginElement + BeginIndex ); return false; } /*============================================================================ Searches for the VAP element in the bar at the given BarIndex at the next price greater than the given r_PriceInTicks. Returns true unless there is no higher price in the bar. r_PriceInTicks and p_VAP will be set according to this next element. If r_PriceInTicks is set to INT_MIN, this method will get the VAP element with the lowest price for the bar. ----------------------------------------------------------------------------*/ template inline bool c_VAPContainerBase::GetNextHigherVAPElement ( const unsigned int BarIndex , int& r_PriceInTicks , const t_VolumeAtPrice** p_VAP ) const { *p_VAP = nullptr; unsigned int BeginIndex, EndIndex; if (!GetBeginEndIndexesForBarIndex(BarIndex, &BeginIndex, &EndIndex)) return false; // Return the element for the lowest price on the first iteration. if (r_PriceInTicks == INT_MIN) { t_VolumeAtPrice& r_LowestVAPElement = m_p_VAPDataElements[BeginIndex]; *p_VAP = &r_LowestVAPElement; r_PriceInTicks = r_LowestVAPElement.PriceInTicks; return true; } // This is a temporary object only for the purpose of providing an object // of the same type for comparison. t_VolumeAtPrice Target; Target.PriceInTicks = r_PriceInTicks; const t_VolumeAtPrice* p_BeginElement = m_p_VAPDataElements + BeginIndex; const t_VolumeAtPrice* p_EndElement = m_p_VAPDataElements + EndIndex; // Note: upper_bound does a binary search (or something algorithmically // equivalent) and returns the lowest element that is greater than the // given Target. const t_VolumeAtPrice* p_NextHigherElement = std::upper_bound(p_BeginElement, p_EndElement, Target, PriceIsLess); // Return false if we have reached the end of the data for the bar. if (p_NextHigherElement >= p_EndElement) return false; *p_VAP = p_NextHigherElement; r_PriceInTicks = p_NextHigherElement->PriceInTicks; return true; } /*============================================================================ Searches for the VAP element in the bar at the given BarIndex at the next price less than the given r_PriceInTicks. Returns true unless there is no lower price in the bar. r_PriceInTicks and p_VAP will be set according to this next element. If r_PriceInTicks is set to INT_MAX, this method will get the VAP element with the highest price for the bar. ----------------------------------------------------------------------------*/ template inline bool c_VAPContainerBase::GetNextLowerVAPElement ( const unsigned int BarIndex , int& r_PriceInTicks , const t_VolumeAtPrice** p_VAP ) const { *p_VAP = nullptr; unsigned int BeginIndex, EndIndex; if (!GetBeginEndIndexesForBarIndex(BarIndex, &BeginIndex, &EndIndex)) return false; // Return the element for the highest price on the first iteration. if (r_PriceInTicks == INT_MAX) { t_VolumeAtPrice& r_HighestVAPElement = m_p_VAPDataElements[EndIndex - 1]; *p_VAP = &r_HighestVAPElement; r_PriceInTicks = r_HighestVAPElement.PriceInTicks; return true; } // This is a temporary object only for the purpose of providing an object // of the same type for comparison. t_VolumeAtPrice Target; Target.PriceInTicks = r_PriceInTicks; const t_VolumeAtPrice* p_BeginElement = m_p_VAPDataElements + BeginIndex; const t_VolumeAtPrice* p_EndElement = m_p_VAPDataElements + EndIndex; // Note: lower_bound does a binary search (or something algorithmically // equivalent) and returns the lowest element that is greater than or // equal to the given Target. This means the next lower element will be // the element just before the returned element. const t_VolumeAtPrice* p_NextLowerElement = std::lower_bound(p_BeginElement, p_EndElement, Target, PriceIsLess); // Move the returned pointer down to the next lower element. --p_NextLowerElement; // Return false if we have reached the end of the data for the bar. if (p_NextLowerElement < p_BeginElement) return false; *p_VAP = p_NextLowerElement; r_PriceInTicks = p_NextLowerElement->PriceInTicks; return true; } /*==========================================================================*/ template inline bool c_VAPContainerBase::GetHighAndLowPriceTicksForBarIndex ( const unsigned int BarIndex , int& r_HighPriceInTicks , int& r_LowPriceInTicks ) const { return GetHighAndLowPriceTicksForBarIndex ( BarIndex , &r_HighPriceInTicks , &r_LowPriceInTicks ); } /*==========================================================================*/ template inline bool c_VAPContainerBase::GetHighAndLowPriceTicksForBarIndex ( const unsigned int BarIndex , int* p_HighPriceInTicks , int* p_LowPriceInTicks ) const { unsigned int BeginIndex, EndIndex; if (!GetBeginEndIndexesForBarIndex(BarIndex, &BeginIndex, &EndIndex)) return false; GetHighAndLowPriceTicks ( BeginIndex , EndIndex , p_HighPriceInTicks , p_LowPriceInTicks ); return true; } /*==========================================================================*/ template inline bool c_VAPContainerBase::GetHighAndLowPriceTicksForBarIndexRange ( const unsigned int FirstBarIndex , const unsigned int LastBarIndex , int* p_HighPriceInTicks , int* p_LowPriceInTicks ) const { unsigned int BeginIndex = GetFirstDataElementIndexForBar(FirstBarIndex); unsigned int EndIndex = GetFirstDataElementIndexForBar(LastBarIndex + 1); if (BeginIndex >= EndIndex) return false; GetHighAndLowPriceTicks ( BeginIndex , EndIndex , p_HighPriceInTicks , p_LowPriceInTicks ); return true; } /*============================================================================ Returns an element with all zeros if there is no element for the requested BarIndex and PriceInTicks. ----------------------------------------------------------------------------*/ template inline const t_VolumeAtPrice& c_VAPContainerBase::GetVAPElementAtPrice ( const unsigned int BarIndex , const int PriceInTicks ) const { static const t_VolumeAtPrice ZeroVAP; unsigned int BeginIndex, EndIndex; if (!GetBeginEndIndexesForBarIndex(BarIndex, &BeginIndex, &EndIndex)) return ZeroVAP; // This is a temporary object only for the purpose of providing an object // of the same type for comparison. t_VolumeAtPrice Target; Target.PriceInTicks = PriceInTicks; t_VolumeAtPrice* p_BeginElement = m_p_VAPDataElements + BeginIndex; t_VolumeAtPrice* p_EndElement = m_p_VAPDataElements + EndIndex; t_VolumeAtPrice* p_FoundElement = std::lower_bound(p_BeginElement, p_EndElement, Target, PriceIsLess); if (p_FoundElement->PriceInTicks == PriceInTicks && p_FoundElement != p_EndElement) { return *p_FoundElement; } return ZeroVAP; } /*============================================================================ Adds the given VolumeAtPrice to the current volume at the given BarIndex and PriceInTicks. If no current volume exists, the volume will be added to a new default element, if possible. Returns true unless the requested element could not be accessed or created. ----------------------------------------------------------------------------*/ template inline bool c_VAPContainerBase::AddVolumeAtPrice ( const int PriceInTicks , const unsigned int BarIndex , const t_VolumeAtPrice& VolumeAtPrice ) { t_VolumeAtPrice* p_VAPElement = nullptr; const bool ElementFound = GetVAPElement(PriceInTicks, BarIndex, &p_VAPElement, true); if (!ElementFound || p_VAPElement == nullptr) return false; *p_VAPElement += VolumeAtPrice; return true; } /*============================================================================ Subtracts the given VolumeAtPrice from the current volume at the given BarIndex and PriceInTicks. If no current volume exists, the volume will be subtracted from a new default element, if possible. Returns true unless the requested element could not be accessed or created. ----------------------------------------------------------------------------*/ template inline bool c_VAPContainerBase::SubtractVolumeAtPrice ( const int PriceInTicks , const unsigned int BarIndex , const t_VolumeAtPrice& VolumeAtPrice ) { t_VolumeAtPrice* p_VAPElement = nullptr; const bool ElementFound = GetVAPElement(PriceInTicks, BarIndex, &p_VAPElement, true); if (!ElementFound || p_VAPElement == nullptr) return false; *p_VAPElement -= VolumeAtPrice; return true; } /*==========================================================================*/ template bool c_VAPContainerBase::SetVolumeAtPrice(const int PriceInTicks, const unsigned int BarIndex, const t_VolumeAtPrice& VolumeAtPrice) { t_VolumeAtPrice* p_VAPElement = nullptr; const bool ElementFound = GetVAPElement(PriceInTicks, BarIndex, &p_VAPElement, true); if (!ElementFound || p_VAPElement == nullptr) return false; *p_VAPElement = VolumeAtPrice; return true; } /*============================================================================ Returns true only if the price of the first element is less than the price of the second element. This can be used as a comparison predicate for sorting and searching. ----------------------------------------------------------------------------*/ template inline bool c_VAPContainerBase::PriceIsLess ( const t_VolumeAtPrice& First , const t_VolumeAtPrice& Second ) { return (First.PriceInTicks < Second.PriceInTicks); } /*============================================================================ Checks if the given BarIndex is currently in the container. If AllocateIfNeeded is true (default) and the bar does not exist, new bar elements will be allocated and initialized so that the bar may exist. Returns true unless the bar does not exist or could not be allocated. ----------------------------------------------------------------------------*/ template inline bool c_VAPContainerBase::EnsureBarExists ( const unsigned int BarIndex , const bool AllocateIfNeeded ) { // Make certain the arrays are initially allocated. if (!InitialAllocation()) return false; // Return true if the given BarIndex is currently within this container. if (BarIndex < m_NumberOfBars) return true; // The given BarIndex is not currently within this container. // Return false if we cannot allocated the requested bar. if (!AllocateIfNeeded) return false; // Add the requested bar. const unsigned int PriorNumberOfBars = m_NumberOfBars; m_NumberOfBars = BarIndex + 1; const unsigned int NumberOfBarsAdded = m_NumberOfBars - PriorNumberOfBars; // Allocate space for more bars if necessary. if (!AllocateBar(BarIndex)) { Clear(); return false; } // Get the index that is just beyond the end of the VAP elements. const unsigned int VAPEndIndex = static_cast ( (m_p_VAPDataElements + m_NumElementsUsed) - m_p_VAPDataElements ); // Set the newly added bars to the end of the VAP data. for (unsigned int NewIndex = PriorNumberOfBars; NewIndex < m_NumberOfBars; ++NewIndex ) { m_p_BarIndexToFirstElementIndexArray[NewIndex] = VAPEndIndex; } return true; } /*==========================================================================*/ template inline bool c_VAPContainerBase::InitialAllocation() { if (m_p_BarIndexToFirstElementIndexArray == nullptr) { m_NumBarsAllocated = m_InitialAllocationElements; m_p_BarIndexToFirstElementIndexArray = static_cast ( realloc ( nullptr , m_NumBarsAllocated * sizeof(unsigned int) ) ); if (m_p_BarIndexToFirstElementIndexArray == nullptr) return false; // Zero New elements. memset ( m_p_BarIndexToFirstElementIndexArray , 0 , m_NumBarsAllocated * sizeof(unsigned int) ); } if (m_p_VAPDataElements == nullptr) { m_NumElementsAllocated = m_InitialAllocationElements*10; m_p_VAPDataElements = static_cast ( realloc ( nullptr , m_NumElementsAllocated * sizeof(t_VolumeAtPrice) ) ); if (m_p_VAPDataElements == nullptr) return false; // Zero New elements. memset ( m_p_VAPDataElements , 0 , m_NumElementsAllocated * sizeof(t_VolumeAtPrice) ); } return true; } /*============================================================================ Makes any necessary allocations and initializations to ensure bar data at the requested BarIndex is safe to access. Returns true unless the bar could not be allocated. ----------------------------------------------------------------------------*/ template inline bool c_VAPContainerBase::AllocateBar ( const unsigned int BarIndex ) { // Do nothing but return true if the requested bar is already allocated. if (BarIndex < m_NumBarsAllocated) return true; const int PriorNumBarsAllocated = m_NumBarsAllocated; m_NumBarsAllocated = m_NumBarsAllocated * 2; if (BarIndex >= m_NumBarsAllocated) m_NumBarsAllocated = m_NumberOfBars; // Note: If there is not enough available memory to expand the block to // the given size, the original block is left unchanged, and nullptr is // returned. void* p_NewBarIndexToFirstElementIndexArray = realloc ( m_p_BarIndexToFirstElementIndexArray , m_NumBarsAllocated * sizeof(unsigned int) ); if (p_NewBarIndexToFirstElementIndexArray == nullptr) return false; m_p_BarIndexToFirstElementIndexArray = static_cast(p_NewBarIndexToFirstElementIndexArray); const int NumAllocatedBarsAdded = m_NumBarsAllocated - PriorNumBarsAllocated; // Initialize the new bar elements to zero. memset ( m_p_BarIndexToFirstElementIndexArray + PriorNumBarsAllocated , 0 , NumAllocatedBarsAdded * sizeof(unsigned int) ); return true; } /*============================================================================ Makes any necessary allocations and initializations to ensure VAP data at the requested ElementIndex is safe to access. Returns true unless the element could not be allocated. ----------------------------------------------------------------------------*/ template inline bool c_VAPContainerBase::AllocateElement ( const unsigned int ElementIndex ) { if (ElementIndex < m_NumElementsAllocated) return true; // The array needs to be increased in size. const int PriorNumElementsAllocated = m_NumElementsAllocated; m_NumElementsAllocated = static_cast(m_NumElementsAllocated * 1.25); // Note: If there is not enough available memory to expand the block to // the given size, the original block is left unchanged, and nullptr is // returned. void* p_NewVAPDataElements = realloc ( m_p_VAPDataElements , m_NumElementsAllocated * sizeof(t_VolumeAtPrice) ); if (p_NewVAPDataElements == nullptr) return false; m_p_VAPDataElements = static_cast(p_NewVAPDataElements); const int NumAllocatedElementsAdded = m_NumElementsAllocated - PriorNumElementsAllocated; // Initialize new elements to zero. memset ( m_p_VAPDataElements + PriorNumElementsAllocated , 0 , NumAllocatedElementsAdded * sizeof(t_VolumeAtPrice) ); return true; } /*============================================================================ Returns the index of the first element in the VAP data array for the bar at the given BarIndex. If the bar is not within the container, the 'end' index, or the size of the VAP data array, is returned. ----------------------------------------------------------------------------*/ template inline unsigned int c_VAPContainerBase::GetFirstDataElementIndexForBar ( const unsigned int BarIndex ) const { if (BarIndex >= m_NumberOfBars) return m_NumElementsUsed; if (m_p_BarIndexToFirstElementIndexArray == nullptr) return m_NumElementsUsed; return m_p_BarIndexToFirstElementIndexArray[BarIndex]; } /*============================================================================ Sets the given *p_BeginIndex and *p_EndIndex parameters to the indexes of the first element and one index beyond the last element of VAP data for the bar at the given BarIndex. *p_BeginIndex and *p_EndIndex are only set when the function returns true, and the function only returns true if there is data for the bar. ----------------------------------------------------------------------------*/ template inline bool c_VAPContainerBase::GetBeginEndIndexesForBarIndex ( const unsigned int BarIndex , unsigned int* p_BeginIndex , unsigned int* p_EndIndex ) const { unsigned int BeginIndex = GetFirstDataElementIndexForBar(BarIndex); unsigned int EndIndex = GetFirstDataElementIndexForBar(BarIndex + 1); if (BeginIndex >= EndIndex) return false; if (p_BeginIndex != nullptr) *p_BeginIndex = BeginIndex; if (p_EndIndex != nullptr) *p_EndIndex = EndIndex; return true; } /*==========================================================================*/ template inline void c_VAPContainerBase::GetHighAndLowPriceTicks ( const unsigned int BeginIndex , unsigned int EndIndex , int* p_HighPriceInTicks , int* p_LowPriceInTicks ) const { if (m_p_VAPDataElements == nullptr) return; if (p_HighPriceInTicks == nullptr && p_LowPriceInTicks == nullptr) return; if (BeginIndex == EndIndex) return; if (EndIndex > m_NumElementsUsed) EndIndex = m_NumElementsUsed; if (p_HighPriceInTicks != nullptr) *p_HighPriceInTicks = m_p_VAPDataElements[EndIndex - 1].PriceInTicks; if (p_LowPriceInTicks != nullptr) *p_LowPriceInTicks = m_p_VAPDataElements[BeginIndex].PriceInTicks; } /****************************************************************************/ // c_VAPContainer class c_VAPContainer : public c_VAPContainerBase { //--- Public Methods ----------------------------------------------------- public: c_VAPContainer(const unsigned int InitialAllocationElements = 1024); unsigned int GetVolumeAtPrice ( const unsigned int BarIndex , const int PriceInTicks ) const; unsigned int GetBidVolumeAtPrice ( const unsigned int BarIndex , const int PriceInTicks ) const; unsigned int GetAskVolumeAtPrice ( const unsigned int BarIndex , const int PriceInTicks ) const; }; /*==========================================================================*/ inline c_VAPContainer::c_VAPContainer ( const unsigned int InitialAllocationElements ) : c_VAPContainerBase(InitialAllocationElements) { } /*============================================================================ Returns 0 if there is no element for the requested BarIndex and PriceInTicks. ----------------------------------------------------------------------------*/ inline unsigned int c_VAPContainer::GetVolumeAtPrice ( const unsigned int BarIndex , const int PriceInTicks ) const { return GetVAPElementAtPrice(BarIndex, PriceInTicks).Volume; } /*============================================================================ Returns 0 if there is no element for the requested BarIndex and PriceInTicks. ----------------------------------------------------------------------------*/ inline unsigned int c_VAPContainer::GetBidVolumeAtPrice ( const unsigned int BarIndex , const int PriceInTicks ) const { return GetVAPElementAtPrice(BarIndex, PriceInTicks).BidVolume; } /*============================================================================ Returns 0 if there is no element for the requested BarIndex and PriceInTicks. ----------------------------------------------------------------------------*/ inline unsigned int c_VAPContainer::GetAskVolumeAtPrice ( const unsigned int BarIndex , const int PriceInTicks ) const { return GetVAPElementAtPrice(BarIndex, PriceInTicks).AskVolume; } /****************************************************************************/ // c_VolumeLevelAtPriceContainer class c_VolumeLevelAtPriceContainer : public c_VAPContainerBase { //--- Public Methods ----------------------------------------------------- public: c_VolumeLevelAtPriceContainer ( const unsigned int InitialAllocationElements = 1024 ); unsigned int GetMaxVolumeAtPrice ( const unsigned int BarIndex , const int PriceInTicks ) const; unsigned int GetTotalVolumeAtPrice ( const unsigned int BarIndex , const int PriceInTicks ) const; }; /*==========================================================================*/ inline c_VolumeLevelAtPriceContainer::c_VolumeLevelAtPriceContainer ( const unsigned int InitialAllocationElements ) : c_VAPContainerBase(InitialAllocationElements) { } /*============================================================================ Returns 0 if there is no element for the requested BarIndex and PriceInTicks. ----------------------------------------------------------------------------*/ inline unsigned int c_VolumeLevelAtPriceContainer::GetMaxVolumeAtPrice ( const unsigned int BarIndex , const int PriceInTicks ) const { return GetVAPElementAtPrice(BarIndex, PriceInTicks).MaxVolume; } /*============================================================================ Returns 0 if there is no element for the requested BarIndex and PriceInTicks. ----------------------------------------------------------------------------*/ inline unsigned int c_VolumeLevelAtPriceContainer::GetTotalVolumeAtPrice ( const unsigned int BarIndex , const int PriceInTicks ) const { return GetVAPElementAtPrice(BarIndex, PriceInTicks).TotalVolume; } /*==========================================================================*/