fast-ta/Indicators/Stochastic.mqh

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__
//+------------------------------------------------------------------+