Перейти к содержимому
Все стратегии

Стратегии

Моментум с пропуском месяца: длинные и короткие позиции в US секторальных ETF

Итог: Отменено

SkippedMonthSectorMomentum

Сводка по итогам

SkippedMonthSectorMomentum проверяла, предсказывает ли моментум за период формирования с пропущенным месяцем в восьми секторальных ETF US доходность следующих 21 сессий, используя портфель с длинными позициями по верхним -2 и короткими по нижним -2. За 3 итераций код так и не прошёл проверку Layer 4 QA. Логика ротации рассчитывала размер новых позиций, пока закрытия старой когорты ещё исполнялись, и зависела от повторного входа с точной временной меткой, поэтому когорты были неполными и несбалансированными. Единственное измерение в песочнице (коэффициент Шарпа -0.02, PF 0.98, -0.125% на позицию для 178 позиций) признали не отражающим гипотезу. Запуск остановили до проверки бэктеста, оптимизации и этапов аналитика и оценки рисков.

Гипотеза

Проверь, опережают ли лидеры секторов аутсайдеров за следующие 21 торговых сессий после многомесячного периода формирования. Торгуй восемью ликвидными секторальными ETF US на синхронизированных дневных барах: покупай два сектора с самым высоким рейтингом и открывай короткие позиции по двум с самым низким. Исключай последние 21 сессий из доходности периода формирования, чтобы отделить среднесрочную устойчивость от немедленного разворота. Это адаптация, а не воспроизведение исходного исследования децилей акций. Оценивай последующую исполнимую доходность, а не средние значения за период формирования, показанные в источнике.

Источник содержит конкретный механизм кросс-секционного моментума. В исходном коде по ошибке возводятся в степень отдельные логарифмические доходности вместо их скользящей суммы, тогда как ранжирование должно выполняться отдельно для каждой даты. Сохрани эту исправленную механику и проверь версию с пропущенным месяцем на уровне секторов. Экспозиция только в длинных позициях — крупнейший дисбаланс портфеля на 83.2%; это предложение добавляет длинные и короткие позиции и несколько инструментов. Доля Hyperliquid ниже целевой, но там уже есть ротация на основе относительной силы. Предложения с опционами неоднократно сталкиваются со структурными сбоями. Ранжирование по доходности сектора за период формирования отличается от привязки к максимуму за 52 недель и ошибок входа по календарю. Ребалансировка — это операционный график, а не утверждение о календарной аномалии. Данные о концентрации корпуса недоступны, поэтому нельзя утверждать, что эти конкретные инструменты использовались редко.

verification_loop: верификация не пройдена (Layer 4 — проверка QA) [class=code_defect]: - [edge_concern] Пробный запуск (без оптимизации, период формирования=126): закрыто позиций — 178, PF 0.98, коэффициент Шарпа -0.02, -0.125% на позицию; после расходов на заём преимущество близко к нулю. Из-за указанного ниже дефекта запуск не измерил гипотезу в заданном виде: когорты были неполными и недостаточного размера. После исправления измерь заново, прежде чем делать выводы. Время выполнения также было большим: 249 с……Показать большеСвернуть

verification_loop: верификация не пройдена (Layer 4 — проверка QA) [class=code_defect]: - [edge_concern] Пробный запуск (без оптимизации, период формирования=126): закрыто позиций — 178, PF 0.98, коэффициент Шарпа -0.02, -0.125% на позицию; после расходов на заём преимущество близко к нулю. Из-за указанного ниже дефекта запуск не измерил гипотезу в заданном виде: когорты были неполными и недостаточного размера. После исправления измерь заново, прежде чем делать выводы. Время выполнения также было большим: 249 с для 1,653 дневных баров по 8 инструментам; для полной истории прогнозируется около 949 с. Для цикла O(8) на бар это неожиданно медленно — нужна профилировка. - [критично] При ротации когорта из 2 длинных и 2 коротких позиций не формируется заново. В calculate_signal для старых позиций отправляется close_position, после чего сразу вызывается _open_pending(). Симулируемая биржа бэктеста ставит команды в очередь (use_message_queue), а у USEQ значение latency_ms=100, поэтому закрытия ещё не исполнены. (а) Новые выбранные инструменты: `gross` всё ещё включает всю старую когорту (около 0.6 x капитала), поэтому `budget - gross` составляет около 0. В результате номинал равен 0 (бит CLEARED, поэтому позиция пропускается на весь цикл из 21 сессий) либо остаётся лишь остаток (позиция недостаточного размера). (б) Инструменты, которые удерживались и выбраны снова: их бит сохраняется (`continue`), и предполагается повторный вход из on_position_closed. Но закрытие исполняется через rotation_ts + 100 мс, поэтому `self.clock.timestamp_ns() != self._rotation_ts` выполняет ранний возврат, а до следующей ротации _open_pending() больше не вызывается. Эти инструменты остаются без позиции на весь цикл. Итог: после первой ротации портфель чередует полные и частичные или пустые когорты и не сбалансирован по долларовой стоимости между длинными и короткими позициями. Это видно в песочнице. Ожидалось примерно 65 ротаций × 4 позиций ≈ 260 позиций, но записано 178, а avg_position_pct составил 13.7% при целевом значении 15%. Торгуемая гипотеза (длинные позиции по верхним -2, короткие по нижним -2, каждые 21 сессий) не соответствует заданной. (строка 168) — исправление: используй постоянное состояние ротации вместо сопоставления по точной временной метке. Сохраняй ожидающие биты до следующей ротации. При расчёте `gross` исключай позиции с ожидающим закрытием только на сокращение (они заменяются). Рассчитывай размер каждой позиции от _rotation_equity * leg_equity_fraction. В on_position_closed и, как резервный вариант, в начале каждого основного бара вызывай _open_pending, когда остаются биты и закрытия предыдущей когорты больше не ожидают исполнения. Удали ограничение `clock == _rotation_ts` или замени его на «тот же интервал ротации». Сбрасывай бит только после фактической отправки ордера либо если размер позиции - [предупреждение] Остановка из-за просадки портфеля (15%) действует до конца запуска и не снимается. В бэктесте за 25 лет одна просадка может навсегда прекратить торговлю и уменьшить выборку. Это соответствует описанию, поэтому это лишь предупреждение, но аналитику нужно знать, сработала ли остановка. (строка 133) — исправление: записывай дату остановки. Если гипотеза это допускает, рассмотри возобновление при следующей ротации после восстановления капитала или паузу, измеряемую в сессиях. - [предупреждение] Для дат вне предоставленной таблицы закрытий XNYS используется подсчёт будних дней. При таком резервном подсчёте нумерация сессий другая, поэтому на границе таблицы скачет фаза ротации (до 1998 / после 2030). На песочницу и окно бэктеста это не влияет. (строка 117) — исправление: пока допустимо. Расширь таблицу или используй src/data/market_calendar, если её можно импортировать, чтобы фаза оставалась непрерывной.

Реализация

Стратегия моментума секторов на 8 SPDR секторальных ETF, использующая дневные бары USEQ. Каждый ETF ранжируется по доходности за период формирования: отношению цены в конце периода к цене в начале за formation_sessions, без учёта последних 21 сессий. Каждые 21 XNYS сессий по календарному графику стратегия закрывает все позиции, покупает 2 ETF с самым высоким рейтингом и открывает короткие позиции по 2 ETF с самым низким рейтингом. На каждую позицию приходится 15% капитала (60% валового объёма). Для каждой позиции на площадке выставлен стоп 12%; просадка портфеля на 15% останавливает торговлю.

Итерация 3 устраняет сбой уровня 2. У синтетических тестовых баров временные метки отсутствуют в предоставленной таблице закрытий XNYS, и на них стратегия выдавала ValueError. Теперь даты вне таблицы сопоставляются с номером сессии, рассчитанным по будним дням. Этот номер зависит только от временной метки бара, поэтому перезапуски, бэктест и торговля на бумаге по-прежнему согласуются. Для дат внутри таблицы сохраняется точная сетка сессий XNYS. Больше ничего не изменилось.

Результаты верификации

Верификация не пройдена (Layer 4 — проверка QA) [class=code_defect]: - [edge_concern] Пробный запуск (без оптимизации, период формирования=126): закрыто позиций — 178, PF 0.98, коэффициент Шарпа -0.02, -0.125% на позицию; после расходов на заём преимущество близко к нулю. Из-за указанного ниже дефекта запуск не измерил гипотезу в заданном виде: когорты были неполными и недостаточного размера. После исправления измерь заново, прежде чем делать выводы. Время выполнения также было большим: 249 с для 1,653 дневных……Показать большеСвернуть

Верификация не пройдена (Layer 4 — проверка QA) [class=code_defect]: - [edge_concern] Пробный запуск (без оптимизации, период формирования=126): закрыто позиций — 178, PF 0.98, коэффициент Шарпа -0.02, -0.125% на позицию; после расходов на заём преимущество близко к нулю. Из-за указанного ниже дефекта запуск не измерил гипотезу в заданном виде: когорты были неполными и недостаточного размера. После исправления измерь заново, прежде чем делать выводы. Время выполнения также было большим: 249 с для 1,653 дневных баров по 8 инструментам; для полной истории прогнозируется около 949 с. Для цикла O(8) на бар это неожиданно медленно — нужна профилировка. - [критично] При ротации когорта из 2 длинных и 2 коротких позиций не формируется заново. В calculate_signal для старых позиций отправляется close_position, после чего сразу вызывается _open_pending(). Симулируемая биржа бэктеста ставит команды в очередь (use_message_queue), а у USEQ значение latency_ms=100, поэтому закрытия ещё не исполнены. (а) Новые выбранные инструменты: `gross` всё ещё включает всю старую когорту (около 0.6 x капитала), поэтому `budget - gross` составляет около 0. В результате номинал равен 0 (бит CLEARED, поэтому позиция пропускается на весь цикл из 21 сессий) либо остаётся лишь остаток (позиция недостаточного размера). (б) Инструменты, которые удерживались и выбраны снова: их бит сохраняется (`continue`), и предполагается повторный вход из on_position_closed. Но закрытие исполняется через rotation_ts + 100 мс, поэтому `self.clock.timestamp_ns() != self._rotation_ts` выполняет ранний возврат, а до следующей ротации _open_pending() больше не вызывается. Эти инструменты остаются без позиции на весь цикл. Итог: после первой ротации портфель чередует полные и частичные или пустые когорты и не сбалансирован по долларовой стоимости между длинными и короткими позициями. Это видно в песочнице. Ожидалось примерно 65 ротаций × 4 позиций ≈ 260 позиций, но записано 178, а avg_position_pct составил 13.7% при целевом значении 15%. Торгуемая гипотеза (длинные позиции по верхним -2, короткие по нижним -2, каждые 21 сессий) не соответствует заданной. (строка 168) — исправление: используй постоянное состояние ротации вместо сопоставления по точной временной метке. Сохраняй ожидающие биты до следующей ротации. При расчёте `gross` исключай позиции с ожидающим закрытием только на сокращение (они заменяются). Рассчитывай размер каждой позиции от _rotation_equity * leg_equity_fraction. В on_position_closed и, как резервный вариант, в начале каждого основного бара вызывай _open_pending, когда остаются биты и закрытия предыдущей когорты больше не ожидают исполнения. Удали ограничение `clock == _rotation_ts` или замени его на «тот же интервал ротации». Сбрасывай бит только после фактической отправки ордера либо если размер позиции - [предупреждение] Остановка из-за просадки портфеля (15%) действует до конца запуска и не снимается. В бэктесте за 25 лет одна просадка может навсегда прекратить торговлю и уменьшить выборку. Это соответствует описанию, поэтому это лишь предупреждение, но аналитику нужно знать, сработала ли остановка. (строка 133) — исправление: записывай дату остановки. Если гипотеза это допускает, рассмотри возобновление при следующей ротации после восстановления капитала или паузу, измеряемую в сессиях. - [предупреждение] Для дат вне предоставленной таблицы закрытий XNYS используется подсчёт будних дней. При таком резервном подсчёте нумерация сессий другая, поэтому на границе таблицы скачет фаза ротации (до 1998 / после 2030). На песочницу и окно бэктеста это не влияет. (строка 117) — исправление: пока допустимо. Расширь таблицу или используй src/data/market_calendar, если её можно импортировать, чтобы фаза оставалась непрерывной.

История итераций

Верификация не пройдена (Layer 1.5 — согласованность гипотезы и конфигурации) [class=hypothesis_mismatch]: - optimization_plan фиксирует плечо на уровне 1.0

Верификация не пройдена (Layer 2 — синтетические сценарии): Использованные параметры: ['leverage', 'min_notional', 'skip_sessions', 'stop_loss_pct', '_optimization_plan', 'formation_sessions', 'max_gross_exposure', 'rebalance_sessions', 'leg_equity_fraction', 'xnys_closure_offsets', 'selection_count_per_side', 'portfolio_drawdown_stop_pct'] Убедись, что __init__ задаёт все атрибуты из self.parameters.get(). - steady_uptrend: ValueError: Сессия находится вне переданного календаря XNYS; обнови метаданные календаря……Показать большеСвернуть

Верификация не пройдена (Layer 2 — синтетические сценарии): Использованные параметры: ['leverage', 'min_notional', 'skip_sessions', 'stop_loss_pct', '_optimization_plan', 'formation_sessions', 'max_gross_exposure', 'rebalance_sessions', 'leg_equity_fraction', 'xnys_closure_offsets', 'selection_count_per_side', 'portfolio_drawdown_stop_pct'] Убедись, что __init__ задаёт все атрибуты из self.parameters.get(). - steady_uptrend: ValueError: Сессия находится вне переданного календаря XNYS; обнови метаданные календаря (временная метка бара: 1735705980000) - steady_downtrend: ValueError: Сессия находится вне переданного календаря XNYS; обнови метаданные календаря (временная метка бара: 1735705980000) - flat_ranging: ValueError: Сессия находится вне переданного календаря XNYS; обнови метаданные календаря (временная метка бара: 1735705980000) - volatility_spike: ValueError: Сессия находится вне переданного календаря XNYS; обнови метаданные календаря (временная метка бара: 1735705980000) - zero_volume: ValueError: Сессия находится вне переданного календаря XNYS; обнови метаданные календаря (временная метка бара: 1735705980000) - price_gap: ValueError: Сессия находится вне переданного календаря XNYS; обнови метаданные календаря (временная метка бара: 1735705980000)

Сводка по итогам

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

После 3 итераций запуск был остановлен в цикле верификации, поскольку проверка Layer 4 QA выявила критический дефект кода. При ротации когорта из 2 длинных и 2 коротких позиций не формировалась заново: закрытия ещё ожидали исполнения при задержке 100 мс USEQ, когда рассчитывался размер новых позиций и проверялся запрет повторного входа с точной временной меткой. В результате позиции пропускались или открывались в недостаточном размере (178 позиций вместо ожидаемых примерно 260, средний размер позиции 13.7% при целевом 15%), а портфель не был сбалансирован по долларовой стоимости.

Открыть длинные позиции по двум ETF секторов US с самыми высокими многомесячными доходностями за период формирования и короткие — по двум с самыми низкими (из восьми), пропустив последние 21 сессий; ребалансировать каждые 21 торговых сессий, чтобы уловить среднесрочный моментум секторов отдельно от краткосрочного разворота.

Полный бэктест или оптимизация не проводились. Единственное свидетельство о результатах — неоптимизированный дымовой тест в песочнице QA (формирование=126): 178 закрытых позиций, PF 0.98, коэффициент Шарпа -0.02 и -0.125% на позицию. QA заявил, что это не проверяло гипотезу, поскольку когорты были неполными и слишком малыми.

Причина отказа

verification_loop: проверка не пройдена (уровень 4 — проверка QA) [класс=code_defect]: - [edge_concern] Дымовой тест (неоптимизированный, формирование=126): 178 закрытых позиций, PF 0.98, коэффициент Шарпа -0.02, -0.125% на позицию; после учета затрат на заимствование преимущество близко к нулю. Дефект ниже означает, что тест не проверял гипотезу в заданном виде, поскольку когорты были неполными и слишком малыми. После исправления повторите измерение, прежде чем делать какие-либо выводы. Время выполнения также……Показать большеСвернуть

verification_loop: проверка не пройдена (уровень 4 — проверка QA) [класс=code_defect]: - [edge_concern] Дымовой тест (неоптимизированный, формирование=126): 178 закрытых позиций, PF 0.98, коэффициент Шарпа -0.02, -0.125% на позицию; после учета затрат на заимствование преимущество близко к нулю. Дефект ниже означает, что тест не проверял гипотезу в заданном виде, поскольку когорты были неполными и слишком малыми. После исправления повторите измерение, прежде чем делать какие-либо выводы. Время выполнения также было большим: 249 с для 1,653 дневных баров по 8 инструментам, при этом для полной истории прогнозируется ~949 с. Для цикла O(8) на бар это неожиданно медленно, код следует профилировать. - [критично] При ротации когорта из 2 длинных/2 коротких позиций не формируется заново. В calculate_signal для прежних позиций отправляется close_position, затем сразу вызывается _open_pending(). В бэктесте SimulatedExchange ставит команды в очередь (use_message_queue), а USEQ имеет latency_ms=100, поэтому ни одно из этих закрытий еще не исполнено. (a) Новые выбранные инструменты: `gross` по-прежнему включает всю старую когорту (~0.6 x капитала), поэтому `budget - gross` составляет ~0. Из-за этого номинал 0 (бит CLEARED, поэтому позиция исключается на весь цикл из 21 сессий) либо остается только остаточная позиция (недостаточный размер). (b) Инструменты, которые удерживались и выбраны повторно: их бит сохраняется (`continue`), и предполагается, что они повторно откроются из on_position_closed. Но закрытие исполняется через rotation_ts + 100 мс, поэтому `self.clock.timestamp_ns() != self._rotation_ts` возвращается раньше, а до следующей ротации _open_pending не вызывается. Эти инструменты остаются без позиции на весь цикл. В итоге после первой ротации портфель чередует полные и частичные либо пустые когорты и не сбалансирован по долларовому объему между длинной и короткой сторонами. Это видно в песочнице. Приблизительно 65 ротаций × 4 позиций ≈ 260 ожидаемых позиций, но записано 178, а avg_position_pct составил 13.7% против целевого значения 15%. Гипотеза (верхние -2 в длинную / нижние -2 в короткую позицию каждые 21 сессий) не соответствует фактическим сделкам. (строка 168) — исправление: используйте постоянное состояние ротации вместо сопоставления точных временных меток. Сохраняйте ожидающие биты до следующей ротации. При расчете `gross` исключайте позиции, по которым ожидается закрытие только на сокращение (их заменяют). Рассчитывайте размер каждой стороны на основе _rotation_equity * leg_equity_fraction. В on_position_closed, а также в качестве резервного варианта в начале каждого основного бара вызывайте _open_pending, когда остаются биты и закрытие позиций предыдущей когорты еще не выполняется. Удалите проверку `clock == _rotation_ts` или замените ее на «тот же интервал ротации». Сбрасывайте бит только после фактической отправки ордера или когда размер равен - [предупреждение] Остановка при просадке портфеля (15%) действует до конца запуска и не переактивируется. В бэктесте за 25 лет одна просадка может навсегда остановить торговлю, что уменьшит выборку. Это соответствует заявленному описанию, поэтому это лишь предупреждение, однако аналитику следует знать, срабатывала ли остановка. (строка 133) — исправление: записывайте дату остановки. Если гипотеза это допускает, рассмотрите повторную активацию при следующей ротации после восстановления капитала или период охлаждения, измеряемый в сессиях. - [предупреждение] Для дат вне предоставленной таблицы закрытий XNYS используется подсчет будних дней. При таком способе нумерация сессий отличается, поэтому на границе таблицы происходит скачок фазы ротации (до 1998 / после 2030). На песочницу или окно бэктеста это не влияет. (строка 117) — исправление: пока допустимо. Расширьте таблицу или используйте src/data/market_calendar, если этот модуль можно импортировать, чтобы сохранить непрерывность фазы.

Анализ

Стратегия следования за импульсом — периоды формирования

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