001 Notes

ORB Python-To-Rust Speedup Record

Scope And Limitations

This note records the available benchmark evidence for a Python-to-Rust ORB backtester migration. It keeps the useful timing data, parity checks, design facts, accepted and rejected optimization history, and claim boundaries in one public article.

Important limitations:

  1. The old Python ORB implementation was not available in the reviewed evidence.
  2. A direct old-Python-ORB vs current-Rust-ORB end-to-end benchmark was not available.
  3. The strongest 100x-class evidence is stage-level, not full ORB end-to-end.
  4. This is an audit of available benchmark artifacts, not a complete commit-by-commit migration history.

Executive Summary

The Rust backtester became much faster because the implementation stopped doing expensive Python-style repeated work:

  1. Load market data into typed arrays once.
  2. Cache loaded OHLCV data in a binary format.
  3. Build higher timeframes once, session by session.
  4. Prepare one ORB entry per candidate/session.
  5. Evaluate many stop/RR combinations in one future-bar scan.
  6. Track unresolved combinations with u64 bitmasks.
  7. Use AVX2 threshold comparison where available.
  8. Reuse worker threads through persistent pools.
  9. Profile scheduler choices and save runtime baselines.
  10. Materialize full trade rows only for selected rows or inspection.

The measured stage-level Rust wins available in the record:

Base data loading              25.192x | #####
Higher-timeframe resample      33.297x | #######
Range discovery                92.306x | ##################
Chunk analytics build           2.128x | #
Dense prepared reconstruction   68.817x | ##############
Finalize event batch build     152.802x | ###############################

Scale: one # is approximately 5x speedup.

The two strongest accepted stage wins are:

  1. range discovery: 118.732s Python -> 1.286s Rust, 92.306x.
  2. finalize event batch build: 21.037s Python -> 0.138s Rust, 152.802x.

The speedup history has three layers in this document:

  1. Within Python: caching, batching, pruning, dense reducer work, and finalize/OOS cleanup.
  2. Python to Rust: six accepted stage ports with measured Python-vs-Rust timings.
  3. Within Rust: typed arrays, binary cache, prepared entries, combo batching, pending masks, AVX2, persistent pools, and scheduler tuning.

Those are the concrete measurements behind the “100x-class” statement. The document does not claim that every full ORB run is always 100x faster end to end.

Evidence Inventory

Evidence used:

  1. Current Rust backtester implementation and ORB profile artifacts.
  2. Current ORB family documentation for single-timeframe, multi-timeframe, human-readable, and multi-timeframe human-readable variants.
  3. Historical benchmark records for the within_session_day_high family.
  4. Saved optimization report snapshot from 2026-05-01.

Snapshot metadata:

FieldValue
familywithin_session_day_high
report date2026-05-01
rustc version1.95.0
cargo version1.95.0
accepted Python/runtime optimizations12
accepted Rust stage ports6
rejected DuckDB/Rust experiments7

Evidence quality key:

LabelMeaning
Direct measuredA saved benchmark gives Python time, Rust time, speedup, and parity status.
Current ORB profileA saved ORB profile gives Rust timings and scheduler tuning, but not Python comparison.
Implementation verifiedThe current Rust implementation contains the named structure, function, or algorithm.
InferredDerived from implementation and known Python bottleneck shape; old Python implementation was not available.
MissingNeeded for complete historical proof but not available in the reviewed evidence.

Visual Architecture Change

flowchart LR
    A["Old inferred Python-style path"] --> B["Load DB rows and Python objects"]
    B --> C["Rebuild or slice timeframes repeatedly"]
    C --> D["Discover signal/range repeatedly"]
    D --> E["Scan future bars per stop/RR row"]
    E --> F["Materialize rows early"]
    F --> G["Aggregate with dict/list/DataFrame overhead"]

    H["Current Rust path"] --> I["Load typed OHLCV arrays or binary cache"]
    I --> J["Build resident session-aware frames once"]
    J --> K["Prepare entry per candidate/session"]
    K --> L["Flatten SL/RR ComboSpec grid"]
    L --> M["One batched future scan"]
    M --> N["u64 masks, AVX2 compares, early exit"]
    N --> O["Summaries first, trades only for selected rows"]

Current Rust ORB Pipeline

flowchart TD
    DB["SQLite DB"] --> RO["Read-only loader"]
    RO --> BC["Binary symbol cache"]
    BC --> OHLCV["OhlcvFrame typed arrays"]
    OHLCV --> RES["Session-aware resample cache"]
    RES --> RSI["RSI planes, if requested"]
    RSI --> BUNDLE["ResidentBundle"]
    BUNDLE --> PREP["prepare_entries"]
    PREP --> ENTRY["PreparedEntry per session"]
    ENTRY --> GRID["Flattened ComboSpec SL/RR grid"]
    GRID --> SCRATCH["WorkerScratch arrays"]
    SCRATCH --> MASK["Pending masks"]
    MASK --> SIMD["AVX2/scalar compare mask"]
    SIMD --> ACC["SummaryAccumulator"]
    ACC --> LB["Leaderboard rows"]
    LB --> TRADES["Selected trade rows"]

Optimization Count Chart

Accepted Python/runtime optimizations   12 | ############
Accepted Rust stage ports                6 | ######
Rejected DuckDB/Rust experiments         7 | #######

Interpretation:

  1. There was a Python optimization phase before the Rust stage-port phase.
  2. Not every Rust or DuckDB experiment was accepted.
  3. The final approach kept only native slices that were fast enough and parity-safe enough.

Speedup Layer 1: Within Python Changes

This phase made the existing Python pipeline faster before the Rust replacement work. The important point is that the later Rust numbers were not measured against a completely unoptimized Python baseline. Python had already been improved through caching, batching, pruning, and less repeated object work.

Python Phase Visual

Timeframe-loading slice
Before Python cleanup     44.595s | ######################
After Python cleanup      20.849s | ##########
Speedup: 2.139x

RR/stop grid slice
Before Python batching     2.268s | ###########
After Python batching      0.950s | #####
Speedup: 2.388x

Finalize/OOS path
Early finalize state    1370.690s | #######################################################
After major cleanup      519.537s | #####################
Derived speedup: 2.638x

Later finalize cleanup chain
Before smaller cleanups   494.746s | ####################
After smaller cleanups    351.280s | ##############
Derived speedup: 1.408x

Scale: timeframe-loading uses approximately 2 seconds per #; RR/stop batching uses approximately 0.2 seconds per #; finalize uses approximately 25 seconds per #. The finalize scale is compressed so the chart stays readable.

What Changed Inside Python

#Python-side changeWhat changed operationallyWhy it was fasterMeasurement / status
11m base-loading contract plus in-memory resampleThe pipeline standardized on loading canonical 1m bars and deriving higher timeframes in memory.Avoided repeatedly loading or rebuilding equivalent timeframe data through slower row/object paths.44.595s -> 20.849s, 2.139x, kept
2RR and stop grid batchingStop-loss and risk/reward variants were grouped instead of being handled as fully separate small jobs.Reduced repeated loop dispatch, repeated future scans, and repeated Python object setup around the same market slice.2.268s -> 0.950s, 2.388x, kept
3Coordinator/finalize checkpoint plus OOS cleanupFinalize work was made more checkpointable and the out-of-sample cleanup path was reduced.Avoided redoing already-completed chunks and reduced repeated post-processing around validation output.1370.690s -> 519.537s, kept
4Finalize input cache plus setup signal cacheStable finalize inputs and setup/signal work were cached between repeated runs.Saved repeated preparation of the same signal and validation inputs.494.746s -> 483.357s, kept
5Dense reducer merge redesignReducer merging was reshaped toward dense intermediate structures.Reduced overhead from nested Python containers during merge-heavy stages.483.357s -> 392.279s, kept
6Prepared-outcome / OOS formatting cleanupPrepared outcome and OOS formatting work was trimmed.Pushed less formatting through hot paths and reduced conversion work before final output.392.279s -> 366.200s, kept
7Finalize input cache v2The finalize input cache was refined.Increased reuse of stable inputs and reduced remaining cache miss/rebuild work.366.660s -> 362.340s, kept
8OOS probability map cleanupOOS probability-map handling was simplified.Reduced lookup/formatting overhead around validation probability data.362.340s -> 360.260s, kept
9Dense-arrays to prepared cleanupDense-array to prepared-output conversion was cleaned up.Reduced conversion overhead when moving from numeric arrays back into prepared Python output objects.360.260s -> 358.400s, kept
10OOS rank tail-pruneLower-value tail candidates were pruned before later ranking/finalize work.Prevented the slowest downstream code from processing candidates that could not matter to the final ranking.358.400s -> 352.780s, kept
11Split finalize cacheThe finalize cache was split by more stable sub-inputs.Allowed reuse of sub-results when only part of the finalize input changed.352.780s -> 351.280s, kept
12Worker batch shapingWorker batch shape was tuned.Improved the target hotspot, but the saved report says end-to-end runtime stayed flat.kept as a hotspot improvement, no material e2e gain

Python Lessons That Carried Into Rust

  1. Loading and resampling needed to be resident and reused.
  2. Stop/RR grids needed to be batched, not expanded into one independent row at a time.
  3. Finalize and OOS work had to avoid repeated setup and repeated formatting.
  4. Dense numeric structures were better than nested Python dictionaries and lists.
  5. Pruning before expensive ranking/finalize work mattered.
  6. Worker shaping mattered, but Python still paid high process, serialization, and object overhead.

Python Phase Takeaway

The Python phase removed obvious waste and delivered real wins, but it did not remove Python’s core bottleneck model: object-heavy row materialization, Python-level loops, nested containers, repeated conversion, process/serialization overhead, and expensive final formatting. That is why the Rust phase still had room for 25x to 152x stage wins.

Accepted Python And Runtime Optimizations

These were applied before or alongside the Rust-stage work. They matter because the Rust speedups were measured against an already-improved Python path, not necessarily the slowest original implementation.

#OptimizationResultKept
11m base-loading contract plus in-memory resample44.595s -> 20.849s, 2.139xyes
2RR and stop grid batching2.268s -> 0.950s, 2.388xyes
3coordinator/finalize checkpoint plus OOS cleanupfinalize 1370.690s -> 519.537syes
4finalize input cache plus setup signal cache494.746s -> 483.357syes
5dense reducer merge redesign483.357s -> 392.279syes
6prepared-outcome / OOS formatting cleanup392.279s -> 366.200syes
7finalize input cache v2366.660s -> 362.340syes
8OOS probability map cleanup362.340s -> 360.260syes
9dense-arrays to prepared cleanup360.260s -> 358.400syes
10OOS rank tail-prune358.400s -> 352.780syes
11split finalize cache352.780s -> 351.280syes
12worker batch shapingtarget hotspot improved, end-to-end flatno material e2e gain

Visual ladder:

Initial representative loading slice   44.595s
After 1m base/in-memory resample        20.849s

Finalize path early state             1370.690s
After checkpoint/OOS cleanup           519.537s
After dense reducer redesign           392.279s
After prepared/OOS cleanup             366.200s
After final smaller cleanups           351.280s

Accepted Rust Stage Ports

These are the six accepted Rust slices from the benchmark history.

#StagePythonRustSpeedupParity / decision
1Base data loading36.899s1.465s25.192xaccepted, exact summary/signature match
2Higher-timeframe resample60.053s1.804s33.297xaccepted, exact summary/signature match
3Range discovery118.732s1.286s92.306xaccepted, exact summary/signature match
4Chunk analytics build17.204s8.084s2.128xaccepted, exact ChunkAnalyticsSummary parity
5Dense prepared reconstruction29.256s0.425s68.817xaccepted as hotspot upper-bound evidence, row-level summary/signature match
6Finalize event batch build21.037s0.138s152.802xaccepted, main and supplement parity true

Python Time vs Rust Time Chart

Scale: one # is approximately 5 seconds. Tiny Rust bars below one unit are marked with ..

Base data loading
Python 36.899s | #######
Rust    1.465s | .

Higher-timeframe resample
Python 60.053s | ############
Rust    1.804s | .

Range discovery
Python 118.732s | ########################
Rust     1.286s | .

Chunk analytics build
Python 17.204s | ###
Rust    8.084s | ##

Dense prepared reconstruction
Python 29.256s | ######
Rust    0.425s | .

Finalize event batch build
Python 21.037s | ####
Rust    0.138s | .

Speedup Layer 2: Within Rust Changes

This section separates two Rust ideas:

  1. Python-to-Rust stage ports: moving a whole hot slice from Python into Rust.
  2. Within-Rust optimization: making the Rust implementation itself faster through layout, batching, SIMD, scheduler tuning, and cache reuse.

The accepted Rust stage-port table above proves the stage-level Python-to-Rust wins. The table below explains the Rust-internal design changes that made those ports fast enough to keep.

Rust Phase Visual

flowchart LR
    A["Naive native port risk"] --> B["Still repeats work"]
    B --> C["Still scans per combo"]
    C --> D["Still allocates per job"]
    D --> E["Still has scheduler imbalance"]

    F["Optimized Rust path"] --> G["Resident typed arrays"]
    G --> H["Prepared entries"]
    H --> I["Flattened combo blocks"]
    I --> J["Scratch reuse and pending masks"]
    J --> K["AVX2 compare masks"]
    K --> L["Persistent pools and tuned scheduler"]

Current ORB Rust Pass-To-Pass Tuning Evidence

The current ORB profile artifact shows that tuning inside Rust matters even after the port exists.

Metricpass0_baselinepass1_reprofileImprovement
Schedulersession_majorhybrid_blockedchanged work partitioning
Workers816more parallelism
Session block124larger session batches
Param block88unchanged
Load/prep3087us1233us2.504x faster
Signal eval499us472us1.057x faster
Combo eval2572us760us3.384x faster
Total wall3073us1233us2.492x faster

Visual:

Combo evaluation
pass0 baseline   2572us | ##########################
pass1 tuned       760us | ########

Total wall
pass0 baseline   3073us | ###############################
pass1 tuned      1233us | ############

Scale: one # is approximately 100us.

Important interpretation:

  1. This is a current ORB profile comparison, not an old Python-vs-Rust comparison.
  2. It proves that Rust still needed scheduler/block-size tuning after the core port.
  3. pass2_reprofile is not shown as an improvement because it used a denser stress shape and produced 21964us combo evaluation. That pass is useful for hotspot discovery, not for claiming a speedup over pass1.

What Changed Inside Rust

#Rust-side changeWhat changed operationallyWhy it was fasterEvidence
1Columnar OhlcvFrame layoutMarket data is held as Vec<u32>, Vec<u16>, and Vec<f32> columns rather than row objects.Hot loops walk compact contiguous arrays with better cache locality and less per-row overhead.implementation verified; base loading 25.192x and resample 33.297x stage evidence
2Binary OHLCV cacheLoaded symbols can be stored in RBCACHE1 binary cache format with typed arrays and session ranges.Later runs avoid repeated SQLite row conversion and rebuild typed vectors directly.implementation verified; base loading stage evidence
3Read-only SQLite loader settingsLoader uses query_only, memory temp store, larger cache size, and mmap size.Optimizes the DB path for read-heavy backtesting instead of generic SQLite behavior.implementation verified
4Session-aware resamplingResampling happens inside session boundaries with session ranges preserved.Higher timeframes are built once and reused without crossing sessions or rebuilding per candidate.implementation verified; resample 33.297x
5Resident frame and RSI cachesResidentBundle, ResidentFrame, and RsiPlane keep timeframes/features resident.Feature construction is reused across variants instead of recomputed through every candidate path.implementation verified
6Prepared ORB entriesprepare_entries builds one numeric entry record per candidate/session before combo evaluation.Signal discovery is separated from stop/RR evaluation, avoiding candidate x combo repeated signal work.implementation verified; ORB effect inferred
7Flattened ComboSpec gridsStop/RR combinations are flattened into blocks.The evaluator can process many combinations together instead of dispatching one Python-like row at a time.implementation verified
8WorkerScratch reuseStop prices, target prices, risks, metrics, and masks reuse per-worker buffers.Avoids repeated allocation and keeps combo arrays hot in memory.implementation verified
9u64 pending masksUp to 64 unresolved combinations are tracked in one word.Resolved combos stop consuming checks, and early exit ends the future-bar scan when all combos resolve.implementation verified
10AVX2 compare masksThreshold checks compare 8 f32 values at a time when AVX2 is available.Stop/target checks become vector operations that feed directly into the pending mask format.implementation verified
11Scalar fallbackNon-AVX2 machines use the same mask contract through scalar loops.Keeps behavior portable without splitting the evaluation model.implementation verified
12Persistent worker poolsWorker threads live across jobs and receive work through channels.Avoids repeated thread creation during benchmark/search workloads.implementation verified
13Scheduler modessession_major, param_major, hybrid_blocked, and thread_per_param support different workload shapes.Work can be split along the dominant dimension instead of forcing one scheduler on every grid.implementation verified; ORB profile pass evidence
14Cached selected tuningProfile/baseline artifacts record selected scheduler and block settings.Later runs can reuse known-good choices instead of rediscovering them from scratch.current ORB profile evidence
15Summary-first outputRust accumulates compact summaries and materializes trade rows only for selected/inspection paths.Avoids Python-style full row expansion across every intermediate candidate.implementation verified; dense prepared and event batch stage evidence

Rust Phase Takeaway

The Rust speedup was not just “rewrite Python in Rust.” The fast path came from changing the execution model:

  1. Row objects became typed arrays.
  2. Repeated data loading became resident caches.
  3. Repeated timeframe construction became session-aware frame reuse.
  4. Repeated signal work became prepared entries.
  5. Per-combo future scans became batched combo blocks.
  6. Per-combo state became bitmasks.
  7. Scalar threshold checks became AVX2 masks when available.
  8. Per-job thread creation became persistent worker pools.
  9. One scheduler became a tuned scheduler matrix.
  10. Full row materialization moved to selected output paths.

That is the reason the accepted Rust stages reached 25.192x, 33.297x, 92.306x, 68.817x, and 152.802x, while rejected native attempts were excluded when they were slower or failed parity.

Benchmark Card 1: Base Data Loading

Stage: base_data_loading

Scope:

  1. Symbols: BANKNIFTY, FINNIFTY, NIFTY, SENSEX
  2. Timeframe: 1m
  3. Iterations: 2

Result:

  1. Python mean: 36.899s
  2. Rust mean: 1.465s
  3. Speedup: 25.192x
  4. Exact summary/signature match: True

Per-symbol parity:

SymbolMatchBarsSessionsTimeframeSignature
BANKNIFTYTrue4628141240114979702488594476994
FINNIFTYTrue4628151240112830231235636915535
NIFTYTrue4628621240116746711586037283007
SENSEXTrue462854124117164122480827181626

Why it matters for ORB:

  1. ORB runs need the same raw intraday market data.
  2. The Rust backtester keeps this in resident typed arrays.
  3. Repeated broad searches avoid repeated Python/SQLite row conversion.

Benchmark Card 2: Higher-Timeframe Resample

Stage: resample_symbol_data

Scope:

  1. Symbols: BANKNIFTY, FINNIFTY, NIFTY, SENSEX
  2. Target timeframes: 2, 3, 5, 7, 10, 15
  3. Iterations: 2

Result:

  1. Python mean: 60.053s
  2. Rust mean: 1.804s
  3. Speedup: 33.297x
  4. Exact summary/signature match: True

Compact parity matrix:

SymbolTFMatchBarsSessionsSignature
BANKNIFTY2True232021124010623403372796047685
BANKNIFTY3True154274124011683861620331284709
BANKNIFTY5True92565124015572291281304323151
BANKNIFTY7True66648124013119536419317246893
BANKNIFTY10True4689912403451223347036751332
BANKNIFTY15True30856124010196389433094323293
FINNIFTY2True23202212406773245237344672967
FINNIFTY3True15427412405452828566907294839
FINNIFTY5True92565124014013925004150641618
FINNIFTY7True6664812406484163754068440467
FINNIFTY10True4689912405091172562065009978
FINNIFTY15True3085612408323565727100361650
NIFTY2True23204512406269823062222553913
NIFTY3True154289124091100024418122270
NIFTY5True92575124010165090913439292164
NIFTY7True66655124015451707410263674778
NIFTY10True4690412406016081311971001549
NIFTY15True3085912407726354906344598091
SENSEX2True232041124112073356468092182653
SENSEX3True15428812413249127358710964883
SENSEX5True9257412411796560672201851377
SENSEX7True6665412419945275399724493284
SENSEX10True4690412416752291735196555283
SENSEX15True308591241638337056868199258

Why it matters for ORB:

  1. ORB depends heavily on 1m, 2m, 3m, 5m, 10m, 15m, 30m, and other timeframes.
  2. Multi-timeframe ORB is only practical if higher-timeframe data is not rebuilt per candidate.
  3. The current Rust resampler keeps candles inside session boundaries and anchors from the session open.

Benchmark Card 3: Range Discovery

Stage: range discovery

Scope:

  1. Symbol: BANKNIFTY
  2. Study timeframe: 1m
  3. Slice shape: 2 policies x (1 red-close mode + 4 low-break rules x windows 1..10) x 3 boundaries
  4. Detector calls: 246
  5. Iterations: 2

Result:

  1. Python single pass: 117.49s
  2. Rust single pass: 1.267s
  3. Python mean: 118.732s
  4. Rust mean: 1.286s
  5. Speedup: 92.306x
  6. Total benchmark wall clock: 240.037s
  7. Exact summary/signature match: True
  8. Accepted: True

Why it matters for ORB:

  1. ORB opening-range and breakout discovery is range/event discovery work.
  2. Moving this class of loop into typed Rust removes the Python row-iteration bottleneck.
  3. This is one of the two strongest accepted 100x-class measurements.

Benchmark Card 4: Chunk Analytics Build

Stage: chunk analytics

Scope:

  1. Range rows: 6293
  2. Attempt rows: 145760
  3. Transition rows: 13529
  4. Timed iterations: 5

Result:

  1. Warmup Python: 16.771s
  2. Warmup Rust: 7.887s
  3. Python mean: 17.204s
  4. Rust mean: 8.084s
  5. Speedup: 2.128x
  6. Total wall clock: 162.444s
  7. Exact ChunkAnalyticsSummary parity: True
  8. Decision: accepted

Interpretation:

  1. This stage improved, but it did not produce a 100x-class gain.
  2. It is included to avoid cherry-picking only the biggest wins.
  3. It shows the audit process kept smaller wins when parity was exact.

Benchmark Card 5: Dense Prepared Reconstruction

Stage: dense prepared reconstruction around _dense_arrays_to_prepared_outcomes

Important read:

  1. Current production Python materialized full nested Python dict/list structures.
  2. The Rust prototype computed exact row-level summary/signature over the same reducer payload.
  3. Therefore 68.817x is a hotspot upper bound, not a guaranteed end-to-end drop-in speedup.
  4. The apples-to-apples reducer-summary loop speedup was even higher: 310.187x.

Correctness:

  1. Row-level summary/signature match: True

Timing:

  1. Python production _dense_arrays_to_prepared_outcomes mean: 29.256403s
  2. Python reducer-summary mean: 131.871543s
  3. Rust reducer-summary mean: 0.425135s

Speedup:

  1. Rust vs Python reducer-summary: 310.187x
  2. Rust vs Python production prepared-path: 68.817x

Row counts included:

Row classRows
baseline1200
baseline-by-year7200
single77521
single-by-year460965
pair1700252
pair-by-year9583646
triple2492830
triple-by-year12569630

Why it matters for ORB:

  1. ORB broad searches can generate large intermediate grids.
  2. Avoiding Python dict/list reconstruction is essential once candidate counts grow.
  3. The current Rust ORB design summarizes first and materializes selected trades later.

Benchmark Card 6: Finalize Event Batch Build

Stage: finalized event batch build

Scope:

  1. Status: accepted
  2. Selected chunk count: 60
  3. Timed worker-side call count: 300
  4. Supplement call count: 7
  5. Trimmed to first 30 chunks: False
  6. Selection reason: full_60_chunks python one-pass 20.956s
  7. Iterations: 12

Result:

  1. Python one-pass: 20.956s
  2. Rust one-pass: 0.080s
  3. Python mean: 21.037s
  4. Rust mean: 0.138s
  5. Speedup: 152.802x
  6. Main parity: True
  7. Supplement parity: True
  8. Total benchmark wall clock: 274.066s

Why it matters for ORB:

  1. This is the strongest accepted 100x-class stage result.
  2. It validates the design principle: compact event batches beat Python object expansion.
  3. ORB inspection and leaderboard workflows benefit from this same “defer heavy rows until needed” approach.

Rejected Or Conditional Experiments

This is important: the migration was not “Rust everything blindly.” Several native or DuckDB experiments were rejected or only conditionally useful.

ExperimentPythonCandidateOutcomeReason
DuckDB final consensus on large sweep1.558s0.467sconditional keepuseful only on large consensus-shaped aggregation
DuckDB dense outcome aggregationn/amuch slowerreject12x to 15x slower than Python reducer path
Dense reducer merge in Rust18.340s27.568srejectslower and failed reducer_signature parity
OOS candidate selection hybrid19.163s48.880srejectexact but Python-side flattening dominated
Grouped row scoring hybrid48.136s48.461srejectexact mode slightly slower than Python
Prediction-cell hybrid0.825s1.011srejectslower and not row-exact
Finalize strategy evaluation in Rust50.442s0.921srejectvery fast but failed exact EvalSummarySet parity

Rejected details from benchmark reports:

  1. Dense reducer merge:

    • payload count: 60
    • selected shard count: 60
    • Python mean: 18.34s
    • Rust mean: 27.568s
    • speedup: 0.665x
    • exact summary/signature match: False
    • mismatch field: reducer_signature
    • decision: rejected
  2. OOS candidate benchmark:

    • Python build_oos_validation mean: 19.162655s
    • Rust-hybrid build_oos_validation mean: 48.880381s
    • speedup: 0.392x
    • exact row equality: True
    • canonical match: True
    • row count: 430
    • rejected because integration overhead made it slower.
  3. Finalized strategy evaluation:

    • stage: _evaluate_local_variants evaluation core only
    • chunk count used: 60
    • group count used: 300
    • summary record count: 2700
    • repeat count used: 4
    • Python mean seconds: 50.442
    • Rust mean seconds: 0.921
    • speedup: 54.792x
    • parity exact: false
    • accepted: false
    • rejected because Rust EvalSummarySet parity did not match Python CPU reference.
  4. Attempt analysis:

    • symbol: BANKNIFTY
    • timeframe: 5m
    • confirmed input count: 4839
    • Python mean: 5.052s
    • Rust mean: 0.036s
    • speedup: 139.656x
    • exact summary/signature match: False
    • not promoted as accepted evidence because parity failed.

Visual accepted/rejected contrast:

Accepted because fast and parity-safe:
base loading               25.192x  parity True
resample                   33.297x  parity True
range discovery            92.306x  parity True
chunk analytics             2.128x  parity True
dense prepared             68.817x  signature True, hotspot upper-bound
event batch build         152.802x  parity True

Rejected or conditional:
dense reducer merge         0.665x  parity False
OOS candidate hybrid        0.392x  parity True but slower
grouped row scoring         0.993x  exact but slower
prediction-cell hybrid      0.816x  not row-exact
finalize strategy eval     54.792x  parity False
attempt analysis           139.656x parity False

Current Runtime Chokepoints After Python Optimization

These were recorded after the accepted Python/runtime cleanup work:

HotspotTime
_run_finalize_chunk_worker38.790s
build_oos_validation38.464s
_dense_arrays_to_prepared_outcomes28.974s
build_event_batch_for_variant14.715s

Interpretation:

  1. Even after Python cleanup, object-heavy finalize/build stages remained expensive.
  2. The accepted Rust ports targeted the same kind of repeated numeric and materialization work.
  3. This explains why native stage ports could still show large wins after Python had already been optimized.

Current ORB Rust Profile Evidence

Current ORB profile evidence:

  1. family: orb
  2. representative candidate: orb_tf1_or1_dl1_rtdirect_sigfirst_breakout_only_cc0_body0p0_scaled_candle_extreme_tgttouch

Profile passes:

PassSchedulerWorkersPinningSession blockParam blockLoad/prepFeature buildSignal evalCombo evalTotal wall
pass0_baselinesession_major8none183087us1us499us2572us3073us
pass1_reprofilehybrid_blocked16none2481233us0us472us760us1233us
pass2_reprofilesession_major16none16422453us0us488us21964us22453us

Profile interpretation:

  1. pass0_baseline identified combo evaluation as the dominant hotspot.
  2. pass1_reprofile reduced combo evaluation from 2572us to 760us on the realistic shape.
  3. pass2_reprofile intentionally used a denser stress shape, so combo evaluation rose to 21964us.
  4. Signal evaluation stayed around 472us to 499us, much smaller than dense combo evaluation.

Visual ORB hotspot chart:

pass0 combo eval   2572us | ##########
pass1 combo eval    760us | ###
pass2 combo eval  21964us | ########################################################################################

Scale: one # is approximately 250us.

Implementation-Level Design Facts

This section summarizes the implementation facts behind the benchmark claims.

Market Data Structure

Current Rust market data is columnar:

OhlcvFrame
- symbol: String
- day_keys: Vec<u32>
- minute_of_day: Vec<u16>
- open: Vec<f32>
- high: Vec<f32>
- low: Vec<f32>
- close: Vec<f32>
- volume: Vec<f32>
- sessions: Vec<SessionRange>

SessionRange
- day_key: u32
- start: usize
- end: usize

RsiPlane
- period: usize
- values: Vec<f32>

ResidentFrame
- timeframe_minutes: usize
- frame: Arc<OhlcvFrame>
- day_to_session: Arc<HashMap<u32, usize>>
- rsi_planes: Vec<Arc<RsiPlane>>

ResidentBundle
- symbol: String
- years: usize
- frames: Vec<ResidentFrame>

Performance impact:

  1. Hot loops use compact arrays.
  2. Session boundaries are known.
  3. Timeframe and RSI data stay resident.
  4. Memory bandwidth and cache locality are much better than Python row objects.

Binary Cache Contract

The market cache stores:

  1. magic bytes: RBCACHE1
  2. cache version: 1
  3. symbol string
  4. row count
  5. session count
  6. database length
  7. database mtime seconds
  8. day_keys
  9. minute_of_day
  10. open
  11. high
  12. low
  13. close
  14. volume
  15. session ranges

Performance impact:

  1. Later runs avoid full SQLite row conversion.
  2. Cache validity is tied to DB size and modification time.
  3. Cached bytes map directly back to typed vectors.

SQLite Read Settings

The Rust database loader applies read-oriented settings:

PRAGMA query_only = ON;
PRAGMA temp_store = MEMORY;
PRAGMA cache_size = -200000;
PRAGMA mmap_size = 268435456;

Performance impact:

  1. The DB path is treated as read-only.
  2. SQLite page/cache behavior is tuned for read-heavy backtests.
  3. The loader avoids unnecessary write-path behavior.

ORB Entry Preparation

Current ORB-like flow:

prepare_entries
  -> prepare_orb_entry
  -> prepare_orb_signal_entry
  -> materialize_orb_prepared_entry
  -> evaluate_entry_combo_block

Prepared entry fields include:

  1. session identity
  2. signal day and minute
  3. decision minute
  4. entry minute
  5. entry price
  6. side
  7. stop kind
  8. base risk
  9. future start index
  10. future end index
  11. session close price
  12. execution model
  13. tradeability metadata

Performance impact:

  1. Signal discovery is paid once per session/candidate.
  2. SL/RR evaluation consumes prepared numeric state.
  3. Full trade records are not required for every leaderboard row.

Combo Evaluation Scratch Buffers

The current evaluator uses reusable scratch:

WorkerScratch
- stop_prices: Vec<f32>
- target_prices: Vec<f32>
- risk_points: Vec<f32>
- metrics: Vec<TradeMetric>
- pending_masks: Vec<u64>

Performance impact:

  1. Fewer allocations.
  2. Contiguous arrays for stop/target thresholds.
  3. Compact pending state.
  4. Same scratch reused across sessions/jobs.

Pending Mask Logic

Current logic:

For each combo block:
  pending_mask = all bits set

For each future bar:
  stop_mask = compare(bar stop-side price, stop_prices) & pending_mask
  clear stopped bits

  target_mask = compare(bar target-side price, target_prices) & pending_mask
  clear target-hit bits

  if all pending masks are zero:
    break early

Performance impact:

  1. Resolved combos stop consuming work.
  2. Up to 64 combos fit in one pending word.
  3. Stop/target checks feed directly into mask operations.

AVX2 Compare Path

The current compare mask has two paths:

if AVX2 detected:
  compare 8 f32 thresholds at a time
  convert comparison result to bitmask
else:
  scalar loop over thresholds

AVX2 operations used:

  1. _mm256_set1_ps
  2. _mm256_loadu_ps
  3. _mm256_cmp_ps
  4. _mm256_movemask_ps

Performance impact:

  1. Threshold checks are vectorized.
  2. The vector result becomes the same bitmask format used by pending masks.
  3. Machines without AVX2 still run the scalar path.

Persistent Worker Pools

Current worker-pool shape:

PersistentPools
- one pool per worker count for pinning=none
- one pool per worker count for pinning=physical
- jobs submitted through channels
- worker threads stay alive until pool drop

Performance impact:

  1. Benchmarks and search jobs avoid repeated thread creation.
  2. Tuning matrices can compare worker counts consistently.
  3. Work can be scheduled by session blocks, combo blocks, or per-param jobs.

Scheduler Modes

Current scheduler modes:

SchedulerShapeUse
session_majorsplit sessions across jobs, evaluate combos inside each sessiongood when entries are many and combo block batching is useful
param_majorsplit combo blocks, iterate entries inside combo blockuseful for combo-heavy workloads
hybrid_blockedsplit both sessions and combo blocksuseful when both dimensions need balancing
thread_per_paramdistribute narrow combo work across workersuseful for certain ORB/MTF grids and defaults

ORB Family Coverage In Rust

Current ORB-like families:

  1. orb
  2. orb_mtf
  3. orb_human
  4. orb_mtf_human

Current strategy dimensions:

DimensionCurrent Rust support
Timeframessingle-timeframe and multi-timeframe
Opening rangeopening range candle count, OR timeframe
Deadlinebreakout deadline candles
Retest logicdirect, single retest, double retest
Signal patternfirst breakout only, opposite reversal allowed, opposite reversal required
Confirmationclose confirmation, confirm chain, higher timeframe confirmation
Executionlegacy same-bar, close-confirm next open, resting limit at level
Stopscaled candle extreme, opposite range, fixed points
Targettouch, next close confirm, confirming bar close
Carrysession exit
Contextgap bucket, open vs previous range, ORB color, breakout side, breakout extension, reversal observed

Performance relevance:

  1. These variants share resident market data.
  2. They share entry preparation.
  3. They share combo-grid evaluation.
  4. They share scheduler tuning and runtime baseline output.
  5. Adding variants does not require returning to Python object-heavy loops.

Code-Level Mapping From Bottleneck To Rust Mechanism

Old bottleneck categoryCurrent Rust mechanismEvidence qualityPerformance effect
DB row conversion per runread-only loader plus binary cachedirect measured, implementation verifiedbase loading 25.192x
Repeated timeframe rebuildscached session-aware OhlcvFrame resamplingdirect measured, implementation verifiedresample 33.297x
Python range/event loopstyped Rust range discovery loopsdirect measuredrange discovery 92.306x
Repeated signal work per SL/RRprepare_entries before combo evalimplementation verified, inferred ORB effectremoves candidate x combo signal repetition
Future scan per combo rowevaluate_entry_combo_block scans many combos togetherimplementation verifiedfewer scans, better cache locality
Python dict/list reconstructionsummary-first Rust output and selected trade materializationdirect measured in dense prepared/event batchdense/event stages 68.817x and 152.802x
Stop/target branch churnpending masks and early exitimplementation verifiedresolved combos are skipped
Scalar threshold checksAVX2 compare mask with scalar fallbackimplementation verified8 thresholds compared per vector on AVX2 machines
Repeated thread creationPersistentPoolsimplementation verifiedlower scheduling overhead for benchmark/search jobs
One fixed schedulertuned scheduler matrix and baselinescurrent ORB profileselects better worker/scheduler/block settings by workload

Claim Ledger

ClaimStatusEvidence
Rust data loading is much faster than Python loadingproven for benchmark stage36.899s -> 1.465s, exact parity true
Rust resampling is much faster than Python resamplingproven for benchmark stage60.053s -> 1.804s, all symbol/timeframe parity true
Rust range discovery reached about 100x speedupproven for benchmark stage118.732s -> 1.286s, 92.306x, parity true
Rust finalize event batch crossed 100xproven for benchmark stage21.037s -> 0.138s, 152.802x, parity true
Dense prepared Rust path showed very large speedupproven as hotspot upper-bound68.817x vs production prepared path, 310.187x reducer-summary
Current ORB combo eval has measurable hotspotsproven in ORB profilepass0/pass1/pass2 timing table
Rust ORB uses typed resident arraysimplementation verifiedOhlcvFrame field list
Rust ORB uses prepared entries and batched combosimplementation verifiedflow and scratch buffer facts
Rust ORB uses AVX2 where availableimplementation verifiedAVX2 function family
Full ORB end-to-end old Python vs Rust is 100xnot provendirect matching artifact not found
Every Rust attempt was acceptedfalserejected experiments table

What Is Directly Measured, Inferred, And Missing

Directly measured:

  1. Six accepted Rust stage ports with Python time, Rust time, speedup, and parity status.
  2. Current ORB Rust profile passes and scheduler choices.
  3. Python/runtime optimization counts and before/after timings.
  4. Rejected experiment timings and reasons.

Inferred:

  1. Exact shape of the old Python ORB loop.
  2. Which old Python functions map to which Rust functions.
  3. Exact end-to-end full ORB speedup.
  4. The detailed order in which individual Rust ORB implementation functions were written.

Missing:

  1. Old Python ORB implementation.
  2. Git commit history from the migration.
  3. Direct old-Python-ORB run artifact on the same fixture as current Rust ORB.
  4. A full end-to-end ORB benchmark matrix comparing old Python and current Rust.

Why The 100x-Class Claim Is Still Technically Fair

It is fair if stated as:

Several heavy benchmarked stages that ORB-like backtesting depends on reached 100x-class Rust speedups, with accepted stage evidence up to 152.802x.

It is not fair if stated as:

Every full ORB backtest is proven to be 100x faster end to end.

Correct wording for future reports:

  1. “100x-class stage speedups were measured.”
  2. “The current Rust ORB architecture applies the same principles: resident typed data, prepared entries, batched combo evaluation, masks, SIMD, and scheduler tuning.”
  3. “A direct end-to-end old Python ORB comparison is still missing.”

Historical Timeline From Available Evidence

2026-04-25
  Chunk scheduling benchmark work was active.

2026-04-26
  Finalize optimization improved end-to-end modestly and finalizer substantially.

2026-04-27
  Python/runtime optimization phase:
  - 1m base-loading contract plus in-memory resample
  - RR/stop batching
  - coordinator/finalize checkpoint and OOS cleanup
  - dense reducer redesign

2026-04-28
  Finalize/OOS cleanup continued:
  - prepared outcome cleanup
  - final input cache v2
  - OOS probability map cleanup
  - dense arrays to prepared cleanup
  - OOS rank prune
  - split finalize cache
  - dense prepared Rust benchmark captured

2026-05-01
  Rust stage-port benchmark suite captured:
  - base data loading
  - higher-timeframe resample
  - range discovery
  - chunk analytics
  - finalize event batch
  - rejected Rust/DuckDB attempts documented

2026-05-27
  Current Rust backtester/ORB artifacts inspected.

2026-06-04
  Benchmark record consolidated from available evidence.

Reproduction Notes

For future benchmark runs:

  1. Rebuild the Rust binary immediately before testing.
  2. Confirm the executable supports the target ORB family before timing it.
  3. Keep cache-warm and cache-cold runs separate.
  4. Preserve the exact fixture: database, symbol, date range, timeframe, ORB parameters, stop/RR grid, and output requirements.
  5. Compare both timing and parity before accepting a speedup claim.

Important stale-binary note:

  1. A stale binary can launch successfully and still reject a newly added family as unsupported.
  2. Before assuming a family is broken, confirm the executable was rebuilt and supports that family.
  3. Toolchain or launch-policy errors should be treated separately from benchmark correctness.

Profile metrics to compare after a new ORB change:

  1. load_prep_us
  2. feature_build_us
  3. signal_eval_us
  4. combo_eval_us
  5. total_wall_us
  6. selected scheduler
  7. worker count
  8. session block
  9. param block

Gap Closure Plan

To make this a complete historical migration record:

  1. Recover the old Python ORB implementation.
  2. Identify one exact old Python ORB command and fixture.
  3. Run or recover old Python timing for cold load, warm load, signal prep, combo evaluation, and output writing.
  4. Run current Rust on the same fixture with the same output requirements.
  5. Add a direct end-to-end table with Python wall time, Rust wall time, speedup, parity checks, and output row counts.
  6. Add function-level mapping between the old Python implementation and the current Rust implementation.

Final Takeaway

The useful record is:

  1. The measured speedups.
  2. The visual comparison.
  3. The benchmark card details.
  4. Accepted Python optimizations.
  5. Accepted Rust stage ports.
  6. Rejected or conditional experiments.
  7. Current ORB profile evidence.
  8. Implementation-level design facts.
  9. Claim boundaries.
  10. Remaining gaps.

The most accurate final statement is:

The Rust migration produced measured 100x-class speedups in several heavy backtesting stages, including 92.306x for range discovery and 152.802x for finalized event batch building. The current Rust ORB engine uses the same core performance design: typed resident data, cached resampling, prepared entries, batched SL/RR evaluation, pending masks, AVX2 compares, persistent workers, and profiled scheduler tuning. A direct full old-Python-ORB versus current-Rust-ORB end-to-end benchmark is still missing, so the 100x claim should be stated as stage-level rather than universal full-run proof.

Comments