forked from MasoodIqbal/RSI-Stoch-MA-EA
164 lines
5.7 KiB
MQL5
164 lines
5.7 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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<maCount; i++)
|
|
{
|
|
ArrayResize(feat, ArraySize(feat)+1);
|
|
feat[ArraySize(feat)-1] = NN_DistByATR(mid, MA_Current[i]);
|
|
}
|
|
|
|
// MA stack alignment: +1 fully above, -1 fully below
|
|
int above=0, below=0;
|
|
for(int i=0; i<maCount; i++)
|
|
{
|
|
if(mid > 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);
|
|
}
|