Ziplime keeps the event-driven strategy model of classic Zipline and zipline-reloaded, but it is not a drop-in replacement. The differences that matter: async strategy functions, Polars data frames, explicit execution styles, and next-bar fills by default.
What stays the same
- Persistent strategy state lives on
context.
initialize sets up; handle_data trades bar by bar.
schedule_function with date_rules and time_rules, record, order_target_percent, pipelines, trading controls.
Quick conversion table
Imports
Import what you use — the algorithm file is a normal Python module. Prefer context methods over the zipline.api functions:
ziplime.api exists for compatibility, but context calls are clearer in async code.
Lifecycle functions
Classic Zipline:
Ziplime:
before_trading_start should be async def so it can await data reads. analyze(context, perf) stays plain def, and perf is still a pandas DataFrame.
A missing await does not raise. The call returns a coroutine that never runs, and the migrated strategy silently places no orders.
Market data
Ziplime expects lists and returns a Polars DataFrame with date, sid and the requested fields:
.iloc, .loc, .at and .iat don’t exist on a Polars frame. If old code depends on pandas, convert explicitly with history.to_pandas(); prefer Polars expressions for new code.
data.history excludes the current bar. Read it with data.current when the signal needs it.
Orders
Order calls are async and take an execution style:
Many Zipline rebalancers loop over order_target_percent and then close what’s left. context.rebalance(weights) does both:
Fill timing changes results
The CLI and the MCP server fill on the next bar by default; Zipline’s typical setup filled on the same bar. Expect a migrated backtest to differ. That difference is the look-ahead the old setup had. run_simulation still defaults to same-bar — pass same_bar_execution=False to compare like for like.
Symbols
Pass a MIC only when a ticker exists on several venues. For named universes, await context.universe_symbols("Q500US") returns tradeable listings and follows membership over time.
Scheduling
Time rules work at any intraday emission rate; on a daily run the time rule is ignored. For a simple weekly or monthly rebalance, if not context.is_month_start(): return at the top of handle_data is often enough.
Portfolio
Positions are keyed by exchange, account and listing, so use helpers:
Pipelines
The Pipeline API is Zipline-like — Pipeline, EquityPricing, CustomFactor, screens, attach_pipeline, pipeline_output. Pricing via EquityPricing is the standard path; custom datasets need custom loaders. For alternative data, point-in-time datasets are usually the simpler route.
Running migrated algorithms
Or from Python with run_simulation(...) — see Install & Run.
Full before and after
Classic Zipline:
Ziplime:
Migration checklist
- Make
initialize, handle_data, before_trading_start and scheduled callbacks async def; keep analyze plain def.
- Add
await to asset lookups, data reads, orders and cancellations.
- Pass lists to
data.current and data.history; replace pandas indexing with Polars.
- Add an execution style such as
MarketOrder() to order calls.
- Replace
portfolio.positions[asset] with portfolio helper methods.
- Pass MIC codes only for ambiguous tickers.
- Rebuild data:
ziplime ingest-assets, then ziplime ingest.
- Expect different numbers from next-bar fills and enforced controls, and set a real commission.