Open-source backtesting library · v1.1.3

Test trading ideas on history, one day at a time.

Clojure Backtesting replays daily market data from CRSP or Yahoo Finance, fills your orders the way a broker would, tracks cash, loans and short positions, shows your strategy only the company reports that were public at the time, and reports how it performed.

strategy.clj
(load-dataset "resources/sample-data/main" "main" add-aprc)
(init-portfolio "1990-01-02" 10000)

;; Hold 50 shares of AAA while its price
;; is above its 10-day moving average
(while (< (compare (get-date) "1990-03-29") 0)
  (let [price (get-permno-price "10001")
        ma10  (moving-avg "10001" 10)]
    (when (and price ma10)
      (order "10001" (if (> price ma10) 50 0)
             :remaining true)))
  (update-eval-report)
  (next-date))
(end-order)

What you can do

A backtest replays history: your strategy sees the market one day at a time, places orders, and the library keeps the books. These are the parts it handles for you.

Day-by-day simulation

A global clock moves through trading days. Your code only sees data up to "today", so it cannot peek at the future.

Orders fill like a broker's

An order fills at the next trading day's close. Orders wait while a security has no price, and expire after a few days.

Margin and short selling

Borrow cash or shares under initial and maintenance margins, with optional commissions and loan interest.

Free data from Yahoo Finance

Download prices, dividends, splits, financial statements and earnings dates for any ticker, with no API key. Or use CRSP and Compustat.

Point-in-time fundamentals

Earnings, balance sheets, 15 ratios, growth rates and the Piotroski F-score, exactly as they were known on each day.

Rank and rebalance

Rank a universe by any measure, hold the top picks at target weights, and rebalance every week, month or quarter.

Dividends and splits

Dividends are reinvested or paid to cash, splits adjust the share count, and holdings of delisted securities become cash.

Technical indicators

Moving averages, EMA, MACD, RSI, rate of change, parabolic SAR, ATR and Keltner Channels.

Performance and benchmarks

Returns, volatility, Sharpe ratio and drawdown, plus beta, alpha and information ratio against an index, and charts in the browser.

Automated rules

Pair a condition with an action, such as a stop-loss, and the backtester checks it every trading day.

Data larger than memory

Data is read from disk one day at a time, with a cache. It has been tested with datasets of up to 30 GB.

How a backtest works

Every backtest follows the same four steps. The Key Concepts page explains each step in more detail.

1

Load the data

load-dataset points the library at daily prices; load-yahoo-dataset loads prices and fundamentals from Yahoo Finance.

2

Start a portfolio

init-portfolio sets the start date and the cash you begin with.

3

Step through the days

Each day, read prices or indicators, call order, then next-date to move on.

4

Review the results

end-order closes the positions. The records and the evaluation report show what happened.

What the example above produced

The strategy at the top of this page runs on the sample dataset that ships with the library. It holds 50 shares of the security AAA (10001) whenever the price closes above its 10-day average, and none otherwise.

Starting cash$10,000
Final value$9,652
Orders filled14
Sharpe ratio−4.72
Max drawdown4.09%
Chart of AAA's closing price and 10-day average from January to March 1990, with seven buys and seven sells, and the portfolio value falling from $10,000 to $9,652.
AAA drifted down from about $51 to $44. Most moves above the average reversed within days, so six of the seven round trips lost money.

The rule lost $348 (3.5%). That is the point of backtesting: you find out on historical data, not with real money. The first fills in the order record:

|      :date | :permno | :price | :aprc | :quantity |
|------------+---------+--------+-------+-----------|
| 1990-01-16 |   10001 |  51.04 | 51.04 |      50.0 |
| 1990-01-23 |   10001 |  50.12 | 50.12 |     -50.0 |
| 1990-02-07 |   10001 |  48.06 | 48.06 |      50.0 |
| 1990-02-08 |   10001 |   47.5 | 47.50 |     -50.0 |

Strategies on fundamentals

Since version 1.1.2 the library also downloads data from Yahoo Finance, knows which company reports were public on each day, and has helpers for strategies that rank and rebalance. This value strategy holds, every month, the two companies with the highest earnings yield:

(build-yahoo-dataset! "resources/sample-yahoo" :out-dir "target/sample-yahoo")
(load-yahoo-dataset "target/sample-yahoo")
(update-transaction-cost 0.001)
(init-portfolio "2023-01-03" 100000)

(while (neg? (compare (get-date) "2024-12-31"))
  (when (new-period? :month)
    (rebalance (equal-weights (top-n earnings-yield 2 :universe ["ALFA" "BETA" "GAMA" "DLTA"]))))
  (next-date)
  (update-eval-report))
(end-order)
(benchmark-stats "IDX")

It runs on the synthetic Yahoo snapshots bundled with the library, two years of made-up companies, against their index:

Final value$162,866
Index, same start$119,376
Beta1.03
Alpha, a year14.8%
Max drawdown16.75%
The value strategy's portfolio value from January 2023 to December 2024, ending at $162,866, against the IDX index scaled to the same start, ending at $119,376. A strip below shows the holdings: ALFA and GAMA until March 2024, then mostly ALFA and BETA.
The picks change only when a new report becomes public. With three companies that report and synthetic prices, the result shows the mechanics, not that value investing works.

The Value Strategy example explains every step, and Yahoo Finance Data shows how to download real data.

Start learning

New to Clojure or to backtesting? Read these pages in order.

Video introduction

A walkthrough of the installation, an example strategy and tips for learning the library. It was recorded with an earlier version of the library, so some details differ from these pages.