123 lines
4.3 KiB
Plaintext
123 lines
4.3 KiB
Plaintext
#ifndef RSI_FORCE_STATE_EA__INDICATORS_MQH
|
|
#define RSI_FORCE_STATE_EA__INDICATORS_MQH
|
|
|
|
// ============================================================
|
|
// Indicator handles + cached series buffers.
|
|
// All buffers are timeseries indexed (index 0 = current bar).
|
|
// ============================================================
|
|
|
|
int g_hRSI = INVALID_HANDLE;
|
|
int g_hEMA9 = INVALID_HANDLE; // EMA(InpRSI_EMA9Period) computed on RSI
|
|
int g_hWMA45 = INVALID_HANDLE; // WMA(InpRSI_WMA45Period) computed on RSI
|
|
int g_hEMA200 = INVALID_HANDLE; // EMA(InpEMA200Period) on close
|
|
int g_hATR = INVALID_HANDLE;
|
|
|
|
// Dynamic arrays so ArraySetAsSeries(...) is allowed.
|
|
double g_RSI[];
|
|
double g_EMA9[];
|
|
double g_WMA45[];
|
|
double g_EMA200[];
|
|
double g_ATR[];
|
|
MqlRates g_Bars[];
|
|
|
|
// How many bars we keep cached. Must be > swing/sideway/cross lookback.
|
|
const int kIndicatorCacheBars = 200;
|
|
|
|
// ------------------------------------------------------------
|
|
// Lifecycle
|
|
// ------------------------------------------------------------
|
|
|
|
bool InitIndicators()
|
|
{
|
|
g_hRSI = iRSI(_Symbol, _Period, InpRSIPeriod, PRICE_CLOSE);
|
|
if (g_hRSI == INVALID_HANDLE) return false;
|
|
|
|
// EMA9 / WMA45 are computed on the RSI buffer (not on price).
|
|
g_hEMA9 = iMA(_Symbol, _Period, InpRSI_EMA9Period, 0, MODE_EMA, g_hRSI);
|
|
if (g_hEMA9 == INVALID_HANDLE) return false;
|
|
|
|
g_hWMA45 = iMA(_Symbol, _Period, InpRSI_WMA45Period, 0, MODE_LWMA, g_hRSI);
|
|
if (g_hWMA45 == INVALID_HANDLE) return false;
|
|
|
|
g_hEMA200 = iMA(_Symbol, _Period, InpEMA200Period, 0, MODE_EMA, PRICE_CLOSE);
|
|
if (g_hEMA200 == INVALID_HANDLE) return false;
|
|
|
|
g_hATR = iATR(_Symbol, _Period, InpATRPeriod);
|
|
if (g_hATR == INVALID_HANDLE) return false;
|
|
|
|
ArraySetAsSeries(g_RSI, true);
|
|
ArraySetAsSeries(g_EMA9, true);
|
|
ArraySetAsSeries(g_WMA45, true);
|
|
ArraySetAsSeries(g_EMA200, true);
|
|
ArraySetAsSeries(g_ATR, true);
|
|
ArraySetAsSeries(g_Bars, true);
|
|
return true;
|
|
}
|
|
|
|
void ReleaseIndicators()
|
|
{
|
|
if (g_hRSI != INVALID_HANDLE) IndicatorRelease(g_hRSI);
|
|
if (g_hEMA9 != INVALID_HANDLE) IndicatorRelease(g_hEMA9);
|
|
if (g_hWMA45 != INVALID_HANDLE) IndicatorRelease(g_hWMA45);
|
|
if (g_hEMA200 != INVALID_HANDLE) IndicatorRelease(g_hEMA200);
|
|
if (g_hATR != INVALID_HANDLE) IndicatorRelease(g_hATR);
|
|
}
|
|
|
|
// Refresh the cached series buffers; returns false on partial copy.
|
|
bool RefreshIndicatorData()
|
|
{
|
|
const int n = kIndicatorCacheBars;
|
|
if (CopyRates(_Symbol, _Period, 0, n, g_Bars) < n) return false;
|
|
if (CopyBuffer(g_hRSI, 0, 0, n, g_RSI) < n) return false;
|
|
if (CopyBuffer(g_hEMA9, 0, 0, n, g_EMA9) < n) return false;
|
|
if (CopyBuffer(g_hWMA45, 0, 0, n, g_WMA45) < n) return false;
|
|
if (CopyBuffer(g_hEMA200, 0, 0, n, g_EMA200) < n) return false;
|
|
if (CopyBuffer(g_hATR, 0, 0, n, g_ATR) < n) return false;
|
|
return true;
|
|
}
|
|
|
|
// ------------------------------------------------------------
|
|
// Helpers used by the state machine on series-indexed buffers
|
|
// ------------------------------------------------------------
|
|
|
|
// True when buffer is monotonically decreasing across [shift .. shift+lookback-1].
|
|
// In timeseries indexing: newer bar = lower index, so "down slope" means
|
|
// older value > newer value, i.e. buffer[i+1] > buffer[i].
|
|
bool IsBufferSlopingDown(const double &buffer[], const int shift, const int lookback)
|
|
{
|
|
for (int i = shift; i < shift + lookback - 1; i++)
|
|
{
|
|
if (!(buffer[i] < buffer[i + 1])) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// True when buffer is monotonically increasing across [shift .. shift+lookback-1].
|
|
bool IsBufferSlopingUp(const double &buffer[], const int shift, const int lookback)
|
|
{
|
|
for (int i = shift; i < shift + lookback - 1; i++)
|
|
{
|
|
if (!(buffer[i] > buffer[i + 1])) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Returns true if (a-b) sign changes anywhere within
|
|
// [fromShift .. fromShift+lookback-1]. Used to ensure RSI has stayed
|
|
// on one side of WMA45 for at least N bars before a fresh trigger.
|
|
bool HasCrossInLastNBars(const double &a[], const double &b[], const int fromShift, const int lookback)
|
|
{
|
|
for (int i = fromShift; i < fromShift + lookback; i++)
|
|
{
|
|
const double diffNew = a[i] - b[i];
|
|
const double diffOld = a[i + 1] - b[i + 1];
|
|
if (diffNew == 0.0 || diffOld == 0.0
|
|
|| (diffNew > 0.0 && diffOld < 0.0)
|
|
|| (diffNew < 0.0 && diffOld > 0.0))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#endif
|