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

Стратегии

Точный перенос стратегии Oil Money от je-suis-tm: 50-дневный OLS для XOP на USO, условие валидности R2>0.7, LONG при закрытии выше подобранного+2 сигма / SHORT ниже подобранного-2 сигма, выход по счётчику через 10 баров, симметричный стоп 3.7% (XOP.USEQ торгуется, USO.USEQ регрессор, 1-DAY, лонг-шорт, неизменные параметры по умолчанию)

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

OilMoneyXopUsoRegressionBreakout

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

Эта стратегия переносит правило пробоя остатка Oil Money на XOP и USO с неизменными исходными параметрами; после 2 итераций она воспроизвела 144 сделок предварительного исследования. Результат на всей истории был слабым: +21.3%, коэффициент Шарпа 0.139, PF 1.053, просадка 35.4% выше заранее установленного лимита 30%. Вневыборочные данные дали отрицательный результат: коэффициент Шарпа отложенной выборки -0.37, около 27% потеряно за 2025-26, а сигнал IC неотличим от шума. Аналитик отказался от этой операционализации как от premise_falsified, не отвергая исходную версию автора на NOKJPY/Brent.

Гипотеза

Это точный перевод «Oil Money Trading backtest» от je-suis-tm (репозиторий quant-trading, Apache-2.0, коммит 611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5). Вся заслуга за стратегию принадлежит автору. Код представляет собой пригодную для торговли стратегию (signal_generation генерирует длинные/короткие сигналы). Остальные функции (oil_money, plot, profit и переоценка портфеля по рынку) служат каркасом и визуализацией. INSTRUMENT MAPPING, единственная неизбежная замена: в оригинале регрессируют NOKJPY (y, торгуемый……Показать большеСвернуть

Это точный перевод «Oil Money Trading backtest» от je-suis-tm (репозиторий quant-trading, Apache-2.0, коммит 611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5). Вся заслуга за стратегию принадлежит автору. Код представляет собой пригодную для торговли стратегию (signal_generation генерирует длинные/короткие сигналы). Остальные функции (oil_money, plot, profit и переоценка портфеля по рынку) служат каркасом и визуализацией. INSTRUMENT MAPPING, единственная неизбежная замена: в оригинале регрессируют NOKJPY (y, торгуемый актив) на нефть Brent (x, регрессор). На платформе нет FX или данных Brent. Ближайший торгуемый аналог с той же экономической структурой (связанный с нефтью актив регрессируют на цену нефти, торгуя связанным с нефтью активом) и историей за несколько десятилетий: y = XOP.USEQ (S&P Oil & Gas E&P ETF, торгуемый актив, основной ряд), x = USO.USEQ (фонд сырой нефти, только регрессор/сигнал, никогда не торгуется). Для обоих доступны дневные бары с середины -2006, около 5,100 общих сессий. Площадка: USEQ, маржа Reg-T, короткие позиции разрешены, комиссия нулевая, смоделированы спред и рыночное воздействие около 0.02-0.05% за полный цикл сделки. Таймфрейм: бары 1-DAY, как в исходных дневных данных. ALGORITHM, построчно из signal_generation (значения по умолчанию: holding_threshold=10, стоп=0.5, rsquared_threshold=0.7, train_len=50). Состояние: позиция в {-1,0,1}, trained=False, счётчик=0, зафиксированные коэффициенты (a,b), sigma, entry_price. Цикл начинается с индекса бара train_len, поэтому первые 50 выровненных баров — период прогрева. На каждом выровненном дневном баре i (закрытия XOP и USO за одну сессию доступны): (A) Если holding != 0: (A1) если counter > holding_threshold, выйти (сигнал = -holding), задать holding=0, trained=False, counter=0 и больше ничего не делать на этом баре (NOTHING; продолжить, без повторной подгонки и входа). (A2) Иначе, если |close_y[i] - entry_price| >= stop, выйти тем же способом и продолжить. (A3) Иначе counter += 1. Учти, что счётчик начинается со значения 0 на баре входа и увеличивается после проверок, поэтому временной выход срабатывает на 12-м баре после бара входа. (B) Если holding == 0: (B1) если модель ещё не обучена, подогнать OLS с константой, y ~ a + b*x, на PREVIOUS 50 барах [i-50, i), исключая бар i. Если R-квадрат > rsquared_threshold (строго), задать trained=True, зафиксировать a и b и установить sigma равной стандартному отклонению остатков в выборке (ddof=0), рассчитанному для генеральной совокупности. Если условие для R-квадрата не выполнено, модель остаётся необученной; на следующем баре подгонка повторяется со сдвинутым окном. (B2) Если модель обучена (включая бар, на котором её только что подогнали), forecast_i = a + b*close_x[i], где используется закрытие USO бара CURRENT и коэффициенты FROZEN. Если close_y[i] > forecast_i + 2*sigma, открыть LONG (holding=1, entry_price=close_y[i]). Иначе, если close_y[i] < forecast_i - 2*sigma, открыть SHORT (holding=-1, entry_price=close_y[i]). Обрати внимание на направление: в исходнике открывают LONG, когда торгуемый актив ABOVE верхнюю границу, и SHORT, когда он BELOW нижнюю. Это пробой остатка с торговлей по направлению движения, а не ставка на возврат. Переведи точно, не исправляй логику на возврат к среднему. Пока модель обучена, а позиция не открыта, её никогда не подгоняют повторно. Те же зафиксированные a, b и sigma применяются к каждому новому x до сделки и последующего выхода (при выходе trained=False, повторная подгонка выполняется на следующем баре). Полосы 1-sigma и их сужение после входа влияют только на график и не меняют логику торговли, поэтому опусти их. PARAMETER TRANSLATION: stop=0.5 задан в абсолютных пунктах NOKJPY. За период выборки автора NOKJPY торговался примерно по 12-14, поэтому 0.5 пунктов — около 3.7% цены. В переносе используется масштабируемое значение stop_fraction=0.037 от entry_price (|close/entry_price - 1| >= 0.037), поскольку 0.5 долларов для актива ETF стоимостью около $40-150 означали бы другое правило. Все остальные значения по умолчанию сохранены без изменений. SIZING: в оригинале покупают фиксированное число акций capital0//max(close по ряду WHOLE), что использует будущие данные и заменено. В переносе размер каждой позиции составляет position_fraction=0.95 текущего капитала счёта, выраженного целым числом акций XOP (1x, примерно как исходный полный номинал), одинаково для длинных и коротких позиций. EXECUTION: в исходнике сделка исполняется по цене закрытия бара сигнала. На платформе сигналы оцениваются при дневном закрытии, а рыночный ордер исполняется при открытии следующей сессии. Проверки стопа и времени используют entry_price = цену закрытия бара сигнала, как в исходнике (df[y][signals!=0].iloc[-1]). PRE-STUDY (предварительный расчёт руководителя исследования в pandas с точным повторением цикла на закрытиях из каталога, 2006-2026, исполнение от закрытия к закрытию, без издержек; не проверен): 144 сделок, среднее +0.38%/сделку, доля прибыльных сделок 56%, фактор прибыли 1.16. Проверка устойчивости с XLE в качестве y: 137 сделок, +0.22%/сделку, PF 1.10. Преимущество невелико, поэтому решающим будет бэктест на платформе.

Пользователь запросил точный перенос, поэтому все параметры по умолчанию зафиксированы, а набор настраиваемых параметров пуст. Адаптированы только инструмент и единицы стопа; обоснование приведено выше. Этот выбор заполняет недостаточно представленные категории: площадка USEQ с 6%, структура сигналов для пар/нескольких инструментов, лонг-шорт при ограничении 17% против 55% только для лонга, а также актив (XOP/USO) практически без истории в корпусе. Он не похож на перечисленные неудачные стратегии: здесь нет Dual Thrust, z-score, RSI2 или логики Черепах, а сигнал основан на проверке валидности межрыночной регрессии. Порог просадки установлен на 30%, поскольку для одиночной позиции 1x в ETF с годовой волатильностью около 40% нужен реалистичный уровень.

premise_falsified: Область вывода: отклоняется операционализация стратегии «Oil Money» je-suis-tm руководителем исследования XOP-на-USO, выполненная с зафиксированными исходными настройками (50-дневный OLS, фильтр R2 > 0.7, пробой остатка в направлении движения на 2 сигма, выход против движения через 10 баров, стоп 3.7%). Оригинальная стратегия автора NOKJPY-на-Brent не отклоняется. Достаточная выборка за всю историю не показывает преимущества после издержек: 144 сделок с 2006 по 2026, коэффициент Шарпа 0.139 (CI……Показать большеСвернуть

premise_falsified: Область вывода: отклоняется операционализация стратегии «Oil Money» je-suis-tm руководителем исследования XOP-на-USO, выполненная с зафиксированными исходными настройками (50-дневный OLS, фильтр R2 > 0.7, пробой остатка в направлении движения на 2 сигма, выход против движения через 10 баров, стоп 3.7%). Оригинальная стратегия автора NOKJPY-на-Brent не отклоняется. Достаточная выборка за всю историю не показывает преимущества после издержек: 144 сделок с 2006 по 2026, коэффициент Шарпа 0.139 (CI -0.31 до 0.57), PF 1.053, коэффициент выплат 0.97. -0.034 исполненного сигнала составляет IC (t = -0.7). Вневыборочные данные дают отрицательный результат: коэффициенты Шарпа во внешних окнах walk-forward-анализа OOS равны -0.09, -0.41, -0.04 и +0.74. На достаточно мощной контрольной выборке (2022-09–2026-10, 32 сделок) коэффициент Шарпа равен -0.37, а 2025-26 принёс убыток около 27%. Максимальная просадка составляет 35.4% при заранее зарегистрированном пределе 30% и превысила его в двух последних обучающих окнах, поэтому процедура завершилась вердиктом insufficient_candidate_coverage (отклонено). Стратегия также убыточна в терциле высокой волатильности. Сравнение с минимальным уровнем комиссий: avg_trade_return_pct составляет 0.302% при минимальном уровне USEQ 0.05%, поэтому причина не в том, что преимущество не покрывает комиссии. Все параметры зафиксированы в контракте как при точном переносе исходной стратегии, поэтому возможности для итераций нет. Вариант устойчивости XLE показал более слабый результат в предварительном исследовании (PF 1.10 до вычета издержек), поэтому переформулировка как revise_hypothesis не рекомендуется. Опасений по поводу преимущества QA не заявлялось. Ошибки в учёте или данных не утверждаются.

Реализация

Точный перенос стратегии Oil Money от je-suis-tm на XOP.USEQ относительно USO.USEQ с барами по 1 дней. Пока позиция не открыта и модель ещё не обучена, подбирается 50-барная OLS для XOP на USO по предыдущим барам; модель принимается только если R2 > 0.7. Затем a, b и стандартное отклонение остатков для генеральной совокупности фиксируются. Открывается LONG, если закрытие XOP выше подобранного значения плюс 2 сигма, и SHORT, если оно ниже подобранного значения минус 2 сигма; оба значения рассчитываются по текущему закрытию USO. Позиция закрывается по правилу времени со счётчиком в 10 баров (12-й бар после входа) или при симметричном движении 3.7% от закрытия бара сигнала; после каждого выхода модель подбирается заново. Рыночные заявки исполняются на открытии следующей сессии.

IMPLEMENTATION FIX, правила не менялись; все значения, заданные контрактом, сохранены. (1) Размер позиции с учётом маржи: акции = min(floor(equity*0.95*lev/close), floor(equity_mtm*lev*0.97/(close*1.03))). Буфер разрыва 3% учитывает исполнение на следующем открытии, а 0.97 — спред, поэтому начальная проверка маржи платформы больше не должна отклонять вход. Это ограничение исполнения; position_fraction остаётся 0.95. На практике срабатывает ограничение, и на каждый вход приходится около 94.2% капитала. (2) Любая……Показать большеСвернуть

IMPLEMENTATION FIX, правила не менялись; все значения, заданные контрактом, сохранены. (1) Размер позиции с учётом маржи: акции = min(floor(equity*0.95*lev/close), floor(equity_mtm*lev*0.97/(close*1.03))). Буфер разрыва 3% учитывает исполнение на следующем открытии, а 0.97 — спред, поэтому начальная проверка маржи платформы больше не должна отклонять вход. Это ограничение исполнения; position_fraction остаётся 0.95. На практике срабатывает ограничение, и на каждый вход приходится около 94.2% капитала. (2) Любая заявка на вход, отмеченная как обрабатываемая, но не отправленная (всё ещё INITIALIZED, например, отклонённая из-за маржи), удаляется из _inflight_entries в начале каждого бара; поэтому единичный отказ больше не сможет блокировать все последующие входы (ранее после одного отказа из-за маржи 135 заявок были потеряны как находящиеся в обработке). Заявки, ожидающие открытия бумажной торговой сессии, сохраняются. (3) Сверка состояния: при входе задаётся _pending_entry. На следующем выровненном баре, когда исполнение на следующем открытии уже произошло, стратегия проверяет, открыта ли позиция. Если нет, она сбрасывает holding=0, trained=False, counter=0 и обрабатывает этот бар как бар выхода в источнике (без повторной подгонки и входа), поэтому отклонённая заявка не запустит фантомный счётчик на 12 баров. Диагностические счётчики entry_not_filled и pruned_unsent_entry записываются в entry_diag. Логика сигнала, подгонки, полос, стопа и выхода по времени побайтно идентична итерации 1. Полный исторический бэктест здесь не запускался: проверка входит в обязанности конвейера. Ожидаемый результат — сделки, распределённые по 2006-2026 (предварительное исследование предполагает около 140), при dropped_entry_inflight около 0. Улучшения 2-5 (варианты периода удержания, стоп только по убытку, хедж USO, повторная подгонка при устаревании данных) меняют правила или создают родственные гипотезы, поэтому здесь не реализованы.

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

Возвращать 1 из min_bars_required(), поскольку стратегия сама проверяет условие len(_pairs) >= train_len. Другой вариант — заполнить _pairs выровненной историей, уже находящейся в self._bars и self._extra_bars[x], при первом вызове.

min_bars_required() возвращает train_len+1 = 51. Базовый шаблон пропускает calculate_signal, пока не накоплено 51 баров основного ряда, а _pairs заполняется только внутри calculate_signal. Поэтому первые 50 баров отбрасываются, а первая подгонка OLS происходит примерно на баре 101, а не на баре 51, как в исходнике (`for i in range(train_len, len(df))`). Правила торговли не меняются, но фактический период прогрева составляет около 100 баров. Это превышает установленное контрактом значение оценки warmup_bars=60, поэтому примерно первые 40 баров каждого окна reset_with_warmup не могут торговать. Такая же задержка возникает после каждого перезапуска реальной или бумажной торговли.

Изменения не нужны. Проверь, что в полном бэктесте entry_not_filled == 0 и dropped_entry_inflight == 0.

Сверка фиктивного входа — дополнение, которого нет в исходнике. Если входящий ордер отклонён, на следующем выровненном баре holding, trained и counter сбрасываются; этот бар считается баром выхода (без повторной подгонки и входа). Это разумный учёт исполнения. В бэктесте TradeTick на следующем открытии исполняет ордер до запуска on_bar бара N+1 (src/backtesting/next_open.py), поэтому такой случай должен возникать только при реальном отказе. Следи за entry_diag.entry_not_filled: ненулевое значение означает, что размер позиции всё ещё упирается в проверку маржи.

В неоптимизированном тестовом окне (1592 дневных баров, около 6.3 лет) стратегия совершила 49 закрытых сделок. Средняя доходность сделки составила -0.167% от номинала при минимальном пороге около 0.05% USEQ; PF 0.885, общая доходность — -14.3%, максимальная просадка — 30.3% при ограничении контракта 30%. Само изменение тарифа 2025 обошлось в 13.2%. Предварительное исследование руководителя проекта (+0.38% на сделку, PF 1.16, исполнение по ценам закрытия, без издержек) не учитывало исполнение на открытии……Показать большеСвернуть

В неоптимизированном тестовом окне (1592 дневных баров, около 6.3 лет) стратегия совершила 49 закрытых сделок. Средняя доходность сделки составила -0.167% от номинала при минимальном пороге около 0.05% USEQ; PF 0.885, общая доходность — -14.3%, максимальная просадка — 30.3% при ограничении контракта 30%. Само изменение тарифа 2025 обошлось в 13.2%. Предварительное исследование руководителя проекта (+0.38% на сделку, PF 1.16, исполнение по ценам закрытия, без издержек) не учитывало исполнение на открытии следующего дня, заём акций для коротких продаж и рыночное воздействие. Все параметры зафиксированы, поэтому вывод определит бэктест на полной истории. Первый вопрос: сохраняется ли преимущество при исполнении на открытии следующего дня по сравнению с исполнением по той же цене закрытия, что и в источнике (вход на остаточном пробое может отдать ночное движение)? Также проверь реструктуризацию и обратное дробление USO (2020) в ряду регрессора. Подразумеваемый размер позиции — около 94% капитала на сделку, поэтому показатели просадки рассчитаны для полного планового размера (L211 не проблема).

Обзор бэктеста

Шарп
0,14
Общая доходность
21,34%
Максимальная просадка
35,41%
Всего сделок
144
Доля прибыльных
52,1%
Профит-фактор
1,05

Предыдущее исправление реализации сработало. Теперь стратегия торгует на всей истории 2006-2026 (годовые доходности рассчитаны для каждого года): 144 закрытых сделок (80 длинных, 64 коротких). Это совпадает с числом 144 в предварительном расчёте руководителя исследования, значит, машина состояний реализована согласно описанию. entry_not_filled и denied_margin составляют по 5 и корректно исключены.

Экономические показатели превышают минимальный порог USEQ на надёжной выборке: avg_trade_return_pct составляет 0.302% при минимуме 0.05% (предпочтительно 0.10%). Ожидаемая прибыль — +$148 на сделку, PF 1.053, общая доходность +21.3% после моделирования воздействия в размере $9.1 тыс. и учёта стоимости займа $751. Ошибок учёта нет: издержки включены в движок, величина остатков составляет около 0.

end_unrealized_pct составляет 0, поэтому заголовочный показатель полностью реализован. Размер позиции — около 94% капитала, как предусмотрено контрактом.

Преимущество очень мало и статистически неразличимо. Коэффициент Шарпа — 0.139 (95% CI -0.31 до 0.57), PSR составляет 0.60, а IC исполненных сигналов — -0.034 (t = -0.7). PF от 1.053 намного ниже минимального уровня качества 1.3.

Максимальная просадка составляет 35.4% при заранее зарегистрированном ограничении контракта 30%; длительность просадки — 3,506 дней. В терциле с высокой волатильностью убыток составляет 25.3%. 2025 (-18.4%) и 2026 YTD (-9.5%) — худшие годы, поэтому опасения QA по поводу недавнего рыночного режима отчасти подтверждаются на полной истории. На уровне отдельных сделок полная история этого не подтверждает: здесь +0.30% против -0.167% в коротком тестовом периоде.

Смоделированное рыночное воздействие составляет долю 29% от валовой прибыли. Ёмкость — около $1.18 млн.

Исполнение на следующем открытии ухудшает преимущество по сравнению с исполнением по тому же закрытию, что и в источнике. В предварительном исследовании прибыль на сделку составляла +0.38% при PF 1.16 без издержек; в этом запуске — +0.30% при PF 1.05. Это согласуется с потерей части прибыли за ночь при входе на пробое. Этот механизм отдельно не проверялся.

QA спросил об обратном сплите акций компании USO 2020. Проверка входных данных не выявила пропущенных или некорректных баров; регрессия повторно подгоняется на скользящем окне из 50 баров, но я независимо не подтвердил, что ряд USO скорректирован на сплит по 2020-04.

Анализ

Реализация точная. Число закрытых сделок — 144 (80 длинных, 64 коротких) — точно совпадает с числом 144 в предварительном исследовании руководителя исследования. Учёт корректен, end_unrealized_pct составляет 0, признаков артефакта нет.

Порог комиссии пройден: avg_trade_return_pct составляет 0.302% при пороге USEQ +0.05% (предпочтительно 0.10%); издержки не являются причиной неудачи.

holding_threshold, band_sigma, кредитное плечо и буферы размера позиции находятся на широких плато при анализе чувствительности.

На истории длиной 20 лет преимущества нет. Коэффициент Шарпа за весь исторический период составляет 0.139 (95% CI -0.31–0.57), а PF равен 1.053. Коэффициент прибыли равен 0.97 при доле прибыльных сделок 52%, что означает практически нулевое математическое ожидание. Фактор восстановления равен 0.60, а коэффициент Кальмара — 0.027.

Максимальная просадка составляет 35.4%, что выше заранее зарегистрированного лимита 30%; просадка длилась 3,506 дней. Два последних обучающих окна walk-forward-анализа отклонены из-за нарушения ограничения 30% DD (34.7% и 35.4%), поэтому для последнего окна нет допустимого кандидата. Результат — insufficient_candidate_coverage (отклонено).

Внешние коэффициенты Шарпа walk-forward-анализа OOS: -0.09, -0.41, -0.04 и +0.74, затем два окна с нулевым результатом. Положительный результат получен лишь в 1 из 4 оценённых окон.

Отложенная выборка (2022-09-18 — 2026-10-09, 1,482 дней, 32 сделок; статистическая мощность достаточна) показала коэффициент Шарпа -0.37 и DD 29.4%. 2025 составляет -18.4%, а 2026 — -9.5% YTD.

IC исполненного прогноза составляет -0.034 (t = -0.7, n = 420), поэтому сигнал пробоя регрессионного остатка не несёт информации.

Стратегия убыточна в терциле повышенной волатильности (коэффициент Шарпа -0.14, -25.3%). Самый тяжёлый стрессовый период — тарифный шок 2025 при -13.2%.

Смоделированное рыночное воздействие составляет долю 29% от валовой прибыли.

Анализ чувствительности выявил резкие провалы для train_len (25 и 63 отрицательны; 45 и 51 близки к нулю) и stop_fraction. Базовый вариант опирается на шум, а не на устойчивое плато.

Заранее зарегистрированные прогнозы: выполнено 3 из 6. Доходность (21% против 40%), PF (1.05 против 1.10) и DD (35% против 30%) не оправдались.

только рекомендательный характер

CONTRACT KEPT — аналитик попросил изменить position_fraction, что запрещено исследовательским контрактом FIXES; руководитель исследований не стал вносить эти изменения (руководитель исследований оставил их без изменений: собственные данные аналитика не требуют изменения правил: position_fraction=0.95 остаётся зарегистрированным значением. Это указанная для портирования замена исходного расчёта размера позиции на весь капитал, в котором использовались данные из будущего. Сбой — дефект реализации: ордер,……Показать большеСвернуть

CONTRACT KEPT — аналитик попросил изменить position_fraction, что запрещено исследовательским контрактом FIXES; руководитель исследований не стал вносить эти изменения (руководитель исследований оставил их без изменений: собственные данные аналитика не требуют изменения правил: position_fraction=0.95 остаётся зарегистрированным значением. Это указанная для портирования замена исходного расчёта размера позиции на весь капитал, в котором использовались данные из будущего. Сбой — дефект реализации: ордер, отклонённый из-за нехватки маржи, всё равно записывается как находящийся в обработке и тем самым блокирует все последующие входы. Разработчику следует исправить это в коде, ограничив число акций размером, допустимым при свободной марже на e). Оставьте position_fraction на значениях, заданных контрактом (Layer 1.5 отклоняет любые другие значения), и примените остальную обратную связь аналитика ниже. Если ничто другое в ней не меняет торговлю, скажите об этом вместо повторной отправки той же стратегии. IMPLEMENTATION FIX, изменений правил нет. Все значения, зафиксированные контрактом (position_fraction 0.95, кредитное плечо 1.0, train_len 50 и т. д.), остаются без изменений. Проблема: после 2008-09-24 стратегия больше никогда не торгует. entry_diag показывает, что подано 149 сигналов на вход, 135 dropped_entry_inflight, denied_margin=1. Первопричина (base_template.py): _submit_entry записывает ордер как находящийся в обработке до вызова submit_order(); затем submit_order() отклоняет его из-за маржи (refuse_over_margin, строка ~1742) и возвращает управление, не отправив ордер. Фантомный ордер никогда не закрывается, поэтому _entry_order_inflight() блокирует все последующие входы. Исправление в стратегии: (1) Пока эта утечка не устранена, не используйте обычный путь _submit_entry. Либо самостоятельно проверяйте маржу до отправки, рассчитывая размер позиции по числу акций, которое укладывается в маржу при ожидаемой цене исполнения (например, используйте margin_capped_size / свободную маржу счёта с небольшим запасом, чтобы ордер не был отклонён), либо после отклонённой отправки удаляйте устаревшую запись входа из self._inflight_entries (удаляйте любую записанную заявку, статус которой всё ещё INITIALIZED/которая так и не была отправлена). Ограничение числа акций суммой, допустимой по марже, — это ограничение исполнения, а не изменение position_fraction. (2) Согласуйте конечный автомат исходного кода с реальной позицией. Устанавливайте _holding/_entry_price/_counter в состояние «удержание» только после фактического открытия позиции (on_position_opened либо проверяйте cache.positions_open на следующем баре). Если вход не исполнился, сбросьте состояние на holding=0, trained=False, как это делает исходный код после выхода, чтобы неисполненный ордер не запускал фиктивный отсчёт длительностью 12 баров. (3) Перед повторной отправкой проверьте результат: полный исторический прогон должен дать порядка 100-150 закрытых сделок, распределённых по 2006-2026, а dropped_entry_inflight должно быть близко к 0. Если торговля снова остановится на полпути, сообщите счётчики entry_diag. Примечание для оператора платформы: утечка, при которой запись создаётся до отклонения, в base_template._submit_entry / _submit_entry_instrument затрагивает любую стратегию, которую хотя бы раз отклонили из-за маржи; её следует исправить централизованно. ## Улучшения из библиотеки (на основе базы знаний; проверьте их, не считайте доказанными) В библиотеке хранится исходный проект Oil Money от je-suis-tm: README, код бэктеста и тепловые карты перебора параметров. В этих переборах доходность в основном зависит от периода удержания и почти не зависит от стопа. Автор использовал размер позиции меньше доступного капитала, переобучал модель только после выхода и называл преимущество «рыночно нейтральным», хотя обычная позиция XOP такой не является. Первое исправление касается исполнения: расчёт размера с учётом маржи, чтобы сделки совершались на протяжении 2006-2026. После этого исходный код позволяет проверить вариант с заранее зарегистрированным периодом удержания, стоп только по убытку, вариант остаточной доходности с хеджированием через USO и переобучение при устаревании модели. 1. [размер позиции] Число акций с учётом маржи и запасом (в исходном коде размер позиции никогда не рассчитывался на весь капитал): Оставьте position_fraction=0.95 целевым значением. При отправке ограничьте число акций: min(floor(equity*0.95*leverage/close), floor(free_margin*0.97/(close*1.03))). Запас 3% покрывает исполнение на следующем открытии, а коэффициент 0.97 учитывает спред. Если рассчитанное число акций меньше 1 акции или ордер отклонён, удалите его из реестра заявок в обработке и сбросьте holding=0, trained=False, counter=0. Устанавливайте _holding/_entry_price/_counter, только когда подтверждено открытие позиции (on_position_opened либо cache.positions_open на следующем баре). Критерий приёмки: около 100-150 закрытых сделок на протяжении 2006-2026, а dropped_entry_inflight близко к 0. — Текущий бэктест с 13 сделками (Sharpe 0.12; после 2008-09-24 сделок нет) не проверяет правило. Один отклонённый ордер заблокировал все последующие входы. Функция portfolio() в исходном коде покупает capital0//max(close) акций, поэтому номинальная стоимость позиции всегда оставалась ниже капитала. Ограничение позиции размером свободной маржи — это ограничение исполнения, как отметил аналитик. Оно не меняет position_fraction. Без этого исправления оценить ни одно из следующих улучшений невозможно. (источник: Oil Money Trading backtest.py, стр. 1) 2. [параметр] Вариант с заранее зарегистрированным периодом удержания: удержание около 9 торговых дней: После запуска с фиксированными условиями контракта зарегистрируйте дополнительный запуск, идентичный ему, кроме holding_threshold=7. Счётчик начинается с 0, а выход срабатывает, когда counter > threshold, поэтому порог 7 приводит к выходу на 9-м баре после входа. Это оптимум для 9-дневного периода на тепловой карте NOK. В качестве пары для проверки устойчивости также зарегистрируйте holding_threshold=15 (17 баров, оптимум для COP). Сравните оба варианта с базовым значением 10 → 12 баров. Не проводите поиск по сетке между ними. — Перебор автора: «точка стопа по прибыли/убытку почти не объясняет доходность. Доходность сильнее коррелирует с длительностью удержания... оптимальный период удержания ... 9 торговых дней». Тепловая карта NOK (README; nok profit heatmap.png): самый тёмный ряд, около +6%, соответствует удержанию 9 дней почти при любой ширине стопа. При удержании 12-19 дней результаты становятся отрицательными (светлая область), если стопы узкие. Тепловая карта COP (cop profit heatmap.png): удержание 5 дней — худший ряд (около -2%–0%), а лучшие ячейки, около 9%, соответствуют 16-17 дням. Период удержания портированной стратегии в 12 баров превышает оптимум NOK и попадает в диапазон, где доходность NOK стала отрицательной. Оптимум различается в зависимости от рынка, поэтому рассматривайте эти значения как две заранее зарегистрированные гипотезы, а не как подобранный параметр. Значения автора получены на выборках для внутривыборочного анализа; решение примет наш бэктест. (источник: Oil Money (README), стр. 1; nok profit heatmap, стр. 1; cop profit heatmap, стр. 1) 3. [выход] Стоп только по убытку: уберите симметричный тейк-профит и дайте прибыли набирать ход до выхода по времени: Замените симметричную проверку |close/entry - 1| >= 0.037 проверкой только на убыток: выходить, когда (close/entry - 1)*holding <= -0.037. Уберите тейк-профит +3.7%; прибыльные сделки удерживайте до выхода по времени. Никогда не подтягивайте стоп ближе, чем на 3.0% от цены входа. — В коде автор говорит, что симметричный стоп — это упрощение: «обратите внимание: я сделал стоп-лосс и стоп-профит симметричными... обычно стоп-лосс и стоп-профит асимметричны». В README стратегия позиционируется как импульсная после слома модели («сразу после слома модели всегда продолжается сильный импульс», а тренд на графике позиций длится около двух месяцев). Тейк-профит +3.7% обрывает именно такие движения. Тепловая карта показывает, что стоп почти не влияет на результат, за исключением самых узких колонок (0.3-0.45 пунктов, около 2-3% от NOKJPY рядом с 15): при удержании 12-19 дней в них самые светлые (наиболее убыточные) ячейки. Тепловая карта COP также показывает, что самый узкий стоп (0.001) даёт худший результат. Стоп по убытку 3.7% при размере позиции 0.95x капитала также удерживает максимальный убыток на сделку около 3.5% капитала плюс гэп. Если применяется правило стопа на уровне 2% капитала, эта сделка всё ещё опирается на маршрут выхода по сигналу и времени, используя ограниченный размер позиции. (источник: Oil Money Trading backtest.py, стр. 1; Oil Money (README), стр. 1; nok profit heatmap, стр. 1; nok trading positions, стр. 1) 4. [режим] Вариант с хеджированием остаточной доходности: торгуйте XOP против USO, умноженного на b, чтобы добиться заявленной автором «рыночной нейтральности»: Зарегистрируйте дополнительную гипотезу с теми же сигналами. При входе откройте позицию в XOP с номиналом N = 0.5*equity в направлении сигнала. Откройте позицию в противоположном направлении по USO с числом акций round(b * XOP_shares), используя зафиксированный наклон OLS b, чтобы компенсировать долларовую экспозицию к сырой нефти. Ограничьте совокупный номинал уровнем 1.0x капитала. Закрывайте обе части одновременно по правилу выхода по времени или стопу. Рассчитывайте стоп по PnL спреда: выходите, когда совокупный PnL <= -2% капитала. Обе части торгуются на USEQ (Reg-T, короткие продажи разрешены). Издержки составляют около 2 x 0.02-0.05% за полный цикл сделки. — Вывод автора: «Главное достоинство этой стратегии — её рыночная нейтральность. Она в разумной степени защищена от рыночных потрясений и бычьих ралли». В README также установлено, что нефть выступает «краткосрочным возмущением» на фоне долгосрочного фактора. Текущая портированная стратегия держит XOP без хеджирования, поэтому каждая сделка несёт полную энергетическую бету, а при движениях сырой нефти типа 2008 (во время которых портированная стратегия прекратила торговлю) PnL в основном отражает движение нефти, а не слом остаточной доходности. Хеджирование через b*USO позволяет выделить остаточную доходность, на которой основан сигнал. Это новая гипотеза, поскольку контракт запрещает торговать USO. Уверенность низкая: преимущество после издержек при половинном номинале может оказаться ниже порога 0.15% на сделку. (источник: Oil Money (README), стр. 1; Pair trading backtest.py, стр. 1) 5. [фильтр] Переобучайте устаревшую модель, когда позиция не открыта (максимальный возраст модели — 50 баров): Добавьте model_max_age_bars=50. Если позиция не открыта и модель обучена, то после прохождения более чем 50 согласованных баров с момента подгонки установите trained=False и подгоните модель заново по последним 50 барам до проверки полосы на этом баре. Условие R2 > 0.7 по-прежнему действует. Записывайте возраст модели при входе и сначала представьте статистику сделок по возрасту модели (<=20, 21-50, >50 баров) для запуска с условиями контракта, чтобы выяснить, связаны ли убыточные сигналы с устаревшими подгонками. — В точном соответствии с исходным циклом модель не переобучается, когда позиция не открыта, поэтому подгонка по спокойному периоду может использоваться месяцами. Более поздний «слом» может оказаться дрейфом относительно старых коэффициентов, а не новым отклонением. В собственном коде автора для парной торговли есть предупреждение: «два актива могут казаться коинтегрированными на протяжении последних двух лет / они могут полностью разойтись... всегда проверяйте статус коинтеграции перед исполнением сделки»; взаимосвязь проверяется на каждом баре. Правила Oil Money README гласят, что модель необходимо «перекалибровать по последним 50 точкам данных» и что валидность подгонки «успешно подтверждает статус петро-валюты ... за последние 50 торговых дней». Это подтверждение валидности охватывает только окно в 50 дней. Файл «nok EG failed.png» показывает, что для длинной выборки OLS с R2 0.73 и статистикой Дарбина—Уотсона 0.012 тест Энгла—Грейнджера не проходит (p около 0.10). Высокий R2 для трендовых уровней не означает, что взаимосвязь стабильна. Перед изменением условий контракта проведите диагностику возраста модели. (источник: Pair trading backtest.py, стр. 1; Oil Money (README), стр. 1; nok EG failed, стр. 1)

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

Возвращать 1 (или небольшое число) из min_bars_required(), поскольку calculate_signal уже проверяет условие len(self._pairs) >= train_len.

min_bars_required() возвращает train_len+1 = 51. Базовый шаблон полностью пропускает calculate_signal, пока не накоплено 51 баров основного ряда, поэтому _pairs начинает заполняться лишь на баре 51, а первая подгонка OLS происходит примерно на баре 101. Фактический прогрев занимает около 100 сессий, а не 50, как в исходнике. Это не меняет логику торговли; торговля просто начинается позже.

При необходимости сверяй _holding с cache.positions_open() в начале calculate_signal.

_holding задаётся в calculate_signal до появления заявки. Если базовая система отклонит вход (размер 0, минимальный номинал или заявка в обработке), внутреннее состояние будет указывать на открытую позицию, которой в действительности нет. Затем модель останется зафиксированной до фантомного выхода по времени или стопу примерно через 12 баров. При капитале 0.95x и сумме свыше $100 ETF такое практически никогда не случается.

Ничего не требуется; это соответствует источнику.

Стоп и проверка времени выполняются только по дневному закрытию; внутрибара стопа нет. Это соответствует источнику (стоп по закрытию), поэтому перенос точный, однако при гэпе стоп 3.7% может быть превышен.

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

Устранить минимальную комиссию за сделку недостаточно: пробой регрессионного остатка, сигнал которого не содержит измеримой информации (IC близко к нулю, PF около 1.05), не выдержит проверку вне выборки или ограничения по просадке.

По итогам проверки бэктеста оптимизация была одобрена, однако затем аналитик отказался от неё, заявив, что premise_falsified. После учёта издержек преимущества не было, IC сигнала оказался -0.034 (t=-0.7), отложенная выборка дала отрицательный результат, а просадка превысила заранее зарегистрированный предел 30%, поэтому допустимых кандидатов не осталось (insufficient_candidate_coverage). Все параметры были зафиксированы контрактом, поэтому перебирать их было невозможно.

Точный перенос стратегии «Oil Money» от je-suis-tm: XOP регрессируется на USO вместо NOKJPY на Brent; модель 50-дневная, OLS, используется только при R2 > 0.7. Длинная позиция открывается, когда закрытие XOP выше подогнанного значения на +2 сигма, короткая — ниже подогнанного значения на -2 сигма; выход — по счётчику 10 баров и стопу 3.7%.

За период 2006-2026 бэктест дал 144 сделок (80 длинных, 64 коротких), общую доходность +21.3%, коэффициент Шарпа 0.139 (CI -0.31 до 0.57), фактор прибыли 1.053, avg_trade_return_pct 0.302% и максимальную просадку 35.4%. На отложенной выборке (2022-09 — 2026-10, 32 сделок) коэффициент Шарпа составил -0.37; положительный результат был только в 1 из 4 оценённых окон OOS walk-forward-анализа.

Анализ

Oil Money Trading backtest

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