2.0 KiB
2.0 KiB
ETF Trend System (K<=4, no leverage) - v2
This is a daily-signal / daily-rebalance trend-following system on a configurable ETF universe.
It is designed for:
- K<=4 holdings
- no leverage (net exposure <= 100%)
- portfolio vol cap (de-risk only), remainder parked in a rates ETF
- practical execution hygiene (cooldown / new listing protection / turnover band)
Signals
-
Trend filter (entry universe): MA(fast) > MA(slow)
- default: MA5 > MA20
-
Ranking score (higher is better):
score = (0.5*R20 + 0.3*R60 + 0.2*R120) / max(vol20, floor) + 0.5*trend_strengthwhere
trend_strength = ma_fast/ma_slow - 1.
Entry
On each rebalance day (daily):
- Candidate must satisfy:
trend_ok == True(MA cross)score >= min_scoremin_history_daysprotection (skip too-new series)cooldown_daysprotection (after exit, avoid immediate re-entry)
Position Sizing
-
Risk parity on
vol20across selected holdings. -
Per-asset cap:
max_weight_per_asset(default 0.50) -
Portfolio vol cap (no leverage):
scale = min(1, target_ann_vol / port_vol(port_vol_window))Remaining weight (1 - sum(weights)) is parked in
rates_fallback.
Exits (checked daily)
A position exits if any triggers:
- Trend break: MA(fast) < MA(slow)
- Chandelier stop: close < highest_close - atr_mult*ATR
- Stop loss from entry: close < entry_price - stop_loss_atr*ATR
- Take profit from entry: close > entry_price + take_profit_atr*ATR
Trading Hygiene
rebalance_band: ignore small weight changes to reduce churn.min_hold_days: do not rebalance-sell a very fresh position (risk exits still apply).new_asset_days/new_asset_max_w: cap weight of a newly-eligible asset for its first N tradable days after it passes the history gate.
Outputs
Backtest runner writes 3 artifacts:
- equity curve parquet:
data/etf_trend_equity_*.parquet - weights parquet:
data/etf_trend_equity_*_weights.parquet - trades parquet:
data/etf_trend_equity_*_trades.parquet