#ifndef MA_AGENT_MQH #define MA_AGENT_MQH #include "AgentBase.mqh" #include "../Core/PeriodCalculator.mqh" class MAAgent : public IAgent { private: RunningStats slopeStats; int period; int minPeriod, maxPeriod; int maHandle; int atrHandle; int lastMAPeriod; int barCount; // Sub-signal learning: correlazione di ogni sub-signal col ritorno RunningCorrelation priceToMaCorr; RunningCorrelation slopeCorr; double lastZ1; // priceToMa z-score double lastZ2; // slope z-score // Cache per Interact (ri-calcolo con regime fresco) double m_currentClose; int m_basePeriod; void RecreateMA(int p) { if(maHandle != INVALID_HANDLE) IndicatorRelease(maHandle); maHandle = iMA(symbol, timeframe, p, 0, MODE_SMA, PRICE_CLOSE); lastMAPeriod = p; } void RecreateATR(int p) { if(atrHandle != INVALID_HANDLE) IndicatorRelease(atrHandle); atrHandle = iATR(symbol, timeframe, p); } double GetMA(int shift=0) { double buf[]; ArraySetAsSeries(buf, true); if(CopyBuffer(maHandle, 0, shift, 1, buf) < 1) return 0; return buf[0]; } double GetATR() { if(atrHandle == INVALID_HANDLE) RecreateATR(14); double buf[]; ArraySetAsSeries(buf, true); if(CopyBuffer(atrHandle, 0, 0, 1, buf) < 1) return 0; return buf[0]; } public: MAAgent(string n="MA", double w=1.0, int minP=8, int maxP=40) : IAgent(n, w), slopeStats(0.05, 30, 500), period(14), minPeriod(minP), maxPeriod(maxP), maHandle(INVALID_HANDLE), atrHandle(INVALID_HANDLE), lastMAPeriod(0), barCount(0), priceToMaCorr(0.1, 5), slopeCorr(0.1, 5), lastZ1(0), lastZ2(0) { signalStats.SetR(5.0); } void Init(string sym, ENUM_TIMEFRAMES tf) override { IAgent::Init(sym, tf); maHandle = INVALID_HANDLE; atrHandle = INVALID_HANDLE; lastMAPeriod = 0; } void Release() override { if(maHandle != INVALID_HANDLE) IndicatorRelease(maHandle); if(atrHandle != INVALID_HANDLE) IndicatorRelease(atrHandle); maHandle = INVALID_HANDLE; atrHandle = INVALID_HANDLE; } double Analyze(const MarketData &data) override { barCount++; m_basePeriod = PeriodCalculator::AutoPeriod(data, minPeriod, maxPeriod); m_currentClose = data.Close(0); RecomputeWithRegime(SHARED_regimeConsensus, SHARED_regimeAgreement); return lastZScore; } // Ri-calcola tutto con valori di regime freschi (chiamato da Analyze e Interact) void RecomputeWithRegime(double regime, double agreement) { double trendStr = MathAbs(regime); double maxIncrease = (double)maxPeriod / MathMax(minPeriod, m_basePeriod) - 1.0; double periodMult = 1.0 + trendStr * agreement * maxIncrease; int newPeriod = (int)MathRound(m_basePeriod * periodMult); if(newPeriod < minPeriod) newPeriod = minPeriod; if(newPeriod > maxPeriod) newPeriod = maxPeriod; if(newPeriod != period) { period = newPeriod; if(maHandle != INVALID_HANDLE && period != lastMAPeriod) RecreateMA(period); } else if(maHandle == INVALID_HANDLE || period != lastMAPeriod) { RecreateMA(period); } double ma = GetMA(0); double maPv = GetMA(1); double atr = GetATR(); double epsAtrM = DATA_EPS(atr); double epsMaM = DATA_EPS(ma); if(MathAbs(atr) < epsAtrM || MathAbs(ma) < epsMaM) { lastZScore = 0; return; } double priceToMa = (m_currentClose - ma) / atr; double slope = (ma - maPv) / atr; signalStats.Update(priceToMa); slopeStats.Update(slope); double z1 = signalStats.ZScore(priceToMa); double z2 = slopeStats.ZScore(slope); lastZ1 = z1; lastZ2 = z2; double wPrice, wSlope; if(priceToMaCorr.Ready() && slopeCorr.Ready()) { double r1 = MathMax(0.0, priceToMaCorr.Correlation()); double r2 = MathMax(0.0, slopeCorr.Correlation()); double sumR = r1 + r2 + DATA_EPS(r1 + r2); wPrice = r1 / sumR; wSlope = 1.0 - wPrice; } else { double s1 = signalStats.Std(); double s2 = slopeStats.Std(); double sumV = s1 + s2; if(sumV < DATA_EPS(MathMax(s1, s2))) { wPrice = wSlope = 1.0 / 2.0; } else { wPrice = s1 / sumV; wSlope = 1.0 - wPrice; } } double norm = MathSqrt(wPrice*wPrice + wSlope*wSlope); lastZScore = (wPrice * z1 + wSlope * z2) / norm; lastZScore = CalibrateZ(lastZScore); lastRawSignal = priceToMa; } void Interact(IAgent *&allAgents[], int count) override { // Rilegge regime fresco (dopo Interact di Consensus) e ri-calcola RecomputeWithRegime(SHARED_regimeConsensus, SHARED_regimeAgreement); } void Learn(double predictedZ, double actualReturnZ) override { IAgent::Learn(predictedZ, actualReturnZ); // Sub-signal learning: quale componente ha predetto meglio? priceToMaCorr.Update(lastZ1, actualReturnZ); slopeCorr.Update(lastZ2, actualReturnZ); } void Save(int fh) const override { IAgent::Save(fh); slopeStats.Save(fh); priceToMaCorr.Save(fh); slopeCorr.Save(fh); } void Load(int fh) override { IAgent::Load(fh); slopeStats.Load(fh); priceToMaCorr.Load(fh); slopeCorr.Load(fh); } void Reset() override { IAgent::Reset(); slopeStats.Reset(); priceToMaCorr.Reset(); slopeCorr.Reset(); lastZ1 = 0; lastZ2 = 0; } string SignalInfo() const override { return name + " z=" + StringFormat("%+.3f", lastZScore) + " period=" + (string)period + " " + signalStats.ToString(); } }; #endif