Skip to content

DevPatel-11/event-driven-backtesting-engine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

33 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

event-driven-backtesting-engine

An event-driven backtesting engine for trying out trading strategies against historical OHLCV data. You feed it a strategy and a CSV/Parquet file of bars, and it replays that data bar-by-bar through a strategy, a risk manager, and a simulated execution layer, tracking a portfolio's cash, positions, and equity along the way.

Why this exists

This started as a follow-up to an earlier project of mine, a limit order book matching engine. After building that, I got curious about the other side of the problem: given a matching/execution layer, how do you actually evaluate whether a trading strategy is any good before risking real money on it? That question is what backtesting engines are for, so I read up on how event-driven backtesters are usually structured, picked a stack (Python, pandas), and built this as a personal project to understand the pieces myself.

It's not a research-grade or production-ready system, there's no live trading, no real broker integration, and the execution model (fixed slippage in basis points, flat per-share commission) is a simplification, not a faithful market microstructure model. It's a working, tested implementation of the classic architecture, built to learn how the pieces fit together.

How it works

Everything flows through a single event loop, one historical bar at a time:

flowchart LR
    Feed["Data Feed\n(CSV / Parquet)"] -- MarketEvent --> Strategy
    Strategy -- SignalEvent --> Risk["Risk Manager"]
    Risk -- OrderEvent --> Exec["Execution Simulator"]
    Exec -- FillEvent --> Portfolio
    Feed -- MarketEvent --> Portfolio
    Portfolio --> Analytics["Performance Analytics"]
Loading
  • Data Feed reads OHLCV bars from a CSV or Parquet file and emits them in order as MarketEvents.
  • Strategy looks at each bar and decides whether to go long or exit, emitting a SignalEvent.
  • Risk Manager turns a signal into a sized OrderEvent, capping position size and risking a fixed fraction of cash per trade.
  • Execution Simulator "fills" the order against the bar's price, applying slippage and commission, producing a FillEvent.
  • Portfolio applies fills to update cash and positions, and marks the position to the latest close every bar to build an equity curve.
  • Performance Analytics computes Sharpe ratio, max drawdown, total return, and win rate from that equity curve, once the run is done.

Here's the same loop for a single bar, in more detail:

sequenceDiagram
    participant Feed as Data Feed
    participant Engine
    participant Strategy
    participant Risk as Risk Manager
    participant Exec as Execution Simulator
    participant Port as Portfolio

    Engine->>Feed: update_bars()
    Feed-->>Engine: MarketEvent
    Engine->>Port: update_market_value(event)
    Engine->>Strategy: on_market_event(event)
    Strategy-->>Engine: SignalEvent(s)
    loop for each signal
        Engine->>Risk: size_order(signal, cash, price)
        Risk-->>Engine: OrderEvent
        Engine->>Exec: execute_order(order, price)
        Exec-->>Engine: FillEvent
        Engine->>Port: update_fill(fill)
    end
Loading

Strategies included

  • Moving average crossover — goes long when a short moving average crosses above a longer one, exits on the reverse cross.
  • Mean reversion — goes long when price drops well below its rolling mean (by a z-score threshold), exits once it reverts.
  • VWAP — goes long when price trades meaningfully below the volume-weighted average price, exits once it moves back above it.
  • TWAP — the same idea as VWAP, but against a plain time-weighted average instead of a volume-weighted one.

Each one implements the same small Strategy interface, so writing a new one just means implementing on_market_event(event) -> list[SignalEvent].

Getting started

pip install -r requirements.txt
PYTHONPATH=. python examples/run_backtest.py

That runs a moving average crossover strategy over the sample data in examples/data/sample_ohlcv.csv and prints final equity, total return, Sharpe ratio, max drawdown, and win rate.

To wire things up yourself:

from backtester.data.csv_feed import CSVDataFeed
from backtester.strategy.moving_average import MovingAverageCrossoverStrategy
from backtester.portfolio.portfolio import Portfolio
from backtester.risk.risk_manager import RiskManager
from backtester.execution.simulator import ExecutionSimulator
from backtester.engine import BacktestEngine
from backtester.analytics.performance import sharpe_ratio, max_drawdown

engine = BacktestEngine(
    data_handler=CSVDataFeed("path/to/data.csv", symbol="AAPL"),
    strategy=MovingAverageCrossoverStrategy(short_window=10, long_window=50),
    portfolio=Portfolio(initial_cash=100_000),
    risk_manager=RiskManager(max_position_size=100, risk_per_trade=0.02),
    execution_simulator=ExecutionSimulator(commission_per_share=0.005, slippage_bps=5.0),
)

portfolio = engine.run()
print(portfolio.total_equity())
print(sharpe_ratio(portfolio.equity_curve))
print(max_drawdown(portfolio.equity_curve))

Running the tests

python -m pytest tests/ -q

Project layout

backtester/
    events.py              # MarketEvent, SignalEvent, OrderEvent, FillEvent
    data/                  # CSV and Parquet feeds
    strategy/               # Strategy interface + moving average, mean reversion, VWAP, TWAP
    risk/                  # position sizing
    execution/              # slippage/commission fill simulation
    portfolio/              # cash, positions, equity curve
    analytics/              # Sharpe, drawdown, total return, win rate
    engine.py               # wires it all together
examples/
    run_backtest.py         # runnable example against sample data
tests/                      # one test file per module above

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages