//+------------------------------------------------------------------+ //| NNFeatures.mqh | //| Feature engineering for QuarterTheory NeuralNet integration | //+------------------------------------------------------------------+ #property strict #include "Config.mqh" #include "GlobalVariables.mqh" // Clamp helper double NN_Clamp(const double x, const double lo, const double hi) { if(x < lo) return lo; if(x > hi) return hi; return x; } // Safe divide double NN_SafeDiv(const double a, const double b, const double fallback=0.0) { if(MathAbs(b) < 1e-9) return fallback; return a / b; } // Normalize distance by ATR (if ATR is 0, fallback) double NN_DistByATR(const double price, const double level) { double atr = (Current_ATR > 0.0 ? Current_ATR : 1.0); return NN_Clamp((price - level) / atr, -10.0, 10.0); } // Encode enums double NN_EncodeBias(TREND_BIAS b) { if(b == BIAS_BULL) return 1.0; if(b == BIAS_BEAR) return -1.0; return 0.0; } double NN_EncodeFamily(MODE_FAMILY f) { if(f == FAMILY_TRENDING) return 1.0; if(f == FAMILY_CHOP) return -1.0; if(f == FAMILY_RANGING) return 0.0; return 0.0; } // ✅ Updated to match YOUR enum: WEAK / CONFIRMED / STRONG double NN_EncodeStrength(TREND_STRENGTH s) { if(s == STRENGTH_WEAK) return 0.25; if(s == STRENGTH_CONFIRMED) return 0.55; if(s == STRENGTH_STRONG) return 0.85; return 0.55; } // ✅ Optional but recommended: encode your PRICE_STATE into a compact signal double NN_EncodePriceState(PRICE_STATE s) { // continuation=+1, pullback/retrace=~0, reversal=-1, range/chop negative, unknown=0 if(s == STATE_CONTINUATION) return 1.0; if(s == STATE_PULLBACK) return 0.2; if(s == STATE_DEEP_RETRACEMENT) return 0.0; if(s == STATE_REVERSAL_ATTEMPT) return -0.5; if(s == STATE_REVERSAL_CONFIRMED) return -1.0; if(s >= STATE_RANGE_MID_DRIFT && s <= STATE_RANGE_BREAK_CONFIRMED) return -0.25; if(s >= STATE_CHOP_NOISE && s <= STATE_CHOP_FAKEOUT_LOOP) return -0.50; return 0.0; } // Build features array. Returns number of features written. int BuildNNFeatures(double &feat[]) { ArrayResize(feat, 0); // --- Get bid/ask safely --- MqlTick t; if(!SymbolInfoTick(_Symbol, t)) return 0; const double bid = t.bid; const double ask = t.ask; const double mid = (bid + ask) * 0.5; const double spreadPts = (ask - bid) / _Point; // --- Core market/context --- ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = NN_Clamp(spreadPts / 50.0, 0.0, 5.0); // scaled spread ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = NN_Clamp(Current_ATR / (_Point * 1000.0), 0.0, 20.0); ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = NN_Clamp(Current_ADX / 100.0, 0.0, 1.0); ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = NN_EncodeBias(Current_Bias); ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = NN_EncodeFamily(Current_Family); ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = NN_EncodeStrength(Current_Strength); // ✅ price state ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = NN_EncodePriceState(Current_State); // --- Stochastic --- ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = NN_Clamp(Stoch_K_Current / 100.0, 0.0, 1.0); ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = NN_Clamp(Stoch_D_Current / 100.0, 0.0, 1.0); ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = NN_Clamp((Stoch_K_Current - Stoch_K_Previous) / 10.0, -5.0, 5.0); // --- MA distances (7,14,21,50,140,230,500,1220) --- // Assumes your Indicators.mqh maps these into MA_Current[0..7] int maCount = MathMin(8, ArraySize(MA_Current)); for(int i=0; i MA_Current[i]) above++; if(mid < MA_Current[i]) below++; } ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = NN_SafeDiv((double)(above - below), (double)maCount, 0.0); // --- MFIB distances --- ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = NN_DistByATR(mid, MFIB_Level_236); ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = NN_DistByATR(mid, MFIB_Level_382); ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = NN_DistByATR(mid, MFIB_Level_050); ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = NN_DistByATR(mid, MFIB_Level_618); ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = NN_DistByATR(mid, MFIB_Level_786); ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = NN_EncodeBias(MFIB_Bias); // --- Key flags --- ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = (MFIB_Reject_Warning ? 1.0 : 0.0); ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = (MFIB_Reclaim_Warning ? 1.0 : 0.0); ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = (MA_Reclaim_Warning ? 1.0 : 0.0); ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = (MA_Reject_Warning ? 1.0 : 0.0); ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = (Pullback_Warning ? 1.0 : 0.0); ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = (Retracement_Warning ? 1.0 : 0.0); ArrayResize(feat, ArraySize(feat)+1); feat[ArraySize(feat)-1] = NN_Clamp((double)Praise_Count / 8.0, 0.0, 1.0); return ArraySize(feat); }