跳轉到

#1746 stop-rule comparison

The previous producer used an inclusive 20-session minimum close as structural support. A close cannot be strictly below a minimum that includes itself, so this branch of the stop rule never fired. Confirmed swing support fixes that definition; this experiment does not establish better investment performance.

Scope and provenance

The read-only capture contains 4,344,852 rows and 2,615 codes (2018-01-02 to 2026-09-24). Replay uses all 4,342,728 stock/ETF rows and 2,614 canonical stock-code capital sleeves, excluding exactly 2,124 non-tradable ^TWII index rows. All 18 TW/TWO listing transfers keep one continuous history and sleeve. There are 2,125 captured market sessions, no invalid adjusted bars and no zero/missing-volume bars. Five instruments never confirm a pivot/common entry.

Input SHA-256: 16824ebcafc13ba6fe779a67a7b165f12df96a0c210d7aa74c32296b43efaacd. Runner SHA-256: 5eee3eef483999e01f8eae3ac2642700b828c5ec407060ca3ee795921e23c112. Baseline producer: 6141a32e22ac64dd9491b0654a34bc02b88a8691.

Detailed outputs: engine-open summary, tradability-screened summary. They include execution/censor counts, 20-session MAE/MFE distributions, fees, and coverage. Large input/trade/event files are retained in task evidence rather than committed.

Rules and execution

  • Actual old rule: same-day inclusive 20-close minimum. The previous-session minimum is reported separately as a sensitivity, not substituted for actual behavior.
  • New rule: latest-by-date strict five-left/five-right adjusted low, available only after the fifth right session. All pre-range history is retained; no expiry or ATR fallback.
  • Each code has NT$5,000,000, a 10% per-position cash cap and 1,000-share lots. Buy fee is 0.1425%; sell fee is 0.1425% plus 0.30% tax. These are the existing BacktestConfig assumptions, applied uniformly, including ETFs. Cooldown is five global sessions; slippage is zero; Sharpe uses 1.5% annual risk-free rate.
  • The synthetic entry rule enters while flat at the earliest common eligible next-session open when price is not already below that rule's stop, and can re-enter after cooldown. It has no production entry ranker or historical action-advice holdings.
  • A close-T decision executes only at the exact next global captured session's adjusted open. Missing/nonpositive opens are censored without retry. The screened sensitivity also blocks zero/missing volume and every single-price O=H=L=C bar. This screen is not a lower-bound return and cannot prove fillability without queue, suspension and slippage data.
  • Daily equity carries the last valid adjusted close across missing sessions. A valid executable global-final close liquidates; otherwise the position remains marked and open. This deliberately corrects the existing engine's entry-price fallback on missing sessions. Figures below are marked-equity returns, not fully liquidated proceeds.

Results

All-bar diagnostics are observations, not transactions: actual old rule 0 / 4,342,728 strict breaches; prior-known sensitivity 426,473 / 4,340,114 (9.8263%); confirmed swing 724,729 / 4,282,259 (16.9240%). For swing, 718,620 have an eligible next open and 6,109 are censored.

Execution Stop rule Stops Marked return CAGR Sharpe Max drawdown Terminal open positions
engine Actual old rule 0 19.1400% 2.0273% 0.238 5.4646% 258
engine Prior-known sensitivity 157,200 8.3731% 0.9258% -0.586 1.6622% 204
engine Confirmed swing 96,019 8.4572% 0.9348% -0.574 1.7164% 204
conservative Actual old rule 0 19.1364% 2.0270% 0.237 5.4650% 337
conservative Prior-known sensitivity 153,956 8.3111% 0.9192% -0.593 1.6627% 276
conservative Confirmed swing 94,696 8.3748% 0.9260% -0.584 1.7153% 266

For actual swing-stop trades, compare net stop-sale proceeds against selling the same shares at the close of the 20th subsequent ticker session. A missing/incomplete horizon is censored. This is a terminal hold-versus-sell counterfactual; it does not include subsequent re-entry opportunity or equal a strategy P&L difference.

Execution Complete 20-session exits Continued fall Rebound Avoided loss NT$ Missed rebound NT$ Net advantage NT$
engine 94,762 46,238 (48.79%) 47,370 (49.99%) 1,574,999,020 1,955,389,898 -380,390,877
conservative 93,457 45,580 (48.77%) 46,764 (50.04%) 1,552,188,897 1,936,735,667 -384,546,770

The swing rule lowers drawdown but also lowers return against the non-triggering old stop in this isolated policy. Missed rebounds exceed avoided losses in both execution assumptions. This supports correcting the stop's meaning, not a claim that the new stop improves strategy returns.

There is no point-in-time listing master: 867 histories start late and 259 end early. Equal capital is allocated across the captured universe, cash is idle outside observed histories, and the result is full-period cash-weighted. Terminal stale marks may be unrealizable; survivorship-free or fully tradable performance is not established.

Reproduce

From investment-agent/backend, with the captured gzip CSV and an output directory outside source:

python scripts/compare_swing_stop_rules.py \
  --input /path/to/ohlc-20260928.csv.gz --output-dir /tmp/swing-stop-study \
  --start 2018-01-01 --end 2026-09-24 --execution-mode engine \
  --require-hash 16824ebcafc13ba6fe779a67a7b165f12df96a0c210d7aa74c32296b43efaacd

python scripts/compare_swing_stop_rules.py \
  --input /path/to/ohlc-20260928.csv.gz --output-dir /tmp/swing-stop-study \
  --start 2018-01-01 --end 2026-09-24 --execution-mode conservative --policy-only \
  --require-hash 16824ebcafc13ba6fe779a67a7b165f12df96a0c210d7aa74c32296b43efaacd

The runner docstring specifies CSV columns and a read-only capture query. --max-codes is only a smoke scope and never changes the market calendar. Focused tests verify causal pre-range pivots, listing transfers, missing-session marks, invalid/final-bar behavior, and the old rule's impossible strict breach. No shared database write or live trade is part of this study.