183 lines
5.9 KiB
Plaintext
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
|