Adds `timeframe` field to DSL expressions so a single strategy can
reference candles from multiple intervals simultaneously. The primary
`candle_interval` still drives evaluation cadence; additional timeframes
are read-only context evaluated in parallel.
**DSL / DAL (`strategy_config.rs`)**
- `timeframe: Option<String>` on `Expr::Field`, `Expr::Func`,
`Expr::ApplyFunc`, and all five legacy shorthand conditions
- `parse_interval_secs(interval)` helper
- `collect_timeframes(params)` walks the rule tree, returns all
referenced interval strings — used by API validation and executor
**Executor (`paper-executor`)**
- `SwymInstrumentData` now keyed by interval: `candle_histories`,
`ema_states`, `trade_prices` are all `HashMap<u64, …>`
- `next_candle_interval_hint` side-channel routes each incoming
`DataKind::Candle` to the correct per-timeframe history
- `candle_ready` is still gated exclusively on the primary timeframe
- `EvalCtx` carries `primary_interval_secs`; resolver helpers
(`candle_history`, `ema_state`, `trade_prices`) translate an
`Option<String>` timeframe to the correct map entry
- `ema_registrations() -> Vec<(u64, usize)>` replaces the old
single-timeframe `ema_periods()` for pre-warming EMA state
- Backtest runner merge-sorts candles from all required intervals by
`time_exchange` and feeds them in chronological order
**API (`paper_runs.rs`)**
- At backtest creation, calls `collect_timeframes` on rule-based
strategies and validates each additional timeframe: must be a known
interval and must have candle data covering the requested range
**Dashboard**
- `AddStrategyPage`: expanded DSL reference panel — added `event_count`,
`apply_func`, `unary_op`, `bars_since`, all func names (`wma`, `atr`,
`adx`, `supertrend`, `sum`), `multiplier`, and a new Multi-timeframe
section with a worked example
- `AddPaperRunPage`: shows per-additional-timeframe coverage chips and
backfill buttons alongside the primary-interval coverage indicator
**Docs / assets**
- `docs/strategy-dsl.md`: added Multi-timeframe expressions section and
updated all path references
- `docs/strategy.schema.json` → `assets/strategy/schema.json` (new
location; updated `$id`, added `TimeframeInterval` definition, added
`timeframe` property to six schema nodes)
- `assets/strategy/emmanuel-ma-v{1..8}.json` →
`assets/strategy/emmanuel-ma/v{1..8}.json` (grouped into subfolder);
seeder `include_str!` paths updated accordingly
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>