Files
options_backtester/backtester/examples/backtester_example.ipynb
T

116 KiB
Raw Blame History

Backtester Examples

Data files

In [1]:
import os
import sys

BACKTESTER_DIR = os.path.realpath(os.path.join(os.getcwd(), '..', '..'))
TEST_DATA_DIR = os.path.join(BACKTESTER_DIR, 'backtester', 'test', 'test_data')
SAMPLE_STOCK_DATA = os.path.join(TEST_DATA_DIR, 'test_data_stocks.csv')
SAMPLE_OPTIONS_DATA = os.path.join(TEST_DATA_DIR, 'test_data_options.csv')

sys.path.append(BACKTESTER_DIR) # Add backtester base dir to $PYTHONPATH

Sample backtest

In [2]:
from backtester import Backtest, Stock, Type, Direction
from backtester.datahandler import HistoricalOptionsData, TiingoData
from backtester.strategy import Strategy, StrategyLeg

First we construct an options datahandler.

In [3]:
options_data = HistoricalOptionsData(SAMPLE_OPTIONS_DATA)
options_schema = options_data.schema

Next, we'll create a toy options strategy.

In [4]:
sample_strategy = Strategy(options_schema)

leg1 = StrategyLeg('leg_1', options_schema, option_type=Type.CALL, direction=Direction.BUY)
leg1.entry_filter = ((options_schema.contract == 'SPX170317C00300000') &
                     (options_schema.dte == 73)) | ((options_schema.contract == 'SPX170421C00500000') &
                                                    (options_schema.dte == 51))

leg1.exit_filter = (options_schema.dte == 44) | (options_schema.dte == 18)

leg2 = StrategyLeg('leg_2', options_schema, option_type=Type.PUT, direction=Direction.BUY)
leg2.entry_filter = ((options_schema.contract == 'SPX170317P00300000') &
                     (options_schema.dte == 73)) | ((options_schema.contract == 'SPX170421P01375000') &
                                                    (options_schema.dte == 51))

leg2.exit_filter = (options_schema.dte == 44) | (options_schema.dte == 18)

sample_strategy.add_legs([leg1, leg2])
sample_strategy.add_exit_thresholds(profit_pct=0.2, loss_pct=0.2)

We do the same for stocks: create a datahandler together with a list of the stocks we want in our inventory and their corresponding weights.

In [5]:
stocks_data = TiingoData(SAMPLE_STOCK_DATA)
stocks = [Stock('VOO', 0.4), Stock('TUR', 0.1), Stock('RSX', 0.5)]

We set our portfolio allocation, i.e. how much of our capital will be invested in stocks, options and cash.

In [6]:
allocation = {'stocks': 0.5, 'options': 0.5, 'cash': 0.0}

Finally, we create the Backtest object.

In [7]:
bt = Backtest(allocation, initial_capital=1_000_000)

bt.stocks = stocks
bt.stocks_data = stocks_data

bt.options_strategy = sample_strategy
bt.options_data = options_data

And run the backtest with a rebalancing period of one month.

In [8]:
bt.run(rebalance_freq=1)
Out [8]:
0% [██████████████████████████████] 100% | ETA: 00:00:00
Total time elapsed: 00:00:00
leg_1 leg_2 totals
contract underlying expiration type strike cost order contract underlying expiration type strike cost order cost qty date
0 SPX170317C00300000 SPX 2017-03-17 call 300 195010.0 Order.BTO SPX170317P00300000 SPX 2017-03-17 put 300 5.0 Order.BTO 195015.0 2.0 2017-01-03
1 SPX170317C00300000 SPX 2017-03-17 call 300 -197060.0 Order.STC SPX170317P00300000 SPX 2017-03-17 put 300 -0.0 Order.STC -197060.0 2.0 2017-02-01
2 SPX170421C00500000 SPX 2017-04-21 call 500 189250.0 Order.BTO SPX170421P01375000 SPX 2017-04-21 put 1375 40.0 Order.BTO 189290.0 2.0 2017-03-01
3 SPX170421C00500000 SPX 2017-04-21 call 500 -185650.0 Order.STC SPX170421P01375000 SPX 2017-04-21 put 1375 -0.0 Order.STC -185650.0 2.0 2017-04-03

The trade log (bt.trade_log) shows we executed 4 trades: we bought 2 calls and 2 puts on 2017-01-03 and 2017-03-01, and exited those positions on 2017-02-01 and 2017-04-03 respectively.

The balance data structure shows how our positions evolved in time:

  • We started with $1000000 on 2017-01-02
  • total capital is the sum of cash, stocks capital and options capital
  • % change shows the inter day change in total capital
  • accumulated return gives the compounded return in total capital since the start of the backtest
In [9]:
bt.balance
Out [9]:
total capital cash VOO TUR RSX options qty calls capital puts capital stocks qty options capital stocks capital % change accumulated return
2017-01-02 1.000000e+06 1000000.000000 NaN NaN NaN NaN NaN NaN NaN 0.0 0.000000 NaN NaN
2017-01-03 9.990300e+05 110117.405920 199872.763320 49993.281167 249986.549593 2.0 389060.0 0.0 16186.0 389060.0 499852.594080 -0.000970 0.999030
2017-01-04 1.004228e+06 110117.405920 201052.238851 50072.862958 251605.333911 2.0 391380.0 0.0 16186.0 391380.0 502730.435720 0.005203 1.004228
2017-01-05 1.002706e+06 110117.405920 200897.553535 49865.950301 250564.686850 2.0 391260.0 0.0 16186.0 391260.0 501328.190686 -0.001516 1.002706
2017-01-06 1.003201e+06 110117.405920 201680.647945 49372.543196 248830.275081 2.0 393200.0 0.0 16186.0 393200.0 499883.466222 0.000494 1.003201
... ... ... ... ... ... ... ... ... ... ... ... ... ...
2017-05-12 1.008599e+06 507298.036182 203126.836072 50621.271130 247552.567157 0.0 0.0 0.0 16415.0 0.0 501300.674359 -0.002805 1.008599
2017-05-15 1.015904e+06 507298.036182 204154.459189 51695.869759 252755.284844 0.0 0.0 0.0 16415.0 0.0 508605.613792 0.007243 1.015904
2017-05-16 1.014651e+06 507298.036182 204117.427725 51811.005327 251424.357064 0.0 0.0 0.0 16415.0 0.0 507352.790116 -0.001233 1.014651
2017-05-17 1.005524e+06 507298.036182 200497.602152 50659.649653 247068.593419 0.0 0.0 0.0 16415.0 0.0 498225.845224 -0.008995 1.005524
2017-05-18 1.001336e+06 507298.036182 201228.973560 49853.700681 242954.816643 0.0 0.0 0.0 16415.0 0.0 494037.490883 -0.004165 1.001336

96 rows × 13 columns

Statistics and Plots

In [10]:
from backtester.statistics import *

Summary table of our options strategy.

In [11]:
summary(bt.trade_log, bt.balance)
Out [11]:
Strategy
Total trades 2
Number of wins 1
Number of losses 1
Win % 50.00%
Largest loss $7280.00
Profit factor 1.00
Average profit $-1595.00
Average P&L % 0.00%
Total P&L % 99.68%

Plot of the accumulated returns over time.

In [12]:
returns_chart(bt.balance)
Out [12]:

Daily returns histogram.

In [13]:
returns_histogram(bt.balance)
Out [13]:
In [14]:
monthly_returns_heatmap(bt.balance)
Out [14]: