Files

323 lines
11 KiB
Plaintext

#ifndef PATTERN_HUNTER_MQH
#define PATTERN_HUNTER_MQH
#include "AgentBase.mqh"
#define PH_BARS 50
#define PH_NUM_PATTERNS 21 // pattern codes -10 to +10
class PatternHunter : public IAgent {
private:
double histHurst[PH_BARS];
double histADX[PH_BARS];
double histMA[PH_BARS];
double histMom[PH_BARS];
double histConsensus[PH_BARS];
int barCount;
int idx;
// Distribuzioni empiriche di ogni agente
RunningStats distHurst, distADX, distMA, distMom;
// Distribuzioni empiriche delle differenze temporali
RunningStats diffMom3, diffMA3;
// Distribuzione empirica della somma MA+Mom (per normalizzazione combo)
RunningStats sumMAMom;
// Correlazione MA-Mom
RunningCorrelation corrMAMom;
// Pattern-specific win rate tracking
RunningStats patternReturns[PH_NUM_PATTERNS];
int patternCounts[PH_NUM_PATTERNS];
int lastPatternCode;
void Push(double &arr[], double val) {
arr[idx] = val;
}
double Get(double &arr[], int lookback=0) const {
int i = idx - lookback;
if(i < 0) i += PH_BARS;
if(i < 0 || i >= PH_BARS) return 0;
return arr[i];
}
double AgentThr(const RunningStats &ds) const {
// Serve almeno 1/3 della finestra per avere una stima affidabile
if(ds.Count() < PH_BARS / 3) return 1.0 / MathSqrt(MathMax(1, ds.Count()));
// Minimo: SE della media (non può essere zero con dati finiti)
return MathMax(1.0 / MathSqrt((double)MathMax(1, ds.Count())), ds.Std());
}
double SafeDenom(double v, double fallback) const {
double eps = DATA_EPS(fallback);
return (MathAbs(v) > eps) ? v : fallback;
}
int SignCount(const double &zH, const double &zA, const double &zM, const double &zMom) {
double tH = AgentThr(distHurst);
double tA = AgentThr(distADX);
double tM = AgentThr(distMA);
double tM2 = AgentThr(distMom);
int pos = 0, neg = 0;
if(zH > tH) pos++; else if(zH < -tH) neg++;
if(zA > tA) pos++; else if(zA < -tA) neg++;
if(zM > tM) pos++; else if(zM < -tM) neg++;
if(zMom > tM2) pos++; else if(zMom < -tM2) neg++;
return pos - neg;
}
public:
double patternZ;
string currentPattern;
double patternStrength;
PatternHunter(string n="Hunter", double w=1.0)
: IAgent(n, w), barCount(0), idx(0), patternZ(0),
currentPattern("none"), patternStrength(0),
distHurst(0.05, 30, 200), distADX(0.05, 30, 200),
distMA(0.05, 30, 200), distMom(0.05, 30, 200),
diffMom3(0.05, 20, 200), diffMA3(0.05, 20, 200),
sumMAMom(0.05, 20, 200), corrMAMom(0.05, 10),
lastPatternCode(0) {
for(int i=0; i<PH_BARS; i++) {
histHurst[i] = histADX[i] = histMA[i] = histMom[i] = histConsensus[i] = 0;
}
for(int i=0; i<PH_NUM_PATTERNS; i++) {
patternCounts[i] = 0;
patternReturns[i] = RunningStats(0.1, 3, 500);
}
}
double Analyze(const MarketData &data) override {
lastZScore = 0;
return 0;
}
void Interact(IAgent *&allAgents[], int count) override {
double hurst=0, adx=0, ma=0, mom=0, consensus=0;
int activeSignals = 0;
for(int i=0; i<count; i++) {
if(!allAgents[i].enabled) continue;
string n = allAgents[i].name;
if(n == "Hurst") { hurst = allAgents[i].lastZScore; activeSignals++; }
if(n == "ADX") { adx = allAgents[i].lastZScore; activeSignals++; }
if(n == "MA") { ma = allAgents[i].lastZScore; activeSignals++; }
if(n == "Momentum") { mom = allAgents[i].lastZScore; activeSignals++; }
if(n == "Consensus") consensus = allAgents[i].lastZScore;
}
// Store in history + update distribuzioni individuali
idx = (idx + 1) % PH_BARS;
Push(histHurst, hurst); distHurst.Update(hurst);
Push(histADX, adx); distADX.Update(adx);
Push(histMA, ma); distMA.Update(ma);
Push(histMom, mom); distMom.Update(mom);
Push(histConsensus, consensus);
if(barCount < PH_BARS) barCount++;
// Aggiorna distribuzioni delle differenze temporali (3-bar changes)
if(barCount > 2) {
double dMom = mom - Get(histMom, 2);
double dMa = ma - Get(histMA, 2);
diffMom3.Update(dMom);
diffMA3.Update(dMa);
}
sumMAMom.Update(ma + mom);
corrMAMom.Update(ma, mom);
// Soglie dinamiche per ogni agente
double tH = AgentThr(distHurst);
double tA = AgentThr(distADX);
double tM = AgentThr(distMA);
double tM2 = AgentThr(distMom);
// Soglia prodotto basata su Std empirici
double agreeThr = tM * tM2;
double divergeThr = -tM * tM2;
// Consensus per regime con Std empirico combinato
double regimeSum = hurst + adx;
double regimeThr = MathSqrt(tH * tH + tA * tA);
// Variazioni temporali: Std empirico delle differenze reali
double momDeltaThr = SafeDenom(diffMom3.Std(), tM2);
double maDeltaThr = SafeDenom(diffMA3.Std(), tM);
// Normalizzazione combo MA+Mom: Std empirico della somma
double comboNorm = SafeDenom(sumMAMom.Std(), MathSqrt(tM*tM + tM2*tM2));
// Pattern detection
int signScore = SignCount(hurst, adx, ma, mom);
int consensusThr = activeSignals - 1;
bool allBull = (signScore >= consensusThr);
bool allBear = (signScore <= -consensusThr);
bool maMomAgree = (ma * mom > agreeThr);
bool maMomDiverge = (ma * mom < divergeThr);
bool regimeTrend = regimeSum > regimeThr;
bool regimeRange = regimeSum < -regimeThr;
double momNow = mom;
double mom3ago = Get(histMom, 2);
double mom6ago = Get(histMom, 5);
bool momAccel = (momNow > mom3ago + momDeltaThr && mom3ago > mom6ago + momDeltaThr);
bool momDecel = (momNow < mom3ago - momDeltaThr && mom3ago < mom6ago - momDeltaThr);
double maNow = ma;
double ma3ago = Get(histMA, 2);
bool maRising = (maNow > ma3ago + maDeltaThr);
bool maFalling = (maNow < ma3ago - maDeltaThr);
int pCode = 0;
string pName = "none";
double pZ = 0;
// Conteggio agenti attivi per la media (solo quelli che contribuiscono)
double nAvg = (double)MathMax(1, activeSignals);
if(allBull && maMomAgree && regimeTrend) {
pCode = 10; pName = "perfect_bull";
pZ = (hurst + adx + ma + mom) / nAvg;
}
else if(allBear && maMomAgree && regimeTrend) {
pCode = -10; pName = "perfect_bear";
pZ = (hurst + adx + ma + mom) / nAvg;
}
else if(maMomAgree && regimeTrend && momAccel) {
pCode = 8; pName = "trend_accel";
pZ = (ma + mom) / comboNorm;
}
else if(maMomAgree && regimeTrend && momDecel) {
pCode = 6; pName = "trend_fatigue";
double fatigueFactor = 1.0 - MathAbs(SHARED_trendStrength);
pZ = (ma + mom) / comboNorm * fatigueFactor;
}
else if(regimeRange && maMomDiverge && MathAbs(mom) > tM2) {
pCode = 7; pName = "range_reversal";
pZ = -mom;
}
else if(regimeRange && maMomAgree && MathAbs(mom) < tM2) {
pCode = 3; pName = "range_quiet";
pZ = 0;
}
else if(maMomDiverge && MathAbs(mom) > tM2 && MathAbs(ma) < tM) {
pCode = 5; pName = "momentum_spike";
double trust = 1.0 - MathMin(1.0, MathAbs(ma) / SafeDenom(tM, 1.0/MathSqrt(MathMax(1, (double)PH_BARS))));
pZ = mom * trust;
}
else if(regimeTrend && maMomDiverge && MathAbs(ma) > tM) {
pCode = 4; pName = "pullback";
pZ = ma;
}
else if(signScore > 0) {
pCode = 2; pName = "leaning_bull";
double margin = (signScore - 1) / (nAvg - 1.0);
pZ = MathTanh(margin);
}
else if(signScore < 0) {
pCode = -2; pName = "leaning_bear";
double margin = (-signScore - 1) / (nAvg - 1.0);
pZ = -MathTanh(margin);
}
else if(barCount < PH_BARS) {
pCode = 0; pName = "warming";
pZ = 0;
}
currentPattern = pName;
patternZ = pZ;
patternStrength = 1.0 - MathExp(-MathAbs(pZ));
SHARED_patternCode = pCode;
SHARED_patternName = pName;
lastPatternCode = pCode;
lastZScore = CalibrateZ(pZ);
}
void Learn(double predictedZ, double actualReturnZ) override {
IAgent::Learn(predictedZ, actualReturnZ);
int codeIdx = lastPatternCode + 10;
if(codeIdx >= 0 && codeIdx < PH_NUM_PATTERNS) {
patternReturns[codeIdx].Update(actualReturnZ);
patternCounts[codeIdx]++;
}
}
void Save(int fh) const override {
IAgent::Save(fh);
for(int i=0; i<PH_BARS; i++) {
FileWriteDouble(fh, histHurst[i]);
FileWriteDouble(fh, histADX[i]);
FileWriteDouble(fh, histMA[i]);
FileWriteDouble(fh, histMom[i]);
FileWriteDouble(fh, histConsensus[i]);
}
FileWriteInteger(fh, barCount);
FileWriteInteger(fh, idx);
distHurst.Save(fh);
distADX.Save(fh);
distMA.Save(fh);
distMom.Save(fh);
diffMom3.Save(fh);
diffMA3.Save(fh);
sumMAMom.Save(fh);
for(int i=0; i<PH_NUM_PATTERNS; i++) {
FileWriteInteger(fh, patternCounts[i]);
patternReturns[i].Save(fh);
}
}
void Load(int fh) override {
IAgent::Load(fh);
for(int i=0; i<PH_BARS; i++) {
histHurst[i] = FileReadDouble(fh);
histADX[i] = FileReadDouble(fh);
histMA[i] = FileReadDouble(fh);
histMom[i] = FileReadDouble(fh);
histConsensus[i]= FileReadDouble(fh);
}
barCount = FileReadInteger(fh);
idx = FileReadInteger(fh);
distHurst.Load(fh);
distADX.Load(fh);
distMA.Load(fh);
distMom.Load(fh);
diffMom3.Load(fh);
diffMA3.Load(fh);
sumMAMom.Load(fh);
for(int i=0; i<PH_NUM_PATTERNS; i++) {
patternCounts[i] = FileReadInteger(fh);
patternReturns[i].Load(fh);
}
}
void Reset() override {
IAgent::Reset();
barCount = 0;
idx = 0;
for(int i=0; i<PH_BARS; i++) {
histHurst[i] = histADX[i] = histMA[i] = histMom[i] = histConsensus[i] = 0;
}
distHurst.Reset();
distADX.Reset();
distMA.Reset();
distMom.Reset();
diffMom3.Reset();
diffMA3.Reset();
sumMAMom.Reset();
corrMAMom.Reset();
for(int i=0; i<PH_NUM_PATTERNS; i++) {
patternCounts[i] = 0;
patternReturns[i].Reset();
}
currentPattern = "none";
patternStrength = 0;
patternZ = 0;
}
string SignalInfo() const override {
return name + " z=" + StringFormat("%+.3f", lastZScore)
+ " pattern=" + currentPattern
+ " str=" + StringFormat("%.2f", patternStrength);
}
};
#endif