341 lines
10 KiB
MQL5
341 lines
10 KiB
MQL5
|
|
/**
|
||
|
|
* @file Stochastic - Concrete Streaming Stochastic indicator using rolling extrema, Simple Moving Average slowing, and configurable signal-line smoothing.
|
||
|
|
* @deps IndicatorBase.mqh, SMA.mqh, EMA.mqh, SMMA.mqh, LWMA.mqh, BarSeriesBuffer.mqh, RollingMin.mqh, RollingMax.mqh, EMovingAverageType.mqh
|
||
|
|
* @state Stateful (Orchestrates rolling extrema, internal moving averages, dynamic signal-line ownership, and output time-series buffers across ticks)
|
||
|
|
* @io Source struct OHLC pricing tick -> Updates rolling extrema, smoothed K, signal D, and output buffers | Request offset and line index -> Returns historical K or D double value
|
||
|
|
* @note AI-assisted code. Independently review and test in a demo environment before live production trading.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#ifndef __STOCHASTIC_MQH__
|
||
|
|
#define __STOCHASTIC_MQH__
|
||
|
|
|
||
|
|
#include "IndicatorBase.mqh"
|
||
|
|
#include "SMA.mqh"
|
||
|
|
#include "EMA.mqh"
|
||
|
|
#include "SMMA.mqh"
|
||
|
|
#include "LWMA.mqh"
|
||
|
|
#include "..\\Storage\\BarSeriesBuffer.mqh"
|
||
|
|
#include "..\\Algorithms\\RollingMin.mqh"
|
||
|
|
#include "..\\Algorithms\\RollingMax.mqh"
|
||
|
|
#include "..\\Common\\EMovingAverageType.mqh"
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Stochastic |
|
||
|
|
//| |
|
||
|
|
//| PURPOSE |
|
||
|
|
//| ------- |
|
||
|
|
//| Streaming Stochastic indicator. |
|
||
|
|
//| |
|
||
|
|
//| RESPONSIBILITIES |
|
||
|
|
//| ---------------- |
|
||
|
|
//| • Maintain Stochastic configuration |
|
||
|
|
//| • Maintain rolling highest and lowest values |
|
||
|
|
//| • Calculate and smooth raw K values |
|
||
|
|
//| • Calculate configurable signal D values |
|
||
|
|
//| • Persist calculated K and D outputs |
|
||
|
|
//| |
|
||
|
|
//| NON-RESPONSIBILITIES |
|
||
|
|
//| -------------------- |
|
||
|
|
//| • Rolling extrema implementation |
|
||
|
|
//| • Moving average implementation |
|
||
|
|
//| • Circular storage implementation |
|
||
|
|
//| • MT5 lifecycle management |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
class Stochastic : public IndicatorBase
|
||
|
|
{
|
||
|
|
private:
|
||
|
|
|
||
|
|
int m_kPeriod;
|
||
|
|
int m_dPeriod;
|
||
|
|
int m_slowingPeriod;
|
||
|
|
EMovingAverageType m_maType;
|
||
|
|
|
||
|
|
CBarSeriesBuffer m_input;
|
||
|
|
CRollingMin m_rollingMin;
|
||
|
|
CRollingMax m_rollingMax;
|
||
|
|
|
||
|
|
SMA m_numeratorSMA;
|
||
|
|
SMA m_denominatorSMA;
|
||
|
|
IndicatorBase *m_signal;
|
||
|
|
|
||
|
|
CBarSeriesBuffer m_main;
|
||
|
|
CBarSeriesBuffer m_signalLine;
|
||
|
|
|
||
|
|
public:
|
||
|
|
|
||
|
|
static const int MAIN;
|
||
|
|
static const int SIGNAL;
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Constructor |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
Stochastic()
|
||
|
|
{
|
||
|
|
m_kPeriod = 0;
|
||
|
|
m_dPeriod = 0;
|
||
|
|
m_slowingPeriod = 0;
|
||
|
|
m_maType = MA_SIMPLE;
|
||
|
|
m_signal = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Destructor |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
~Stochastic()
|
||
|
|
{
|
||
|
|
DeInit();
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Parameters |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void SetParameters(const int kPeriod,
|
||
|
|
const int dPeriod,
|
||
|
|
const int slowingPeriod,
|
||
|
|
const EMovingAverageType maType)
|
||
|
|
{
|
||
|
|
m_kPeriod = kPeriod;
|
||
|
|
m_dPeriod = dPeriod;
|
||
|
|
m_slowingPeriod = slowingPeriod;
|
||
|
|
m_maType = maType;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Initialize |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
virtual bool Init()
|
||
|
|
{
|
||
|
|
DeInit();
|
||
|
|
|
||
|
|
if(m_kPeriod <= 0 ||
|
||
|
|
m_dPeriod <= 0 ||
|
||
|
|
m_slowingPeriod <= 0)
|
||
|
|
{
|
||
|
|
return(false);
|
||
|
|
}
|
||
|
|
|
||
|
|
if(m_maxBarsBack == 0)
|
||
|
|
m_maxBarsBack = m_kPeriod;
|
||
|
|
|
||
|
|
if(m_maxBarsBack <= 0)
|
||
|
|
return(false);
|
||
|
|
|
||
|
|
if(!m_input.Init(m_kPeriod))
|
||
|
|
return(false);
|
||
|
|
|
||
|
|
if(!m_rollingMin.Init(m_kPeriod))
|
||
|
|
return(false);
|
||
|
|
|
||
|
|
if(!m_rollingMax.Init(m_kPeriod))
|
||
|
|
return(false);
|
||
|
|
|
||
|
|
m_numeratorSMA.SetParameters(m_slowingPeriod);
|
||
|
|
m_numeratorSMA.SetMaxBarsBack(m_maxBarsBack);
|
||
|
|
m_numeratorSMA.SetErrorValue(m_errorValue);
|
||
|
|
|
||
|
|
if(!m_numeratorSMA.Init())
|
||
|
|
return(false);
|
||
|
|
|
||
|
|
m_denominatorSMA.SetParameters(m_slowingPeriod);
|
||
|
|
m_denominatorSMA.SetMaxBarsBack(m_maxBarsBack);
|
||
|
|
m_denominatorSMA.SetErrorValue(m_errorValue);
|
||
|
|
|
||
|
|
if(!m_denominatorSMA.Init())
|
||
|
|
return(false);
|
||
|
|
|
||
|
|
switch(m_maType)
|
||
|
|
{
|
||
|
|
case MA_SIMPLE:
|
||
|
|
m_signal = new SMA;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case MA_EXPONENTIAL:
|
||
|
|
m_signal = new EMA;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case MA_SMOOTHED:
|
||
|
|
m_signal = new SMMA;
|
||
|
|
break;
|
||
|
|
|
||
|
|
case MA_LINEAR_WEIGHTED:
|
||
|
|
m_signal = new LWMA;
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
return(false);
|
||
|
|
}
|
||
|
|
|
||
|
|
if(CheckPointer(m_signal) != POINTER_DYNAMIC)
|
||
|
|
{
|
||
|
|
m_signal = NULL;
|
||
|
|
return(false);
|
||
|
|
}
|
||
|
|
|
||
|
|
if(m_maType == MA_SIMPLE)
|
||
|
|
((SMA*)m_signal).SetParameters(m_dPeriod);
|
||
|
|
else if(m_maType == MA_EXPONENTIAL)
|
||
|
|
((EMA*)m_signal).SetParameters(m_dPeriod);
|
||
|
|
else if(m_maType == MA_SMOOTHED)
|
||
|
|
((SMMA*)m_signal).SetParameters(m_dPeriod);
|
||
|
|
else
|
||
|
|
((LWMA*)m_signal).SetParameters(m_dPeriod);
|
||
|
|
|
||
|
|
m_signal.SetMaxBarsBack(m_maxBarsBack);
|
||
|
|
m_signal.SetErrorValue(m_errorValue);
|
||
|
|
|
||
|
|
if(!m_signal.Init())
|
||
|
|
{
|
||
|
|
DeInit();
|
||
|
|
return(false);
|
||
|
|
}
|
||
|
|
|
||
|
|
if(!m_main.Init(m_maxBarsBack))
|
||
|
|
{
|
||
|
|
DeInit();
|
||
|
|
return(false);
|
||
|
|
}
|
||
|
|
|
||
|
|
if(!m_signalLine.Init(m_maxBarsBack))
|
||
|
|
{
|
||
|
|
DeInit();
|
||
|
|
return(false);
|
||
|
|
}
|
||
|
|
|
||
|
|
return(true);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Reset |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
virtual void Reset()
|
||
|
|
{
|
||
|
|
m_input.Reset();
|
||
|
|
m_rollingMin.Reset();
|
||
|
|
m_rollingMax.Reset();
|
||
|
|
m_numeratorSMA.Reset();
|
||
|
|
m_denominatorSMA.Reset();
|
||
|
|
|
||
|
|
if(CheckPointer(m_signal) != POINTER_INVALID)
|
||
|
|
m_signal.Reset();
|
||
|
|
|
||
|
|
m_main.Reset();
|
||
|
|
m_signalLine.Reset();
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Deinitialize |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
virtual void DeInit()
|
||
|
|
{
|
||
|
|
if(CheckPointer(m_signal) == POINTER_DYNAMIC)
|
||
|
|
delete m_signal;
|
||
|
|
|
||
|
|
m_signal = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Update |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
virtual void Update(const Source &src)
|
||
|
|
{
|
||
|
|
ESeriesEvent event = m_input.Update(src.time, src.close);
|
||
|
|
|
||
|
|
switch(event)
|
||
|
|
{
|
||
|
|
case SERIES_FIRST_SAMPLE:
|
||
|
|
m_rollingMin.Append(src.low);
|
||
|
|
m_rollingMax.Append(src.high);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case SERIES_APPEND:
|
||
|
|
m_rollingMin.Append(src.low);
|
||
|
|
m_rollingMax.Append(src.high);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case SERIES_REPLACE_LAST:
|
||
|
|
m_rollingMin.ReplaceLast(src.low);
|
||
|
|
m_rollingMax.ReplaceLast(src.high);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case SERIES_RESET:
|
||
|
|
Reset();
|
||
|
|
|
||
|
|
m_input.Update(src.time, src.close);
|
||
|
|
m_rollingMin.Append(src.low);
|
||
|
|
m_rollingMax.Append(src.high);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
double lowest = m_rollingMin.Value();
|
||
|
|
double highest = m_rollingMax.Value();
|
||
|
|
|
||
|
|
double numerator = src.close - lowest;
|
||
|
|
double denominator = highest - lowest;
|
||
|
|
|
||
|
|
Source numeratorSource =
|
||
|
|
Source::From(numerator, src.time);
|
||
|
|
|
||
|
|
Source denominatorSource =
|
||
|
|
Source::From(denominator, src.time);
|
||
|
|
|
||
|
|
m_numeratorSMA.Update(numeratorSource);
|
||
|
|
m_denominatorSMA.Update(denominatorSource);
|
||
|
|
|
||
|
|
double numeratorAverage =
|
||
|
|
m_numeratorSMA.GetValue(0, SMA::MAIN);
|
||
|
|
|
||
|
|
double denominatorAverage =
|
||
|
|
m_denominatorSMA.GetValue(0, SMA::MAIN);
|
||
|
|
|
||
|
|
double smoothedK = m_errorValue;
|
||
|
|
|
||
|
|
if(numeratorAverage != m_errorValue &&
|
||
|
|
denominatorAverage != m_errorValue)
|
||
|
|
{
|
||
|
|
smoothedK = denominatorAverage == 0.0
|
||
|
|
? 0.0
|
||
|
|
: 100.0 * numeratorAverage / denominatorAverage;
|
||
|
|
}
|
||
|
|
|
||
|
|
double signalD = m_errorValue;
|
||
|
|
|
||
|
|
if(smoothedK != m_errorValue)
|
||
|
|
{
|
||
|
|
Source signalSource = Source::From(smoothedK, src.time);
|
||
|
|
|
||
|
|
m_signal.Update(signalSource);
|
||
|
|
signalD = m_signal.GetValue(0, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
m_main.Update(src.time, smoothedK);
|
||
|
|
m_signalLine.Update(src.time, signalD);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Value Access |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
virtual double GetValue(const int index,
|
||
|
|
const int lineIndex = 0) const
|
||
|
|
{
|
||
|
|
double value;
|
||
|
|
|
||
|
|
if(lineIndex == MAIN)
|
||
|
|
{
|
||
|
|
if(m_main.At(index, value))
|
||
|
|
return(value);
|
||
|
|
}
|
||
|
|
else if(lineIndex == SIGNAL)
|
||
|
|
{
|
||
|
|
if(m_signalLine.At(index, value))
|
||
|
|
return(value);
|
||
|
|
}
|
||
|
|
|
||
|
|
return(m_errorValue);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const int Stochastic::MAIN = 0;
|
||
|
|
const int Stochastic::SIGNAL = 1;
|
||
|
|
|
||
|
|
#endif // __STOCHASTIC_MQH__
|
||
|
|
//+------------------------------------------------------------------+
|