← К каталогу
Исходный код · ID 4705

XMC Hurst Regime Module

xmc_hurstregime.mq5
Вы автор или правообладатель?

Сообщите нам, если считаете, что материал размещён без разрешения.

Краткий обзор

О программе

XMC Hurst Regime Module — это библиотека MQL5 (include-файл .mqh) для расчёта показателя Хёрста и классификации рыночных режимов. Модуль реализует классический анализ R/S (Rescaled Range) с линейным шагом размеров отрезков и логарифмической регрессией для оценки показателя Хёрста в диапазоне [0, 1].

Класс CHurstRegimeFilter предоставляет готовое решение для интеграции в советники: автоматическое обнаружение новых баров, кэширование ценовых данных, EMA-сглаживание и классификация режима — тренд, средняя реверсия или случайное блуждание. Поддерживается работа на нескольких символах и таймфреймах одновременно.

Статистические функции доступны как для текущего состояния через класс, так и для произвольных исторических баров через свободные функции. Модуль не содержит торговой логики — он только предоставляет данные о режиме рынка.

Получить полный код

Поиск по коду файла

#property version   "2.10"
#property strict

#ifndef XMC_HURST_REGIME_MQH
#define XMC_HURST_REGIME_MQH

enum ENUM_HURST_REGIME
{
   HURST_REGIME_TRENDING    =  1,  // Persistent / Trending
   HURST_REGIME_RANDOM_WALK =  0,  // Random Walk / Indeterminate
   HURST_REGIME_MEAN_REVERT = -1   // Anti-Persistent / Mean Reverting
};

struct HurstConfig
{
   string            symbol;              // "" = current chart symbol
   ENUM_TIMEFRAMES   timeframe;           // PERIOD_CURRENT = current TF

   int               lookback;            // Total lookback bars (min 32)
   int               min_chunk;           // Smallest R/S chunk size (min 2)
   int               chunk_step;          // Linear step between chunk sizes
   int               smoothing_period;    // EMA period (0 = raw, no smoothing)

   double            trend_threshold;     // H >= this  -> TRENDING
   double            reversion_threshold; // H <= this  -> MEAN_REVERT

   ENUM_APPLIED_PRICE applied_price;      // Price type for R/S input
};

#define MAX_HURST_BARS 1024

string HurstResolveSymbol(const string sym)
{
   if(sym == "" || sym == NULL)
      return Symbol();
   return sym;
}

double HurstGetPrice(const string sym,
                     const ENUM_TIMEFRAMES tf,
                     const int bar,
                     const ENUM_APPLIED_PRICE applied_price)
{
   switch(applied_price)
   {
      case PRICE_OPEN:
         return iOpen(sym, tf, bar);

      case PRICE_HIGH:
         return iHigh(sym, tf, bar);

      case PRICE_LOW:
         return iLow(sym, tf, bar);

      case PRICE_MEDIAN:
         return (iHigh(sym, tf, bar) +
                 iLow(sym, tf, bar)) / 2.0;

      case PRICE_TYPICAL:
         return (iHigh(sym, tf, bar) +
                 iLow(sym, tf, bar) +
                 iClose(sym, tf, bar)) / 3.0;

      case PRICE_WEIGHTED:
         return (iHigh(sym, tf, bar) +
                 iLow(sym, tf, bar) +
                 2.0 * iClose(sym, tf, bar)) / 4.0;

      case PRICE_CLOSE:
      default:
         return iClose(sym, tf, bar);
   }
}

double HurstCalcRS(const string sym,
                   const ENUM_TIMEFRAMES tf,
                   const int start_shift,
                   const int size,
                   const ENUM_APPLIED_PRICE applied_price)
{
   if(size < 2)
      return 0.0;

   double sum = 0.0;
   for(int i = 0; i < size; i++)
      sum += HurstGetPrice(sym, tf, start_shift - i, applied_price);

   double mean = sum / size;

   double sq_sum = 0.0;
   for(int i = 0; i < size; i++)
   {
      double diff = HurstGetPrice(sym, tf, start_shift - i, applied_price) - mean;
      sq_sum += diff * diff;
   }
   double std_dev = MathSqrt(sq_sum / size);

   if(std_dev <= 1.0e-12)
      return 0.0;

   double cumulative = 0.0;
   double cum_min = 0.0;
   double cum_max = 0.0;

   for(int i = 0; i < size; i++)
   {
      cumulative += HurstGetPrice(sym, tf, start_shift - i, applied_price) - mean;

      if(cumulative < cum_min)
         cum_min = cumulative;

      if(cumulative > cum_max)
         cum_max = cumulative;
   }

   double range = cum_max - cum_min;

   if(range <= 0.0)
      return 0.0;

   return range / std_dev;
}

double GetHurstRaw(const string sym,
                   const ENUM_TIMEFRAMES tf,
                   const int bar,
                   const HurstConfig &cfg)
{
   string symbol = HurstResolveSymbol(sym);
   int lookback  = MathMax(32, cfg.lookback);

   if(iBars(symbol, tf) < lookback + bar + 1)
      return -1.0;

   int step  = MathMax(1, cfg.chunk_step);
   int min_c = MathMax(2, cfg.min_chunk);

   double log_n[64];
   double log_rs[64];

   ArrayInitialize(log_n, 0.0);
   ArrayInitialize(log_rs, 0.0);

   int point_count = 0;

   /*
      Linear chunk stepping: 8, 14, 20, 26, 32, 38, ...
      (faithful to the original MQL5 specification)
   */
   for(int chunk_size = min_c;
       chunk_size <= lookback && point_count < 64;
       chunk_size += step)
   {
      int complete_chunks = lookback / chunk_size;

      if(complete_chunks < 1)
         continue;

      double rs_sum = 0.0;
      int valid_count = 0;

      /*
         Prices are chronological: oldest -> newest.
         MQL4 shift increases toward older bars, so
         the oldest part of the lookback has the largest shift.
      */
      int oldest_shift = bar + lookback - 1;

      for(int c = 0; c < complete_chunks; c++)
      {
         int start_shift = oldest_shift - c * chunk_size;

         if(start_shift < bar)
            break;

         double rs = HurstCalcRS(symbol,
                                  tf,
                                  start_shift,
                                  chunk_size,
                                  cfg.applied_price);

         if(rs > 0.0 && MathIsValidNumber(rs))
         {
            rs_sum += rs;
            valid_count++;
         }
      }

      if(valid_count > 0)
      {
         double avg_rs = rs_sum / valid_count;

         if(avg_rs > 0.0)
         {
            log_n[point_count]  = MathLog((double)chunk_size);
            log_rs[point_count] = MathLog(avg_rs);
            point_count++;
         }
      }
   }

   if(point_count < 2)
      return -1.0;

   double sum_x  = 0.0;
   double sum_y  = 0.0;
   double sum_xy = 0.0;
   double sum_x2 = 0.0;
   double n = (double)point_count;

   for(int i = 0; i < point_count; i++)
   {
      sum_x  += log_n[i];
      sum_y  += log_rs[i];
      sum_xy += log_n[i] * log_rs[i];
      sum_x2 += log_n[i] * log_n[i];
   }

   double denominator = n * sum_x2 - sum_x * sum_x;

   if(MathAbs(denominator) <= 1.0e-12)
      return -1.0;

   double H = (n * sum_xy - sum_x * sum_y) / denominator;

   if(H < 0.0)
      H = 0.0;
   else if(H > 1.0)
      H = 1.0;

   return H;
}

double HurstSmoothEMA(double current_h, double prev_smoothed, int period)
{
   if(period <= 1)
      return current_h;

   double alpha = 2.0 / ((double)period + 1.0);
   return alpha * current_h + (1.0 - alpha) * prev_smoothed;
}

ENUM_HURST_REGIME GetHurstRegime(double h, const HurstConfig &cfg)
{
   if(h < 0.0 || !MathIsValidNumber(h))
      return HURST_REGIME_RANDOM_WALK;

   if(h >= cfg.trend_threshold)
      return HURST_REGIME_TRENDING;

   if(h <= cfg.reversion_threshold)
      return HURST_REGIME_MEAN_REVERT;

   return HURST_REGIME_RANDOM_WALK;
}

string HurstRegimeToString(ENUM_HURST_REGIME regime)
{
   switch(regime)
   {
      case HURST_REGIME_TRENDING:    return "TRENDING";
      case HURST_REGIME_RANDOM_WALK: return "RANDOM_WALK";
      case HURST_REGIME_MEAN_REVERT: return "MEAN_REVERTING";
      default:                       return "UNKNOWN";
   }
}

double GetHurstForBar(const string sym,
                      const ENUM_TIMEFRAMES tf,
                      const int bar,
                      const HurstConfig &cfg,
                      double prev_smoothed)
{
   double raw_h = GetHurstRaw(sym, tf, bar, cfg);

   if(raw_h < 0.0 || !MathIsValidNumber(raw_h))
      return raw_h;

   if(cfg.smoothing_period > 0)
      return HurstSmoothEMA(raw_h, prev_smoothed, cfg.smoothing_period);

   return raw_h;
}

class CHurstRegimeFilter
{
private:

   HurstConfig       m_cfg;

   string            m_symbol;
   ENUM_TIMEFRAMES   m_timeframe;

   double            m_price_cache[MAX_HURST_BARS];
   int               m_cache_count;      // number of valid entries

   double            m_prev_smoothed;
   double            m_hurst;           // current smoothed (or raw) H
   double            m_raw_hurst;       // current raw H

   ENUM_HURST_REGIME m_regime;

   datetime          m_last_bar;        // new-bar cache
   bool              m_initialized;
   bool              m_valid;           // false when history insufficient

   string Sym() const
   {
      if(m_symbol != "" && m_symbol != NULL)
         return m_symbol;
      return Symbol();
   }

   double CalculateRaw(const int bar)
   {
      return GetHurstRaw(Sym(), m_timeframe, bar, m_cfg);
   }

   bool LoadPriceCache(int max_shift)
   {
      if(max_shift >= MAX_HURST_BARS)
         return false;

      if(iBars(Sym(), m_timeframe) < max_shift + 1)
         return false;

      for(int i = 0; i <= max_shift; i++)
      {
         double price = HurstGetPrice(Sym(), m_timeframe, i,
                                      m_cfg.applied_price);

         if(price <= 0.0 || !MathIsValidNumber(price))
         {
            m_cache_count = 0;
            return false;
         }

         m_price_cache[i] = price;
      }

      m_cache_count = max_shift + 1;
      return true;
   }

   double CalcRS_Cached(int start_shift, int size)
   {
      if(size < 2)
         return 0.0;

      if(start_shift >= m_cache_count)
         return 0.0;

      if(start_shift - size + 1 < 0)
         return 0.0;

      double sum = 0.0;
      for(int i = 0; i < size; i++)
         sum += m_price_cache[start_shift - i];
      double mean = sum / size;

      double sq_sum = 0.0;
      for(int i = 0; i < size; i++)
      {
         double diff = m_price_cache[start_shift - i] - mean;
         sq_sum += diff * diff;
      }
      double std_dev = MathSqrt(sq_sum / size);

      if(std_dev <= 1.0e-12)
         return 0.0;

      double cumulative = 0.0;
      double cum_min = 0.0;
      double cum_max = 0.0;

      for(int i = 0; i < size; i++)
      {
         cumulative += m_price_cache[start_shift - i] - mean;

         if(cumulative < cum_min)
            cum_min = cumulative;

         if(cumulative > cum_max)
            cum_max = cumulative;
      }

      double range = cum_max - cum_min;

      if(range <= 0.0)
         return 0.0;

      return range / std_dev;
   }

   double CalculateRawCached(int bar)
   {
      int lookback = MathMax(32, m_cfg.lookback);

      if(bar + lookback > m_cache_count)
         return -1.0;

      int step  = MathMax(1, m_cfg.chunk_step);
      int min_c = MathMax(2, m_cfg.min_chunk);

      double log_n[64];
      double log_rs[64];

      ArrayInitialize(log_n, 0.0);
      ArrayInitialize(log_rs, 0.0);

      int point_count = 0;
      int oldest_shift = bar + lookback - 1;

      for(int chunk_size = min_c;
          chunk_size <= lookback && point_count < 64;
          chunk_size += step)
      {
         int complete_chunks = lookback / chunk_size;

         if(complete_chunks < 1)
            continue;

         double rs_sum = 0.0;
         int valid_count = 0;

         for(int c = 0; c < complete_chunks; c++)
         {
            int start_shift = oldest_shift - c * chunk_size;

            if(start_shift < bar)
               break;

            double rs = CalcRS_Cached(start_shift, chunk_size);

            if(rs > 0.0 && MathIsValidNumber(rs))
            {
               rs_sum += rs;
               valid_count++;
            }
         }

         if(valid_count > 0)
         {
            double avg_rs = rs_sum / valid_count;

            if(avg_rs > 0.0)
            {
               log_n[point_count]  = MathLog((double)chunk_size);
               log_rs[point_count] = MathLog(avg_rs);
               point_count++;
            }
         }
      }

      if(point_count < 2)

Показано первые 50% кода (символов: 14000)

Открыть по тарифу

Купите тариф с пакетом открытий или войдите, если тариф уже есть. Открытый файл останется в вашем кабинете навсегда.

Разовая покупка файла

На этой странице откроется полный исходный код файла вместо ознакомительного фрагмента.

Чтобы оформить заказ, введите email и перейдите к оплате.

Стоимость полного кода
1 400₽ ≈ $17.13