#ifndef REGIME_ADX_MQH #define REGIME_ADX_MQH #include "AgentBase.mqh" #include "../Core/PeriodCalculator.mqh" class RegimeADX : public IAgent { private: int adxPeriod; int userPeriod; int adxHandle; int lastADXPeriod; int adxMinP, adxMaxP; double prevZ; void Recreate(int p) { if(adxHandle != INVALID_HANDLE) IndicatorRelease(adxHandle); adxHandle = iADX(symbol, timeframe, p); lastADXPeriod = p; } double GetADX() { if(adxHandle == INVALID_HANDLE) Recreate(adxPeriod); double buf[]; ArraySetAsSeries(buf, true); if(CopyBuffer(adxHandle, 0, 0, 1, buf) < 1) return 0; return buf[0]; } double GetDI(int plusMinus=1, int shift=0) { if(adxHandle == INVALID_HANDLE) Recreate(adxPeriod); double buf[]; ArraySetAsSeries(buf, true); if(CopyBuffer(adxHandle, plusMinus, shift, 1, buf) < 1) return 0; return buf[0]; } public: RegimeADX(string n="ADX", double w=1.0, int period=0) : IAgent(n, w), userPeriod(period), adxPeriod(0), adxHandle(INVALID_HANDLE), lastADXPeriod(0), adxMinP(7), adxMaxP(30), prevZ(0) { signalStats.SetR(50.0); } void Init(string sym, ENUM_TIMEFRAMES tf) override { IAgent::Init(sym, tf); adxHandle = INVALID_HANDLE; lastADXPeriod = 0; } void Release() override { if(adxHandle != INVALID_HANDLE) IndicatorRelease(adxHandle); adxHandle = INVALID_HANDLE; } double Analyze(const MarketData &data) override { // Periodo: fisso se utente lo specifica, altrimenti data-driven + EWMA if(userPeriod > 0) { adxPeriod = userPeriod; } else { int newP = PeriodCalculator::AutoPeriod(data, adxMinP, adxMaxP); if(adxPeriod <= 0) adxPeriod = newP; else { double pAlpha = 1.0 / (1.0 + signalStats.Count() * 0.05); pAlpha = MathMax(0.05, pAlpha); // solo floor adxPeriod = (int)MathRound(pAlpha * newP + (1.0 - pAlpha) * adxPeriod); } if(adxPeriod < adxMinP) adxPeriod = adxMinP; if(adxPeriod > adxMaxP) adxPeriod = adxMaxP; } if(adxPeriod <= 0) { lastZScore = 0; return 0; } if(adxHandle == INVALID_HANDLE || adxPeriod != lastADXPeriod) Recreate(adxPeriod); double adx = GetADX(); double epsAdx = DATA_EPS(adx); if(MathAbs(adx) < epsAdx) { lastZScore = 0; return 0; } // Normalizza ADX via EWMA signalStats.Update(adx); double zRaw = signalStats.ZScore(adx); // EWMA alpha: scala con conteggio campioni, solo floor data-driven double alpha = 1.0 / (1.0 + signalStats.Count() * 0.1); double minAlpha = 1.0 / MathMax(2.0, (double)MathMax(1, adxPeriod)); alpha = MathMax(minAlpha, alpha); // solo floor, niente max clamp prevZ = (1.0 - alpha) * prevZ + alpha * zRaw; double calibrated = CalibrateZ(prevZ); lastZScore = MathTanh(calibrated); lastRawSignal = adx; // Pubblica nel contesto condiviso SHARED_adxZ = lastZScore; SHARED_adxRaw = adx; return lastZScore; } void Interact(IAgent *&allAgents[], int count) override {} void Learn(double predictedZ, double actualReturnZ) override {} void Save(int fh) const override { IAgent::Save(fh); FileWriteDouble(fh, prevZ); } void Load(int fh) override { IAgent::Load(fh); prevZ = FileReadDouble(fh); } void Reset() override { IAgent::Reset(); prevZ = 0; } string SignalInfo() const override { return name + " z=" + StringFormat("%+.3f", lastZScore) + " ADX=" + StringFormat("%.1f", SHARED_adxRaw) + " p=" + (string)adxPeriod + " " + signalStats.ToString(); } }; #endif