Skip to main content
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.