Library / SDK
quarkfin/qf-lib avatar
quarkfin/qf-lib

QF-Lib: an event-driven backtester and research toolkit for Python quants

Modular Python library that provides an advanced event driven backtester and a set of high quality tools for quantitative finance. Integrated with various data vendors and brokers, supports Crypto, Stocks and Futures.

976 stars138 forksPythonApache-2.0

At a glance

What is it?
QF-Lib is a modular Python library from the CERN Pension Fund that focuses on event-driven backtesting, with pluggable data vendors, look-ahead bias guards and PDF reporting built in. It suits quants who need a research stack rather than a single script, and it brings pinned dependencies and a heavy document toolchain with it.
Who is it for?
Adopt QF-Lib if you are running systematic research across several asset classes, you want the backtester, the data layer and the reporting layer in one codebase, and you can live with the pinned dependency ranges in requirements.txt, including WeasyPrint 52.5. Do not adopt it for a single strategy script on a modern Python release, since the classifiers list only Python 3.8 through 3.12.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 69 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • quarkfin/qf-lib976 ★ · +2
Star history
Repository2026-10-212026-11-07
quarkfin/qf-lib974976

2 Stars in the last 17 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

A pension fund's research stack, published as a library

QF-Lib is described as a Python library providing high quality tools for quantitative finance, with a large part of the project dedicated to backtesting investment strategies. The origin is visible in the packaging: setup.py lists the author as CERN Pension Fund and carries a CERN copyright header, and the project classifiers mark it as intended for science and research use under the Apache License 2.0.

That provenance shapes what the library is. It is not a retail trading app with a broker connector and a ticker tape. It is the research environment of an institutional investor, published so that the same machinery can be reused: the backtester, the data access layer, the analysis containers and the document generation.

Documentation lives on Read the Docs with separate installation, configuration and API sections, and the repository ships a docs/ directory, a demo_scripts/ directory for runnable examples, an input/ directory and a readme_example_files/ directory holding the artefacts the README examples produce. Packaging is handled by setup.py with versioneer for version derivation, so the version number is taken from the repository state rather than typed into a file by hand.

An event-driven backtester that replays the trading day

The centre of the library is a backtester with an event-driven architecture. The README describes it as simulating events such as daily market opening or closing, and as designed to test and evaluate any custom investment strategy. That word event-driven matters, because it is the main structural choice in the library and it separates QF-Lib from a vectorised backtest.

In a vectorised approach, you compute an indicator over the whole price history, then align the result with the price series and slice it. Everything is calculated at once, which is fast but makes it easy to leak future information into the past. In an event-driven backtester, the strategy is a sequence of events that fire in sequence, each event sees the data available at its timestamp, and the portfolio is updated as the events pass. The events here are described at day granularity, with market opening and closing as the examples, which is the right resolution for daily strategies and the wrong one for intraday order simulation.

The stated goal is to evaluate any custom strategy, and the flexibility comes from that architecture rather than from a strategy catalogue. You write the strategy logic against the event model, and the library handles the clock, the data feed and the bookkeeping around it.

Look-ahead bias guards and containers that extend pandas

Two features deserve separate attention because they are where research libraries most often fail. The first is a set of tools to prevent look-ahead bias in the backtesting environment, listed as a main feature. In an event-driven design, the correct behaviour is that a strategy cannot read a value stamped later than its current event, and providing that as a library guarantee is more trustworthy than asking every contributor to remember it.

The second is the data containers. The README describes adapted data containers that extend the functionality of pandas Series and Dataframes. The point is not to replace pandas but to carry domain information alongside the numbers, so that a price series carries its ticker, currency and date convention, and a set of containers can be checked for consistency before a study runs.

The dependency list shows what that costs in libraries. requirements.txt pins pandas at 1.4.0 through 2.2.3, numpy at 1.19.3 through 1.26.4, xarray, matplotlib, Pillow, scikit-learn, seaborn and openpyxl. Those are upper and lower bounds rather than exact versions, which is more flexible than an exact pin and less predictable than no bounds at all, and it is the sort of range that changes when you install on a new machine.

Four data vendors behind one selection layer

Data sourcing is where most backtesting libraries differentiate, and QF-Lib offers a choice of Bloomberg, Quandl, Haver Analytics and Portara. The README describes this as flexible data sourcing with the possibility of an easy selection of the data source, and the installation guide has a dedicated section, install-optional-data-providers, for the additional dependencies each provider needs.

The vendor list tells you something about the intended user. Bloomberg and Haver Analytics are institutional terminals with per-seat licences, Quandl is a marketplace, and Portara is a simulator oriented service. In other words, this is a library for a desk that already pays for data, not a free tier for a hobbyist.

That has a concrete consequence for evaluation. The backtester can run without any vendor once data is in hand, but the vendor-specific packages are optional and must be installed separately, and each carries its own authentication and licensing terms. The library does not bundle market data, and it does not proxy or resell any of it.

Installing the library and its document toolchain

Installation from PyPI is a single pip command:

bash
pip install qf-lib

If you would rather work from a clone, the README describes downloading the project and running the setup script from the qf_lib directory, the same one where you found the file after cloning:

bash
python setup.py install

The prerequisites are where the friction is. The library uses WeasyPrint to export documents to PDF, and WeasyPrint needs additional dependencies that differ per platform, with the README pointing at the platform-specific instructions for Linux, macOS and Windows. On Windows, the GTK3+ runtime can be installed with the GTK-for-Windows-Runtime-Environment-Installer releases, and the file to download and run is named gtk3-runtime-x.x.x-x-x-x-ts-win64.exe.

The pinned dependency set is what a working install looks like on paper:

bash
pandas>=1.4.0,<=2.2.3
xarray>=0.21.0,<=2024.11.0
numpy>=1.19.3,<=1.26.4
matplotlib>=3.4.0,<=3.6.1
Pillow>=9.0.0,<=10.2.0
scikit-learn>=1.0.0,<=1.6.0
seaborn>=0.11.1
openpyxl>=3.0.7,<=3.1.5
WeasyPrint==52.5
Jinja2>=3.0.0,<=3.1.4

WeasyPrint is pinned to an exact version while everything else is a range, which reflects how tightly the PDF export path is coupled to that library.

Study results rendered as PDFs from document templates

The reporting side is a first-class part of the library rather than an afterthought. The README describes summary generation in which all performed studies can be summarized with a practical and informative document explaining the results, and several document templates are available in the project under the analysis section of the structure reference.

The mechanism is Jinja2 plus WeasyPrint. Jinja2 is the templating layer, and WeasyPrint is what turns the rendered document into a PDF, which is why the exact WeasyPrint pin exists. The inputs that feed this live in the input/ directory, and the readme_example_files/ directory holds the artefacts the README examples generate, which gives you a concrete reference for what a finished summary looks like.

For a research workflow this is the part that pays off. A backtest that only prints a return figure gets re-run by nobody. A backtest that emits a document with the study description, the parameters and the resulting metrics is something you can send to an investment committee, and building that by hand for every study is the busywork this library is designed to absorb.

Where QF-Lib is the wrong shape for the job

Several constraints are structural rather than incidental. The most immediate one is the Python range: the classifiers in setup.py list Python 3.8 through 3.12, so on a newer interpreter you are outside the tested set even if the code runs. The pinned upper bounds on pandas, numpy and matplotlib have the same effect, since a fresh environment resolves to the highest allowed version, and an allowed version can still break a workflow that was written against an older one.

Second, the event granularity. The README describes events such as daily market opening or closing, so an intraday strategy with order book semantics, partial fills and queue position is outside what this backtester models. Third, the document toolchain: WeasyPrint pulls in a native rendering stack, GTK3+ on Windows among it, so the PDF export path adds a native dependency to an otherwise pure Python project, and the missing optional vendor packages add more.

Fourth, and this is the one that decides adoption for a lot of teams, the library assumes licensed data. With no vendor configured, you are running the backtester, not the research.

Against a pandas script you write yourself

The honest comparison is against the alternative many quants actually use: a few hundred lines of pandas, an indicator, a shift to avoid look-ahead, and a loop that trades. That approach is lighter, has no native dependencies, and gives you total control over the mechanics. What it does not give you is the framework around the strategy: an event clock, data containers that carry ticker and currency context, vendor abstraction, bias guards and a reporting pipeline.

The difference in approach shows up in who does the work. A hand-written script makes every participant responsible for not leaking future data, for handling corporate actions, for keeping the accounting consistent. QF-Lib moves those into the library, at the cost of adopting its conventions and its version ranges.

A second comparison is against vectorised backtesting. Vectorised code is shorter and faster for many questions, and it is the wrong shape for anything where the portfolio state affects what happens next, because the state cannot be expressed as a column operation. An event-driven backtester handles that case properly and pays for it in per-event overhead.

The practical rule is that a single strategy on one dataset with one output number is a script problem. A research programme with repeated studies, several data vendors and a report to produce is a framework problem, and that is the case QF-Lib was written for.

Editorial conclusion

Adopt QF-Lib if you are running systematic research across several asset classes, you want the backtester, the data layer and the reporting layer in one codebase, and you can live with the pinned dependency ranges in requirements.txt, including WeasyPrint 52.5. Do not adopt it for a single strategy script on a modern Python release, since the classifiers list only Python 3.8 through 3.12. Before you start, check which of Bloomberg, Quandl, Haver Analytics or Portara you can actually license, and confirm your environment matches the pinned pandas, numpy and matplotlib ranges, since the releases move in small steps: v4.0.4 on 2025-10-02, v4.0.5 on 2026-02-02 and v4.0.6 on 2026-04-08, with the last push on 2026-08-31.

Frequently asked questions

How do I install QF-Lib?

Run pip install qf-lib for the released package. To install from a clone, the README describes running python setup.py install from the qf_lib directory, and WeasyPrint plus its platform-specific dependencies are needed for the PDF export path.

Which market data providers does QF-Lib support?

Bloomberg, Quandl, Haver Analytics and Portara, selected through the configurable data sourcing layer. Each provider needs extra optional dependencies, which the installation guide covers under the optional data providers section, and each carries its own licence terms.

What does the event-driven architecture in QF-Lib actually do?

The backtester simulates events such as daily market opening and closing, so a strategy reacts to the data available at each point in time instead of being computed over the whole history at once. That is what allows the project to provide tools for preventing look-ahead bias.

How does QF-Lib generate the summary document for a study?

Studies are summarised through document templates available in the project, with Jinja2 doing the templating and WeasyPrint exporting the result to PDF. The README lists summary generation as a main feature of the library.

Which Python versions does QF-Lib support?

The classifiers in setup.py list Python 3.8, 3.9, 3.10, 3.11 and 3.12. The dependency ranges are pinned as well, for example pandas 1.4.0 through 2.2.3 and numpy 1.19.3 through 1.26.4, so a new environment resolves to the highest allowed version.

What licence is QF-Lib released under?

Apache License 2.0, stated both in the repository licence field and in setup.py, with the LICENSE file at the repository root. Read that file for the exact terms before you redistribute the library or build on it.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. quarkfin/qf-lib on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/quarkfin-qf-lib.svg)](https://hysenlabs.com/projects/quarkfin-qf-lib)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/quarkfin-qf-lib.svg)](https://hysenlabs.com/projects/quarkfin-qf-lib)

Was this useful?