pybroker-multi-interval
GitHub指导构建、调试基于PyBroker的多时间框架量化策略,涵盖区间压缩、指标绑定、模型训练及回测验证。
Trigger Scenarios
Install
npx skills add edtechre/pybroker --skill pybroker-multi-interval -g -y
SKILL.md
Frontmatter
{
"name": "pybroker-multi-interval",
"description": "Build, wire, and debug multi-timeframe PyBroker strategies using the bundled PyBroker wiki references generated from the local docs. Use when an agent needs to trade a base timeframe with confirmation from coarser weekly or monthly bars, compress bars into higher time intervals, declare compressed bars with the intervals parameter of add_execution, read completed higher-timeframe bars with ctx.interval and IntervalContext, bind indicators to intervals with Indicator.intervals, train models per interval with ModelSource.intervals, choose interval formats such as every-n-bars ints, duration strings like 5m or 1h, or calendar strings like weekly and monthly, pass timeframe to backtest, walkforward, or optimize, compress OHLCV bars standalone with compress_bars, guard warmup while interval arrays are still empty, or debug interval errors such as undeclared intervals, missing timeframe, or intervals not strictly coarser than the base data."
}
PyBroker Multi-Interval
Overview
Build multi-timeframe PyBroker strategies that trade a base timeframe while confirming regime and trend on strictly coarser compressed intervals such as weekly and monthly bars. Covers the three interval formats, providing compressed bars with add_execution(intervals=...), computing indicators and training models per interval by binding them with .intervals(...), reading completed bars through ctx.interval(...), and standalone compression with compress_bars. Strategy code only ever sees completed compressed bars, which keeps higher-timeframe logic free of partial-bar lookahead.
Workflow
- Extract the multi-interval spec: base timeframe and bar spacing, the coarser intervals and their formats, the job of each timeframe (regime, trend, timing), whether each interval needs raw bars only or per-interval indicators and models, whether base-timeframe variants must be kept alongside bound ones, backtest versus walkforward, and desired output file/notebook.
- Ask only for missing blockers. If details are absent but noncritical, make conservative assumptions and state them in the final answer or code comments.
- Read
references/wiki-index.mdto choose the smallest relevant wiki page. For nontrivial multi-interval work, also readreferences/multi-interval-patterns.md. - Build a complete runnable multi-interval surface:
- start scripts with
pybroker.disable_progress_bar()andpybroker.enable_data_source_cache("<name>")(orpybroker.enable_cacheswhen models are trained) - choose each interval's format (an int greater than 1 for every-n-bars, a single-unit duration string such as
"5m"or"1h", or a calendar string such as"weekly") and keep every interval strictly coarser than the base timeframe - declare bars-only intervals with
add_execution(..., intervals=...); bind per-interval indicator computation and model training withind.intervals(...)/model_source.intervals(...)passed toindicators=/models=, including"base"when the base-timeframe variant must also exist - write execution logic that reads
ctx.interval("weekly")completed bars behind length guards (if len(weekly.close) < 10: return) and sets orders on the basectxonly - run
backtest/walkforwardwithtimeframe="<base spacing>"; usepybroker.compress_bars(df, interval, base_timeframe=...)for standalone compression and validation
- start scripts with
- Validate the produced code as far as the environment allows. At minimum, run syntax checks for created Python files. When practical, backtest against a small local DataFrame and run the multi-interval bump-last-bar test from
references/multi-interval-patterns.md.
Implementation Rules
- Treat PyBroker as a backtesting framework, not a source of financial advice. Make strategy assumptions explicit and avoid performance claims that are not supported by a produced backtest.
- Use completed historical bar data only.
ctx.interval(...)exposes only completed compressed bars: the week or month still forming is never visible, and execution logic must never reconstruct the forming bin from base bars to peek at it. A value at barimay depend only on inputs at indexiand earlier, and negative indexing into full-length arrays is forbidden (a negative index silently wraps to the end of the series — the future). - Interval grammar: an interval is an int greater than 1 (every n base bars), a duration string of digits plus one unit letter
s/m/h/d("5m","1h"; never"5min"or compound spans like"1h 30m", and week durations such as"2w"are rejected — use"weekly"or"14d"), or a calendar string"daily"/"weekly"/"monthly"/"quarterly"/"yearly"(weeks start Monday, months on the 1st, quarters in January/April/July/October). Every interval must be strictly coarser than the base timeframe, and empty or duplicate interval lists raiseValueError. - Whenever any execution declares or binds an interval,
backtest/walkforward/optimizerequiretimeframe=stating the base bar spacing (e.g.timeframe="1d"), which is validated against the observed data spacing. Thetimeframegrammar is wider than interval grammar — compound spans like"1h 30m"and thewunit are legal there — so never reuse atimeframestring as an interval. add_execution(..., intervals=...)provides compressed bars only and is scoped to that execution: sibling executions andset_before_exec/set_after_execcallbacks cannot read them, and no indicator or model is ever computed on an interval unless bound to it. Bound intervals are automatically unioned intoctx.interval, so do not repeat them inintervals=.- Binding is exhaustive:
ind.intervals("weekly")/model_source.intervals("weekly")replaces base-timeframe computation, so include the literal"base"(e.g..intervals("base", "weekly")) to keep the base variant; unbound sources default to base."base"is valid only inside a binding — it raises inintervals=and inctx.interval. ctx.interval(...)returns a read-onlyIntervalContextexposingbars,dates, OHLCV arrays,indicator(name),model(name),input(name),preds(name), and registered custom columns as attributes (there is novwapproperty). Arrays hold completed compressed bars only and are empty during warmup, so guard every read with a length check such asif len(weekly.close) < 10: return. Setting order or stop attributes on anIntervalContextraises — set them on the basectx.- Read interval values with base names only:
weekly.indicator("sma_10"), never"sma_10@weekly". The@character is reserved for the interval-suffixed names PyBroker generates itself and is invalid in registered indicator and model names. - Only trainable models bind to intervals; a pretrained model (
ModelLoader) raisesValueError. An interval-bound model'strain_fnreceives compressed-bar DataFrames with its registered indicators under their base column names — work on a.copy()when adding a target and never widen or mutate the input frame.lookaheadis measured in the bound interval's compressed bars, and PyBroker warns when the hold-out empties the train set. Read predictions withctx.interval(...).preds(name)behind a length guard;TestResult.signalscontain base-timeframe values only unless"base"is in the binding. - Compress standalone with
pybroker.compress_bars(df, interval, base_timeframe=...), which accepts a single symbol only (multi-symbol frames raise, pointing tocompress_symbol_from_frame/compress_intervals_from_frame). Aggregation: open from the first base bar, high/low extremes, close from the last base bar, volume summed, VWAP volume-weighted, custom columns take the last value, and each compressed bar is dated by the last base bar it contains. - On any interval
ValueError, match the message against the Common Errors table inreferences/multi-interval-patterns.mdbefore changing code — the messages name the fix. - Never use pandas to implement indicator or execution logic. Indicator functions operate on
BarDataNumPy arrays (the same function runs unchanged on whichever interval it is bound to), prefer the vectorized helpers (highv,lowv,sumv,returnv,cross,atr), JIT-compile explicit loops with a nested Numba@njitkernel, and return a full-length one-dimensional array with NaN warmup bars — never a shortened array. Never construct apd.Seriesorpd.DataFrameand never call pandas methods such as.rolling,.ewm,.shift, or.applyinside an indicator function or a per-bar execution function; the only sanctioned pandas is thetrain_fn/input_data_fnmodel boundary. - Guard base-timeframe lookbacks with
ctx.barsorwarmup=and interval lookbacks with length guards (base-barwarmup=is no substitute — compressed arrays fill on their own schedule), and set at most one order side per symbol per bar. - Start generated scripts with
pybroker.disable_progress_bar()so progress output does not flood agent context, andpybroker.enable_data_source_cache("<name>")(orpybroker.enable_cacheswhen models are trained) so repeated runs do not refetch data. Addpybroker.disable_logging()when running many backtests, such as parameter optimization. - Fills always price off base-timeframe bars: declaring
intervals=or binding an indicator to an interval changes what the execution function reads, never how an order fills. Orders fill atPriceType.MIDDLE— the midpoint of the low and high of the execution base bar, which under the defaultbuy_delay/sell_delayof1is the base bar after the signal, soPriceType.CLOSEmeans the next base bar's close, not the compressed bar's close. Override withctx.buy_fill_price/ctx.sell_fill_price, which take aPriceType(OPEN,HIGH,LOW,CLOSE,MIDDLE,AVERAGE), a number, or a(symbol, bar_data)callable whosebar_datais likewise base-timeframe. A limit price only gates the fill: the order still fills at the fill price, never at the limit. StrategyConfig.exit_on_last_bardefaults toFalse, which leaves any position still open when the data ends out oftrade_count,win_rate,total_pnland every other trade-level metric, with its P&L stranded inunrealized_pnl. Setexit_on_last_bar=Truewhenever trade statistics are reported; the liquidation lands on the symbol's final base bar, usually mid-way through the last compressed bar.calc_bootstrapis abacktest/walkforwardparameter defaulting toFalse, not aStrategyConfigfield; passcalc_bootstrap=Trueto populateresult.bootstrapwith BCa confidence intervals for profit factor and Sharpe plus percentile bounds on max drawdown, and setStrategyConfig.bars_per_yearto the base timeframe's bar count or the Sharpe intervals are per-bar rather than annualized.- Report
result.metrics_dfas the human-readable summary. When structured output is needed (agent parsing, saved report files, downstream tools), useresult.to_json()/result.to_json_str(): the default payload serializes metrics, trades, orders, and bootstrap capped atmax_rows=100rows per table,symbols=filters to specific tickers, andinclude=opts intoportfolio/positions/metrics_df/signals/stops. Do not replace themetrics_dfprint outright: the default JSON payload (trades plus orders) is usually larger than the metrics table. - If a Numba
@njitfunction fails to compile or raises a cryptic error such as aTypingError, re-run once with theNUMBA_DISABLE_JIT=1environment variable to get a readable Python traceback, fix the underlying code, then remove the variable so the backtest runs compiled. - Self-test novel multi-interval logic for lookahead with the bump-last-bar check in
references/multi-interval-patterns.md: change only the final base bar and assert every completed compressed output is unchanged — the trailing partial bin absorbs the bump. - If exact API names, constructor parameters, or methods matter, read
references/api-public-surface.md. - For exact type signatures, read the matching
references/pybroker_*.pyistub:IntervalContextinpybroker_context.pyi;TimeframeIntervalandCalendarIntervalinpybroker_types.pyi;compress_bars,Indicator.intervals/IntervalBoundIndicator, andModelSource.intervals/IntervalBoundModelinpybroker_model.pyi; thebacktest/walkforward/optimizetimeframe=parameters inpybroker_strategy.pyi. - If the user wants a standalone file, copy and adapt
assets/multi_interval_template.py.
Common Deliverables
- Standalone
.pymulti-timeframe backtest script that trades a base timeframe with higher-interval confirmation. - Upgrade of a single-timeframe strategy to read higher-interval bars, indicators, or predictions.
.intervals(...)bindings that wire per-interval indicators and models into an existing strategy.- Standalone bar-compression analysis with
compress_barson a local DataFrame. - Debugging notes and patches for interval ValueErrors, empty warmup arrays, and partial-bar lookahead.
Resources
references/wiki-index.md: start here for topic routing across the bundled references.references/wiki-15-multiple-time-intervals.md: interval types, compressing bars, multi-timeframe strategies, and binding indicators and models to intervals.references/wiki-05-writing-indicators.md: custom indicators, vector helpers, TA-Lib, built-in indicators, and indicator sets.references/wiki-06-training-a-model.md: model training, model predictions, caching, and walkforward analysis.references/api-public-surface.md: generated public API signatures and first docstring sentences from local source.references/pybroker_context.pyi: generated type stubs forExecContext(including its writable order/stop attributes),IntervalContext,RotationContext,ExecResult, and the slippage models.references/pybroker_types.pyi: generated type stubs for enums,BarData,Portfolio, order/trade/position records, and evaluation result types.references/pybroker_model.pyi: generated type stubs formodel(),indicator(), vector helpers, data sources, and top-level module functions.references/pybroker_strategy.pyi: generated type stubs forStrategy,StrategyConfig,TestResult, and the optimization types.references/multi-interval-patterns.md: load when writing nontrivial multi-interval code; interval grammar, bars-versus-binding scope, compression semantics, the interval error table, and the validation checklist.assets/multi_interval_template.py: copy and adapt when creating a new standalone multi-timeframe script.
Version History
- db53c67 Current 2026-08-20 08:26


