Привет
Первая запись: переход от дискреционной торговли к алгоритмам и бэктестированию gap fade стратегий.
Запуск исследовательского журнала algos.trade. После нескольких лет дискреционной торговли акциями малой капитализации США (US small caps), преимущественно по стратегии утреннего затухания гэпов (gap fade) на speculari-fund.blogspot.com, исследовательская работа и исполнение перенесены в систематический код.
Этот блог задуман как исследовательский блокнот: прикладная статистика, бэктесты, распределения доходностей и инфраструктура прямого исполнения на рынке. Никаких прогнозов, «сигналов» и околорыночного шума.
Сводные метрики базовой модели
Примечание: приведенные ниже числовые показатели являются демонстрационным примером (placeholder) для проверки оформления таблиц и не представляют собой реальные торговые результаты.
Ниже приведены предварительные сводные результаты бэктеста базовой модели затухания утренних гэпов на выборке за 2024–2026 гг. (тикеры с капитализацией до $1B и ценой акции от $1 до $15):
| Метрика | Значение | Описание |
|---|---|---|
| Trades | 1,428 | Количество исполненных сделок |
| Win Rate | 61.4% | Доля прибыльных сделок |
| Profit Factor | 1.84 | Отношение валовой прибыли к убыткам |
| Sharpe Ratio | 2.12 | Коэффициент Шарпа (годовой, risk-free = 0%) |
| Max Drawdown | -8.6% | Максимальная просадка эквити |
| Avg Holding Time | 42 min | Среднее время нахождения в позиции |
| Avg Win / Avg Loss | 1.16 | Отношение среднего выигрыша к среднему проигрышу |
Модуль фильтрации кандидатов
Фрагмент Python-пайплайна предварительного отбора кандидатов на премаркете по величине гэпа и относительному объему (RVOL):
from dataclasses import dataclass
import pandas as pd
@dataclass(frozen=True)
class GapFilterConfig:
min_gap_pct: float = 15.0
min_premarket_volume: int = 250_000
min_rvol: float = 2.0
min_price: float = 1.0
max_price: float = 20.0
def filter_gap_candidates(df: pd.DataFrame, cfg: GapFilterConfig) -> pd.DataFrame:
"""
Фильтрация пула тикеров премаркета для gap fade модели.
Ожидаются колонки: symbol, prev_close, pm_open, pm_volume, avg_daily_vol.
"""
gap_pct = ((df["pm_open"] - df["prev_close"]) / df["prev_close"]) * 100.0
rvol = df["pm_volume"] / (df["avg_daily_vol"] * 0.1)
mask = (
(gap_pct >= cfg.min_gap_pct)
& (df["pm_volume"] >= cfg.min_premarket_volume)
& (rvol >= cfg.min_rvol)
& (df["pm_open"].between(cfg.min_price, cfg.max_price))
)
result = df.loc[mask].copy()
result["gap_pct"] = gap_pct.round(2)
result["rvol"] = rvol.round(2)
return result.sort_values(by="gap_pct", ascending=False).reset_index(drop=True)
В следующих записях разберем учет транзакционных издержек, шорт-локейтов (borrow fees) и специфику моделирования проскальзывания в утренние минуты открытого рынка.