Strategy Lab 01 - Trend-following strategy

Tutorial Navigation

Track Tutorial notebook
Roadmap Tutorial 00 - Roadmap
Strategy Lab 01 Trend-Following Lab
Tutorial Sequence 01 Real Market Data and Feature Factory
Tutorial Sequence 02 Decomposition-aware MA and MACD
Strategy Lab 02 Oscillation-Reversion Lab
Strategy Expansion 03 Method-Specific Variants
Tutorial Sequence 03 Residual Mean Reversion
Strategy Expansion 04 Component Pair Trading
Tutorial Sequence 04 Donchian Breakout
Tutorial Sequence 05 Pair-Spread Stat-Arb
Tutorial Sequence 06 Cross-Sectional Rotation
Native SSA Replay 07 Native SSA High-Return / Low-Drawdown

Executed Notebook

这篇只做一件事:用 DeTime 分解出来的 trend 直接产生交易信号。

策略逻辑:

  1. trend_slope > 0trend_strength 足够强,说明进入上涨趋势状态。
  2. cycle_position 不在过热区,避免在周期顶部追进去。
  3. residual_abs_z 不过大,避免追到结构外的异常拉伸。
  4. 如果有成交量分解,则用 volume_trend_slope / volume_residual_z 确认参与度。
  5. 信号在第 t 根 bar 结束后生成,回测用下一根 bar 的开盘价成交。
In [1]
from pathlib import Path

import pandas as pd
from IPython.display import Image, display
from quant_trading.data import load_sample_goog_ohlcv
from quant_trading.decomposition_features import walkforward_price_volume_features
from quant_trading.strategy_lab import (
    TrendFollowingConfig,
    OscillationReversionConfig,
    backtest_signal_set,
    execution_price_panel,
    decomposition_trend_following_signals,
    decomposition_oscillation_reversion_signals,
    plot_signal_analysis,
    stats_table,
)
from quant_trading.strategy_baselines import (
    buy_and_hold_weights,
    dual_moving_average_weights,
    bollinger_mean_reversion_weights,
)
from quant_trading.strategy_lab import backtest_target_weights_next_bar

CHART_DIR = Path("examples/quant_trading/reports/strategy_lab/charts")
In [2]
ohlcv = load_sample_goog_ohlcv(trim_start="2014-01-01")
symbol = "GOOG"
close = ohlcv["Close"].rename(symbol).to_frame()
volume = ohlcv["Volume"].rename(symbol).to_frame()
execution_prices = execution_price_panel(ohlcv, field="Open", next_bar=True)
execution_prices.columns = [symbol]

features = walkforward_price_volume_features(
    close, volume, method="STL", period=126, train_window=504, step=5, z_window=63
)
list(features)[:8]
In [3]
signal = decomposition_trend_following_signals(
    close,
    features,
    config=TrendFollowingConfig(
        entry_trend_strength=0.15,
        exit_trend_strength=0.02,
        max_entry_cycle_position=1.25,
        max_entry_residual_abs_z=2.5,
        use_volume_confirmation=True,
        allow_short=False,
    ),
    name="detime_STL_trend_following",
)

bt = backtest_signal_set(
    close, signal, execution_prices=execution_prices, fee_bps=5, slippage_bps=2, periods_per_year=252
)

baselines = {
    "buy_hold": buy_and_hold_weights(close),
    "classic_sma_20_100": dual_moving_average_weights(close, fast=20, slow=100),
}
results = {signal.name: bt}
for name, weights in baselines.items():
    results[name] = backtest_target_weights_next_bar(
        close, weights, execution_prices=execution_prices, fee_bps=5, slippage_bps=2, periods_per_year=252, name=name
    )

stats_table(results)
In [4]
bt.orders.tail(10)
In [5]
bt.trades.tail(10)
In [6]
out = CHART_DIR / "notebook_01_trend_following.png"
plot_signal_analysis(ohlcv, signal, bt, asset=symbol, output_path=out, title="DeTime STL trend-following strategy")
display(Image(filename=str(out)))
out.as_posix()