//+------------------------------------------------------------------+
//|                                             TrailingBloomRNN.mqh |
//|                                  Copyright 2026, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include <Expert\ExpertTrailing.mqh>

// wizard description start
//+------------------------------------------------------------------+
//| Description of the class                                         |
//| Title=Trailing Stop with Bloom Filter and Simple RNN Filter      |
//| Type=Trailing                                                    |
//| Name=BloomRNN                                                    |
//| Class=CTrailingBloomRNN                                          |
//| Page=                                                            |
//| Parameter=FilterSize,int,1024,Bloom Filter Bitset Size           |
//| Parameter=HashCount,int,3,Number of Hash Functions               |
//| Parameter=RNNThreshold,double,0.7,RNN Activation Threshold       |
//+------------------------------------------------------------------+
// wizard description end

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CTrailingBloomRNN : public CExpertTrailing
  {
protected:
   //---Bloom Filter Data
   uchar             m_bitset[];        // Bit array for Bloom Filter
   int               m_filter_size;     // Size in bits
   int               m_hash_count;      // Number of hashes per tick

   //---RNN Component (Simplified Weights/State for the Article Example)
   double            m_hidden_state;    // Simple RNN recurrent state
   double            m_rnn_threshold;   // Filter sensitivity

   //---Internal Logic
   uint              Hash(double price, int seed);
   bool              IsDuplicateTick(double price);
   void              UpdateRNN(double &price);
   double            GetRNNOutput();

public:
                     CTrailingBloomRNN(void);
                    ~CTrailingBloomRNN(void);

   //---Setters for Wizard
   void              FilterSize(int size)  { m_filter_size = size;  }
   void              HashCount(int count)  { m_hash_count = count;  }
   void              RNNThreshold(double t) { m_rnn_threshold = t;   }

   virtual bool      InitIndicators(CIndicators *indicators);
   virtual bool      CheckTrailingStopLong(CPositionInfo *position, double &sl, double &tp) override;
   virtual bool      CheckTrailingStopShort(CPositionInfo *position, double &sl, double &tp) override;
  };

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CTrailingBloomRNN::CTrailingBloomRNN(void)
  {
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CTrailingBloomRNN::~CTrailingBloomRNN(void)
  {
  }

//+------------------------------------------------------------------+
//| Initialization: Set up Bloom Filter Bitset                       |
//+------------------------------------------------------------------+
bool CTrailingBloomRNN::InitIndicators(CIndicators *indicators)
  {
   if(!CExpertTrailing::InitIndicators(indicators))
      return false;
   ArrayResize(m_bitset, (m_filter_size / 8) + 1);
   ArrayInitialize(m_bitset, 0);
   m_hidden_state = 0.5; // Initial neutral state
   return true;
  }
//+------------------------------------------------------------------+
//| Check Long: Bloom -> RNN -> Execution                            |
//+------------------------------------------------------------------+
bool CTrailingBloomRNN::CheckTrailingStopLong(CPositionInfo *position, double &sl, double &tp)
  {
   if(position == NULL)
      return false;
   double price = m_symbol.Bid();
//---1. Bloom Filter Deduplication
   if(IsDuplicateTick(price))
      return false;
//---2. Sequential Neural Filter
   if(MathAbs(m_rnn_threshold) > 0.0)//zero means threshold is not used
     {
      UpdateRNN(price);
      if(MathAbs(m_hidden_state) < m_rnn_threshold)
         return false;
     }
//---3. Trailing Execution
   double new_sl = price - (m_symbol.StopsLevel() * m_symbol.Point());
   double current_sl = position.StopLoss();
   if(new_sl > current_sl || current_sl == 0.0)
     {
      sl = NormalizeDouble(new_sl, m_symbol.Digits());
      return true;
     }
   return false;
  }

//+------------------------------------------------------------------+
//| Check Long: Bloom -> RNN -> Execution                            |
//+------------------------------------------------------------------+
bool CTrailingBloomRNN::CheckTrailingStopShort(CPositionInfo *position, double &sl, double &tp)
  {
   if(position == NULL)
      return false;
   double price = m_symbol.Bid();
//---1. Bloom Filter Deduplication
   if(IsDuplicateTick(price))
      return false;
//---2. Sequential Neural Filter
   if(MathAbs(m_rnn_threshold) > 0.0)//zero means threshold is not used
     {
      UpdateRNN(price);
      if(MathAbs(m_hidden_state) < m_rnn_threshold)
         return false;
     }
//---3. Trailing Execution
   double new_sl = price + (m_symbol.StopsLevel() * m_symbol.Point());
   double current_sl = position.StopLoss();
   if(new_sl < current_sl || current_sl == 0.0)
     {
      sl = NormalizeDouble(new_sl, m_symbol.Digits());
      return true;
     }
   return false;
  }


//+------------------------------------------------------------------+
//| Bloom Filter Logic: Tick Deduplication                           |
//+------------------------------------------------------------------+
bool CTrailingBloomRNN::IsDuplicateTick(double price)
  {
   uint price_bits = (uint)MathRound(price / m_symbol.Point());
   bool already_exists = true;
   for(int i = 0; i < m_hash_count; i++)
     {
      uint h = Hash(price_bits, i) % m_filter_size;
      int byte_idx = int(h / 8);
      int bit_idx = int(h % 8);
      if(!(m_bitset[byte_idx] & (1 << bit_idx)))
        {
         already_exists = false;
         m_bitset[byte_idx] |= uchar(1 << bit_idx); // Mark as seen
        }
     }
   return already_exists;
  }

//+------------------------------------------------------------------+
//| Simple RNN Filter: Sequential Memory                             |
//+------------------------------------------------------------------+
void CTrailingBloomRNN::UpdateRNN(double &price)
  {
//---Simplified RNN logic for MQL5 demonstration
//---Hidden_t = tanh(W_h * Hidden_t-1 + W_i * Input_t)
   double input_normalized = (price - m_symbol.Bid()) / m_symbol.Point();
   m_hidden_state = MathTanh(0.5 * m_hidden_state + 0.5 * input_normalized);
  }


//+------------------------------------------------------------------+
//| FNV-1a Style Hash for Bloom Filter                               |
//+------------------------------------------------------------------+
uint CTrailingBloomRNN::Hash(double val, int seed)
  {
   uint hash = 2166136261 + seed;
   string s = DoubleToString(val, 8);
   for(int i = 0; i < StringLen(s); i++)
      hash = (hash ^ s[i]) * 16777619;
   return hash;
  }
//+------------------------------------------------------------------+
