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

183 lines
5.9 KiB
Plaintext

#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