123 lines
3.8 KiB
Plaintext
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
|