Files
TR_Agent/MQL5/Experts/MultiAgentTest/Agents/RegimeADX.mqh
T

123 lines
3.8 KiB
Plaintext

#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