Rime
A daily regime allocator for leveraged equity ETFs
Weaver Capital LP · deployed strategy · Python, pandas, NumPy, Alpaca API, Streamlit
Rime is a daily allocator. Each morning it splits capital between leveraged equity ETF exposure, a volatility hedge, and cash. It’s been running live on Alpaca since April 2026.
There are three parts to it: a base signal, a macro and risk overlay on top of that, and the infrastructure that runs the whole thing unattended. I’ll go through them in that order.
The volatility hedge had a huge run during the 2020 COVID crash, and a stretch like that lifts the headline number for the entire period. I publish both because 1.58 is the better guide to what the strategy does in an ordinary year.
Figures above are simulated results from a historical backtest over 2012–2025, not live trading results. Simulated performance has inherent limitations: it is constructed with hindsight, does not reflect the impact of real order flow, and does not guarantee future results. A live track record is available on request.
The problem
Leveraged index ETFs rebalance daily, so the compounded return depends on the path and not just the endpoint. That’s what makes them decay in choppy markets. A sideways market grinds them down even when the index finishes flat. Holding one indefinitely is a bet that volatility stays low.
Rime holds that exposure only while the drag is small, and sits somewhere else the rest of the time.
How it decides
The base signal. A shallow decision tree over a set of trend and volatility state variables. I kept it shallow on purpose. Fourteen years of daily bars isn’t a big sample, and every extra split is another chance to fit noise.
The macro and risk overlay. The macro layer uses cross-asset series that tend to move ahead of equities instead of alongside them. On top of that sits a volatility circuit breaker that cuts leveraged exposure when volatility spikes. It runs on hysteresis, so the level that switches it on isn’t the level that switches it off. That stops a noisy signal near the threshold from triggering it repeatedly. A rebalance deadband covers everything else: if the target hasn’t moved much, nothing trades.
I keep the specific state variables, macro series, and thresholds off a public page. Happy to walk through them in an interview.
The infrastructure
One decision function. Research and production call the same function. There’s no separate notebook that approximates the live system. That gap is where live behavior starts diverging from what I tested.
State that survives a crash. State persists between runs in a JSON-backed store written atomically, so an interrupted run resumes cleanly instead of rebuilding its view of the current regime from a truncated window. Order handling is idempotent, so a retry after a partial failure doesn’t double-submit.
Data quality gates. Market and macro feeds come through a pipeline with Parquet caching, data-quality checks, and lookahead-safe time alignment. Bad ticks, stale bars, and missing sessions get caught before anything reaches the allocator. If the feed looks wrong, the system sits out.
47 pytest tests across the decision tree, state transitions, gating logic, and the risk overlays. They cover regime transitions, breaker behavior at the hysteresis boundaries, and deadband logic. Mostly they exist so I can change things without guessing.
Monitoring. A Streamlit and Plotly dashboard surfaces live positions, system state, and performance. I need to be able to see what it is doing when something looks wrong.
Validation
One backtest number doesn’t tell you much, so I put it through a few things that could have killed it:
| Walk-forward folds | Parameters fit on one window, evaluated on the next, rolling forward. No single global fit. |
|---|---|
| Pre/post-2020 splits | 2020 is an outlier regime that flatters almost any volatility-responsive strategy. Reported separately for exactly that reason. |
| Slippage & commission stress | Costs scaled well past realistic levels to find where the edge disappears. |
| Block bootstrap | Resampling in blocks to preserve autocorrelation, testing whether results depend on the specific historical path. |
Of those four, the 2020 split is the one that changed how I report the strategy. Quoting only the number that includes 2020 would overstate what Rime does in a normal year. That is why both figures are at the top of this page instead of buried down here.
What I’d do next
- Swap the shallow tree for a probabilistic regime model, and check honestly whether it beats the rules instead of assuming it will.
- Model execution against intraday liquidity instead of assuming a fill at the daily close.
- Widen the macro overlay carefully. More inputs means more degrees of freedom.