Skip to main content

Professional event-driven backtesting framework for quantitative trading strategies

Project description

enode-backtester

Lightweight event-driven backtesting framework that wires together data handlers, strategies, sizing logic, a portfolio, execution simulation, and analysis helpers. The library is designed as a foundation you can extend with richer metrics, visualisations, and strategy research.


Repository Layout

  • backtester/__init__.py – Public entry point exporting run_backtest, utility helpers, and default sizing/execution models.
  • backtester/data.py – Data-handling abstractions. DataFrameDataHandler streams pandas data frames into the event queue as StockEvents.
  • backtester/event.py – Event hierarchy that flows through the engine (market data, signals, orders, fills).
  • backtester/strategy.py – Base strategy contract; provides the signal helper for emitting SignalEvents.
  • backtester/sizer.py – Base sizing abstraction plus a simple FixedSizeSizer.
  • backtester/portfolio.py – Tracks cash, positions, trade log, equity curve, and reacts to incoming events.
  • backtester/execution.py – Simulated execution handler with configurable slippage and commission models.
  • backtester/engine.py – Core event loop that coordinates the handlers and processes the event queue.
  • backtester/analysis.py – Post-run utilities, including an optional PyFolio tear sheet and trade log export.
  • backtester/models.py – Pydantic models shared across modules (e.g., StockCandle).

Installation

# using uv (recommended)
uv sync

# or using pip / virtualenv
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

pyfolio is optional; install it (pip install pyfolio) if you need tear-sheet generation.


Creating a Strategy

  1. Subclass backtester.strategy.BaseStrategy.
  2. Implement on_stock_event(self, event) to react to incoming candles (event.payload is a StockCandle).
  3. Use self.signal(symbol, timestamp, action) to enqueue SignalEvents (action is typically "buy" or "sell").
from collections import deque

from backtester.strategy import BaseStrategy
from backtester.event import StockEvent


class MySMAStrategy(BaseStrategy):
    def __init__(self, event_queue, data_handler, window: int = 20):
        super().__init__(event_queue, data_handler)
        self.window = window
        self.prices: dict[str, deque[float]] = {}

    def on_stock_event(self, event: StockEvent) -> None:
        symbol = event.payload.symbol
        price = event.payload.close
        history = self.prices.setdefault(symbol, deque(maxlen=self.window))
        history.append(price)

        if len(history) < history.maxlen:
            return

        avg_price = sum(history) / len(history)
        if price > avg_price:
            self.signal(symbol, event.payload.timestamp, "buy")
        elif price < avg_price:
            self.signal(symbol, event.payload.timestamp, "sell")

Running a Backtest

import pandas as pd

from backtester import FixedSizeSizer, run_backtest, get_trade_log_df
from backtester.data import DataFrameDataHandler
from my_strategies import MySMAStrategy

# 1. Prepare your market data as pandas DataFrames keyed by symbol
data_dict: dict[str, pd.DataFrame] = {
    "AAPL": aapl_df,
    "GOOGL": googl_df,
}

# 2. Choose a sizing model (optional)
sizer = FixedSizeSizer(default_size=10)

# 3. Execute the backtest
portfolio = run_backtest(
    data_dict=data_dict,
    strategy_class=MySMAStrategy,
    initial_cash=100_000.0,
    sizer=sizer,
)

# 4. Inspect results
trade_log = get_trade_log_df(portfolio)
print(trade_log.tail())
print(f"Final equity: {portfolio.equity_curve[-1][1]:,.2f}")

run_backtest instantiates all core components for you:

  • DataFrameDataHandler feeds candles into the event queue.
  • Your strategy consumes StockEvents and emits SignalEvents.
  • Portfolio generates OrderEvents using the provided Sizer.
  • SimulatedExecutionHandler fills orders with configurable commission and slippage.

After the engine finishes processing all data, you receive the final Portfolio object for downstream analysis.


Extending the Framework

  • Metrics & Visuals: Add new functions under backtester.analysis or integrate notebooks that consume the trade log and equity curve.
  • Strategies: Drop additional strategy classes alongside your own module; they only need to inherit from BaseStrategy.
  • Execution/Sizing: Create custom classes that follow the abstract base class signatures in execution.py and sizer.py.
  • Data: Implement new handlers by subclassing BaseDataHandler if you need live feeds, streaming data, or alternative storage.

Contributing

Pull requests and issues welcome—especially around additional strategy templates, validation rules, and analytical tooling.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

enode_backtester-0.1.0.tar.gz (198.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

enode_backtester-0.1.0-py3-none-any.whl (192.2 kB view details)

Uploaded Python 3

File details

Details for the file enode_backtester-0.1.0.tar.gz.

File metadata

  • Download URL: enode_backtester-0.1.0.tar.gz
  • Upload date:
  • Size: 198.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.9

File hashes

Hashes for enode_backtester-0.1.0.tar.gz
Algorithm Hash digest
SHA256 8ecd2d8edc21adb81dfebdde1f13f0295bceabdb899caff7a8a259798405f96d
MD5 7e5920c5c2c00316ec960cab5d03cfcf
BLAKE2b-256 ae0f81bd3b97010de38fec2aa66b8969240140c605ee4f67ffcf1dc3510dc26d

See more details on using hashes here.

File details

Details for the file enode_backtester-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for enode_backtester-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 77aabb63e122790135eba0785e2f1ef46545b8cd1fa878fe1f43b7238f41d38d
MD5 08dbb3269c153d87b1bcfa6f99d96617
BLAKE2b-256 6861972ce82c35a4078ef023aeb8d727c10a86a78a41c82be5a1d4339cce49ec

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page