Блог им. DedBoroded

Как программиста, меня всегда раздражали классические описания торговых паттернов:
«Левое плечо должно быть примерно на одном уровне с правым».
«Линия шеи может иметь небольшой наклон».
«Между вершинами должно пройти некоторое время».
«Тело свечи маленькое, а нижняя тень достаточно длинная».
Для человека это звучит нормально. Трейдер смотрит на график и действительно видит голову и плечи, двойную вершину, флаг, повешенного или какую-нибудь другую фигуру с красивым названием. Для алгоритма все эти описания почти бесполезны. Машина не понимает, что такое «примерно», «небольшой» и «достаточно длинный». Ей нужны числа, интервалы и конкретные условия. Как только начинаешь переводить классическое описание паттерна в код, появляется зоопарк из коэффициентов:
высота второго пика должна отличаться от первого не более чем на 12%;
глубина впадины должна составлять от 20% до 60% высоты движения;
наклон линии не должен превышать определённого значения;
объём должен вести себя ещё каким-то образом.
Потом находится график, который человек уверенно называет «головой и плечами», но алгоритм его отбрасывает. Ослабляешь одно условие — алгоритм начинает находить огромное количество мусора. Добавляешь новое условие. Потом ещё одно. В итоге получается не торговая система, а слой из if, else и параметров, смысл которых через полгода уже никто не помнит. Я пошёл другим путём. Вместо того чтобы объяснять роботу, как должна выглядеть конкретная фигура, я поставил более простой вопрос:
Были ли в истории участки графика, похожие на тот, который наблюдается сейчас, и что происходило после них?
Меня не особенно интересует, называется текущая формация «головой и плечами», «чашкой с ручкой», «кривым флагом» или вообще никак не называется. Если текущий участок рынка похож на несколько исторических участков, а после них цена вела себя определённым образом, это уже можно исследовать алгоритмически.
Любой участок графика можно представить в виде обычного вектора. Предположим, есть последние N баров:
X=(x1,x2,x3,…,xN)
Это может быть последовательность цен закрытия, относительных изменений, нормализованных цен или другого выбранного признака. Далее берётся исторический участок той же длины:
Y=(y1,y2,y3,…,yN)
После этого остаётся измерить, насколько две последовательности похожи. Для этого существует много методов:
евклидово расстояние;
косинусное сходство;
корреляция;
Dynamic Time Warping;
Matrix Profile;
кластеризация;
нейросетевые представления временных рядов.
Я начал с обычной корреляции Пирсона. Не потому, что это самый современный и модный алгоритм. Как раз наоборот: он старый, простой, быстрый и понятный. Это хороший базовый инструмент, поведение которого можно проверить и объяснить. Коэффициент корреляции Пирсона рассчитывается так:

Если не углубляться в математику, результат находится в диапазоне от −1 до 1. Значение, близкое к 1, означает, что два участка менялись похожим образом. Значение около нуля говорит об отсутствии выраженного сходства. Значение около −1 соответствует почти зеркальному движению. Для поиска похожих паттернов в первую очередь интересны положительные значения, находящиеся выше выбранного порога. В упрощённом виде логика выглядит так:
<code class="python">current_window = get_current_window(length=N)<br /><br />candidates = []<br /><br />for historical_window in history:<br /> correlation = pearson(<br /> current_window,<br /> historical_window,<br /> )<br /><br /> if correlation >= minimum_correlation:<br /> candidates.append(historical_window)</code>
На этом этапе алгоритм получает список исторических участков, геометрия которых похожа на текущую. Но здесь начинается самое важное.
Корреляция — это ещё не прогнозВысокая корреляция говорит только о том, что два участка истории похожи друг на друга внутри рассматриваемого окна. Она ничего не говорит о том, что произойдёт дальше. Это принципиальный момент, который легко упустить. Особенно когда на экране видишь два почти одинаковых графика и мозг сам дорисовывает продолжение. Корреляция в такой системе — только первичный фильтр. Она отбирает потенциальных кандидатов. Торгового сигнала на этом этапе ещё нет. Более того, у коэффициента Пирсона есть свойства, которые одновременно являются его преимуществами и недостатками. Например, он практически не обращает внимания на абсолютный уровень цены. Участок графика на значении 15 000 может быть похож на участок на значении 20 000. Для поиска формы это удобно. Пирсон также почти не реагирует на линейное изменение масштаба. Движение на 10 пунктов и движение на 100 пунктов могут получить высокую корреляцию, если их форма совпадает. И вот здесь уже возникает проблема. Геометрия одинаковая, но рыночная ситуация совершенно разная. Один участок сформировался в спокойном рынке, другой — во время резкого увеличения волатильности. Для управления позицией, постановки стопа и оценки возможного продолжения это две разные реальности. Кроме того, два обычных растущих участка могут получить высокую корреляцию просто потому, что оба росли. Это ещё не значит, что внутри них действительно присутствовала одна и та же структура.
Есть и проблема времени. Корреляция сравнивает точки последовательно: первая точка с первой, вторая со второй и так далее. Если один и тот же паттерн в одном случае сформировался за 40 минут, а в другом за 70 минут, обычный Пирсон может оценить их сходство хуже, чем человек. Поэтому взять кандидатов только по коэффициенту корреляции и сразу открыть сделку — плохая идея. Так получится не торговый робот, а генератор красивых совпадений.
После первичного поиска похожих участков логично проверить, в каком состоянии рынок находился в каждом случае. Форму последних N баров можно дополнить набором контекстных характеристик:
S = (направление, волатильность, положение относительно уровня, расстояние до уровня, состояние рынка, время сессии,…)
Логика здесь следующая:
Если текущий рынок направлен вверх, исторический кандидат тоже должен был находиться в растущем рынке. Если текущий участок смотрит вниз, кандидат также должен формироваться в нисходящем движении. Если текущий паттерн находится выше выбранного контрольного уровня, кандидат тоже должен находиться выше него. Если ниже — ниже. Причём проверялся не только сам факт нахождения по одну сторону от уровня. Учитывалось и относительное расстояние. Участок, расположенный почти вплотную к уровню, и участок, находящийся далеко от него, формально могут иметь одинаковую форму, но рыночный контекст у них уже отличается. Также сравнивались внутренние состояния рынка, положение цены относительно дополнительных ориентиров, направление движения и волатильность. Идея здесь вполне здравая: недостаточно найти похожую картинку. Нужно найти похожую картинку, возникшую в похожих условиях. Разберём эти условия.
Если текущий рынок растущий, в первую очередь интересны исторические кандидаты, которые тоже формировались на растущем рынке. Сравнивать текущую восходящую структуру с внешне похожим участком внутри сильного падения обычно бессмысленно. Форма может совпадать, а причины её появления и распределение дальнейших результатов будут совершенно разными. То же самое относится к боковому рынку. Паттерн, сформированный внутри узкого диапазона, нельзя бездумно смешивать с таким же рисунком внутри направленного движения.
Допустим, в системе используется некоторый контрольный уровень. Если текущая цена находится выше него, а исторический кандидат сформировался ниже, это могут быть два разных рыночных состояния, даже при высокой корреляции между последовательностями цен. Также имеет значение расстояние до уровня. Условно можно представить два случая:
цена находится на 0,1% выше уровня;
цена находится на 2% выше уровня.
Технически оба участка находятся сверху. Но характер рынка, запас движения и вероятность возврата к уровню могут отличаться. Поэтому сравнивать желательно не абсолютное расстояние в пунктах, а нормализованное расстояние: например, относительно цены, волатильности или среднего диапазона бара.
Представим два участка:
в первом цена за рассматриваемое время прошла 20 пунктов;
во втором — 120 пунктов.
После нормализации графики могут выглядеть практически одинаково. Корреляция будет высокой. Но математическое ожидание движения, допустимый стоп, размер рыночного шума и вероятность случайного выбивания позиции отличаются в разы. Поэтому волатильность исторического кандидата должна быть сопоставима с текущей. Не обязательно совпадать до последнего знака. Это было бы бессмысленно. Но она должна была находиться в определённом диапазоне. Для такой проверки можно использовать:
стандартное отклонение изменений цены;
средний диапазон бара;
ATR;
реализованную волатильность;
амплитуду движения внутри окна;
отношение амплитуды к текущей цене.
Можно дополнительно разделять рынок на условные состояния:
устойчивый рост;
устойчивое падение;
боковое движение;
повышенная волатильность;
сжатие диапазона;
переходное состояние.
Тогда кандидат проходит дальше только в том случае, если его состояние совпадает с текущим или хотя бы считается совместимым. В упрощённом виде эта логика выглядит так:
<code class="python">current_window = get_current_window(length=N)<br />current_state = describe_market_state(current_window)<br /><br />candidates = []<br /><br />for historical_point in historical_points:<br /> historical_window = get_window(<br /> end=historical_point,<br /> length=N,<br /> )<br /><br /> correlation = pearson(<br /> current_window,<br /> historical_window,<br /> )<br /><br /> if correlation < minimum_correlation:<br /> continue<br /><br /> historical_state = describe_market_state(<br /> historical_window<br /> )<br /><br /> if not same_direction(<br /> current_state,<br /> historical_state,<br /> ):<br /> continue<br /><br /> if not same_side_of_reference_level(<br /> current_state,<br /> historical_state,<br /> ):<br /> continue<br /><br /> if not comparable_distance_to_level(<br /> current_state,<br /> historical_state,<br /> ):<br /> continue<br /><br /> if not comparable_volatility(<br /> current_state,<br /> historical_state,<br /> ):<br /> continue<br /><br /> if not compatible_market_state(<br /> current_state,<br /> historical_state,<br /> ):<br /> continue<br /><br /> candidates.append({<br /> "point": historical_point,<br /> "correlation": correlation,<br /> "state": historical_state,<br /> })</code>На бумаге всё выглядит логично. Чем больше совпадает параметров, тем более релевантным кажется кандидат.
Но у всех этих фильтров есть и обратная сторона: когда одновременно работает множество разных условий, становится сложно понять, что именно влияет на результат. Допустим, после включения всех фильтров робот стал прибыльнее. Почему?
Потому что действительно полезно сравнивать только одинаковую волатильность?
Потому что направление рынка было определено правильно?
Потому что фильтр положения относительно уровня случайно исключил несколько убыточных периодов?
Или потому что количество сделок сократилось настолько, что в тесте осталось несколько удачных случаев?
Ответить на это без отдельных экспериментов невозможно. Есть и вторая проблема: каждый дополнительный фильтр требует порога. Например:
насколько волатильность может отличаться;
какое расстояние до уровня считать сопоставимым;
какой наклон означает рост;
когда боковое движение превращается в тренд;
насколько состояния должны быть идентичными;
какой временной интервал использовать для их расчёта.
Чем больше таких порогов, тем больше возможностей подогнать систему под исторические данные. Получается довольно неприятная ситуация. Контекстные фильтры могут быть полезными, но именно они же могут превратить нормальную идею в переоптимизированный конструктор. Поэтому важно отдельно понять, что даёт сам поиск похожих участков по Пирсону без большого количества дополнительных ограничений. После этого каждый фильтр можно возвращать по одному и проверять его вклад:
Базовый вариант — только корреляция.
Добавляется направление рынка.
Отдельно проверяется фильтр волатильности.
Затем положение относительно уровня.
После этого расстояние до уровня.
Потом совместимость состояний рынка.
Результаты сравниваются на данных, которые не участвовали в настройке.
Так хотя бы можно понять, какой фильтр действительно улучшает результат, а какой просто красиво работает на прошлом периоде.
После первичного отбора остаётся некоторое количество кандидатов. Для каждого кандидата берётся последующий участок рынка определённой длиной.
Чтобы сравнивать движения между собой, будущие траектории приводятся к относительному виду. Например, точка окончания найденного паттерна принимается за ноль, а всё дальнейшее движение измеряется относительно неё. В результате алгоритм получает не одну линию будущего, а набор исторических сценариев. И это намного честнее, чем рисовать на графике единственную «прогнозную траекторию». Рынок не обязан повторить какой-то конкретный исторический участок. Даже очень похожие паттерны могут иметь разное продолжение. Поэтому нормальный результат алгоритма — не точная линия будущей цены, а распределение возможных исходов. Из этого распределения можно получить:
долю кандидатов, после которых цена выросла/снизилась;
медианное изменение цены;
среднее изменение;
диапазон наиболее типичных результатов;
максимальное благоприятное движение;
максимальное неблагоприятное движение;
вероятность достижения цели раньше стопа;
степень согласованности кандидатов между собой.
Особенно важен последний пункт. Допустим, алгоритм нашёл двадцать подходящих исторических участков. После десяти из них цена выросла, после других десяти упала примерно на такую же величину. Среднее продолжение может выглядеть как аккуратная горизонтальная линия. Только никакого прогноза здесь нет. Кандидаты просто не согласны между собой.
Другой случай: пятнадцать независимых кандидатов показали умеренный рост, четыре остались около нуля и только один завершился сильным падением. Это уже другое распределение. Здесь можно обсуждать вероятностное преимущество, возможный стоп и соотношение риска к ожидаемому результату.
Кандидатов также можно взвешивать. Участок с корреляцией 0,97 и почти идентичной волатильностью логично учитывать сильнее, чем участок, который едва прошёл минимальный порог. Но с весами легко переборщить. Сложная формула взвешивания часто создаёт красивый результат на истории и разваливается на новых данных. Поэтому чем проще и понятнее вес, тем лучше. В упрощённом виде полный алгоритм может выглядеть так:
<code class="python">current = get_current_window()<br />current_state = describe_market_state(current)<br /><br />matches = []<br /><br />for point in history_before_now:<br /> candidate = get_window(point)<br /><br /> similarity = pearson(<br /> current,<br /> candidate,<br /> )<br /><br /> if similarity < threshold:<br /> continue<br /><br /> candidate_state = describe_market_state(<br /> candidate<br /> )<br /><br /> if state_filters_enabled:<br /> if not states_are_compatible(<br /> current_state,<br /> candidate_state,<br /> ):<br /> continue<br /><br /> if overlaps_existing_candidate(<br /> point,<br /> matches,<br /> ):<br /> continue<br /><br /> future = get_future_path(<br /> after=point,<br /> horizon=forecast_horizon,<br /> )<br /><br /> matches.append({<br /> "similarity": similarity,<br /> "future": normalize(future),<br /> "state": candidate_state,<br /> })<br /><br />forecast = aggregate_future_paths(matches)<br /><br />if forecast.edge_after_costs > required_edge:<br /> send_signal(forecast)</code>На словах всё просто. Основные проблемы начинаются внутри каждой функции.
Допустим, рынок несколько часов двигался практически без изменения характера. При использовании скользящего окна алгоритм может найти десятки соседних кандидатов:
первый заканчивается в 14:30;
второй — в 14:31;
третий — в 14:32;
четвёртый — в 14:33;
и так далее.
Формально это разные участки. Фактически они относятся к одному и тому же рыночному эпизоду и почти полностью перекрывают друг друга. Если считать каждый такой участок независимым кандидатом, получится ложная уверенность. Алгоритм скажет: «У меня есть сто исторических примеров». На самом деле у него может быть три события, каждое из которых породило несколько десятков перекрывающихся окон. Поэтому кандидатов необходимо разрежать или объединять. Между ними должен существовать минимальный временной интервал. Другой вариант — группировать соседние совпадения в единый исторический эпизод и выбирать внутри группы лучшего кандидата. Например:
<code class="python">def remove_overlapping_candidates(<br /> candidates,<br /> minimum_distance,<br />):<br /> candidates = sorted(<br /> candidates,<br /> key=lambda item: item["similarity"],<br /> reverse=True,<br /> )<br /><br /> selected = []<br /><br /> for candidate in candidates:<br /> if all(<br /> abs(<br /> candidate["point"]<br /> - selected_item["point"]<br /> ) >= minimum_distance<br /> for selected_item in selected<br /> ):<br /> selected.append(candidate)<br /><br /> return selected</code>
Это очень важный момент. Количество найденных кандидатов само по себе почти ничего не говорит о качестве прогноза. Сначала нужно понять:
сколько среди них независимых рыночных эпизодов;
насколько сильно они перекрываются;
не относятся ли они к одному и тому же дню;
не возникли ли они в одной продолжительной трендовой фазе;
совпадают ли их дальнейшие движения.
Иногда текущий рынок похож на большое количество исторических участков. Иногда даже при относительно мягком пороге алгоритм почти ничего не находит. Это может означать, что текущая структура:
очень типична для выбранного инструмента;
слишком проста и в основном состоит из обычного тренда;
относится к редкому состоянию рынка;
возникла при необычной волатильности;
плохо описывается выбранной длиной окна.
Но большое количество кандидатов не означает автоматически высокую надёжность прогноза. Если все кандидаты являются копиями одного исторического движения, пользы от такой статистики немного.
Выбор длины анализируемого участка — один из самых чувствительных параметров всей системы. Короткое окно быстро реагирует на изменения, но часто сравнивает обычный рыночный шум. Кандидатов становится много, а их полезность снижается. Длинное окно описывает более крупную структуру и обычно даёт более специфичные совпадения. Но кандидатов становится меньше, а старые участки могут относиться к рынку, который уже изменился. Нет одной универсально правильной длины. Она зависит от:
инструмента;
таймфрейма;
прогнозного горизонта;
характера торговли;
среднего времени удержания позиции;
структуры ликвидности;
того, какую форму вообще предполагается находить.
При этом окно поиска и горизонт прогноза — разные параметры. То, что анализируются последние полтора часа, не означает, что прогноз тоже должен строиться на полтора часа вперёд. Можно анализировать 90 минут истории и смотреть продолжение на 15 минут. Можно взять 60 минут и изучать следующие два часа. Эти величины нужно проверять отдельно. Есть и более сложный вариант — использовать несколько масштабов одновременно. Например:
короткое окно описывает локальный паттерн;
среднее окно показывает текущий импульс;
длинное окно задаёт общий рыночный контекст.
Технически это выглядит интересно. Но здесь снова действует простое правило: сложность должна появляться только тогда, когда есть измеримая проблема, которую она решает. Добавлять ещё три окна просто потому, что система с тремя окнами выглядит умнее, бессмысленно.
У обычной корреляции есть ещё одно ограничение. Она предполагает, что похожие события развиваются примерно с одинаковой скоростью. Предположим, текущий паттерн сформировался за 60 баров. Исторически почти такая же структура возникла за 80 баров. Человек может растянуть график по горизонтали и увидеть практически полное совпадение. Пирсон при прямом сравнении точка к точке может оценить такие участки значительно хуже. Для решения этой проблемы существует Dynamic Time Warping — динамическое выравнивание временных рядов.
Этот метод позволяет условно растягивать и сжимать отдельные участки последовательности, подбирая более подходящее соответствие между точками. Но за гибкость приходится платить. Dynamic Time Warping:
работает медленнее;
сложнее в настройке;
может находить сходство там, где его уже слишком много;
требует ограничений на допустимую деформацию времени.
Если разрешить алгоритму слишком свободно растягивать последовательности, почти любой график можно сделать похожим на любой другой. Поэтому переход от Пирсона к более сложному методу не является автоматическим улучшением. Сначала нужно доказать, что проблема разной скорости паттернов действительно существенна и влияет на торговый результат.
Когда рассказываешь о поиске похожих участков, почти неизбежно возникает вопрос: почему не использовать нейросеть? Можно взять автоэнкодер, получить скрытое представление каждого участка графика, сложить эмбеддинги в векторную базу и искать ближайших соседей.
Можно обучить Transformer, свёрточную сеть или другую модель временных рядов. Технически это возможно. Но сложная модель не является гарантом правильного решения задачи.
Если в данных есть утечка будущего, нейросеть найдёт её быстрее корреляции.
Если исторические кандидаты неправильно разделены, модель запомнит перекрывающиеся участки.
Если не учтены комиссии и проскальзывание, она оптимизирует преимущество, которого в реальной торговле не существует.
Если в качестве цели выбрана неправильная метрика, модель будет великолепно решать неправильную задачу.
Более того, сложную модель значительно тяжелее отлаживать. Когда корреляция выдала кандидата, можно открыть оба участка и глазами увидеть, почему они похожи. Когда нейросеть решила, что два участка находятся рядом в скрытом пространстве размерностью 128, объяснить результат уже сложнее. Это не означает, что нейросети бесполезны. У них есть смысл, когда простой метод упирается в конкретное ограничение. Например:
корреляция плохо справляется с нелинейными искажениями;
форма паттерна зависит от нескольких признаков одновременно;
важна не только цена, но и объём, волатильность, скорость и микроструктура;
последовательный перебор истории становится слишком дорогим;
простой алгоритм перестал давать улучшения, а его ограничения уже понятны.
Тогда можно переходить к обучаемым представлениям. Но начинать стоит с модели, которую можно проверить. Иначе вместо торгового алгоритма получается сложная система, которая уверенно выдаёт непонятный результат.
Нейросеть не превращает слабую идею в сильную. Она просто делает слабую идею менее прозрачной.
По сути, описанный подход близок к методу ближайших соседей для временных рядов. Я не пытаюсь доказать, что на графике сформировалась фигура с определённым названием. Логика звучит иначе:
Текущая траектория цены похожа на набор исторических траекторий. Эти траектории могли возникать в сопоставимом состоянии рынка. После них наблюдалось определённое распределение результатов.
Это и есть алгоритмическое определение паттерна.
Не картинка из учебника.
Не субъективное впечатление.
Не две линии, нарисованные на графике после завершения движения.
дурачок?
кто это будет читать? тем боле, что отработка 50/50![]()
А «50/50» без расчётов, выборки и определения результата — это не статистика, а мнение наугад. Если есть цифры и аргументы — обсудим. Если нет, содержательной части в комментарии я не вижу.
oreshkinalexey, Текущая траектория цены похожа на набор исторических траекторий. Эти траектории могли возникать в сопоставимом состоянии рынка. После них наблюдалось определённое распределение результатов.
это алгоритм??? математика???![]()
Я бы здесь разделил две вещи: длину выборки для проверки и срок жизни самого преимущества. Проверять стратегию действительно нужно на достаточно длинном участке, иначе легко принять случайность за закономерность. Но зарабатывать она при этом может только локально — пока сохраняется подходящий рыночный режим.
У меня хороший пример был в 2014 году. Весь год отлично работал простейший робот на стандартном MACD 12–26–9 без дополнительных условий. Стратегия была примитивной, но заработала очень хорошо. После этого рынок изменился — и преимущество исчезло. Больше в таком виде робот нормально не работал.
Было ли это случайным совпадением, конкретной фазой волатильности, структурой трендов или сочетанием нескольких факторов — задним числом можно придумать десяток убедительных объяснений. Для практической торговли важнее другое: вовремя заметить, что преимущество появилось, использовать его, пока оно сохраняется, и отключить систему, когда статистика начинает деградировать.
Поэтому я не ищу стратегию, которая обязана работать двадцать лет без изменений. Для меня более реалистичная модель — набор локальных преимуществ с ограниченным сроком жизни и постоянный контроль их состояния.
oreshkinalexey, это все понятно любому начинающему алготрейдеру (локальные преимущества с ограниченным сроком жизни). Но по факту, как только эти преимущества поддаются обнаружению, они почти сразу же перестают работать.
Поэтому моя стратегия — пересиживать локальное противодействие и дожидаться тейкпрофитов, рассчитывая силы со свободной маржой (при выносах) и свопами (при затяжном пересиживании) — это уже второй уровень прогнозирования целесообразности взятия позы. Первый — где будет цена.
Первый уровень прогнозирования базируется на экстраполяции ценовых рядов и возврате к среднеквадратичному в процессе движения к прогнозным уровням. И все бы было очень неплохо, если бы не свопы)))
Судя по вашим комментариям, вы способны обсуждать только готовые сигналы и отдельные сделки. Как только речь заходит о более сложных вещах — алгоритмах, статистике и методиках проверки, вы начинаете демонстрировать удивительное непонимание. Разжёвывать статью до доступного вам уровня я не собираюсь. Поищите контент попроще — вероятно, на другой площадке. Здесь вам явно сложно.
Вы очень подробно изложили проблемы паттерного анализа и веско пояснили, почему с сетями лезть в рынок надо очень умеючи.
Поскольку я когда-то тоже и метод ближних соседей, и случайные леса и много еще чего пытался применить к ценовым рядам и к наборам параметров, из них вычисленных, могу написать вывод, который для себя сделал.
Нужно меньше параметров!
Как можно меньше.
И каждый добавляемый параметр, каждый добавляемый порог должен приносить ощутимую пользу.
Есть такой критерий Акаике, который можно использовать для отбрасывания избыточных параметров и он не один такой. Но, имхо, и это лишнее.
Есть доходность, есть риск. Вот этих двух оценок для эквити, полученного системами, достаточно для сравнения. По крайней мере мне — достаточно.
Пишите еще, тут есть практики системной торговли, хотя и немного.
Спасибо. Полностью согласен: каждый новый параметр и каждый порог — это налог на устойчивость системы. Добавить условие легко, доказать его реальную пользу намного сложнее.
AIC и BIC полезны, когда есть внятная вероятностная модель и сопоставимые спецификации. В торговых алгоритмах с фильтрами и дискретными правилами я больше доверяю последовательному ablation-тестированию: убираем или добавляем один компонент, затем смотрим walk-forward, стабильность результата по соседним значениям параметров и деградацию на новых данных.
Доходность и риск действительно являются финальной проверкой. Но внутри разработки мы также смотрим оборот, хвостовые убытки, устойчивость по периодам и чувствительность к издержкам.
Я, наоборот, сначала ищу 1, много 2 сильных индикатора. Потом, если получается, добавляю по одному то или иное. Но не более 3 всего, редко 4-х.
Интересно, а портфельную задачу Вы как решаете? Зачастую проще на набор простых, не слишком хороших систем, навернуть портфель, чем искать системы с высоким, допустим, шарпом сразу.
Нет, обычно тоже снизу вверх, а не с комбайна из десятков индикаторов. Сначала проверяю максимально простую гипотезу и базовый сигнал. Затем добавляю по одному признаку или фильтру и смотрю не только прирост итогового Sharpe, но и устойчивость по периодам, соседним значениям параметров, инструментам и издержкам.
По портфельной задаче согласен: портфель умеренно хороших и слабо связанных систем часто лучше одной системы с красивым Sharpe. Мы смотрим не только корреляцию готовых equity, но и общие источники риска: одинаковые инструменты, направления, горизонты удержания, зависимость от волатильности и торговых сессий.
Простая корреляция доходностей может дать ложное ощущение диверсификации. Две системы способны иметь низкую обычную корреляцию, но одновременно разваливаться в одном кризисном режиме. Поэтому интереснее условные корреляции в хвостах, совместные просадки и вклад каждой системы в портфельный риск.
При распределении капитала я скорее за ограниченный risk budgeting с жёсткими лимитами, чем за «чистую» оптимизацию Марковица. На торговых данных ковариационная матрица нестабильна, и оптимизатор охотно превращает небольшую ошибку оценки в огромный вес.
В целом подход тот же: несколько понятных систем, разные источники преимущества, ограничение концентрации и минимум магии в оптимизаторе.
Кстати, Вы из С-Пб и давно в теме. Не пересекались с группой Голяндиной из Универа, они пытались применить метод гусеницы в том числе для многомерных ценовых рядов. Я даже с этой дамой когда-то переписывался.
лично с группой Голяндиной не пересекался, хотя сам метод «Гусеница»-SSA знаком. Насколько понимаю, там идея существенно интереснее обычного поиска корреляции: ряд укладывается в траекторную матрицу, затем через SVD выделяется низкоранговая структура — трендовые и колебательные компоненты, а остаток трактуется как шум. Для нескольких связанных рядов используется MSSA.
Для нашей задачи это может быть интересно как минимум в двух местах: как предварительное выделение структуры перед поиском похожих участков и как способ построения более содержательного признакового пространства вместо сравнения сырых цен.
Но к финансовым рядам я бы применял осторожно. Выбор длины окна и группировки компонент там сам становится набором параметров, а нестационарность рынка быстро превращает красивое разложение прошлого в слабый прогноз будущего. Плюс всё нужно считать строго rolling, иначе очень легко получить утечку через разложение полного ряда.
В любом случае направление интересное. Посмотрю их работы по многомерной SSA подробнее. А что именно вы называете «лобовым подходом» к портфельной задаче — равные лимиты риска, фиксированное число систем или собственная схема отбора?
Я действую иначе, но описывать не могу. Тут уже, как префе, «карты к орденам».
ТО есть это хорошо ложится на радиофизику, на акустику, на фазированные антенные решетки и так далее. Когда я начинал на рынке, это было у меня в бэкграунде. И когда все вот это не стало работать на ценовых рядах, был сильно разочарован. Возможно и можно тут подъехать на какой-нибудь кривой козе, но у меня нет идей на эту тему.
по схеме с равным риском согласен: веса, обратные оценке риска, — хороший базовый benchmark именно потому, что такая схема почти ничего лишнего не пытается оценивать. Связи между системами она игнорирует, но это иногда меньшее зло, чем строить точную оптимизацию на шумной и нестабильной ковариационной матрице.
Для меня любая более сложная схема распределения капитала должна сначала обыграть такой baseline вне выборки — после учёта оборота, ограничений и издержек. Коммерческие детали вашего варианта раскрывать, конечно, не прошу.
По Фурье и SSA тоже согласен с основной оговоркой: математически разложить можно любой ряд, но красивый спектр или набор собственных компонент ещё не означает, что у рынка существует устойчивый физический базис.
На ценовых рядах выделенные компоненты часто зависят от выбранного окна и меняются вместе с рыночным режимом. Поэтому я рассматриваю подобные методы не как декодер «истинных рыночных циклов», а максимум как преобразование признаков, сжатие пространства или фильтрацию.
Дальше проверка одна: улучшился ли rolling out-of-sample результат после издержек. Если нет, SVD просто красиво переписал тот же шум.
Интересно, у вас всё развалилось уже на устойчивости выделенных компонент или компоненты выглядели убедительно, но не давали прогноза вне выборки?
Вообще, меня удивило, что почти весь стандартный арсенал фильтрации, от фильтра НЧ баттерворта до численной реализации преобразования Гильберта не превзошли в моих исследованиях обычную скользяшку. И все АМА, КАМА и прочие адаптивные штучки тоже я счел малополезными. Даже на вейвлеты потратил время впустую.
И это при том, что и сами скользяшки тоже не подарок, но хотя бы проще — некуда.
это показательный результат. Фильтрация и прогнозирование — всё-таки разные задачи. Баттерворт, преобразование Гильберта, вейвлеты или SSA могут красиво разложить либо сгладить уже наблюдаемый ряд, но сами по себе не создают информацию о следующем движении цены.
В реальном времени за сглаживание обычно приходится платить задержкой: уменьшаем лаг — возвращаем шум. Адаптивные AMA, KAMA и подобные методы дополнительно оценивают текущее состояние ряда, то есть добавляют параметры и ещё один источник нестабильности.
Поэтому EMA оказывается сильным базовым вариантом: один понятный параметр, прозрачный лаг и минимум возможностей для переоптимизации.
Сравнивать более сложные фильтры с EMA, на мой взгляд, нужно при сопоставимом эффективном лаге и степени сглаживания. Параметры каждого метода подбираются только на прошлом участке, затем фиксируются и проверяются на следующем, ранее не использованном отрезке. После этого обучающее и тестовое окна сдвигаются вперёд, а итог строится только из последовательных тестовых участков. То есть речь идёт о walk-forward OOS-проверке, а не об одном удачно выбранном тестовом периоде.
Если адаптивный метод предполагает периодическую перенастройку, в тесте нужно воспроизводить ровно тот же график перенастройки, который был бы доступен в реальной торговле. Иначе будущая информация незаметно попадёт в параметры.
Если после такой проверки и учёта издержек сложный фильтр не обыгрывает EMA, значит он просто более дорогим способом сгладил тот же ряд.
Полностью выбрасывать такие методы я бы всё же не стал. Они могут быть полезны как вспомогательные признаки режима, оценки отношения сигнал/шум или предварительного преобразования перед поиском аналогов. Но ожидать от них самостоятельного предсказателя направления, вероятно, не стоит.
Интересно, вы сравнивали методы при примерно одинаковом эффективном лаге и степени сглаживания? И параметры фиксировались один раз или перенастраивались последовательно по мере движения по истории?
И то, что число параметров у ЕМА минимально, тоже большой плюс. А всякие дополнительные подпорки к системам с небольшим числом параметров я очень не люблю. Обычно, за небольшой прибыток получаем риск неустойчивости.
Вообще, тема числа параметров интуитивно понятная, но лично у меня не формализована.
Вот пример. Допустим, есть два однопараметирических индикатора, у каждого свой порог. Итого 4 подбираемых параметра и ценовой ряд раскрашивается в 4 цвета.
Допустим также, что у нас есть один однопараметрический индикатор без порога (вернее, порог естественный и не является переменным). И есть день недели. Подбираемый параметр 1, а ценовой ряд красится в 10 красок.
Вопрос, где больше подгонки.
хороший пример того, почему количество настраиваемых чисел само по себе ещё не определяет сложность системы.
В первом варианте есть четыре явно подбираемых параметра: два периода индикаторов и два порога. Во втором числовой параметр действительно один, но взаимодействие двух состояний индикатора с пятью днями недели делит выборку уже на десять режимов — если каждая «краска» используется как отдельное торговое состояние.
Где больше подгонки, по числу параметров или цветов заранее не определить. Важен весь класс проверенных гипотез:
— были ли индикаторы выбраны заранее или отобраны из десятков вариантов;
— был ли день недели включён до теста или после просмотра результатов;
— перебирались ли отдельные дни и их комбинации;
— назначались ли разные действия каждой из десяти ячеек;
— сколько независимых сделок осталось внутри каждого режима.
Например, у пяти дней недели всего один категориальный признак, но если разрешить выбирать любые комбинации дней, получаем уже 32 возможные маски включения и исключения — ещё до подбора параметра индикатора. Формально числовой параметр один, а фактический перебор уже немаленький.
Поэтому я бы говорил не просто о количестве параметров, а об эффективной сложности системы: сколько разных правил она способна породить и сколько вариантов фактически было просмотрено при выборе финального.
Проверять при этом нужно весь процесс целиком. Выбор индикатора, порога, дней недели и правил взаимодействия выполняется только на обучающем окне, после чего всё фиксируется и проверяется на следующем OOS-участке. Затем окна сдвигаются вперёд. Если день недели или схема раскраски выбирались с учётом результатов тестового периода, это такая же подгонка, как и оптимизация обычного коэффициента.
Поэтому во втором варианте подгонки вполне может оказаться больше, несмотря на один формально настраиваемый параметр. Само число коэффициентов — слишком грубая оценка сложности.
smart-lab.ru/blog/1013172.php
тему прочитал. В вашем примере уже есть нормальный эмпирический эксперимент, и я бы не пытался заранее назначать абстрактный штраф за каждый дополнительный параметр.
Я бы столкнул лоб в лоб два полностью определённых процесса:
На каждом шаге оба варианта оптимизируются на одном и том же прошлом участке, после чего параметры фиксируются и проверяются на следующем OOS-отрезке. Затем окно сдвигается. В результате нас интересует не общий Sortino за 25 лет, а то, насколько преимущество раздельной настройки регулярно выживает на новых данных.
В качестве грубой, но понятной оценки можно взять:
прирост раздельной настройки на обучении минус её прирост на OOS.
Допустим, отдельные параметры дали относительно общих +1 к Sortino внутри выборки, а на последовательных OOS-участках осталось только +0,1. Значит, примерно 90% красивого преимущества испарилось после выхода на новые данные. Я бы называл это не универсальной мерой переподгонки, а эмпирической оценкой оптимизма конкретной процедуры.
Второй тест, на мой взгляд, ещё интереснее: построить матрицу переноса параметров. Параметры, подобранные на XLI, применить к остальным ETF, параметры XLK — тоже ко всем и так далее.
Если на OOS каждый инструмент устойчиво лучше работает именно со своими параметрами, чем с чужими или общими, значит индивидуальность инструментов реальна. Если красивая диагональ появляется только на обучении, а на новых данных исчезает, то мы просто дали оптимизатору 36 способов подстроиться под шум.
При этом 12 ETF нельзя считать двенадцатью полностью независимыми подтверждениями: они связаны общим рынком. Поэтому сравнивать лучше не только результаты отдельных систем, но и временной ряд всего портфеля, а доверительные интервалы строить блоками по датам.
Я бы ещё добавил промежуточный вариант: не один набор параметров на всех и не отдельный набор на каждый инструмент, а несколько групп похожих ETF. Возможно, реальный максимум устойчивости окажется именно посередине.
В общем, здесь согласен с вами: считать количество параметров недостаточно. Гораздо полезнее посмотреть, сколько преимущества раздельной настройки переживает перенос во времени и на соседние инструменты. Такой результат уже можно пощупать руками, а не только обсуждать на уровне степеней свободы.
Спонсора не ищу. Цель статьи — показать подход, получить содержательную техническую критику и продемонстрировать уровень задач, которыми занимается наша команда.
Мы почти 15 лет работаем на стыке трейдинга и разработки торгового ПО. Поэтому да, публикации одновременно являются частью профессиональной репутации. Если после них приходят интересные проекты от трейдеров, управляющих или компаний — это нормальный результат, а не скрытая цель под видом благотворительности.
А простым путем пойти не пробовали?
), 100 лет назад пошагово расписал, как искать мат. пропорции! ![]()
Для МТ4 более 15 лет в сети бесплатные индикаторы валяются, для поиска всех математических пропорций. Кто захотел, уже освоил и собрал свою базу моделей.
Есть координата Х, есть Y. Прошлое движение составило столько то пунктов и столько то баров.
Текущее движение скорректировалось к прошлому на столько то баров, столько то пунктов.
А если еще сможете эти пункты и бары привести к пропорциям кое какого эталона, получите одни и те же значения.
У. Д. Ганна читайте, он для таких как вы (хотя нет, такие как вы его не поймут
Когда читаю очередной такой перл, ржу не могу.
Этот «простой путь» является базовой нормализацией по амплитуде и длительности, а не каким-то тайным знанием. Более того, корреляция Пирсона уже инвариантна к линейному сдвигу и масштабу цены. Проблема различной скорости формирования паттерна действительно существует, но она решается ресемплингом, Dynamic Time Warping и другими методами выравнивания временных рядов.
Главный вопрос вообще не в том, можно ли найти одинаковые пропорции. Найти их можно сколько угодно. Главный вопрос — дают ли они устойчивое условное распределение будущего на данных вне выборки, после комиссий, спреда и проскальзывания.
Ссылка на Ганна и бесплатные индикаторы MT4 этого не доказывает. Покажите формальные правила, независимый тест и статистику — тогда будет предметный разговор. Без этого «математические пропорции» остаются обычным рисованием по уже сформированному графику.
А аргумент «это написал Ганн сто лет назад» в техническом обсуждении весит примерно столько же, сколько возраст самого аргумента.
Так и хочется ответить, и ключи от квартиры тоже?!
Так и быть, дам два скрина… Ваша вумная ТС так умеет?
Остальное в ветке smart-lab.ru/blog/1299966.php было.
Рыночное движение цены не поддается вычислению.
ВСЕ.
Вы спорите с утверждением, которого в статье нет. Я не пытаюсь вычислить точную будущую цену и не считаю рынок детерминированным.
Задача значительно скромнее: оценить условное распределение исходов и проверить, существует ли после издержек статистическое преимущество. Для торговли не требуется знать будущее точно. Достаточно, чтобы вероятность и величина выигрыша в среднем компенсировали вероятность и величину убытка.
«Нельзя вычислить всё» не означает «нельзя оценить ничего».
С первой частью согласен: у любой неэффективности есть ограниченная ёмкость и срок жизни. По мере того как в неё приходит капитал, растут market impact, проскальзывание и конкуренция, а доходность сжимается. Эд Торп прямо писал, что конкуренция снижает доходность, поэтому исследования и разработка должны идти постоянно. При этом обнаруженные им неэффективности исчезали не мгновенно, а постепенно, иногда в течение многих лет.
Но я бы не связывал издержки непосредственно с величиной статистического преимущества. Они зависят прежде всего от ликвидности, оборота, размера позиции, скорости исполнения и того, насколько стратегия стала переполненной. Небольшая система может годами использовать преимущество, которое просто неинтересно крупному капиталу из-за ограниченной ёмкости.
Не соглашусь и с тем, что прибыль возможна только на замедленной реакции рынка либо на прямом моделировании его реакции. Есть структурные потоки, ограничения участников, предоставление ликвидности, относительные перекосы, вынужденные сделки и различия во временных горизонтах. Всё это тоже может создавать локальное преимущество.
Цифра «менее 1%» звучит эффектно, но без методики подсчёта остаётся фигурой речи. Отдельные участники всегда будут лучше информированы, быстрее, дисциплинированнее или просто будут решать другую задачу, чем большинство.
У Торпа есть хороший пример с Мэдоффом: тысячи инвесторов и их консультантов не провели элементарную проверку, потому что каждый предполагал, что её уже сделал кто-то другой. Большое количество умных и богатых участников само по себе не гарантирует ни рациональности, ни эффективности.
Поэтому моя задача не найти вечный «грааль». Задача — обнаружить локальное преимущество, оценить его ёмкость и деградацию, использовать, пока оно существует, и отключить систему, когда статистика перестанет его подтверждать.
oreshkinalexey,
Но, именно крупный капитал разрабатывает мощные системы, которые прочесывают рынок ежечасно и как синий кит собирают эти мелкие профиты.
Вечных граалей не существует. Интересно было бы создать систему, которая сама бы регулярно выискивала новые локальные преимущества и определяла деградацию прежних и не только по паттернам.
Я бы приобрел что-то подобное или поучаствовал в разработке.
А если не можешь, значит ты плохой программист.
PS Чем размусоливать школьные зады про корреляцию, почему бы не привести пример её успешного применения в своей игре. Или хотя бы в реалистичном тестировании на истории котировок.
Таблицы решений — это способ представления набора правил. Они могут заменить лес вложенных
if, но не уменьшают количество условий, не устраняют переоптимизацию и не доказывают статистическую состоятельность стратегии.Можно перенести двести сомнительных правил из кода в красивую таблицу. Мусор от смены формата мусором быть не перестанет. Статья именно об этом: проблема находится не в синтаксисе реализации, а в формализации и проверке гипотезы.
Запрос на реальные результаты справедливый. Отдельная статья с walk-forward-тестированием, чувствительностью параметров и результатами работы системы будет полезна. Но публиковать внутреннюю логику коммерческой разработки целиком ради удовлетворения случайного комментатора мы, разумеется, не будем.
И ещё: назвать корреляцию «школьными задами» несложно. Сложнее корректно применить даже такой простой инструмент к нестационарным данным, не допустить утечку будущего и не обмануть себя бэктестом. Именно на этом обычно и заканчивается квалификация любителей громких формулировок.
имхо. Паттерн — любая траектория, любая комбинация, любой отрезок, длина и размер на кривой цены)) т.е. в реальности паттернов бесконечно много… точнее мы можем их придумывать бесконечно, даже на случайном графике, даже на любом шуме. Ну и так сказать межпатерный переход формализовать еще сложнее (в природе то паттернов не существует). А сама формализация уже как условие и алгоритм, «грааль» по выявлению «паттернов» которых на самом деле нет
С ограниченностью выборки согласен. Любой вывод делается не о «рынке вообще», а о конкретном инструменте, периоде, микроструктуре и доступной истории. Именно поэтому результат нельзя считать вечным свойством рынка. Он требует последовательной проверки, контроля деградации и переоценки по мере поступления новых данных.
Я также не считаю паттерны самостоятельными объектами природы. В алгоритме паттерн — это операционное определение: класс участков, признанных похожими выбранной метрикой при заданном окне и контексте. Изменим метрику или масштаб — изменится и множество паттернов.
То, что потенциальных разбиений бесконечно много, не опровергает подход. Наоборот, именно поэтому нужны ограничения сложности, минимальное число параметров, out-of-sample-проверка и контроль множественного перебора гипотез.
Формально интерес представляет не существование «фигуры», а отличие:
P(будущее∣текущее состояние)от безусловного распределения движения рынка. Если отличия нет или оно исчезает на новых данных, паттерн признаётся бесполезным.
Формулировка «рынок антиисторичен» мне кажется слишком сильной. Рынок нестационарен: в нём есть локальные повторения, смены режимов и структурные разрывы. Поэтому история не является готовым ответом, но остаётся единственными данными, на которых вообще можно проверять гипотезы.
В целом замечание правильное: я бы действительно уточнил в статье, что речь идёт о конечном историческом диапазоне, а не об универсальном законе рынка.
Пипец, как тяжело общаться с прогульщиками, мнящими себя супер кодерами...