API Reference

Evaluation and Plots

Compute performance metrics, print the evaluation report, compare with a benchmark and draw charts.

On this page
  1. Record the Metrics
    1. update-eval-report
  2. Read the Report
    1. print-eval-report
    2. Individual metrics
  3. Compare with a Benchmark
    1. benchmark-stats
  4. Rolling Window
    1. update-rolling-window
  5. Plotting
    1. plot

This page features functions for computing metrics that evaluate portfolio performance, printing the evaluation report, comparing with a benchmark, and plotting.

Record the Metrics

update-eval-report

This function computes the evaluation metrics for today and adds them to the evaluation report (see print-eval-report for the list of metrics). It does nothing before the first order has filled or after the run has ended.

Arguments: none

Return

nil

Example

=> (update-eval-report)
;; update the evaluation metrics today

Call it on the days you want in the report, typically once per day after next-date. Over many dates, updating every day takes a lot of memory and time.

Read the Report

This function prints the evaluation report in a table.

Parameters:

  • [n] - number of rows to print, from the start. Without it, or with zero or a negative number, the entire report is printed.

Output explanation:

 Column  Format   Meaning
date YYYY-MM-DD Date of record
tot-value int, $ Total value of the portfolio (cash plus long positions minus short positions)
vol float, % Volatility: sample standard deviation of the daily returns since the start
r-vol float, % Volatility over the rolling window (the last 30 days by default)
sharpe float Annualised Sharpe ratio since the start: mean daily return / standard deviation × √252, with a zero risk-free rate
r-sharpe float Annualised Sharpe ratio over the rolling window
pnl-pt int, $ Profit and loss per trade: (total value − initial capital) / number of filled orders, not counting the orders end-order places to close positions
max-drawdown float, % Maximum drawdown: the largest decline of the total value from a previous peak, as a percentage of that peak

The daily returns are the log returns in the daily-ret column of the portfolio record.

Example:

(print-eval-report 5)
|      :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% |

The same rows are kept as numbers (not formatted) in the eval-record atom, which is handy for plotting.

Individual metrics

These functions compute a metric for the current state without touching the report. The examples were taken during a run on the sample dataset.

FunctionReturn
portfolio-totalCurrent total value of the portfolio
portfolio-daily-retToday's daily (log) return
portfolio-total-retTotal (log) return since the start
get-daily-returnsAll daily returns so far
volatility / rolling-volatilityVolatility since the start / over the rolling window, as a fraction
sharpe-ratio / rolling-sharpe-ratioAnnualised Sharpe ratio since the start / over the rolling window
pnl-per-tradeProfit and loss per filled order of the strategy, in dollars
max-drawdownMaximum drawdown, as a fraction of the peak

Example:

(portfolio-total)
;; => 9924.099999999999

(portfolio-total-ret)
;; => -0.007618950633236242

(pnl-per-trade)
;; => -25.300000000000484

(max-drawdown)
;; => 0.007590000000000145

Compare with a Benchmark

benchmark-stats

(benchmark-stats permno)

Compares the portfolio with security permno of the main dataset, typically an index or an ETF, over the run so far. It uses the days on which both have a value, and needs no evaluation report.

KeyMeaning
:daysThe number of daily returns compared
:portfolio-returnThe portfolio's total return over those days, as a fraction
:benchmark-returnThe benchmark's total return, from its adjusted price, so dividends are included
:excess-return:portfolio-return minus :benchmark-return
:betaCovariance of the daily returns over the benchmark's variance: how strongly the portfolio moved with it
:alpha252 × (mean daily return − beta × the benchmark's mean daily return): the yearly return beyond what beta explains, with a zero risk-free rate
:correlationCorrelation of the daily returns
:tracking-errorStandard deviation of the daily return differences, × √252
:information-ratioMean daily return difference over its standard deviation, × √252

Daily returns are log returns, as in the portfolio record. The result is nil with fewer than two common days. :beta and :alpha are nil when the benchmark's returns do not vary, :correlation when either series does not, and :information-ratio when the return differences do not. A portfolio that stays in cash has a beta of 0.

Example: the Value Strategy against the IDX index of the Yahoo sample, after end-order:

(into (sorted-map) (benchmark-stats "IDX"))
{:alpha 0.14832626714576205, :benchmark-return 0.1937698633503946, :beta 1.025831533487977, :correlation 0.6612759265648038, :days 520, :excess-return 0.43489652224687814, :information-ratio 0.9698313526498363, :portfolio-return 0.6286663855972727, :tracking-error 0.15522644373296574}

The portfolio returned 62.9% against the index's 19.4%, with a beta of about 1. The benchmark has to be in the main dataset: with Yahoo data, download an index or ETF along with the stocks, such as "SPY" or "^GSPC".

Rolling Window

update-rolling-window

This function sets the time window, in trading days, for the rolling metrics (r-vol and r-sharpe). By default, the window is 30.

Parameters:

  • n - the new time window

Example:

(update-rolling-window 10)
;; output:
Time window is updated as 10.

Plotting

plot

This function draws a line chart with Oz (Vega-Lite), which opens in the browser. It works from a plain REPL as well as from Gorilla REPL.

Parameters:

  • dataset - a sequence of maps, e.g. (deref portfolio-value), (deref eval-record) or (deref order-record)
  • series - the key whose value names the line each row belongs to, shown in the legend
  • x - the key of the x-axis (time) values, e.g. :date
  • y - the key of the y-axis values, e.g. :tot-value. Pass two keys, y1 y2, to draw both on independent y-axes.
  • full-date - true to label the x-axis with full dates (year, month, day); false lets the chart choose the labels

Example:

;; tag every row with the series name
(def data-to-plot
  (map #(assoc % :plot "sharpe ratio") (deref eval-record)))

(plot data-to-plot :plot :date :sharpe true)

To compare securities, give each row the security as its series:

(def prices
  (for [day (reverse (get-prev-n-days 20)) row day]
    (select-keys row [:PERMNO :date :PRC])))

(plot prices :PERMNO :date :PRC true)

The Plotting_frame.clj worksheet has more examples.