# BBSB Sampled Report Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Add a `backtest-bbsb-report` command that samples multiple non-overlapping historical windows, runs a fixed Bollinger Band squeeze-breakout strategy on each window, and outputs one single-page HTML report with client-side segment switching. **Architecture:** Keep the existing report paths intact. Add one parallel `bbsb_report.py` module that follows the same public-candle pool and deterministic block-sampler shape as `bbmr_report.py`, but swaps in breakout entry rules plus fixed intrabar TP/SL exits. The CLI gets one new command and the HTML shell stays journal-first. **Tech Stack:** Python 3.11+, `pandas`, `bokeh`, existing OKX public candle client, existing CLI test harness, plain HTML/CSS/JS --- ## File Structure - Modify: `/home/lxy/okx-codex-trader/okx_codex_trader/cli.py` - add `backtest-bbsb-report` CLI command and dispatch - Create: `/home/lxy/okx-codex-trader/okx_codex_trader/bbsb_report.py` - deterministic sampler - local dataclasses for sampled segment definitions/results - BBSB strategy runner - aggregate and per-segment result shaping - Modify: `/home/lxy/okx-codex-trader/tests/test_cli.py` - CLI contract for new command - Create: `/home/lxy/okx-codex-trader/tests/test_bbsb_report.py` - sampler, strategy, generator, and single-page HTML tests - Modify: `/home/lxy/okx-codex-trader/README.md` - add new command example after implementation is stable ### Task 1: CLI Contract For `backtest-bbsb-report` **Files:** - Modify: `/home/lxy/okx-codex-trader/tests/test_cli.py` - Modify: `/home/lxy/okx-codex-trader/okx_codex_trader/cli.py` - [ ] **Step 1: Write the failing CLI test** ```python def test_backtest_bbsb_report_generates_single_page_report(capsys, tmp_path): main, client, _, _, bbsb_calls = build_main_with_stubs() output_file = tmp_path / "bbsb.html" exit_code = main( [ "backtest-bbsb-report", "--symbol", "BTC-USDT-SWAP", "--bar", "3m", "--history-limit", "5000", "--leverage", "2", "--segments", "8", "--window-size", "300", "--output-file", str(output_file), ] ) assert exit_code == 0 assert client.get_candles_called_with == ("BTC-USDT-SWAP", "3m", 5000) assert bbsb_calls[0]["segments"] == 8 assert bbsb_calls[0]["window_size"] == 300 parser = build_parser() assert tuple(parser._option_string_actions["--symbol"].choices) == ("BTC-USDT-SWAP", "ETH-USDT-SWAP") ``` - [ ] **Step 2: Run test to verify it fails** Run: `cd /home/lxy/okx-codex-trader && .venv/bin/python -m pytest tests/test_cli.py -k bbsb -q` Expected: FAIL because `backtest-bbsb-report` is not implemented - [ ] **Step 3: Write minimal CLI implementation** ```python bbsb_report = subparsers.add_parser("backtest-bbsb-report") bbsb_report.add_argument("--symbol", choices=("BTC-USDT-SWAP", "ETH-USDT-SWAP"), required=True) bbsb_report.add_argument("--bar", required=True) bbsb_report.add_argument("--history-limit", type=int, required=True) bbsb_report.add_argument("--leverage", type=int, choices=(1, 2, 3), required=True) bbsb_report.add_argument("--segments", type=int, required=True) bbsb_report.add_argument("--window-size", type=int, required=True) bbsb_report.add_argument("--output-file", required=True) ``` ```python if args.command == "backtest-bbsb-report": candles = client.get_candles(args.symbol, args.bar, args.history_limit) report = bbsb_report_fn( candles=candles, leverage=args.leverage, output_file=Path(args.output_file), symbol=args.symbol, bar=args.bar, segments=args.segments, window_size=args.window_size, ) print(_dump_json(report)) return 0 ``` - [ ] **Step 4: Run test to verify it passes** Run: `cd /home/lxy/okx-codex-trader && .venv/bin/python -m pytest tests/test_cli.py -k bbsb -q` Expected: PASS ### Task 2: Deterministic Sampler Contract **Files:** - Create: `/home/lxy/okx-codex-trader/tests/test_bbsb_report.py` - Create: `/home/lxy/okx-codex-trader/okx_codex_trader/bbsb_report.py` - [ ] **Step 1: Write the failing sampler tests** ```python def test_sample_segments_rejects_history_pool_too_small(): candles = build_linear_candles(1_000) with pytest.raises(ValueError, match="history pool is too small"): sample_segments(candles=candles, segments=8, window_size=300, warmup_bars=69, seed=7) def test_sample_segments_returns_non_overlapping_ranges(): candles = build_linear_candles(5_000) sampled = sample_segments(candles=candles, segments=4, window_size=300, warmup_bars=69, seed=7) for left_index, left in enumerate(sampled): for right in sampled[left_index + 1 :]: assert left.report_end <= right.context_start or right.report_end <= left.context_start def test_sample_segments_is_deterministic_for_same_seed(): candles = build_linear_candles(5_000) first = sample_segments(candles=candles, segments=4, window_size=300, warmup_bars=69, seed=7) second = sample_segments(candles=candles, segments=4, window_size=300, warmup_bars=69, seed=7) assert first == second def test_sample_segments_uses_exact_block_candidates_and_drops_partial_tail(): candles = build_linear_candles(1_300) sampled = sample_segments(candles=candles, segments=3, window_size=300, warmup_bars=69, seed=7) assert [segment.context_start for segment in sampled] == sorted(segment.context_start for segment in sampled) assert all(segment.context_start % (300 + 69) == 0 for segment in sampled) assert all(segment.report_end <= 1_300 for segment in sampled) def test_generate_bbsb_sampled_report_rejects_invalid_sampling_result(tmp_path, monkeypatch): monkeypatch.setattr("okx_codex_trader.bbsb_report.sample_segments", lambda **_: []) with pytest.raises(ValueError, match="invalid sampling result"): generate_bbsb_sampled_report( candles=build_linear_candles(5_000), leverage=2, output_file=tmp_path / "bbsb.html", symbol="BTC-USDT-SWAP", bar="3m", segments=2, window_size=300, ) ``` - [ ] **Step 2: Run tests to verify they fail** Run: `cd /home/lxy/okx-codex-trader && .venv/bin/python -m pytest tests/test_bbsb_report.py -k "sample_segments or invalid_sampling" -q` Expected: FAIL because sampler functions do not exist - [ ] **Step 3: Write minimal deterministic sampler** ```python WARMUP_BARS = 69 SAMPLER_SEED = 7 ``` ```python def sample_segments(...): block_size = window_size + warmup_bars if len(candles) < segments * block_size: raise ValueError("history pool is too small") context_starts = list(range(0, len(candles) - block_size + 1, block_size)) ... ``` - [ ] **Step 4: Run tests to verify they pass** Run: `cd /home/lxy/okx-codex-trader && .venv/bin/python -m pytest tests/test_bbsb_report.py -k "sample_segments or invalid_sampling" -q` Expected: PASS ### Task 3: BBSB Strategy Rules **Files:** - Modify: `/home/lxy/okx-codex-trader/tests/test_bbsb_report.py` - Modify: `/home/lxy/okx-codex-trader/okx_codex_trader/bbsb_report.py` - [ ] **Step 1: Write the failing strategy tests** ```python def test_run_bbsb_segment_produces_long_breakout_trade(): result = run_bbsb_segment(candles=build_long_breakout_fixture(), leverage=2, warmup_bars=69) assert result.trade_count == 1 assert result.trades[0]["side"] == "Long" def test_run_bbsb_segment_produces_short_breakout_trade(): result = run_bbsb_segment(candles=build_short_breakout_fixture(), leverage=2, warmup_bars=69) assert result.trade_count == 1 assert result.trades[0]["side"] == "Short" def test_run_bbsb_segment_stop_loss_takes_precedence_over_take_profit(): result = run_bbsb_segment(candles=build_ambiguous_exit_fixture(), leverage=2, warmup_bars=69) assert result.trades[0]["exit_price"] == pytest.approx(expected_stop_price) def test_run_bbsb_segment_does_not_generate_entry_from_final_reported_candle(): result = run_bbsb_segment(candles=build_final_bar_breakout_fixture(), leverage=2, warmup_bars=69) assert result.trade_count == 0 def test_run_bbsb_segment_marks_open_position_to_market_but_keeps_journal_realized_only(): result = run_bbsb_segment(candles=build_open_tail_fixture(), leverage=2, warmup_bars=69) assert result.trade_count == 0 assert result.trades == [] assert result.total_return != 0 assert result.open_position is not None def test_run_bbsb_segment_uses_population_std_and_previous_50_completed_bandwidths_only(): result = run_bbsb_segment(candles=build_indicator_contract_fixture(), leverage=2, warmup_bars=69) assert result.trade_count == 1 assert result.trades[0]["entry_time"] == "expected timestamp from the first bar that only passes with ddof=0 and previous-50 median" def test_run_bbsb_segment_does_not_allow_tp_or_sl_on_entry_candle(): result = run_bbsb_segment(candles=build_entry_bar_tp_sl_fixture(), leverage=2, warmup_bars=69) assert result.trade_count == 0 assert result.open_position is not None def test_run_bbsb_segment_exit_exhausts_bar_without_same_bar_reentry(): result = run_bbsb_segment(candles=build_same_bar_reentry_fixture(), leverage=2, warmup_bars=69) assert result.trade_count == 1 assert len(result.entries) == 1 ``` - [ ] **Step 2: Run tests to verify they fail** Run: `cd /home/lxy/okx-codex-trader && .venv/bin/python -m pytest tests/test_bbsb_report.py -k run_bbsb_segment -q` Expected: FAIL because BBSB runner does not exist - [ ] **Step 3: Write minimal BBSB segment runner** ```python def run_bbsb_segment(...): ... if position is not None and index > int(position["entry_index"]): if stop_hit: ... continue if take_profit_hit: ... continue ... ``` - [ ] **Step 4: Run tests to verify they pass** Run: `cd /home/lxy/okx-codex-trader && .venv/bin/python -m pytest tests/test_bbsb_report.py -k run_bbsb_segment -q` Expected: PASS ### Task 4: Generator And Single-Page HTML Report **Files:** - Modify: `/home/lxy/okx-codex-trader/tests/test_bbsb_report.py` - Modify: `/home/lxy/okx-codex-trader/okx_codex_trader/bbsb_report.py` - [ ] **Step 1: Write the failing generator and HTML tests** ```python def test_generate_bbsb_sampled_report_rejects_insufficient_history_pool(tmp_path): candles = build_linear_candles(1_000) with pytest.raises(ValueError, match="history pool is too small"): generate_bbsb_sampled_report( candles=candles, leverage=2, output_file=tmp_path / "bbsb.html", symbol="BTC-USDT-SWAP", bar="3m", segments=8, window_size=300, ) def test_render_bbsb_sampled_report_contains_summary_and_segment_switcher(): html = render_bbsb_sampled_report(...) assert "BBSB sampled report" in html assert "Average Return Across Segments" in html assert "Median Return Across Segments" in html assert "Best Segment Return" in html assert "Worst Segment Return" in html assert "segment-selector" in html def test_render_bbsb_sampled_report_contains_trade_journal_and_plot_payload(): html = render_bbsb_sampled_report(...) assert "Trade Journal" in html assert "Sampled Range Start Time" in html assert "plot0" in html def test_generate_bbsb_sampled_report_hides_warmup_labels_and_reports_ranges(tmp_path): report = generate_bbsb_sampled_report(...) assert report["segment_count"] == 2 html = Path(report["report_file"]).read_text() assert "expected reported start time" in html assert "expected reported end time" in html assert "warmup timestamp" not in html def test_render_bbsb_sampled_report_embeds_exact_metric_values(): html = render_bbsb_sampled_report( ..., aggregate_summary={ "aggregate_trade_count": 12, "average_return": 0.125, "median_return": 0.05, "best_segment_return": 0.3, "worst_segment_return": -0.2, }, segment_results=[ { "index": 0, "start_time": "2026-04-01 00:00", "end_time": "2026-04-01 15:00", "trade_count": 3, "total_return": 0.1, "win_rate": 0.6667, "max_drawdown": 0.05, "trades": [...], "plot_div": "