Learn

Get Started

Install the library, run your first backtest on the bundled sample data, and read the results. It takes about ten minutes.

On this page
  1. Requirements
  2. Install
    1. Clone the repository
    2. Add it to your project
    3. The sample data
  3. Choose a REPL
    1. Option 1: Leiningen REPL
    2. Option 2: Gorilla REPL
  4. Your First Backtest
    1. Step 1: Load the dataset
    2. Step 2: Start the portfolio
    3. Step 3: Write the strategy
    4. Step 4: Check the orders
    5. Step 5: View the portfolio
    6. Step 6: Read the evaluation report
    7. Step 7: Save and plot the results
  5. Use the library in your own namespace
  6. Try fundamentals
  7. Next steps

Requirements

  • Java 11 or later
  • Leiningen, the Clojure build tool

Tip: New to Clojure? You can follow this page without knowing much of it. When you want to learn more, Clojure by Example teaches the syntax quickly, ClojureDocs documents the core functions with examples, and Clojure for the Brave and True is a free book that goes deeper.

Install

There are two ways to get the library. To follow this page and run the examples, clone the repository: it has the sample data and the example worksheets. To use the library in a project of your own, add it as a dependency.

Clone the repository

Clone the repository and cd into it:

git clone https://github.com/clojure-finance/clojure-backtesting.git
cd clojure-backtesting

Add it to your project

The library is on Clojars. With Leiningen, add it to the :dependencies in project.clj:

[com.github.clojure-finance/clojure-backtesting "1.1.3"]

With the Clojure CLI, add it to the :deps in deps.edn:

com.github.clojure-finance/clojure-backtesting {:mvn/version "1.1.3"}

Then require the namespaces you need, as shown below. The paths on this site, such as "resources/sample-data/main", are relative to a clone of the repository; in your own project, point load-dataset at your data, or download data from Yahoo Finance.

The sample data

The repository ships with a small sample dataset in resources/sample-data, so everything on this site runs without downloading anything else. It holds three securities over January to March 1990, each with a quirk that exercises part of the library:

SecurityTickerNotes
10001AAAPays a dividend on 1990-02-15
10002BBBSplits 2-for-1 on 1990-03-01
10003CCCHas no prices from 1990-02-05 to 1990-02-09

It also has a matching Compustat-style dataset of company fundamentals in resources/sample-data/compustat. A second sample, in resources/sample-yahoo, holds synthetic Yahoo Finance downloads for 2022 to 2024, with financial statements and earnings dates; the Yahoo Finance Data page describes it. To use the full CRSP and Compustat data or your own data, see Data and Trading Rules.

Choose a REPL

You run strategies interactively, in a REPL. There are two options.

Option 1: Leiningen REPL

Start a REPL in the project directory:

lein repl

The REPL starts in the clojure-backtesting.user namespace, where every function of the library is already available, so you can type the code below directly. For a quick demo run without a REPL, use lein run.

Option 2: Gorilla REPL

Gorilla REPL is a notebook in the browser, similar to Jupyter Notebook. The example strategies in the examples folder are Gorilla worksheets.

  1. Start it in the project directory:

    make start
    
  2. When it is ready, it prints something like:

    Gorilla-REPL: 0.6.0
    Unable to reach update server.
    Started nREPL server on port 50649
    Running at http://127.0.0.1:50651/worksheet.html .
    Ctrl+C to exit.
    
  3. Open the URL from the output in your browser.

    The Gorilla REPL start page in a browser

  4. Click the menu button at the top right and choose "Load a worksheet", then pick one of the worksheets in the examples folder.

    The Load a worksheet dialog listing the example worksheets

  5. Run a cell with Ctrl + Enter.

For more about Gorilla REPL, see its website.

Your First Backtest

This section goes through the simple_trading_strategy.clj worksheet step by step. Type or paste each block into the REPL.

Step 1: Load the dataset

Load the dataset by giving its directory. add-aprc adds the adjusted price columns the backtester needs; the sample dataset already has them, so it only prints a note.

(load-dataset "resources/sample-data/main" "main" add-aprc)
The dataset is already furnished by add-aprc. No more modification is needed.
"Date range: 1990-01-02 ~ 1990-03-30"

Step 2: Start the portfolio

Pass the starting date ("YYYY-MM-DD") and the initial capital to init-portfolio. This sets the backtester's clock to that date.

(init-portfolio "1990-01-02" 10000)
;; => "Date: 1990-01-02 Cash: $10000"

You can check which securities trade on the current date:

(available-permnos)
;; => ("10001" "10002" "10003")

Step 3: Write the strategy

This strategy is deliberately simple, to show the mechanics. Over 10 trading days it will:

  • buy 50 shares of AAA (10001) on the first day
  • sell 10 shares of AAA on every other day
(def num-of-days (atom 10))

(while (pos? @num-of-days)
  (when (= 10 @num-of-days)
    (order "10001" 50 :print true)) ; buy 50 shares
  (when (odd? @num-of-days)
    (order "10001" -10 :print true)) ; sell 10 shares
  (update-eval-report) ; update the evaluation metrics every day
  (println (get-date))
  (next-date) ; move on to the next trading day
  (swap! num-of-days dec))

The loop calls next-date every time so that the backtester moves on to the next trading day. An order fills at the close of the next trading day, which is why each Order: line carries the following day's date:

1990-01-02
Order: 1990-01-03 | 10001 | 50.000000.
1990-01-03
Order: 1990-01-04 | 10001 | -10.000000.
1990-01-04
1990-01-05
Order: 1990-01-08 | 10001 | -10.000000.
1990-01-08
1990-01-09
Order: 1990-01-10 | 10001 | -10.000000.
1990-01-10
1990-01-11
Order: 1990-01-12 | 10001 | -10.000000.
1990-01-12
1990-01-15
Order: 1990-01-16 | 10001 | -10.000000.

Why the next day? You decide after seeing today's closing price, and nobody can trade at a price that is already in the past. Filling at the next close keeps the backtest honest. Key Concepts has a diagram.

Step 4: Check the orders

(print-order-record)
|      :date | :permno | :price | :aprc | :quantity |
|------------+---------+--------+-------+-----------|
| 1990-01-03 |   10001 |  50.81 | 50.81 |      50.0 |
| 1990-01-04 |   10001 |  50.22 | 50.22 |     -10.0 |
| 1990-01-08 |   10001 |   50.4 | 50.40 |     -10.0 |
| 1990-01-10 |   10001 |  49.28 | 49.28 |     -10.0 |
| 1990-01-12 |   10001 |  49.29 | 49.29 |     -10.0 |
| 1990-01-16 |   10001 |  51.04 | 51.04 |     -10.0 |

Step 5: View the portfolio

All 50 shares have been sold, so only cash is left:

(print-portfolio)
| :asset | :price | :aprc | :quantity | :tot-val |
|--------+--------+-------+-----------+----------|
|   cash |    N/A |   N/A |       N/A |  9961.80 |

The portfolio record has one row per trading day. Pass a number to print only that many rows, or a negative number to print all of them. The columns are explained on the Portfolio page.

(print-portfolio-record -1)
|      :date | :tot-value | :daily-ret | :tot-ret | :loan | :short | :leverage |  :margin |
|------------+------------+------------+----------+-------+--------+-----------+----------|
| 1990-01-02 |  $10000.00 |      0.00% |    0.00% | $0.00 |  $0.00 |      0.00 |  100.00% |
| 1990-01-03 |  $10000.00 |      0.00% |    0.00% | $0.00 |  $0.00 |      0.00 |  393.62% |
| 1990-01-04 |   $9970.50 |     -0.30% |   -0.30% | $0.00 |  $0.00 |      0.00 |  496.34% |
| 1990-01-05 |   $9976.50 |      0.06% |   -0.24% | $0.00 |  $0.00 |      0.00 |  495.16% |
| 1990-01-08 |   $9977.70 |      0.01% |   -0.22% | $0.00 |  $0.00 |      0.00 |  659.90% |
| 1990-01-09 |   $9963.00 |     -0.15% |   -0.37% | $0.00 |  $0.00 |      0.00 |  665.40% |
| 1990-01-10 |   $9944.10 |     -0.19% |   -0.56% | $0.00 |  $0.00 |      0.00 | 1008.94% |
| 1990-01-11 |   $9951.70 |      0.08% |   -0.48% | $0.00 |  $0.00 |      0.00 | 1001.98% |
| 1990-01-12 |   $9944.30 |     -0.07% |   -0.56% | $0.00 |  $0.00 |      0.00 | 2017.51% |
| 1990-01-15 |   $9960.40 |      0.16% |   -0.40% | $0.00 |  $0.00 |      0.00 | 1956.86% |
| 1990-01-16 |   $9961.80 |      0.01% |   -0.38% | $0.00 |  $0.00 |      0.00 |  100.00% |

The margin is above 100% because part of the portfolio is cash, so its equity is larger than its stock position. It only matters once you borrow (see Long, short and borrowed money).

Step 6: Read the evaluation report

Because update-eval-report was called every day in the loop, the evaluation report has a row for each day since the first order filled. The metrics are explained on the Evaluation page.

(print-eval-report)
|      :date | :tot-value |    :vol |  :r-vol | :sharpe | :r-sharpe | :pnl-pt | :max-drawdown |
|------------+------------+---------+---------+---------+-----------+---------+---------------|
| 1990-01-03 |     $10000 | 0.0000% | 0.0000% |  0.0000 |    0.0000 |      $0 |       0.0000% |
| 1990-01-04 |      $9970 | 0.1706% | 0.1706% | -9.1652 |   -9.1652 |    $-14 |       0.2950% |
| 1990-01-05 |      $9976 | 0.1603% | 0.1603% | -5.8258 |   -5.8258 |    $-11 |       0.2950% |
| 1990-01-08 |      $9977 | 0.1424% | 0.1424% | -4.9785 |   -4.9785 |     $-7 |       0.2950% |
| 1990-01-09 |      $9963 | 0.1341% | 0.1341% | -7.3148 |   -7.3148 |    $-12 |       0.3700% |
| 1990-01-10 |      $9944 | 0.1316% | 0.1316% | -9.6582 |   -9.6582 |    $-13 |       0.5590% |
| 1990-01-11 |      $9951 | 0.1338% | 0.1338% | -7.1789 |   -7.1789 |    $-12 |       0.5590% |
| 1990-01-12 |      $9944 | 0.1253% | 0.1253% | -7.8645 |   -7.8645 |    $-11 |       0.5590% |
| 1990-01-15 |      $9960 | 0.1377% | 0.1377% | -4.5745 |   -4.5745 |     $-7 |       0.5590% |

Watch out: updating the report every day takes memory and time over long runs. Over many years, update it less often, or print only a few rows.

Step 7: Save and plot the results

The records stay in memory by default. To also write them as CSV files, set an output directory before init-portfolio:

(update-output-dir "out")
(init-portfolio "1990-01-02" 10000)

The run then writes out_order_record.csv, out_portfolio_value_record.csv and out_evaluation_report.csv into out/. The files are complete once the run ends, through end-order or by reaching the end of the dataset.

plot draws a line chart in the browser. Tag each row with a series name, then pass the series key, the x key and the y key:

;; portfolio daily return
(def data-to-plot (map #(assoc % :plot "portfolio") (deref portfolio-value)))
(plot data-to-plot :plot :date :daily-ret true)

;; volatility from the evaluation report
(def data-to-plot (map #(assoc % :plot "volatility") (deref eval-record)))
(plot data-to-plot :plot :date :vol true)

See plot for details, and the Plotting_frame.clj worksheet for more examples.

Use the library in your own namespace

lein repl already has every function loaded. In a file or namespace of your own, require the library's namespaces:

(ns my-strategy
  (:require [clojure-backtesting.data :refer :all]
            [clojure-backtesting.data-management :refer :all]
            [clojure-backtesting.portfolio :refer :all]
            [clojure-backtesting.order :refer :all]
            [clojure-backtesting.evaluate :refer :all]
            [clojure-backtesting.plot :refer :all]
            [clojure-backtesting.counter :refer :all]
            [clojure-backtesting.automation :refer :all]
            [clojure-backtesting.parameters :refer :all]
            [clojure-backtesting.indicators :refer :all]
            [clojure-backtesting.direct :refer :all]
            [clojure-backtesting.fundamentals :refer :all]   ; point-in-time fundamentals and ratios
            [clojure-backtesting.strategy :refer :all]       ; ranking and rebalancing
            [clojure-backtesting.yahoo :refer :all]))        ; Yahoo Finance data

Leave out the namespaces you don't use. plot needs a browser, and automation is only for automations.

Try fundamentals

The sample above has three securities and only a few Compustat-style filings. The Yahoo sample has full company reports and earnings dates, so you can try the fundamentals functions in a few lines. Build it, load it, and ask on a given day what each company was worth and earned:

(build-yahoo-dataset! "resources/sample-yahoo" :out-dir "target/sample-yahoo")
(load-yahoo-dataset "target/sample-yahoo")
(init-portfolio "2024-11-15" 100000)

(pe-ratio "ALFA")            ;; => 18.632838423003843
(rank-by earnings-yield)     ;; => [["ALFA" 0.05366868843586461] ["BETA" 0.03332584981650266] ["GAMA" 0.027329231371534268]]

The answers use only the reports that were public on 2024-11-15. With a network connection, download-yahoo! fetches the same kind of data for real tickers; Yahoo Finance Data explains how, and the Value Strategy example builds a complete strategy on it.

Next steps