EWMarkup v1 — это алгоритм точного (exact) распознавания структур волн Эллиотта на основе ценовых экстремумов (зигзага). В его основе лежит подход динамического программирования (Dynamic Programming) «снизу-вверх» (bottom-up), который концептуально схож с алгоритмом Кока-Янгера-Касами (CYK) для синтаксического анализа формальных грамматик.
Алгоритм принимает на вход последовательность чередующихся ценовых точек (например, собранных с помощью SimpleExtremumFinder) и строит из них дерево вложенных волновых моделей. Главная задача алгоритма — найти такую комбинацию моделей (Импульсы, Зигзаги, Треугольники и т.д.), которая максимально соответствует правилам волновой теории и имеет наивысшую оценку (Score).
-
Вход: Список экстремумов
List<BarPoint> pointsдлины$N+1$ , что дает$N$ базовых сегментов (отрезков между двумя соседними экстремумами). -
Состояние (DP Матрица): Алгоритм использует двумерную матрицу
dp[i, j], где каждая ячейка хранит список гипотез (объектовExactParsedNode), представляющих возможные волновые структуры, начинающиеся в точкеiи заканчивающиеся в точкеj. -
Инициализация (длина 1): На первом шаге каждый отдельный сегмент от точки
$i$ до$i+1$ (диагональdp[i, i]) объявляется базовой моноволной (с типомSIMPLE_IMPULSE, счетомScore = 1.0и длиной 1 волна). Эта моноволна затем «продвигается» (Promote) для участия в формировании более крупных структур.
Алгоритм итеративно увеличивает длину рассматриваемого участка графика
Для каждого отрезка dp[i, M] (узлы p) и правую часть dp[M+1, j] (узлы c) и пытается присоединить правую часть как новую подволну к незавершенной левой части.
Прежде чем объединить p и c в новую структуру, проводится ряд жестких проверок:
-
Проверка незавершенности: Узел
pдолжен нуждаться в новых подволнах (p.WaveCount < p.ExpectedWaves), а узелcдолжен быть полностью завершенной самостоятельной моделью. -
Чередование направлений: Соседние подволны обязаны иметь противоположные направления (
lastWave.IsUp != c.IsUp). -
Грамматика Эллиотта (Model Compatibility): Функция
IsAllowedпроверяет, может ли модель узлаcвыступать в роли$K$ -й волны для модели узлаp(согласно правилам вElliottWavePatternHelper). Например, 2-я волна Импульса не может быть Импульсом, она должна быть коррекционной структурой (Зигзаг, Флэт и т.д.). -
Геометрические правила (Constraints): В
CheckWaveConstraintsиCheckFinalConstraintsпроверяются нерушимые правила волновой теории (hard rules).- Пример для Импульса: Волна 2 не перекрывает начало Волны 1. Волна 3 не может быть самой короткой. Волна 4 не заходит на территорию Волны 1 (с учетом исключений для диагоналей).
Поскольку комбинаторика может породить миллионы гипотез, используется агрессивный механизм ограничения:
- Для каждой ячейки
dp[i, j]отслеживается минимальный порог выживанияminScore[i, j]. - Predictive Scoring (Предиктивная оценка): Вместо того чтобы создавать тяжелый объект
ExactParsedNodeпри каждом удачном совпадении правил, алгоритм до создания объекта вычисляет ожидаемый балл (predictedScore) новой комбинации. - Если в ячейке уже накоплено максимум допустимых гипотез (500) и
predictedScore <= minScore, алгоритм мгновенно пропускает итерацию, экономя память и CPU. - Периодическая чистка: Как только размер корзины превышает лимит (например, > 1000 элементов), список принудительно сортируется по
Scoreи урезается до 500 лучших, аminScoreобновляется.
Балл (Score) узла вычисляется функцией CalculateScore и является главным критерием выбора "лучшей" разметки. Он состоит из нескольких множителей:
- Probability Coefficient (Вес модели): Базовая вероятность модели. Например, Импульсы (
IMPULSE) получают значительный множитель (x5.0), чтобы алгоритм отдавал предпочтение чистым трендовым движениям, а не сложным вложенным коррекциям. - Геометрическое среднее подволн: Оценки всех входящих в модель подволн перемножаются, и берется корень степени их количества.
- Fibonacci Scoring (Фибоначчи соотношения): Оцениваются длины подволн относительно друг друга.
- Применяются карты Фибоначчи (
GetFiboWeight). Например, 2-я волна импульса соотносится к 1-й, 3-я к 1-й, и т.д. - В зависимости от соотношения (0.382, 0.618, 1.618), начисляется повышающий коэффициент, если соотношение идеально, и нейтральный (1.0), если нет.
- Применяются карты Фибоначчи (
- Чередование (Alternation): Для Импульсов проверяется правило чередования коррекций (волна 2 и волна 4). Если одна из них глубокая (Deep), а вторая мелкая (Shallow), балл дополнительно умножается на 1.2.
- Штраф за незавершенность: Если модель еще не достроена (например, сформированы только 3 волны Импульса из 5), её итоговый счет понижается
Math.Pow(0.6, missingWaves), чтобы полностью готовые модели имели приоритет перед фрагментарными (при прочих равных).
Как только узел p набирает нужное количество подволн (например, 5 для Импульса или 3 для Зигзага), он считается «полным». Вызывается функция Promote:
- Проверяется, что общее (net) направление движения цены от старта до конца модели совпадает с флагом
IsUpэтой модели. Волны не могут двигаться против своего макро-тренда. - Полный узел добавляется в ячейку
dp[Start, End]как единая волна 1-го порядка. Теперь на следующих итерациях длины$L$ эта крупная структура может выступать как подволна (например, Волна 1) для еще более старшей модели (супер-структуры).
В конце работы алгоритма ячейка dp[0, N-1] (охватывающая весь график от первой до последней точки) содержит список всех возможных разметок.
Алгоритм сортирует их по убыванию Score и возвращает верхние. В индикаторах обычно берется самый первый вариант (OrderByDescending(x => x.Score).First()), который конвертируется в плоский список точек и связей для отрисовки графики (MarkupResult). Для незавершенных моделей (последняя текущая волна) строятся пунктирные проекции будущих целей на основе Fibo-зон.