Moonshot Backtest

Notebooks provide a great way to develop your strategy logic interactively, but Moonshot backtests and parameter scans are often superior tools for running the code and analyzing performance, especially for large datasets that may not fit into memory.

Because Moonshot is pandas-based, your interactive strategy code can readily be transferred to a Moonshot strategy. The file qval.py contains our transferred strategy code. To facilitate parameter scans, we've factored out some hard coded parameters and stored them as class attributes on the strategy (see for example VALUE_TOP_N_PCT and QUALITY_TOP_N_PCT).

Install strategy file

Moonshot looks for strategies inside the "moonshot" directory, so execute the following cell to "install" the strategy by moving the file to that location:

The ! sytax below lets us execute terminal commands from inside the notebook.

In [1]:
# make directory if doesn't exist
!mkdir -p /codeload/moonshot

!mv qval.py /codeload/moonshot/

Moonshot reloads this file each time you run a backtest. Thus, any edits you make to qval.py will automatically be reflected in the subsequent backtest.

Run backtest

Note the CODE we assigned to our strategy, "qval":

class QuantitativeValue(Moonshot):

    CODE = "qval"

This is how we refer to this strategy for running a backtest.

Since this strategy requires a large amount of fundamental and price data, we probably can't load a 10-20 year backtest into memory. We use the segment parameter to tell Moonshot to run the backtest in 1-year segments and concatenate the results. ("A" is the pandas offset alias for annual frequency.)

We've added Jupyter's %time magic to provide a gauge of total runtime for this backtest.

TIP: open the detailed logs while running the backtest to monitor moonshot's progress through the backtest segments.

In [2]:
from quantrocket.moonshot import backtest
%time backtest("qval", start_date="2010-01-01", end_date="2018-01-01", segment="A", filepath_or_buffer="qval_backtest.csv")
CPU times: user 0 ns, sys: 20 ms, total: 20 ms
Wall time: 1min 12s

Performance tear sheets

We can view a performance tear sheet using Moonchart, a companion library to Moonshot, as well as with pyfolio.

Moonchart

In [3]:
from moonchart import Tearsheet
Tearsheet.from_moonshot_csv("qval_backtest.csv")

Pyfolio

In [4]:
import pyfolio as pf
In [5]:
pf.from_moonshot_csv("qval_backtest.csv")
Start date2010-01-04
End date2017-12-29
Total months95
Backtest
Annual return11.8%
Cumulative returns143.4%
Annual volatility20.3%
Sharpe ratio0.65
Calmar ratio0.33
Stability0.76
Max drawdown-35.6%
Omega ratio1.12
Sortino ratio0.92
Skew-0.23
Kurtosis1.81
Tail ratio1.00
Daily value at risk-2.5%
Gross leverage1.00
/opt/conda/lib/python3.6/site-packages/numpy/core/fromnumeric.py:52: FutureWarning: 'argmin' is deprecated, use 'idxmin' instead. The behavior of 'argmin'
will be corrected to return the positional minimum in the future.
Use 'series.values.argmin' to get the position of the minimum now.
  return getattr(obj, method)(*args, **kwds)
Worst drawdown periodsNet drawdown in %Peak dateValley dateRecovery dateDuration
035.642015-04-152016-01-19NaTNaN
127.752011-04-292011-10-032012-02-16210
218.392012-03-192012-06-042013-01-02208
318.072010-04-232010-07-062010-11-05141
412.692014-09-052014-10-152015-02-12115
Stress Eventsmeanminmax
US downgrade/European Debt Crisis-0.07%-7.28%5.52%
Fukushima0.27%-1.77%2.32%
EZB IR Event-0.14%-2.22%1.84%
Flash Crash-0.58%-3.67%4.11%
Apr140.04%-2.13%1.28%
Oct140.09%-3.02%2.84%
Fall2015-0.31%-4.83%4.33%
Recovery0.06%-7.28%5.52%
New Normal0.05%-5.29%4.33%
Top 10 long positions of all timemax
qval100.00%
Top 10 short positions of all timemax
Top 10 positions of all timemax
qval100.00%

Next Up

Part 5: Parameter Scans