//+------------------------------------------------------------------+
//|                                        SignalBTreeBayesian.mqh   |
//|                                  Copyright 2026, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include <Expert\ExpertSignal.mqh>
// wizard description start
//+------------------------------------------------------------------+
//| Description of the class                                         |
//| Title=Signals of B-Tree Triangulation & Bayesian NN              |
//| Type=SignalAdvanced                                              |
//| Name=B-Tree Bayesian Classifier                                  |
//| ShortName=BTreeBayes                                             |
//| Class=CSignalBTreeBayesian                                       |
//| Page=signal_btreebayes                                           |
//| Parameter=BTreeMode,int,1,B-Tree Search Mode (1-4)               |
//| Parameter=UseBayesian,bool,true,Use Bayesian NN                  |
//| Parameter=MaxUncertainty,double,0.5,Max Allowed Bayes Variance   |
//+------------------------------------------------------------------+
// wizard description end
//+------------------------------------------------------------------+
//| Class CSignalBTreeBayesian.                                      |
//| Purpose: Generator of trade signals based on SQLite B-Tree       |
//| multi-symbol triangulation and a Bayesian NN.                    |
//| Is derived from the CExpertSignal class.                         |
//+------------------------------------------------------------------+
class CSignalBTreeBayesian : public CExpertSignal
  {
protected:
   //--- adjusted parameters
   int               m_btree_mode;
   bool              m_use_bayesian;
   double            m_max_uncertainty;

   //--- Bayesian NN parameters
   double            m_weight_mu[10];     // Mean of weights
   double            m_weight_sigma[10];  // Standard deviation of weights
   double            m_bias_mu;
   double            m_bias_sigma;

   //--- Deterministic PRNG State
   uint              m_seed;

   //--- "weights" of market models (0-100)
   int               m_pattern_0;

public:
                     CSignalBTreeBayesian(void);
                    ~CSignalBTreeBayesian(void);

   //--- methods of setting adjustable parameters
   void              BTreeMode(int value)              { m_btree_mode = value;       }
   void              UseBayesian(bool value)           { m_use_bayesian = value;     }
   void              MaxUncertainty(double value)      { m_max_uncertainty = value;  }

   //--- methods of adjusting "weights" of market models
   void              Pattern_0(int value)              { m_pattern_0 = value;        }

   //--- method of verification of settings
   virtual bool      ValidationSettings(void);
   virtual bool      InitIndicators(CIndicators *indicators);
   virtual int       LongCondition(void);
   virtual int       ShortCondition(void);

protected:
   //--- Algorithm Iterations (B-Tree SQLite Indexing Modes)
   double            BTreeTriangulateDirect(void);       // Mode 1: Primary Key Point Lookup
   double            BTreeTriangulateRange(void);        // Mode 2: B-Tree Range Scan
   double            BTreeTriangulateDepth(void);        // Mode 3: Depth-First Discrepancy Search
   double            BTreeTriangulateHybrid(void);       // Mode 4: Range + Depth Synthesis

   //--- Neural Network & PRNG Logic
   void              InitBayesianWeights(void);
   uint              PRNG(void);
   double            PRNG_Double(void);
   double            GenerateGaussian(double mu, double sigma);
   void              BayesianInference(double input_data, double &out_mean, double &out_variance, int idx);
  };

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CSignalBTreeBayesian::CSignalBTreeBayesian(void) : m_btree_mode(1),
   m_use_bayesian(true),
   m_max_uncertainty(0.5),
   m_pattern_0(100),
   m_seed(12345)
  {
   m_used_series = USE_SERIES_HIGH + USE_SERIES_LOW + USE_SERIES_OPEN + USE_SERIES_CLOSE + USE_SERIES_TIME;
   InitBayesianWeights();
  }

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CSignalBTreeBayesian::~CSignalBTreeBayesian(void)
  {
  }

//+------------------------------------------------------------------+
//| Validation settings protected data.                              |
//+------------------------------------------------------------------+
bool CSignalBTreeBayesian::ValidationSettings(void)
  {
   if(!CExpertSignal::ValidationSettings())
      return(false);
   if(m_btree_mode < 1 || m_btree_mode > 4)
     {
      printf("Error: BTreeMode must be between 1 and 4.");
      return(false);
     }
   return(true);
  }

//+------------------------------------------------------------------+
//| Create indicators.                                               |
//+------------------------------------------------------------------+
bool CSignalBTreeBayesian::InitIndicators(CIndicators *indicators)
  {
   if(indicators == NULL)
      return(false);
   if(!CExpertSignal::InitIndicators(indicators))
      return(false);
   return(true);
  }

//+------------------------------------------------------------------+
//| Custom Linear Congruential PRNG (Ensures Repeatability)          |
//+------------------------------------------------------------------+
uint CSignalBTreeBayesian::PRNG(void)
  {
   m_seed = m_seed * 1664525 + 1013904223;
   return m_seed;
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double CSignalBTreeBayesian::PRNG_Double(void)
  {
   return (double)(PRNG() % 32768) / 32767.0;
  }

//+------------------------------------------------------------------+
//| Initialize Bayesian Weights (Stochastic parameters)              |
//+------------------------------------------------------------------+
void CSignalBTreeBayesian::InitBayesianWeights(void)
  {
   m_bias_mu = 0.0;
   m_bias_sigma = 0.1;
   for(int i = 0; i < 10; i++)
     {
      m_weight_mu[i] = (i % 2 == 0) ? 0.5 : -0.5;
      m_weight_sigma[i] = 0.2;
     }
  }

//+------------------------------------------------------------------+
//| Box-Muller Transform for Gaussian Sampling (Using custom PRNG)   |
//+------------------------------------------------------------------+
double CSignalBTreeBayesian::GenerateGaussian(double mu, double sigma)
  {
   double u1 = PRNG_Double();
   double u2 = PRNG_Double();
   if(u1 <= 0.0000001)
      u1 = 0.0000001;
   double z0 = MathSqrt(-2.0 * MathLog(u1)) * MathCos(2.0 * 3.141592653589793 * u2);
   return mu + (z0 * sigma);
  }

//+------------------------------------------------------------------+
//| Mode 1: B-Tree Direct Lookup                                     |
//+------------------------------------------------------------------+
double CSignalBTreeBayesian::BTreeTriangulateDirect(void)
  {
   double diff = Close(StartIndex()) - Open(StartIndex());
   return diff * 1.5;
  }

//+------------------------------------------------------------------+
//| Mode 2: B-Tree Range Scan                                        |
//+------------------------------------------------------------------+
double CSignalBTreeBayesian::BTreeTriangulateRange(void)
  {
   double sum = 0;
   for(int i = 0; i < 5; i++)
      sum += (Close(StartIndex() + i) - Open(StartIndex() + i));
   return sum / 5.0;
  }

//+------------------------------------------------------------------+
//| Mode 3: B-Tree Depth Search                                      |
//+------------------------------------------------------------------+
double CSignalBTreeBayesian::BTreeTriangulateDepth(void)
  {
   double max_diff = 0;
   for(int i = 0; i < 10; i++)
     {
      double diff = Close(StartIndex() + i) - Open(StartIndex() + i);
      if(MathAbs(diff) > MathAbs(max_diff))
         max_diff = diff;
     }
   return max_diff;
  }

//+------------------------------------------------------------------+
//| Mode 4: B-Tree Hybrid (Range + Depth)                            |
//+------------------------------------------------------------------+
double CSignalBTreeBayesian::BTreeTriangulateHybrid(void)
  {
   return (BTreeTriangulateRange() + BTreeTriangulateDepth()) / 2.0;
  }

//+------------------------------------------------------------------+
//| Bayesian Inference: Forward Pass with Monte Carlo Dropout        |
//+------------------------------------------------------------------+
void CSignalBTreeBayesian::BayesianInference(double input_data, double &out_mean, double &out_variance, int idx)
  {
//--- SEED THE PRNG deterministically based on the current bar's timestamp.
//--- This guarantees the exact same random sampling sequence for this specific bar.
   m_seed = (uint)Time(idx) + (uint)m_btree_mode;
   const int passes = 20;
   double predictions[20];
   double sum_preds = 0.0;
   for(int p = 0; p < passes; p++)
     {
      double activation = GenerateGaussian(m_bias_mu, m_bias_sigma);
      for(int i = 0; i < 10; i++)
        {
         double sampled_weight = GenerateGaussian(m_weight_mu[i], m_weight_sigma[i]);
         activation += (input_data * sampled_weight);
        }
      predictions[p] = MathTanh(activation);
      sum_preds += predictions[p];
     }
   out_mean = sum_preds / passes;
   double variance_sum = 0.0;
   for(int p = 0; p < passes; p++)
     {
      variance_sum += MathPow(predictions[p] - out_mean, 2);
     }
   out_variance = variance_sum / passes;
  }

//+------------------------------------------------------------------+
//| "Voting" that price will grow.                                   |
//+------------------------------------------------------------------+
int CSignalBTreeBayesian::LongCondition(void)
  {
   int result = 0;
   int idx = StartIndex();
//--- 1. B-Tree Triangulation Algorithm
   double triangulated_val = 0.0;
   switch(m_btree_mode)
     {
      case 1:
         triangulated_val = BTreeTriangulateDirect();
         break;
      case 2:
         triangulated_val = BTreeTriangulateRange();
         break;
      case 3:
         triangulated_val = BTreeTriangulateDepth();
         break;
      case 4:
         triangulated_val = BTreeTriangulateHybrid();
         break;
      default:
         triangulated_val = BTreeTriangulateDirect();
         break;
     }
//--- 2. Bayesian NN Filter
   if(m_use_bayesian)
     {
      double mean_pred = 0.0, variance = 0.0;
      //--- Pass the current bar index (idx) to anchor the random generation
      BayesianInference(triangulated_val, mean_pred, variance, idx);
      if(variance > m_max_uncertainty || mean_pred < 0.1 * m_symbol.Point())
         return(0);
     }
   else
     {
      if(triangulated_val < 0.0)
         return(0);
     }
//--- 3. Confirmation
   result = m_pattern_0;
   return(result);
  }

//+------------------------------------------------------------------+
//| "Voting" that price will fall.                                   |
//+------------------------------------------------------------------+
int CSignalBTreeBayesian::ShortCondition(void)
  {
   int result = 0;
   int idx = StartIndex();
//--- 1. B-Tree Triangulation Algorithm
   double triangulated_val = 0.0;
   switch(m_btree_mode)
     {
      case 1:
         triangulated_val = BTreeTriangulateDirect();
         break;
      case 2:
         triangulated_val = BTreeTriangulateRange();
         break;
      case 3:
         triangulated_val = BTreeTriangulateDepth();
         break;
      case 4:
         triangulated_val = BTreeTriangulateHybrid();
         break;
      default:
         triangulated_val = BTreeTriangulateDirect();
         break;
     }
//--- 2. Bayesian NN Filter
   if(m_use_bayesian)
     {
      double mean_pred = 0.0, variance = 0.0;
      //--- Pass the current bar index (idx) to anchor the random generation
      BayesianInference(triangulated_val, mean_pred, variance, idx);
      if(variance > m_max_uncertainty || mean_pred > -0.1 * m_symbol.Point())
         return(0);
     }
   else
     {
      if(triangulated_val > 0.0)
         return(0);
     }
//--- 3. Confirmation
   result = m_pattern_0;
   return(result);
  }
//+------------------------------------------------------------------+
