ZIPLIME
The backtester your AI can't fool.
ZIPLIME is an open-source Python backtesting engine built to catch what AI-written strategies get wrong:
look-ahead, fills that never happened, costs that quietly disappear.
Claude Code or Codex will write you a strategy in a minute, and it will sound convincing either way.
ZIPLIME checks whether the backtest behind it is honest.
What the engine checks · Quick start · Use it with your AI · How it compares · License · Docs
next-bar fills by default · look-ahead checks on vectorized signals · point-in-time data · Zipline-style API · works inside Claude Code, Codex and Cursor · AGPL-3.0, your strategies stay yours
Why this exists
AI made writing a trading strategy free. It didn't make knowing whether the strategy is true any cheaper.
A model will write you a backtester, an HFT algorithm, a factor model — fluently, with nice charts. If you don't know the field well, you can't tell which parts are nonsense, and asking the same model doesn't help: it wrote them.
Ziplime is the second opinion that can't be talked round. The model can be confident; the engine is strict. It won't fill an order at a price that didn't exist yet, show a strategy a number before it was public, or quietly drop a cost it can't apply — and when it refuses, it says where and why.
The AI proposes. The engine checks. You decide.
🛡 What the engine refuses to fake
Strict by default — the shortcuts that make backtests look good have to be asked for by name.
- Next-bar fills. An order fills on the bar after the decision. Filling at the close of the bar the decision was made on is an explicit flag (
--same-bar-execution), never something a strategy inherits. - Vectorized signals are checked for causality.
compute_signalsis recomputed on truncated history; a signal that used the future — a full-sample mean, ashift(-1)— stops the run before it starts:Signal 'momentum' looks ahead. At bar 240 (2024-03-15) the whole-history computation gives 1.0, but computing from the first 240 bars alone -- all that was known then -- gives 2.0. - Intraday bars without look-ahead. Vendor bars are placed on the simulation clock so no bar leaks into the one before it.
- Point-in-time data by knowledge date. Fundamentals, insider and congressional-trading datasets are windowed by when a row became public, not by the period it describes.
- Costs are applied or refused, never dropped. A cost model the vectorized kernel can't apply stops the run by name — unpaid costs don't show up as an error, they show up as a higher return.
- No tool places an order on its own. The local AI server has no order tool at all; on the hosted platform a live deployment is created paused and starts only when you say so.
What the engine can't check: whether your idea has an edge, whether your data source has survivorship bias (Yahoo's does — delisted stocks aren't there), whether you tuned twenty parameters on two hundred trades. That judgment is still yours.
Found a way to sneak the future past the engine? That's a bug — open an issue.
🐍 Quick start
pip install ziplime
ziplime ingest-assets # once: the instrument list (Yahoo, no key)
ziplime ingest -b demo -s AAPL,MSFT --start-date 2019-01-01 --end-date 2025-12-31
# sma.py
from ziplime.domain.bar_data import BarData
from ziplime.finance.execution import MarketOrder
from ziplime.trading.trading_algorithm import TradingAlgorithm
async def initialize(context: TradingAlgorithm):
context.assets = [await context.symbol("AAPL"), await context.symbol("MSFT")]
context.fast, context.slow = 20, 50
async def handle_data(context: TradingAlgorithm, data: BarData):
for asset in context.assets:
bars = await data.history(assets=[asset], fields=["close"], bar_count=context.slow)
if len(bars) < context.slow:
continue
closes = bars["close"]
target = 0.5 if closes[-context.fast:].mean() > closes.mean() else 0.0
await context.order_target_percent(asset=asset, style=MarketOrder(), target=target)
ziplime run -f sma.py -b demo -s AAPL,MSFT --start-date 2020-01-01 --end-date 2025-12-31
Coming from Zipline? The shape is the same — initialize, handle_data, order_target_percent. Two things change: everything that touches the engine is awaited, and data.history returns a Polars frame, so .iloc / .loc become bars["close"][-1]. A missing await doesn't raise; it silently places no orders. See the docs.
🤖 Use it with your AI
Whatever writes your strategies — Claude Code, Codex, Cursor — can call Ziplime directly through MCP, so the numbers come from the engine instead of from the model. Same checks, whoever wrote the code.
Local — ziplime mcp |
Hosted — ziplime.limex.com/mcp |
|
|---|---|---|
| Install | pip install "ziplime[mcp]" |
nothing |
| Account | none | ZipLime account |
| Data | Yahoo Finance (free), point-in-time datasets on Hugging Face, your own CSVs | Professional US equity data with fundamentals and factors, from the Nasdaq-100 to every US listing |
| What the agent can do | ingest, write, check, backtest, compare | all of that, plus screening, factor analysis and relationship discovery |
| Live trading | from your terminal, never through a tool | readiness check → paused deployment → explicit start, via Lime |
Local — Claude Code:
pip install "ziplime[mcp]"
claude mcp add ziplime -- ziplime mcp
Claude Desktop, Cursor, Windsurf or any client that launches stdio servers:
{ "mcpServers": { "ziplime": { "command": "ziplime", "args": ["mcp"] } } }
If you installed into a virtualenv, put the full path to its ziplime executable in command — desktop apps don't see your shell's PATH.
Hosted — Claude Code: claude mcp add --transport http ziplime https://ziplime.limex.com/mcp, or in any client:
{ "mcpServers": { "ziplime": { "url": "https://ziplime.limex.com/mcp" } } }
You: Find S&P 500 names with rising margins near their 52-week high. Has that ever predicted returns? If it has, backtest it and tell me what it would cost to run.
Agent:
screen_market→analyze_signal→create_strategy→check_strategy_code→run_backtest→get_backtest_metrics→compare_execution_costs→assess_live_readiness…and reports the factor's information coefficient by horizon, the backtest and the real cost of its fills — then stops before anything goes live.
What the hosted server adds — universes, fundamentals, factor research
| Universe | Covers | Instruments |
|---|---|---|
Q100US |
Nasdaq-100 | 96 |
Q500US |
S&P 500 | 455 |
Q1500US |
S&P 1500 Composite | 1,299 |
USALL |
every US equity with data | 3,197 |
Quantopian veterans will recognise the names.
- Screening —
screen_marketruns your condition across a universe with fundamentals and factor data alongside prices: balance sheet, income statement, cash flow, value, growth, profitability, momentum, short interest and more. - Factor analysis —
analyze_signalreports the information coefficient per horizon, returns by quantile, the top-minus-bottom spread and turnover. If you have used Alphalens, you know the shape. - Relationship discovery —
discover_relationshipsmeasures how market states described in words lined up with what happened next, holding part of the history back;validate_relationshiptests the finding on the period it never saw. - Execution costs —
compare_execution_costsreprices a finished run's actual fills under different commission schedules without rerunning. - Path to live —
assess_live_readiness→prepare_deployment_plan→create_live_deployment(paused) →start_live_deployment, called only when you ask in so many words.
Tool reference — 14 local tools, 40+ hosted
Local — ziplime mcp:
| Stage | Tools |
|---|---|
| Data | list_data_providers · list_bundles · ingest_instruments · ingest_market_data |
| Build | get_strategy_language_reference · write_strategy · check_strategy_code · list_strategies · read_strategy |
| Test | run_backtest · list_backtests · get_backtest · compare_backtests |
| Live | describe_live_trading — explains the route to live; places no orders |
Hosted, including:
| Stage | Tools |
|---|---|
| Research | search_tickers · list_universes · list_themes · screen_market · analyze_signal · get_stock_factors · discover_relationships · validate_relationship |
| Build | create_strategy · update_strategy · copy_strategy · check_strategy_code · get_strategy_language_reference |
| Test | run_backtest · get_backtest_metrics · get_backtest_trades · compare_backtests · compare_execution_costs · promote_backtest |
| Ship | assess_live_readiness · prepare_deployment_plan · create_live_deployment · start_live_deployment · pause_live_deployment · get_live_deployment_activity |
The two servers run the same engine under different code contracts. Locally a strategy is a normal Python module: imports, before_trading_start, analyze and a config class all work. The hosted server runs strategies in a sandbox without imports. Each server hands the agent its own rules through get_strategy_language_reference.
⚡ Event-driven and vectorized, one ledger
An event-driven engine is right for strategies that read their own book — sizing off portfolio value, exposure caps, futures rolls. A vectorized engine is right for arithmetic and for sweeps. Ziplime has both, and both report through the same ledger, cost models and metrics, so the results are directly comparable.
The vectorized design is inspired by vectorbt — credit where it's due. The kernel is Ziplime's own (pip install "ziplime[numba]" compiles its inner loop; without it the same loop runs interpreted), and vectorbt serves as a reference oracle in the test suite: tests/vectorized/reference/vectorbt checks fills against it and asserts the deliberate differences instead of hiding them.
| What is vectorized | Execution & costs | Futures / options | For | |
|---|---|---|---|---|
Signals inside a run — compute_signals |
the strategy's arithmetic | Ziplime's blotter, every cost model | ✅ | one strategy, faster |
Sweep, then report — simulate_signals |
the whole run | Ziplime's kernel and cost models | refused | hundreds of variants |
WARMUP = 60 # sessions of history the indicators need
def compute_signals(context, prices): # one pass over the whole history
return {
"fast": prices.close.rolling(20).mean(),
"slow": prices.close.rolling(60).mean(),
}
async def handle_data(context, data): # bar by bar, as usual
if not context.signals.is_ready("slow"):
return
... # sizing, caps, rolls stay here
context.signals is cut to the simulation clock — on bar t there is no accessor that returns row t+1. A sweep's winner is replayed through the ledger with reconciliation after every bar. From examples/vectorized: 48 moving-average combinations swept in 0.2 s, winner replayed through the ledger; vectorizing a strategy's signals cut time spent in handle_data by 2.3×.
📈 Asset classes and data
- Equities & ETFs — US markets out of the box
- Futures — dated contracts with multipliers, expirations and rolls; seven example strategies on real Yahoo futures chains in
examples/futures - Options — spreads, structures and 0DTE examples in
examples/options - Bonds and cross-asset portfolios —
examples/cross_asset
| Source | What | Cost |
|---|---|---|
| Yahoo Finance | Historical OHLCV, futures chains | Free |
| Hugging Face Hub | Point-in-time fundamentals, insider trades, congressional trades, earnings calendar — mounted straight into data.history, no ingest (pip install "ziplime[huggingface]") |
Free |
| CSV | Your own data | Free |
| Hosted server | US universes up to 3,197 equities with fundamentals and factors, plus research tools | ZipLime account |
| Lime Trader SDK | Real-time & historical | Broker account |
| LimexHub | Professional-grade market data and fundamentals for local runs | Subscription |
🔴 Backtest → live
Execution sits behind one interface. A backtest uses SimulationExchange; live trading swaps in LimeTraderSdkExchange and runs the same strategy file — see examples/run_live_trading.py (credentials via LIME_SDK_CREDENTIALS_FILE). Lime is the reference adapter; another broker means subclassing the same exchange interface.
Live trading is never exposed as a local MCP tool: a tool that submits real orders is one misunderstanding away from a model using it.
🍋 Zipline, rebuilt
Zipline set the standard for Python backtesting and then stalled on old pandas and old Python. Ziplime started as a fork of it — the git history says so — and most of the engine has since been rewritten. The files that still carry Zipline code keep Quantopian's copyright and Apache 2.0 notice (see NOTICE).
What Ziplime kept is the programming model. What it replaced: Polars and Parquet in the data layer instead of pandas/NumPy and bcolz, asyncio end to end, any bar size from 1 second to 1 month, a vectorized kernel next to the event-driven engine, and Python 3.12+.
🧭 Pick your door
| You are… | Start here |
|---|---|
| Writing strategies with Claude Code or Codex | Connect Ziplime so your agent backtests for real. |
| A quant or Python developer | pip install ziplime — the engine, local, yours. |
| Researching factors on real universes | The hosted server — S&P 1500 data, screening and factor analysis without a data pipeline. |
| A trader who doesn't want to code | ziplime.limex.com — chat, backtest, deploy in the browser. |
| Building a product for your own clients | Ziplime is AGPL-3.0; for a closed product, see the commercial license. |
⚔️ How it compares
As of October 2026. Something wrong or out of date? Open an issue and we'll fix the table.
| Ziplime | QuantConnect LEAN | zipline-reloaded | NautilusTrader | backtrader | vectorbt | |
|---|---|---|---|---|---|---|
| Engine core | Python · Polars | C# · Python API | Python · pandas | Rust · Python API | Python | NumPy/Numba |
| Simulation | event-driven + vectorized | event-driven | event-driven | event-driven | event-driven | vectorized |
| Zipline-style API | ✅ (async) | — | ✅ | — | — | — |
| Futures | ✅ | ✅ | ✅ | ✅ | ✅ | PRO only¹ |
| Options | ✅ | ✅ | — | ✅ | — | — |
| Built-in fundamental / alt data | ✅ free PIT datasets + hosted² | ✅ QC Data Library | — | — | — | — |
| Live trading | ✅ Lime³ | ✅ many brokers | — | ✅ many venues | ✅ IB, Oanda | — |
| Hosted, no-install version | ✅ | ✅ | — | — | — | — |
| Official MCP server | ✅ local + hosted | ✅ cloud⁴ | — | — | — | — |
| License | AGPL-3.0⁵ | Apache-2.0 | Apache-2.0 | LGPL-3.0 | GPL-3.0 | Apache-2.0 + Commons Clause |
| Actively maintained⁶ | ✅ | ✅ | ⚠️ | ✅ | ❌ | ✅ |
¹ Contract multipliers for futures are a VectorBT PRO feature. ² Free: fundamentals, insider, congressional-trading and earnings-calendar datasets on the Hugging Face Hub, windowed by knowledge date. Hosted: US universes with fundamentals and factors. ³ Lime is the built-in adapter; other brokers plug in by subclassing one exchange interface. ⁴ QuantConnect's MCP server drives the QuantConnect cloud API and needs a QuantConnect account. Ziplime's local server runs on your own machine with no account. ⁵ With an additional permission that keeps your strategies outside the AGPL, and a commercial license for closed products. See License. ⁶ Latest PyPI releases: zipline-reloaded July 2025, backtrader April 2023; the others released within the last two months.
🤝 Community
- 💬 GitHub Discussions — questions and ideas
- ⭐ Star the repo — it's how new people find us
- 📖 Documentation
- 🐛 Issues · Good first issues
- 🏠 Built by Limex
Nothing in Ziplime or its docs is investment advice.
⚖️ License
Ziplime is open source under the GNU AGPL v3, with an additional permission for your strategies.
Do I have to publish my strategies?
No. Strategies, signal code, configuration and data that use Ziplime through its strategy interface — initialize, handle_data, compute_signals and the rest — are yours, under any terms you choose. Your alpha stays yours.
Can a fund or a prop desk use Ziplime internally? Yes. Running Ziplime, modified or not, for your own research and trading puts you under no obligation to publish anything. Obligations begin only when you distribute Ziplime, or offer a modified version of it to outside users over a network.
Building a product or a service for clients on top of Ziplime? Either make the source of your modified Ziplime available to its users under AGPL-3.0, or get a commercial license. The commercial license is also the answer if your company's policy doesn't allow AGPL. Write to us:
Contributing. Pull requests are accepted under a Contributor License Agreement, which is what lets the commercial license exist. A bot walks you through it on your first PR.
Earlier code. Everything published before the switch, including releases up to 1.19.x, remains available under GPL-3.0.
The hosted platform at ziplime.limex.com is covered by its own terms of service.
The AI proposes. The engine checks. You decide. 🍋
Metadata
Release files for ziplime 2.11.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ziplime-2.11.0.tar.gz | 913.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ziplime-2.11.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 2.0 MB
Release files / ziplime-2.11.0.tar.gz
| Download URL | ziplime-2.11.0.tar.gz |
|---|---|
| Size | 913.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cf4f5a8c13c34d14c3e83f3ca2bf87a6bf603ffab8b358bfb64482fcd6daf0da
|
|
BLAKE2b-256 checksum How to use checksums |
5456cb7a506f7f58d8e257bcc184ad3c21c880ede99e4f73dd9552c6b9bacc3b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.5.1 CPython/3.12.3 Linux/6.17.0-1022-azure
|
Release files / ziplime-2.11.0-py3-none-any.whl
| Download URL | ziplime-2.11.0-py3-none-any.whl |
|---|---|
| Size | 1.1 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3c3d961344907cb55dd03234de1be3e6f6471ff5435cbf4c4bfe19bcdd7d33d2
|
|
BLAKE2b-256 checksum How to use checksums |
ffaf2d84959ff2ba0d511580e839c3855e43a06c87135e35160f07ba060e0107
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.5.1 CPython/3.12.3 Linux/6.17.0-1022-azure
|