English
preview
Декомпозиция частичной информации: Когда два индикатора вместе информативнее, чем каждый по отдельности

Декомпозиция частичной информации: Когда два индикатора вместе информативнее, чем каждый по отдельности

MetaTrader 5 — Индикаторы |
46 0
Hammad Dilber
Hammad Dilber

Содержание


Введение

Отбор признаков в трейдинге почти всегда выполняется по одному признаку за раз. Вы вычисляете корреляцию, взаимную информацию или коэффициент вроде MIC между каждым индикатором и целевой переменной, сортируете столбец и оставляете несколько лучших признаков. Такая процедура может найти только признаки, которые несут информацию сами по себе.

Рассмотрим исключающее ИЛИ. Две монеты с равновероятными исходами и целевая переменная, равная 1, когда результаты бросков различаются, и 0, когда совпадают. Результат броска любой монеты по отдельности ничего не сообщает о целевой переменной — ровно ноль бит. Вместе они однозначно определяют целевую переменную — один бит информации. Любая когда-либо написанная процедура попарного отбора отвергает оба входных признака, хотя сама пара идеальна. На рынке это соответствует утверждению: "это движение имеет значение только в том случае, если его подтверждает объем".

Декомпозиция частичной информации (PID), предложенная Уильямсом и Биром в 2010 году, позволяет это измерить. Для двух источников X1 и X2 и целевой переменной T декомпозиция PID разделяет информацию, которую несет эта пара, на четыре части. Согласно определению, принятому в этой библиотеке по умолчанию, все четыре атома неотрицательны: информация только от X1 (уникальная информация U1); информация только от X2 (U2); одинаковая информация от обоих источников (избыточность R); информация, доступная только в паре (синергия S). Исключающее ИЛИ представляет собой один бит чистой синергии S. Две копии одного и того же индикатора — чистая избыточность R.

В этой статье мы с нуля реализуем такую декомпозицию для MQL5, а также механизм проверки того, отражает ли полученная оценка реальную связь. Оценка каждого из четырех атомов завышена из-за конечного размера выборки: для двух независимых столбцов шума выборка из 250 строк дает около 0,10 бита синергии, которой на самом деле нет. Интерпретировать оценку атома можно только в сравнении с нулевой моделью, построенной на тех же данных. Все, что приведено ниже, выполняется в рабочем терминале, а все указанные численные результаты получены с помощью скриптов, прилагаемых к статье.

Как выглядит итоговая оценка

Ниже приведены результаты сканирования XAUUSDm на таймфрейме D1 по 2435 строкам истории. Все 66 пар из 12 признаков оцениваются относительно волатильности следующих пяти баров, а результат каждой пары сравнивается с нулевой моделью на основе 99 перестановок. Вот главный результат, еще до перехода к теории:

[PID] [1] the real bars
[PID]       2435 rows from 2500 bars, horizon 5, target fwd volatility
[PID]       2435 rows over 75 cells, 32.5 per cell
[PID]       99 draws, p floor 0.0200, alpha 0.050 reachable; family-wise floor 0.0100, one-sided
[PID]       66 pairs from 12 live features, 99 draws, block 10
[PID]       scan took 157 ms
[PID]       pair                        R       U1       U2        S     null95        p
[PID]       ret1 + volZ             0.01664  0.00074  0.00882  0.04094    0.01650   0.0200

Важно не само значение в столбце S, а его сравнение с null95 — 95-м процентилем оценок той же пары при перемешивании целевой переменной. Лучшая пара — сегодняшняя доходность за один бар и z-оценка сегодняшнего объема — дает 0,04094 бита синергии, тогда как значение нулевой модели достигает 0,01650. Значение p равно 0,0200, потому что это нижняя граница, достижимая при 99 перестановочных прогонах, а не точная оценка для этой пары. В разделе 6 объясняются эта граница и связанные с ней ограничения.

Именно это и дает эта библиотека. Далее объясняется, как это работает и насколько результатам можно доверять.


Четыре атома, три уравнения и одна недостающая аксиома

Начнем с того, что уже дает информация Шеннона. Для целевой переменной T и источников X1 и X2 есть три величины взаимной информации, которые можно вычислить непосредственно по совместному распределению: I(T;X1), I(T;X2) и I(T;X1,X2), причем пара рассматривается как одна составная переменная. Через четыре атома они выражаются следующим образом:

I(T;X1) = U1 + R
I(T;X2) = U2 + R
I(T;X1,X2) = U1 + U2 + R + S

Три уравнения, четыре неизвестных — и никакие алгебраические ухищрения не устраняют эту неопределенность. Одна лишь информация Шеннона не определяет эту декомпозицию. Обычно дополнительно задают определение избыточности: достаточно определить R, а остальные атомы следуют из него, поскольку U1 = I(T;X1) - R, U2 = I(T;X2) - R и S = I(T;X1,X2) - I(T;X1) - I(T;X2) + R.

Сначала стоит зафиксировать одно следствие. Если вычесть третье уравнение из суммы первых двух, получим R - S = I(T;X1) + I(T;X2) - I(T;X1,X2) — коинформацию, которая определяется данными и остается неизменной при любой мере избыточности. При любой аксиоме избыточности разность R - S должна совпадать с коинформацией, вычисленной непосредственно по данным. Это самая сильная из доступных внутренних проверок; на логических функциях с аналитически известными результатами погрешность составляет 0,000e+00.

Канонический способ понять смысл четырех атомов — выполнить декомпозицию для логических функций, результаты которых известны аналитически: RDN (оба источника копируют целевую переменную), UNQ1 (целевая переменная — первый источник, второй — независимый бросок монеты), XOR, AND, OR и COPY (целевая переменная — пара).

 Stacked bars showing R, U1, U2 and S for the RDN, UNQ1, XOR, AND, OR and COPY gates

Рис. 1. Четыре атома при I_min для шести логических функций, декомпозиция которых известна аналитически. RDN — один бит чистой избыточности, UNQ1 — один бит уникальной информации первого источника, XOR — один бит чистой синергии. Логические элементы AND и OR одновременно содержат и R, и S. По построению U2 равна нулю во всех шести случаях.

Сравните столбец XOR со столбцом AND. XOR — один бит информации, доступный только в паре. AND более типичен для реальных данных: каждый вход по отдельности немного повышает шансы — 0,31 бита избыточности, — а для получения оставшейся половины бита нужны оба входа. Реальные рыночные пары выглядят как гораздо более слабый AND.

Три меры, реализованные в этой библиотеке

Поскольку определение избыточности задается аксиоматически, а не выводится, существуют конкурирующие определения, дающие разные результаты. Библиотека включает три таких варианта и позволяет вызывающему коду выбрать один из них:

//+------------------------------------------------------------------+
//| Which extra axiom fixes the fourth unknown                       |
//+------------------------------------------------------------------+
enum ENUM_PID_MEASURE
  {
   PID_IMIN,      // Williams and Beer 2010, the original
   PID_IMMI,      // Barrett 2015, the smaller of the two mutual informations
   PID_ICCS       // Ince 2017, pointwise common change in surprisal
  };

I_min — исходное предложение Уильямса и Бира: для каждого значения целевой переменной берется меньшая из двух величин специфической информации источников, а затем вычисляется среднее. Эта мера гарантирует неотрицательные атомы, поэтому используется по умолчанию и является единственной мерой, которую может отображать стековый индикатор (индикатор с накоплением). I_MMI берет меньшее из двух значений взаимной информации и всегда зануляет один уникальный атом. I_ccs — поточечная мера Инса, вычисляемая по распределению максимальной энтропии, а не по данным; она может давать отрицательные значения атомов.

Именно на логической функции COPY результаты этих мер расходятся. В целевую переменную копируются два независимых бита, поэтому интуитивно каждый источник несет один бит, которого нет у другого. I_min дает R = 1 и S = 1, то есть две независимые монеты оказываются полностью избыточными. Именно за это меру критикуют с 2010 года. I_ccs дает U1 = U2 = 1 при R = S = 0, причем оба результата получены одной и той же библиотекой по одной и той же таблице:

[PID] [4] COPY - the axiom decides, and the two disagree
[PID]   PASS  I_min: R and S both one bit                      I_min R 1.000000 U1 0.000000 U2 0.000000 S 1.000000
[PID]   PASS  I_ccs: one bit unique to each source             I_ccs R 0.000000 U1 1.000000 U2 1.000000 S 0.000000

Целый бит расхождения на таблице из четырех строк. Библиотека фиксирует обе оценки, не усредняя их: выбор аксиомы — решение при моделировании, которое принимает аналитик, а не деталь, скрытая в коде. В разделе 8 показано, как тот же выбор меняет вывод на реальных данных по золоту.


Совместная таблица и логарифмы, которых в ней нет

Все описанное выше определяется по совместному распределению вероятностей трех переменных, а рыночные данные поступают как непрерывные значения типа double. Поэтому первая задача — преобразовать тройку (целевая переменная, источник, источник) в трехмерную таблицу целочисленных частот. Эта таблица — единственная оценка, которую строит библиотека; на ее основе вычисляются все энтропии и атомы, а также выполняются все перестановочные прогоны нулевой модели.

Объем таблицы равен произведению числа бинов по трем переменным. По умолчанию намеренно используется грубое разбиение: три бина для целевой переменной и по пять для каждого источника, всего 75 ячеек: 2435 строк дневных данных по золоту дают примерно 32 строки на ячейку. При пяти бинах целевой переменной и семи бинах каждого источника получилось бы 10 строк на ячейку; в таком случае оценки атомов в основном отражают смещение оценивания. Фасадный слой выводит это отношение рядом с каждой оценкой, а если оно ниже 10, прямо сообщает об этом словами.

Равночастотные бины, формируемые по рангу

Используются равночастотные бины, а не бины равной ширины. При разбиении распределения доходностей с тяжелыми хвостами на бины одинаковой ширины почти все строки попадают в средний бин, а крайние ячейки остаются пустыми. Пустая ячейка не вносит вклада, но все равно увеличивает потребность в данных.

//+------------------------------------------------------------------+
//| Equal-frequency bins, assigned by rank                           |
//|                                                                  |
//|  The rank comes from a stable order on (value, index), so equal  |
//|  values resolve the same way on both sides of the cross-check.   |
//|  Equal frequencies also keep every marginal near uniform, which  |
//|  is what leaves the joint table populated.                       |
//+------------------------------------------------------------------+
bool CPIDJoint::QuantileBins(const double &x[], const int n, const int nbins,
                             int &out[])
  {
   if(n <= 0 || nbins < 2)
      return(false);

//--- sort the positions rather than the values, so the original row of
//--- every observation survives the sort and can be written back to
   int idx[], work[];
   ArrayResize(idx, n);
   ArrayResize(work, n);
   for(int i = 0; i < n; i++)
      idx[i] = i;
   MergeIndex(x, idx, work, 0, n - 1);

//--- the bin is a share of the rank order, so it depends on nothing but
//--- position: a fat tail moves a value, never the bin it lands in
   ArrayResize(out, n);
   for(int rank = 0; rank < n; rank++)
     {
      int b = (int)(((long)rank * (long)nbins) / (long)n);
      if(b >= nbins)
         b = nbins - 1;   // the last bin absorbs the rounding
      out[idx[rank]] = b;
     }
   return(true);
  }

Сортировка по паре (value, index) делает обработку одинаковых значений детерминированной. Это позволяет при перекрестной проверке на Python требовать точного равенства, а не приблизительного совпадения. Ранжирование также означает, что границы разбиения определяются квантилями самой выборки. Для непрерывного исключающего ИЛИ, заданного с порогом 0,5, граница двух бинов вместо этого проходит по медиане, а восстановленная синергия возрастает с 0,717 бита при 3000 строках лишь до 0,908 при 48 000 строках. Это занижение обусловлено разбиением на бины, а не оценивателем.

Каждый логарифм извлекается из таблицы один раз

Энтропия по таблице частот представляет собой сумму слагаемых вида klog2(k), причем каждое k — целое число, не превышающее количества строк. Поэтому внутри циклов не требуется вычислять логарифмы:

//+------------------------------------------------------------------+
//| Tabulate every value the counts can take                         |
//|                                                                  |
//|  Index zero holds zero for both tables: an empty cell adds       |
//|  nothing to an entropy and must not reach MathLog.               |
//+------------------------------------------------------------------+
void CPIDLogTable::Build(const int maxCount)
  {
   int need = (maxCount < 1 ? 1 : maxCount) + 1;
   if(need <= m_size)
      return;   // a null reuses one table across thousands of decompositions

   ArrayResize(m_log2, need);
   ArrayResize(m_xlog2x, need);
   m_log2[0] = 0.0;
   m_xlog2x[0] = 0.0;
//--- every entropy in this library is built from integer cell counts, so
//--- tabulating the integers removes MathLog from every sweep that follows
   for(int k = 1; k < need; k++)
     {
      m_log2[k] = MathLog((double)k) * PID_LOG2E;
      m_xlog2x[k] = (double)k * m_log2[k];
     }
   m_size = need;
  }

После построения таблицы частот каждая энтропия вычисляется как log2(N) минус сумма по ячейкам с использованием табличных значений. Перестановочная нулевая модель требует пересчета всех энтропий на каждом прогоне, поэтому при сканировании 66 пар с 99 прогонами выполняется около 6 500 полных декомпозиций.


От избыточности к четырем атомам

Когда таблица готова, расчет решетки сводится к арифметике. Вычисляем три значения взаимной информации, находим R по выбранной аксиоме и получаем остальные три атома:

//+------------------------------------------------------------------+
//| Redundancy first, then the other three in closed form            |
//+------------------------------------------------------------------+
bool CPIDLattice::Decompose(const CPIDJoint &j, const CPIDLogTable &tab,
                            const ENUM_PID_MEASURE m, CPIDMaxEnt &fit,
                            SPIDAtoms &out)
  {
   out.Reset();
   out.measure = m;
   if(j.Total() <= 0)
      return(false);

//--- the three mutual informations are the only quantities Shannon fixes
   out.i1 = CPIDInfo::MutualTX1(j, tab);
   out.i2 = CPIDInfo::MutualTX2(j, tab);
   out.i12 = CPIDInfo::MutualTPair(j, tab);

//--- the axiom supplies the fourth, and the other three then subtract out
   out.r = CPIDRedundancy::Of(m, j, tab, fit);
   out.u1 = out.i1 - out.r;
   out.u2 = out.i2 - out.r;
   out.s = out.i12 - out.i1 - out.i2 + out.r;

//--- only I_ccs runs a fit, so only I_ccs can come back unreliable
   out.fitError = (m == PID_ICCS ? fit.Error() : 0.0);
   out.reliable = (m != PID_ICCS || fit.Converged());
   return(true);
  }

Два поля результата ничего не говорят о рынке и служат защитой от неверной интерпретации вызывающим кодом. fitError показывает отклонение подгонки по принципу максимальной энтропии, лежащей в основе I_ccs, от целевых маргинальных распределений. reliable принимает значение false, если подгонка не сошлась: неудачная подгонка возвращает нулевую избыточность, которая может выглядеть как правдоподобный результат декомпозиции.

Специфическая информация, которую усредняет I_min

I_min определяется через величину, которую большинство программ для теории информации никогда не вычисляет: информацию источника об одном конкретном значении целевой переменной, а не о целевой переменной в целом.

//+------------------------------------------------------------------+
//| I(t; X) for one target value, the term I_min averages            |
//|                                                                  |
//|  A relative entropy between p(x|t) and p(x), so it is never      |
//|  negative, and averaging it over t returns I(T;X).               |
//+------------------------------------------------------------------+
double CPIDInfo::SpecificInfo(const CPIDJoint &j, const CPIDLogTable &tab,
                              const int source, const int t)
  {
   int n = j.Total();
   int ct = j.CountT(t);
   if(n <= 0 || ct <= 0)
      return(0.0);

   int nx = (source == 1 ? j.N1() : j.N2());
   double sJoint = 0.0, sMarg = 0.0;
//--- accumulate the two halves of sum p(x|t) log( p(x|t) / p(x) ) in raw
//--- counts, so every logarithm is a table lookup on an integer
   for(int x = 0; x < nx; x++)
     {
      int ctx = (source == 1 ? j.CountT1(t, x) : j.CountT2(t, x));
      if(ctx <= 0)
         continue;   // an empty cell contributes nothing
      int cx = (source == 1 ? j.Count1(x) : j.Count2(x));
      sJoint += tab.XLog2X(ctx);
      sMarg += (double)ctx * tab.Log2(cx);
     }
//--- the trailing terms undo the count scaling the two sums carry
   return((sJoint - sMarg) / (double)ct - tab.Log2(ct) + tab.Log2(n));
  }

Усреднение этой величины по t с весами p(t) дает ровно I(T;X). Набор тестов проверяет это тождество для всех шести логических функций, и оно выполняется с точностью до 1,1e-16. Избыточность в I_min — то же среднее, но для каждого t берется меньшее из слагаемых двух источников; отсюда и название.

Распределение, лежащее в основе I_ccs

I_ccs вычисляется не по данным. Вычисление выполняется по распределению максимальной энтропии, воспроизводящему три парных маргинальных распределения данных. Это модель со всеми парными взаимодействиями, но без члена взаимодействия трех переменных. Библиотека получает это распределение с помощью итеративной пропорциональной подгонки: масштабирует таблицу так, чтобы ее маргинальное распределение (T,X1) совпадало с распределением данных, затем делает то же для (T,X2) и (X1,X2) и повторяет цикл. Для этого распределения каждая ячейка вносит вклад в виде локальной коинформации, но только при совпадении знаков всех четырех локальных слагаемых. Это и есть "общее изменение неожиданности", которого требует данная мера.

Подгонка со скоростью сходимости 1/n и ячейка, которую нужно исключить

Простая версия этой подгонки не работает. В детерминированной таблице, например для функции AND, ограничения требуют нулевых значений в некоторых ячейках, а пропорциональная подгонка приближается к нулю лишь со скоростью 1/n. Для AND максимальная ошибка маргинального распределения составляет 8,065e-03 после 10 проходов и 8,333e-06 после 10 000, что в точности соответствует 0,083/n. При таком подходе для достижения 1e-12 понадобилось бы порядка 1e11 проходов.

 Log-scale plot of marginal error against passes, showing the 0.083 over n curve and a vertical drop to machine zero once a dying cell is dropped

Рис. 2. Сама по себе пропорциональная подгонка все время следует кривой 0,083/n, показанной серым под пунктирной эталонной линией. Удаление ячейки, значение которой продолжает уменьшаться после целого блока проходов, позволяет вместо этого завершить подгонку с машинным нулем.

Это исправление состоит из двух частей, и нужны обе. Ячейки, которым соответствует нулевая строка в любом двумерном маргинальном распределении, удаляются перед первым проходом, поскольку их значения не могут быть ненулевыми. Затем, если после блока проходов подгонка все еще не сошлась, удаляется и любая ячейка, значение которой заметно уменьшалось на протяжении всего блока: оно стремится к нулю. Этап отсечения — фрагмент из середины Fit:

//--- fit in blocks of passes, so a cell can be watched over an interval
   m_passes = 0;
   double before[];
   ArrayResize(before, cells);
   for(int round = 0; round < PID_IPF_ROUNDS; round++)
     {
      if(RunBlock())
         break;
      ArrayCopy(before, m_q);   // the state one block back
      if(RunBlock())
         break;

//--- a cell still shrinking across a whole block is on its way to zero,
//--- and the fit reaches that boundary only if the cell is dropped outright
      bool pruned = false;
      double kept = 0.0;
      for(int i = 0; i < cells; i++)
        {
         if(m_q[i] > PID_EPS && before[i] > PID_IPF_DECAY * m_q[i])
           {
            m_q[i] = 0.0;
            pruned = true;
           }
         kept += m_q[i];
        }
      if(!pruned || kept <= 0.0)
         break;
      for(int i = 0; i < cells; i++)
         m_q[i] /= kept;   // dropping mass costs a renormalization
     }

   m_error = MarginalError();
   return(Converged());
  }

Порог по величине не решает эту задачу: значение ячейки, стремящейся к нулю в функции AND, после 200 проходов все еще равно 4e-04 — намного выше любого epsilon, который можно было бы считать нулем. Эту ячейку выявляет именно тенденция изменения, а не величина ее значения. После исправления подгонка воспроизводит каждое маргинальное распределение логических функций с ошибкой 0,000e+00, а для рыночных таблиц сходится с ошибкой около 1e-15 за 16-25 проходов. I_ccs не считает результат надежным, если подгонка не достигла требуемой точности.


Оценки атомов не равны нулю даже при отсутствии связи

Это самое важное свойство метода, и из него следуют все решения о значимости. Возьмите два столбца независимого равномерного шума и независимую целевую переменную, разбейте их на бины по предусмотренной сетке 3 x 5 x 5 и выполните декомпозицию. Здесь совершенно нечего обнаруживать. Тем не менее библиотека возвращает положительное число:

[PID] [4] the finite-sample floor on independent columns
[PID]       rows   250   R 0.009956  S 0.100330  total 0.113182
[PID]       rows   500   R 0.004370  S 0.056004  total 0.066346
[PID]       rows  1500   R 0.006509  S 0.021054  total 0.032671
[PID]       rows  6000   R 0.000296  S 0.005197  total 0.005712

 Log-log plot of reported synergy and redundancy against row count for independent columns, falling from 0.10 bits at 250 rows to 0.005 at 6000

Рис. 3. Оценки синергии и избыточности для столбцов, между которыми вообще нет связи. Нижняя граница снижается по мере добавления строк и никогда не достигает нуля, поэтому никакой фиксированный порог не может ее заменить.

Причина проста: при 75 ячейках и 250 строках в большинстве ячеек содержится по два-три наблюдения, и случайная структура этой разреженной таблицы неотличима от реальной структуры. Необработанную оценку атома нельзя интерпретировать, сравнивать при разных настройках или проверять по порогу. Интерпретировать можно только положение атома относительно распределения, построенного на тех же данных после разрушения связи: перемешать целевую переменную, сохранить исходное попарное соответствие двух источников, снова выполнить декомпозицию и повторить.

Длина блока здесь — несущий параметр

Перемешивание отдельных строк — очевидный способ реализации, но здесь он неверен по причине, указанной в заголовке функции. Целевая переменная учитывает пять будущих баров, поэтому соседние строки используют большую часть одних и тех же баров, а метки образуют серии. Разрушение этих серий дает нулевую модель, порог которой данные преодолевают слишком легко.

//+------------------------------------------------------------------+
//| Permute whole blocks, never single rows                          |
//|                                                                  |
//|  Rows a horizon apart share most of the same forward bars, so a  |
//|  label sequence arrives in runs. Shuffling one row at a time     |
//|  destroys those runs and yields a null the data clears too       |
//|  easily. A block of about twice the run length preserves them.   |
//+------------------------------------------------------------------+
void CPIDRandom::BlockShuffle(const int &src[], int &dst[], const int n,
                              const int block)
  {
   int b = (block < 1 ? 1 : block);
   int nblocks = (n + b - 1) / b;

//--- Fisher-Yates over the block order, so the blocks move and the rows
//--- inside each one never do
   int order[];
   ArrayResize(order, nblocks);
   for(int i = 0; i < nblocks; i++)
      order[i] = i;
   for(int i = nblocks - 1; i > 0; i--)
     {
      int j = Below(i + 1);
      int tmp = order[i];
      order[i] = order[j];
      order[j] = tmp;
     }

//--- write the blocks out in their new order; the last one is short
//--- whenever the block length does not divide the row count
   ArrayResize(dst, n);
   int w = 0;
   for(int k = 0; k < nblocks; k++)
     {
      int start = order[k] * b;
      int stop = MathMin(start + b, n);
      for(int i = start; i < stop; i++)
         dst[w++] = src[i];
     }
  }

Длина блока определяется перебором вариантов с проверкой ложноположительных срабатываний, а не просто задается. Двадцать чистых случайных блужданий, по 12 признаков в каждом; все 66 пар оценивались относительно нулевой модели из 39 перестановочных прогонов при номинальном alpha = 0,05. Все срабатывания здесь ложные по построению:

[PID] [2] false positives on random walks, by block length
[PID]       nominal alpha 0.05, 20 walks, 39 draws
[PID]       block  1   per-pair 0.141   any pair fires 1.000   family-wise 0.700
[PID]       block  5   per-pair 0.065   any pair fires 1.000   family-wise 0.300
[PID]       block 10   per-pair 0.060   any pair fires 0.900   family-wise 0.250
[PID]       block 20   per-pair 0.070   any pair fires 1.000   family-wise 0.150
[PID]       block 40   per-pair 0.058   any pair fires 1.000   family-wise 0.150

 Grouped bars showing that some pair clears on nearly every walk, that the family-wise gate falls from 0.70 to 0.15 as the block grows, and that the per-pair rate stays near nominal

Рис. 4. Ложные срабатывания на двадцати случайных блужданиях. Пунктирная линия обозначает номинальное значение 0,05. При независимом перемешивании критерий значимости для семейства проверок срабатывает на 70% случайных блужданий; блоки длиной в два горизонта снижают эту долю до 0,25.

Из этой таблицы следуют две оценки. Доля срабатываний для отдельной пары близка к номинальной при любой длине блока, кроме одной, поэтому проверка одной пары дает примерно корректный результат. Столбец для семейства проверок, которому соответствует средний столбец каждой группы на рис. 4, показывает последствия независимого перемешивания: 70% чистых случайных блужданий при длине блока 1, 25% — при предусмотренной длине блока в два горизонта, 15% — при длине блока 20 и 40. Этот столбец позволяет отсеивать настройки, а не выбирать одну из них. Более длинный блок сохраняет большую часть последовательности, поэтому реальному эффекту сложнее превысить порог нулевой модели; эти издержки здесь не измеряются. Обратите также внимание: средний столбец ни разу не достигает пунктирной отметки 0,05. Фактический показатель для поиска по 12 признакам составляет 0,25 при предусмотренной настройке и 0,15 в лучшем случае.

Нижняя граница p-значения, из-за которой значимость может быть недостижима при заданном alpha

Перестановочное p-значение не может быть меньше 1/(число перестановочных прогонов + 1), а для двустороннего критерия — меньше удвоенного этого значения:

//+------------------------------------------------------------------+
//| A two-sided p-value, with ties counted as at least as extreme    |
//|                                                                  |
//|  Counting ties on both sides is the standard permutation-test    |
//|  treatment and errs toward refusing rather than reporting.       |
//+------------------------------------------------------------------+
double CPIDNull::PValue(const int atom, const double observed)
  {
   if(m_draws <= 0)
      return(1.0);
//--- count both tails, since an atom can be surprising by sitting below
//--- its null as well as above it
   int ge = 0, le = 0;
   for(int k = 0; k < m_draws; k++)
     {
      double v = m_draw[k][atom];
      if(v >= observed - PID_EPS)
         ge++;
      if(v <= observed + PID_EPS)
         le++;
     }
//--- the observation counts as its own draw, which is what puts the
//--- floor at 2/(draws+1) rather than at zero
   double up = (double)(ge + 1) / (double)(m_draws + 1);
   double lo = (double)(le + 1) / (double)(m_draws + 1);
   return(MathMin(1.0, 2.0 * MathMin(up, lo)));
  }

//+------------------------------------------------------------------+
//| The smallest p-value the draw count can produce                  |
//|                                                                  |
//|  A two-sided p is twice the one-sided tail, so the floor is      |
//|  2/(ndraw+1). Ask for a threshold below it and no result can     |
//|  ever clear, however strong the effect.                          |
//+------------------------------------------------------------------+
double CPIDNull::PFloor(const int ndraw, const bool twoSided = true)
  {
   if(ndraw <= 0)
      return(1.0);
   return((twoSided ? 2.0 : 1.0) / (double)(ndraw + 1));
  }

С этим ограничением легко столкнуться. При 19 перестановочных прогонах минимальное двустороннее p-значение составляет 0,10, поэтому критерий при alpha = 0,05 никогда не сработает, какой бы сильной ни была связь. Такое сканирование покажет ноль значимых пар на любом наборе данных. Библиотека указывает свою нижнюю границу рядом с каждой оценкой. Правило обработки равных значений в том же блоке важно для статистики, которую перемешивание почти не меняет: без него сравнение зависит от одного-двух последних битов числа, не несущих никакой информации.

Важно: нижняя граница для двустороннего критерия равна 2/(draws+1), а не 1/(draws+1). По умолчанию используются 39 перестановочных прогонов, что дает ровно 0,05. Поэтому индикатор может достичь порога 0,05, но не более низкого. Для alpha = 0,01 требуется не менее 199 перестановочных прогонов.

Сканирование всех пар и однократные затраты на нулевую модель

Рис. 4 уже показывает издержки самого поиска: на чистом шуме какая-нибудь из 66 пар проходит собственный порог в 90-100% случайных блужданий, поэтому лучшую пару нужно сравнивать с распределением лучших пар. Вычислительные затраты остаются приемлемыми, поскольку для одной перестановки целевой переменной оцениваются все пары до перехода к следующей перестановке. Ниже приведен фрагмент из ScanPairs:

   CPIDRandom rng;
   rng.Seed(m_seed);
   int shuffled[];
   SPIDAtoms a;

//--- one shuffle per draw, scored against every pair: that yields the
//--- per-pair nulls and the distribution of the winner for the same cost
   for(int k = 0; k < m_draws; k++)
     {
      rng.BlockShuffle(tbin, shuffled, rows, m_block);   // blocks keep the target's runs
      double best = -1.0e308;
      for(int p = 0; p < m_npairs; p++)
        {
         for(int i = 0; i < rows; i++)
           {
            ba[i] = bins1[c1[p] * rows + i];
            bb[i] = bins2[c2[p] * rows + i];
           }
         if(!j.Build(shuffled, ba, bb, rows, m_nt, m_n1, m_n2))
            return(false);
         if(!CPIDLattice::Decompose(j, m_tab, m_measure, fit, a))
            return(false);
         m_drawS[k * m_npairs + p] = a.s;
         drawR[k * m_npairs + p] = a.r;
         if(a.s > best)
            best = a.s;
        }
      m_familyBest[k] = best;   // the best-of-all-pairs this draw reached
     }

Каждый перестановочный прогон дает 66 декомпозиций — столько же требуется для нулевых моделей отдельных пар. Дополнительно сохраняется максимум по парам в m_familyBest, поэтому p-значение для семейства проверок получается без дополнительных затрат.


Проверка — логические функции с аналитически известными результатами, внешний эталон и общий поток случайных чисел

Библиотека проверяется на трех уровнях, которые намеренно различаются по характеру проверки.

Этим логическим функциям вообще не нужен эталон. Декомпозиции XOR, AND, OR, COPY, RDN и UNQ1 можно вывести аналитически, поэтому воспроизводящий их тест проверяет соответствие математическим результатам, а не другой реализации:

[PID] [1] XOR - neither source alone carries anything
[PID]   PASS  I(T;X1) is zero                                  0.000000000
[PID]   PASS  I(T;X2) is zero                                  0.000000000
[PID]   PASS  I(T;X1,X2) is one bit                            1.000000000
[PID]   PASS  S is one bit                                     1.000000000

Эталонная реализация на Python действительно существует и независима. dit 2.2 устанавливается и работает на текущей версии Python и реализует те же три меры, что и PID_WB, PID_MMI и PID_CCS. При перекрестной проверке каждая экспортированная таблица заново строится по ее меткам, выполняется ее декомпозиция по схеме, а результат сравнивается и с экспортом MQL5, и с dit.

Поток случайных чисел общий, поэтому нулевая модель проверяется на равенство, а не сопоставляется статистически. Тип ulong в MQL5 и целые числа с применением маски в Python одинаково ведут себя при переполнении, поэтому инициализация xorshift64* через splitmix64 порождает одинаковые 64-битные слова в обеих реализациях. Эти перестановки не просто статистически похожи на перестановки MQL5 — они идентичны, и обе нулевые модели должны совпадать в каждом перестановочном прогоне:

[6] the null, redrawn from the same stream
  PASS  39 draws reproduce exactly                           worst 4.460e-15
  PASS  the p-value for S is well formed                     p 0.2000, floor 0.0500
  PASS  the null mean for S sits above zero                  0.013087 against an observed 0.017743
23 PASS, 0 FAIL   [ALL PASS]

Полные результаты проверки цепочки на 25.08.2026:

Уровень
Скрипт или команда
Результат
Схема
python pid_prototype.py
29 PASS, 0 FAIL
Поток и таблица
PID_Test_Info.mq5
24 PASS, 0 FAIL (9 мс)
Логические функции с аналитически известными результатами
PID_Test_Gates.mq5
23 PASS, 0 FAIL (1,9 мс)
Решетка на данных
PID_Test_Lattice.mq5
26 PASS, 0 FAIL (0,08 с)
Нулевая модель и поиск
PID_Test_Null.mq5
10 PASS, 0 FAIL (2,4 с)
Перекрестная проверка
Экспортируйте, затем запустите python pid_crosscheck.py
23 PASS, 0 FAIL
Рынок
PID_Scan_Market.mq5
XAUUSDm D1, 2 435 строк, от 155 до 170 мс на одно сканирование

Выполните экспорт перед перекрестной проверкой: программа на Python читает файл, записанный скриптом MQL5, а устаревшие экспортированные данные намеренно приводят к провалу проверок. Если файл окажется в другом месте, укажите этот путь в PID_EXPORT. Один из приведенных выше допусков намеренно отличается от остальных: Результаты I_ccs совпадают с dit с точностью до 6,137e-11, тогда как результаты I_min и I_MMI — примерно до 4e-15. Причина в том, что dit получает то же распределение с помощью универсального оптимизатора, собственная невязка которого составляет около 1e-10. Более широкий допуск — не наша ошибка.


Что на самом деле говорит золото

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

#
Признак
Что это такое
#
Признак
Что это такое
0
ret1
логарифмическая доходность за 1 бар
6
bbPos
положение внутри полосы Боллинджера
1
ret5
логарифмическая доходность за 5 баров
7
atrRatio
отношение ATR(14) к ATR(50)
2
rsi14
RSI по Уайлдеру
8
maDist
расстояние от SMA(20), выраженное в ATR
3
stoch14
положение по стохастику в диапазоне за 14 баров
9
volZ
z-оценка тикового объема за 50 баров
4
cci20
индекс товарного канала
10
rangeATR
диапазон бара, выраженный в ATR
5
macdHist
гистограмма MACD, масштабированная по ATR
11
streak
длина серии однонаправленных баров со знаком

Параметры InpFeature1 и InpFeature2 в индикаторе и советнике задаются индексами из этой таблицы. В обоих случаях по умолчанию используются 0 и 9 — ret1 и volZ. Три целевые переменные — будущая доходность с учетом знака, ее абсолютное значение и будущая реализованная волатильность. Ответ полностью зависит от того, какую целевую переменную вы рассматриваете.

Три целевые переменные и два контрольных варианта

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

[PID] [2] three targets against two controls
[PID]       target            source        cleared  best S   pFam
[PID]       fwd return        real          7 of 66  0.02917  0.0700
[PID]       fwd return        shuffled      5 of 66  0.02496  0.1800
[PID]       fwd return        walk          1 of 66  0.02329  0.1100
[PID]       |fwd return|      real          9 of 66  0.02538  0.0900
[PID]       |fwd return|      shuffled      2 of 66  0.02220  0.2100
[PID]       |fwd return|      walk          8 of 66  0.02073  0.1700
[PID]       fwd volatility    real         11 of 66  0.04094  0.0100
[PID]       fwd volatility    shuffled      6 of 66  0.03629  0.0100
[PID]       fwd volatility    walk          3 of 66  0.02541  0.0600

Последние два столбца отвечают на разные вопросы. Столбец cleared подсчитывает пары, чье значение S превысило порог собственной нулевой модели, — это 66 отдельных тестов; pFam показывает, как часто лучшая из 66 пар на перемешанной целевой переменной достигает результата лучшей реальной пары. Поэтому в строке может быть больше пар, прошедших порог, но хуже p-значение для семейства проверок. Для доходности с учетом знака на реальных барах проходят порог 7 из 66 пар против 5 у перемешанного контрольного варианта, а pFam составляет 0,0700 и не проходит порог alpha: сигнала нет — к тому же выводу на этом инструменте пришел совершенно другой метод.

Будущая волатильность сильнее всего отличается от обоих контрольных вариантов, но это видно не по столбцу cleared: в нем доходность с учетом знака выглядит сильнее, хотя именно для этой целевой переменной только что не обнаружили сигнала. Различие видно в p-значении для семейства проверок: 0,0100 против 0,0600 у случайного блуждания. В строке с перемешиванием также указано 0,0100, но это нижняя граница p-значения при 99 перестановочных прогонах. Существенное различие показано на рис. 5: лучшая реальная пара дает 0,04094 бита против 0,03629 после изменения временного порядка. Различие реальное, но небольшое.

Bars comparing synergy on real bars against the 95th percentile of each pair's own null, with a dashed line marking the best pair found on time shuffled bars

Рис. 5. Пять сильнейших пар для будущей волатильности, каждая сопоставлена с 95-м процентилем собственной нулевой модели. Пунктирная линия обозначает результат лучшей пары, найденной тем же сканированием после изменения временного порядка баров; он выше результатов трех из пяти реальных пар.

Важно: одно сканирование — одна реализация выборочного процесса. При запуске того же сканирования на следующий день, когда окно из 2 500 баров сдвинулось всего на один бар, p-значение для семейства проверок перемешанного контрольного варианта изменилось с 0,0700 до 0,0100, а результаты на реальных данных почти не изменились. Повторите расчет таблицы для нескольких дат окончания, прежде чем считать какие-либо из этих результатов свойством золота.

Одни и те же бары при всех трех аксиомах

В разделе 3 утверждалось, что выбор меры избыточности — решение при моделировании. Вот как это решение меняет вывод на реальных данных при одинаковых барах, бинах и нулевой модели для обеих рассмотренных выше целевых переменных:

Целевая переменная
Аксиома
Пары, прошедшие порог alpha
Лучшая пара
Наибольшее S
p-значение для семейства проверок
будущая волатильность
I_min
11 из 66
ret1 + volZ
0,04094
0,0100
будущая волатильность
I_MMI
13 из 66
ret1 + volZ
0,04167
0,0100
будущая волатильность
I_ccs
21 из 66
ret1 + rangeATR
0,03597
0,0100
доходность с учетом знака
I_min
7 из 66
rsi14 + macdHist
0,02917
0,0700
доходность с учетом знака
I_MMI
7 из 66
rsi14 + macdHist
0,03042
0,0500
доходность с учетом знака
I_ccs
4 из 66
rsi14 + atrRatio
0,02356
0,3200

Сопоставляйте эти два блока: каждый из них по отдельности может ввести в заблуждение. Для будущей волатильности при I_ccs порог проходит 21 из 66 пар, а при I_min — 11; эти две меры также расходятся в выборе самой сильной пары. Для доходности с учетом знака порядок меняется на противоположный: I_ccs становится самой консервативной мерой с p-значением для семейства проверок 0,3200, тогда как I_MMI достигает ровно порога alpha = 0,05 на тех же барах, где I_min не прошла порог при 0,0700. Таким образом, приведенное выше заключение о направлении касается I_min, а не золота. Всегда указывайте аксиому рядом с атомом.

Сохраняется ли оценка вне выборки

При сканировании история делится на отметке 70%, пары ранжируются по первой части выборки, а затем проверяются по второй. Во второй части выборки 685 строк против 1 685 в первой. Как видно из рис. 3, нижняя граница оценок повышается при уменьшении числа строк, поэтому необработанная оценка атома вне выборки несопоставима. Сопоставимы ранг среди 66 пар и отклонение от собственной нулевой модели второй части выборки:

[PID] [4] out-of-sample transfer of the chosen pairs
[PID]       head 1685 rows, tail 685 rows - the floor differs, so rank is the comparable column
[PID]       pair                   rank in  rank out   over null95
[PID]       atrRatio + volZ              1        18           no
[PID]       rsi14 + atrRatio             2         1           no
[PID]       mean out-of-sample rank 23.2 of 66, chance would give 33.5
[PID]       1 of 5 clear their own out-of-sample null

Пять самых сильных пар внутри выборки дают средний ранг вне выборки 23,2 из 66 против 33,5, ожидаемых при случайном выборе. Порядок сохраняется лишь частично: одна из пяти пар проходит порог собственной нулевой модели вне выборки, а самая сильная пара первой части выборки опускается на 18-е место.

Если объединить эти три результата, то вывод будет кратким. Для целевой переменной направления нет результата, который выдерживал бы сравнение с собственным контрольным вариантом при аксиоме по умолчанию; при самой мягкой из трех аксиом результат лишь достигает порога. Для целевой переменной волатильности информации немного больше, чем для сопоставимого случайного блуждания; преимущество над барами с измененным порядком невелико. p-значение для семейства проверок упирается в одностороннюю нижнюю границу разрешения, а ранжирование лишь частично сохраняется после разбиения с очисткой.


Использование оценки

Индикатор

Декомпозиция — четыре числа, сумма которых дает пятое, поэтому естественный способ отображения — представление с накоплением, а не линия. Индикатор отображает R, затем R+U1, затем R+U1+U2 и итоговое значение в виде четырех гистограмм в подокне; каждый атом образует слой между двумя соседними уровнями. Поверх них он строит одну пунктирную линию: тот же итог, но с заменой S на 95-й процентиль собственного нулевого распределения этого окна. Фрагмент цикла построения графика:

//--- a decomposition costs tens of milliseconds, so only every step-th bar
//--- recomputes and the bars between it hold the reading that came before
   for(int bar = firstBar; bar < rates_total; bar++)
     {
      bool due = (((bar - firstBar) % InpStep) == 0);
      if(due)
        {
         SPIDAtoms tmp;
         double tn = 0.0, tp = 1.0;
         int tr = 0;
         if(ReadAt(bar, tmp, tn, tp, tr))
           {
            a = tmp;
            n95 = tn;
            pv = tp;
            rows = tr;
            have = true;
            computed++;
           }
        }
      //--- nothing is drawn until the first window succeeds, so the line
      //--- never opens on a reading that was never computed
      if(!have)
         continue;

//--- cumulative sums, so the four atoms render as one stack; the null rides
//--- on the same base as S, and the stack top crossing it is the reading
      g_r[bar] = a.r;
      g_ru1[bar] = a.r + a.u1;
      g_ru1u2[bar] = a.r + a.u1 + a.u2;
      g_total[bar] = a.r + a.u1 + a.u2 + a.s;
      g_noise[bar] = a.r + a.u1 + a.u2 + n95;
      g_s[bar] = a.s;
      g_p[bar] = pv;
      g_null95[bar] = n95;
     }

Именно эта геометрия и составляет весь смысл визуализации. Верхний слой, поднимающийся над пунктирной линией, показывает, что синергия превышает порог своей нулевой модели. Не нужно считывать вторую ось или мысленно сравнивать два графика.

Перед подключением индикатора необходимо учесть три свойства. Линия ступенчатая: ее значение вычисляется каждые InpStep баров и сохраняется между обновлениями, поскольку для каждой оценки нужно рассчитывать перестановочную нулевую модель. Окно заканчивается на последнем закрытом баре. При этом InpWindow задает 600 строк против 2 435 при сканировании, поэтому, как видно из рис. 3, пунктирная линия здесь проходит в несколько раз выше. Именно поэтому нулевая модель пересчитывается для каждого окна, а не фиксируется один раз.

Время выполнения, измеренное внутри индикатора с помощью GetMicrosecondCount, составляет от 25 до 50 мс для 40 окон на 200 отображаемых барах по результатам шести запусков на терминале без нагрузки. Поэтому InpHistoryBars можно задавать с запасом. Рис. 6 получен не в одном из этих запусков: создание снимка экрана использует тот же поток графика. Для той же работы этот запуск показал 41 мс на панели и 53 мс в логе.

Subwindow showing the four atoms stacked with the null level drawn over the stack, and a panel listing the atoms, the level noise reaches, the p-value and the floor that p-value can reach

Рис. 6. Четыре атома показаны с накоплением в подокне; пунктирная линия обозначает уровень, которого достигает шум. Панель выводит ту же оценку в числовом виде, включая p-значение и нижнюю границу разрешения, которую допускает число перестановочных прогонов.

Эта оценка проходит порог с минимальным запасом, который может показать визуализация: синергия 0,07794 против уровня шума 0,06759; p-значение 0,0500 находится ровно на нижней границе, допускаемой 39 перестановочными прогонами. Если бы результат одного перестановочного прогона превысил наблюдаемое значение, проверка значимости уже не была бы пройдена, хотя изображение осталось бы тем же. Обратите также внимание: null95 соответствует односторонней проверке, тогда как при расчете p хвостовая вероятность удваивается. Поэтому слой может подняться над линией, но проверка значимости все еще не будет пройдена — здесь это обычная ситуация. Решение в обоих случаях — увеличить число перестановочных прогонов.

Советник и контрольный вариант, который его превзошел

Декомпозиция дает оценку силы связи, но не ее направления, поэтому она не может служить сигналом входа. С ее помощью можно определить, стоит ли доверять совместной оценке двух признаков; советник использует ее только для одной задачи — выбора расстояния до стоп-лосса. Во всех вариантах вход выполняется по простому пробою канала Дончиана; различается только показатель волатильности, по которому определяется стоп-лосс.

//+------------------------------------------------------------------+
//| The volatility this bar's stop is built from                     |
//|                                                                  |
//|  All three arms take the same entry and the same formula, so     |
//|  only this number differs between them. When the decomposition   |
//|  is not significant every arm falls back to the sample mean, so  |
//|  the three trade populations stay close without being equal.     |
//+------------------------------------------------------------------+
double VolatilityNow(const double v1, const double v2)
  {
//--- the control arm, and the fallback for every arm when the pair's
//--- synergy did not clear its own null
   double global = g_rule.GlobalMean();
   if(!g_significant || InpVolMode == PID_VOL_CONSTANT)
      return(global);

//--- the reading itself: the mean of the joint cell this bar lands in
   double cell = g_rule.PredictAt(v1, v2);
   if(InpVolMode == PID_VOL_INVERTED)
      cell = 2.0 * global - cell;   // the opposite control, reflected about the mean
//--- a thin cell can hold an extreme mean, so clamp before it sizes a stop
   if(cell < global * 0.20)
      cell = global * 0.20;
   if(cell > global * 5.00)
      cell = global * 5.00;
   return(cell);
  }

Эту функцию используют три варианта. Constant всегда использует среднее по выборке — контрольный вариант для случая, когда "декомпозиция ничего не вносит". Cell использует среднее значение целевой переменной в ячейке совместной таблицы, в которую попадает текущая пара; это и есть оценка. Inverted зеркально отражает оценку по ячейке относительно среднего по выборке — контрольный вариант для случая, когда "оценка направляет в неверную сторону". В последнем столбце указано стандартное отклонение расстояний до стоп-лосса по сделкам, выраженное в процентах от среднего расстояния.

Вариант
Сделки
Чистая прибыль
Профит-фактор
На сделку
Просадка баланса
Разброс расстояний до стоп-лосса
Constant (контрольный вариант)
142
+347,03
1,01
+2,44
9,80%
55,6%
Cell (оценка)
143
+512,30
1,01
+3,58
9,95%
56,7%
Inverted (контрольный вариант)
142
+731,14
1,01
+5,15
9,46%
55,0%

XAUUSDm D1, с 01.01.2018 по 01.08.2026, депозит 100 000, риск 1% на сделку, пара ret1 + volZ относительно будущей волатильности. Контрольный вариант Inverted оказывается лучшим по чистой прибыли, математическому ожиданию на сделку и просадке; при работающем прогнозе такого соотношения результатов не бывает. Впрочем, все три варианта фактически остаются около нуля: профит-фактор 1,01 в каждом, а результаты находятся в диапазоне от 0,35 до 0,73% за 8,5 года.

Цепочка причин, стоящая за этим результатом, интереснее самой таблицы. Значения в сетке различаются, поэтому прогноз мог варьироваться, а ограничитель так и не сработал. Главная проблема — критерий значимости: в одиннадцати или двенадцати из шестнадцати перестроений модели значимой синергии не обнаружилось. Поэтому в большинстве сделок во всех трех вариантах использовалась одна и та же постоянная величина, а разброс расстояний до стоп-лосса увеличился лишь с 55,6% у контрольного варианта до 56,7% у варианта с оценкой. Разброс значений в сетке, редко выполняемый критерий значимости и 142 сделки, которых недостаточно, чтобы различить варианты.

Важно: советник демонстрирует использование оценки; наличие торгового преимущества не заявляется. Советник прилагается к статье, поскольку контроль по трем вариантам — это корректный способ проверить прогноз, а также потому что победил вариант, который предполагался ошибочным. Тестирование на истории одного лишь варианта Cell, без контрольных вариантов, выглядело бы как небольшой успех.


Заключение

Библиотека выполняет поставленные перед ней задачи. Она разделяет информацию двух индикаторов о целевой переменной на уникальные, избыточные и синергетические части. Библиотека содержит три конкурирующих определения избыточности и показывает их расхождение на целый бит в таблице из четырех строк. Она проверяет вычисления по логическим функциям с аналитически известными результатами, независимой эталонной реализации на Python и перестановочной нулевой модели, побитово идентичной в обоих языках, а также сканирует все 66 пар примерно за 160 мс с поправкой на множественные проверки в рамках семейства.

Она не находит на золоте торгового преимущества, которое можно использовать на практике. Уверенно утверждать это позволяет строгость проверки численного результата, а не само число. Аргументация опиралась на три составляющие: нижний уровень оценок, измеренный на независимых столбцах и показывающий, что необработанная оценка атома сама по себе ничего не значит; нулевую модель на основе блочных перестановок, длина блоков которой обоснована проверкой ложноположительных срабатываний, а не просто задана; два контрольных варианта, оцененных по той же целевой переменной, что и опровергаемый ими результат. Уберите любую из них — и результат по волатильности будет выглядеть как открытие.

К статье прилагаются: одиннадцать подключаемых файлов с совместной таблицей, энтропиями, тремя аксиомами избыточности, решеткой, нулевой моделью на основе блочных перестановок, слоем признаков без использования будущих данных и правилом прогнозирования по сетке; четыре тестовых скрипта; скрипт экспорта и программа перекрестной проверки на Python, которая сверяет атомы с dit, а нулевую модель — в каждом перестановочном прогоне; скрипт рыночного сканирования; индикатор; советник. В таблице ниже они перечислены по отдельности.

#
Имя файла
Тип
Описание
1
PID.mqh
Подключаемый файл Фасадный слой: все пары, нулевая модель для семейства проверок, лучшая пара
2
PIDJoint.mqh
Подключаемый файл Разбиение на бины с одинаковым числом наблюдений и трехмерная таблица частот
3
PIDLogTable.mqh
Подключаемый файл log2(k) и klog2(k) табулируются один раз, поэтому при проходах нет вызовов MathLog
4
PIDInfo.mqh
Подключаемый файл Энтропии, взаимная и специфическая информация по частотам
5
PIDRedundancy.mqh
Подключаемый файл Аксиома: I_min, I_MMI, I_ccs и подгонка по принципу максимальной энтропии
6
PIDLattice.mqh
Подключаемый файл Избыточность на входе, четыре атома на выходе, доступные уравнения согласованности
7
PIDRandom.mqh
Подключаемый файл splitmix64 для инициализации xorshift64* и блочное перемешивание
8
PIDNull.mqh
Подключаемый файл Перестановочная нулевая модель, ее процентили и нижняя граница p-значения
9
PIDData.mqh
Подключаемый файл Преобразование баров в двенадцать признаков без использования будущих данных и три целевые переменные для будущих значений
10
PIDRule.mqh
Подключаемый файл Прогноз по совместной сетке с сохранением границ бинов
11
PIDPanel.mqh
Подключаемый файл Геометрия панели, используемая для отображения и его проверки
12
PID_Test_Info.mq5
Скрипт
Проверка соответствия потока, таблица значений, двойная проверка взаимной информации
13
PID_Test_Gates.mq5
Скрипт
Проверка шести логических функций по их аналитическим декомпозициям
14
PID_Test_Lattice.mq5
Скрипт
Тождества, искусственно заданные связи, нижняя граница и вычислительные затраты
15
PID_Test_Null.mq5
Скрипт
Ложные срабатывания в зависимости от длины блока и мощность критерия
16
PID_Export_ForCrosscheck.mq5
Скрипт
Пайплайн вычислений с полной точностью для проверки на Python
17
PID_Scan_Market.mq5
Скрипт
Реальные бары, оба контрольных варианта, три целевые переменные, аксиомы, проверка переноса вне выборки
18
PID_Synergy.mq5
Индикатор
Четыре атома с накоплением ниже уровня, которого достигает шум
19
PID_Volatility_EA.mq5
Советник
Демонстрация: стоп-лосс по оценке из ячейки совместной таблицы, два контрольных варианта
20
pid_prototype.py
Python
Схема и ее тестовые эталоны, проверенные по dit
21
pid_crosscheck.py
Python
Проверка экспортированных данных по dit, numpy и самим экспортированным данным
22
MQL5.zip
Архив
Все файлы проекта в соответствующих подпапках; распакуйте в каталог данных терминала

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/24382

Прикрепленные файлы |
MQL5.zip (72.79 KB)
MQL5 Bootstrap (IV): Вспомогательные средства для трейлинг-стопов и переноса стоп-лосса в безубыток MQL5 Bootstrap (IV): Вспомогательные средства для трейлинг-стопов и переноса стоп-лосса в безубыток
В этой статье представлены переиспользуемые утилиты MQL5 для управления трейлинг-стопами и переносом стоп-лосса в безубыток. В ней рассматриваются трейлинг-стоп с фиксированным расстоянием в пунктах, трейлинг на основе скользящей средней, ATR и Parabolic SAR, трейлинг в денежном выражении и периодический трейлинг, а также перенос стоп-лосса в безубыток по пунктам и в денежном выражении. Предусмотрены пороги активации, логика пошагового переноса, защита от переноса стопа в обратном направлении и проверка ограничений брокера. Примеры кода и классы Bootstrap показывают, как интегрировать эти вспомогательные средства в советники, чтобы стандартизировать управление позициями и сократить дублирование кода.
Рыночная микроструктура в MQL5 (Часть 8): Оценка силы микротренда Рыночная микроструктура в MQL5 (Часть 8): Оценка силы микротренда
В восьмой части добавляется побарная оценка силы микротренда для NQ M1. Функция GetMicroTrendStrength() формирует непрерывную композитную оценку в диапазоне [-1, +1] из согласованности EMA, нормализованного по ATR положения цены, согласованности наклонов и объема. Штраф за противоречие подавляет конфликты между согласованностью EMA и положением цены. Сессионно-адаптивные пороги масштабируются в зависимости от показателя достоверности из седьмой части, регулируя частоту сигналов в разных режимах. Результаты включают метку с семью состояниями, бинарный сигнал и проверку устойчивости, откалиброванные по 514 нью-йоркским сессиям (май 2024 г. - май 2026 г.).
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
MQL5 Bootstrap (III): Упрощенные средства работы с новостями MQL5 Bootstrap (III): Упрощенные средства работы с новостями
В этой статье представлены унифицированная модель новостей и набор переиспользуемых классов MQL5 для работы с Экономическим календарем MetaTrader 5. Вы сможете получать, фильтровать и кэшировать события по времени, валюте, стране и важности с помощью единого интерфейса для трех провайдеров: встроенного календаря, CSV и SQLite. Фреймворк поддерживает экспорт и импорт, поиск следующего и предыдущего событий, а также надежное тестирование на истории в Тестере стратегий без изменения торговой логики.