165 lines
5.4 KiB
MQL5
165 lines
5.4 KiB
MQL5
|
|
/**
|
||
|
|
* @file RollingMax - Stateful O(1) amortized rolling maximum algorithm using delayed live-value insertion and monotonic deque storage.
|
||
|
|
* @deps MonotonicDeque.mqh
|
||
|
|
* @state Stateful (Maintains committed historical maximum candidates and a separately tracked live value)
|
||
|
|
* @io Appended or replaced double values -> Updates rolling maximum state | Request value -> Returns current rolling maximum
|
||
|
|
* @note AI-assisted code. Independently review and test in a demo environment before live production trading.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#ifndef __ROLLING_MAX_MQH__
|
||
|
|
#define __ROLLING_MAX_MQH__
|
||
|
|
|
||
|
|
#include "..\\Storage\\MonotonicDeque.mqh"
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| CRollingMax |
|
||
|
|
//| |
|
||
|
|
//| PURPOSE |
|
||
|
|
//| ------- |
|
||
|
|
//| Maintains a rolling maximum using O(1) amortized operations. |
|
||
|
|
//| |
|
||
|
|
//| RESPONSIBILITIES |
|
||
|
|
//| ---------------- |
|
||
|
|
//| • Maintain committed historical maximum candidates |
|
||
|
|
//| • Maintain current live value separately |
|
||
|
|
//| • Commit finalized live values on append |
|
||
|
|
//| • Evict expired historical candidates |
|
||
|
|
//| • Support arbitrary live-value replacement |
|
||
|
|
//| |
|
||
|
|
//| NON-RESPONSIBILITIES |
|
||
|
|
//| -------------------- |
|
||
|
|
//| • Deque storage implementation |
|
||
|
|
//| • Series storage |
|
||
|
|
//| • Stream event handling |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
class CRollingMax
|
||
|
|
{
|
||
|
|
private:
|
||
|
|
|
||
|
|
CMonotonicDeque m_deque;
|
||
|
|
int m_capacity;
|
||
|
|
long m_nextIndex;
|
||
|
|
double m_liveValue;
|
||
|
|
bool m_hasLive;
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Commit |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void Commit(const double value)
|
||
|
|
{
|
||
|
|
if(m_capacity == 1)
|
||
|
|
return;
|
||
|
|
|
||
|
|
long index = m_nextIndex++;
|
||
|
|
|
||
|
|
long frontIndex;
|
||
|
|
double frontValue;
|
||
|
|
|
||
|
|
long expiry = index - (m_capacity - 1);
|
||
|
|
|
||
|
|
while(m_deque.Front(frontIndex, frontValue) &&
|
||
|
|
frontIndex <= expiry)
|
||
|
|
{
|
||
|
|
m_deque.PopFront();
|
||
|
|
}
|
||
|
|
|
||
|
|
long backIndex;
|
||
|
|
double backValue;
|
||
|
|
|
||
|
|
while(m_deque.Back(backIndex, backValue) &&
|
||
|
|
backValue <= value)
|
||
|
|
{
|
||
|
|
m_deque.PopBack();
|
||
|
|
}
|
||
|
|
|
||
|
|
m_deque.PushBack(index, value);
|
||
|
|
}
|
||
|
|
|
||
|
|
public:
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Constructor |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
CRollingMax()
|
||
|
|
{
|
||
|
|
m_capacity = 0;
|
||
|
|
m_nextIndex = 0;
|
||
|
|
m_liveValue = 0.0;
|
||
|
|
m_hasLive = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Initialize |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool Init(const int capacity)
|
||
|
|
{
|
||
|
|
if(capacity <= 0)
|
||
|
|
return(false);
|
||
|
|
|
||
|
|
if(!m_deque.Init(capacity))
|
||
|
|
return(false);
|
||
|
|
|
||
|
|
m_capacity = capacity;
|
||
|
|
|
||
|
|
Reset();
|
||
|
|
|
||
|
|
return(true);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Reset |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void Reset()
|
||
|
|
{
|
||
|
|
m_deque.Reset();
|
||
|
|
|
||
|
|
m_nextIndex = 0;
|
||
|
|
m_liveValue = 0.0;
|
||
|
|
m_hasLive = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Append |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void Append(const double value)
|
||
|
|
{
|
||
|
|
if(m_hasLive)
|
||
|
|
Commit(m_liveValue);
|
||
|
|
|
||
|
|
m_liveValue = value;
|
||
|
|
m_hasLive = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Replace Last |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool ReplaceLast(const double value)
|
||
|
|
{
|
||
|
|
if(!m_hasLive)
|
||
|
|
return(false);
|
||
|
|
|
||
|
|
m_liveValue = value;
|
||
|
|
|
||
|
|
return(true);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Value |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
double Value() const
|
||
|
|
{
|
||
|
|
if(!m_hasLive)
|
||
|
|
return(0.0);
|
||
|
|
|
||
|
|
long frontIndex;
|
||
|
|
double frontValue;
|
||
|
|
|
||
|
|
if(!m_deque.Front(frontIndex, frontValue))
|
||
|
|
return(m_liveValue);
|
||
|
|
|
||
|
|
return(MathMax(frontValue, m_liveValue));
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
#endif // __ROLLING_MAX_MQH__
|
||
|
|
//+------------------------------------------------------------------+
|