algorithmic trading  ·  python  ·  live execution

// no more wait
.. just await

How the Zipline ecosystem evolved toward asynchronous live trading — and why a single keyword in front of every function call turns out to be the answer we were all circling around.

I spent several years on Quantopian. Writing strategies, running backtests, staring at equity curves — convincing myself I was ready for the real market. When the platform shut down in 2020, like many others I went looking for what came next. What I found was a rich, earnest, sometimes frustrating ecosystem of forks, each one trying to answer the same question: how do you keep the Quantopian-style API and actually trade live with it?

I worked through most of them. Each taught me something. And each one, in its own way, kept running into the same wall.


The ecosystem

The history of Zipline forks is one of the better illustrations of how genuinely hard it is to bridge backtesting and live execution in a single framework. Each project found its own angle, and each one contributed something real to the community.

zipline-live archived

One of the earliest forks to seriously pursue live trading — connecting Zipline to Interactive Brokers via the TWS API. A genuine pioneer: it proved the concept was workable and gave the community its first real taste of Zipline-compatible live execution. The authors worked within the existing synchronous core, bridging real-time broker data through threading. The project is no longer maintained, but as a proof of concept for its time it pointed the way for everything that came after.

zipline-reloaded maintained

Perhaps the most honest project in this list — and I mean that as a genuine compliment. zipline-reloaded staked out a clear lane: keep the original Zipline alive and working on modern Python, with up-to-date dependencies. No live trading promises, no overreach. If you need reliable backtesting with a familiar API, this is a strong, well-maintained choice. It knows exactly what it is.

zipline-trader low activity

A meaningful step forward: Python 3, Alpaca support, extended calendars for crypto markets. The authors did real work modernising the stack and gave the community a usable tool for getting first live strategies off the ground. Broker integration relied on polling in a separate thread — a pragmatic choice within the constraints of the existing architecture. Activity has quieted down since, but the project left a visible mark and likely helped many people execute their first live trade.

zipline-live

open source · archived

The original pioneer. Proved live trading was possible in a Zipline-compatible API. IB via TWS.

zipline-reloaded

open source · active

Backtesting only, done well. Modern Python, active maintenance, honest scope.

zipline-trader

open source · low activity

Python 3, Alpaca, crypto calendars. Got people to their first live execution.

Ziplime

open source · active

async core. await everywhere. The broker is part of the architecture from day one. → github

These projects form a lineage, not a competition. Ziplime stands on the shoulders of the work done before it — without zipline-live and zipline-trader mapping the terrain, it would have been much harder to see exactly where the architectural ceiling was.


Where the ceiling is

Every fork up to Ziplime worked with the same synchronous Zipline core — and that was a reasonable choice. Rewriting the foundation is expensive, breaks backward compatibility, and changes the public contract of every function people had been using for years. Adding a threading wrapper on top is far more practical.

But there is a ceiling to that approach. In production, every familiar call is a network request:

zipline & forks — synchronous contract
# looks instant in backtest — blocks the thread in live trading
df            = data.history(assets=[asset], fields=["close"], bar_count=context.long_window)
context.asset = context.symbol("AAPL")
order_buy     = context.order_target_percent(asset=asset, target=1.0, style=MarketOrder())

In a backtest, data comes from disk — everything is instant. In live trading each of these calls can take 50–500 ms waiting on a broker or data provider. Synchronous code blocks the entire thread for that time. With a multi-asset strategy the delays compound on every tick.

Threading addresses part of the problem but introduces its own: shared mutable state, subtle race conditions, hard-to-reproduce bugs under load. That is the ceiling zipline-live and zipline-trader ran into — not for lack of effort, but because the constraint was architectural.

"You can't bolt async onto a synchronous foundation from the outside and expect it to hold under real market conditions."

What Ziplime changes

Ziplime took the step the other forks reasonably avoided: it rewrote the core engine for an asynchronous model. Every function that touches I/O became a coroutine — and that is visible directly in the API:

ziplime — asynchronous contract
async def handle_data(context, data):

  # non-blocking — event loop stays free while we wait for the broker
  df = await data.history(
      assets=[asset],
      fields=["close"],
      bar_count=context.long_window
  )

  context.asset = await context.symbol("AAPL")

  order_buy = await context.order_target_percent(
      asset=asset,
      target=1.0,
      style=MarketOrder()
  )

Behind each await is a hand-off to Python's asyncio event loop. While one coroutine waits for the broker to respond, the loop can handle other work — processing another asset, listening on a WebSocket feed, writing to a log. No threading, no shared state, fully deterministic.

In a backtest, await resolves instantly from local storage. In live trading, the exact same await makes a real network request without blocking anything else. One strategy file, two execution modes.

Synchronous core

Delays compound across calls. Threading as a workaround. The broker is an external layer grafted onto the engine.

Ziplime: async core

Event loop owns all I/O. Parallel requests via asyncio.gather(). The broker is a first-class citizen from day one.

For multi-asset strategies this matters significantly. Instead of fetching history for ten instruments sequentially, you can dispatch all ten requests concurrently with asyncio.gather() and wait for the slowest one — not the sum of all of them.


Closing

Looking at the Zipline fork ecosystem as a single arc, you see a community steadily working toward something. zipline-reloaded keeps the original alive. zipline-live and zipline-trader mapped out what live trading in this API space looks like — and where it strains.

Ziplime took that accumulated knowledge and asked a different question: what if instead of adapting a synchronous engine to the real market, we designed the engine so the real market felt native to it?

For years we waited for live trading to work properly in Zipline-land. Turns out the answer was just await.

github.com/Limex-com/ziplime

Open source, actively maintained. Drop a star if the idea resonates — and check the docs to get your first async strategy running.

View on GitHub