diff --git a/backtester/test/backtester/test_backtester.py b/backtester/test/backtester/test_backtester.py index 5ccf014..b241513 100644 --- a/backtester/test/backtester/test_backtester.py +++ b/backtester/test/backtester/test_backtester.py @@ -1,13 +1,14 @@ import numpy as np -from backtester.enums import Stock from backtester.strategy import Strategy, StrategyLeg from backtester.enums import Type, Direction from backtester import Backtest -def test_backtest(sample_datahandler_stocks, sample_datahandler_options): - tl_long, balance_long = run_backtest(sample_datahandler_stocks, sample_datahandler_options, Direction.BUY) +def test_backtest(sample_stock_portfolio, sample_stocks_datahandler, sample_options_datahandler): + bt = run_backtest(sample_stock_portfolio, sample_stocks_datahandler, sample_options_datahandler, + sample_options_strategy(Direction.BUY, sample_options_datahandler.schema)) + tl_long, balance_long = bt.trade_log, bt.balance last_day_balance_long = balance_long.iloc[-1].values @@ -30,7 +31,9 @@ def test_backtest(sample_datahandler_stocks, sample_datahandler_options): assert (np.isclose(total_costs, [195015.0, -197060.0, 189290.0, -185650.0], atol=tol)).all() assert (dates == ['2017-01-03', '2017-02-01', '2017-03-01', '2017-04-03']).all() - tl_short, balance_short = run_backtest(sample_datahandler_stocks, sample_datahandler_options, Direction.SELL) + bt = run_backtest(sample_stock_portfolio, sample_stocks_datahandler, sample_options_datahandler, + sample_options_strategy(Direction.SELL, sample_options_datahandler.schema)) + tl_short, balance_short = bt.trade_log, bt.balance last_day_balance_short = balance_short.iloc[-1].values @@ -51,22 +54,50 @@ def test_backtest(sample_datahandler_stocks, sample_datahandler_options): assert (dates == ['2017-03-01', '2017-04-03']).all() -def run_backtest(stock_data, options_data, direction): - schema = options_data.schema - test_strat = strategy(direction, schema) - stocks = portfolio() +# We use Portfolio Visualizer (https://www.portfoliovisualizer.com/backtest-portfolio) +# to find the actual return for the Ivy porfolio. - bt = Backtest({'stocks': 0.50, 'options': 0.50, 'cash': 0}) + +def test_backtest_only_stocks(ivy_portfolio, ivy_portfolio_datahandler, sample_options_datahandler): + allocation = {'stocks': 1.0, 'options': 0.0, 'cash': 0.0} + bt = run_backtest(ivy_portfolio, + ivy_portfolio_datahandler, + sample_options_datahandler, + sample_options_strategy(Direction.BUY, sample_options_datahandler.schema), + allocation=allocation) + + print(bt.balance.columns) + balance = bt.balance[1:] + tolerance = 0.0001 + assert np.allclose(balance['total capital'], balance['cash'] + balance['stocks capital'], rtol=tolerance) + assert np.allclose(balance['total capital'], bt.initial_capital * balance['accumulated return'], rtol=tolerance) + + actual_return = 1.025 + return_tolerance = 0.01 + assert np.isclose(balance['accumulated return'].iloc[-1], actual_return, rtol=return_tolerance) + + +def run_backtest(stocks, + stock_data, + options_data, + strategy, + allocation={ + 'stocks': 0.50, + 'options': 0.50, + 'cash': 0 + }, + **kwargs): + bt = Backtest(allocation, **kwargs) bt.stocks = stocks - bt.options_strategy = test_strat + bt.options_strategy = strategy bt.options_data = options_data bt.stocks_data = stock_data bt.run(rebalance_freq=1) - return bt.trade_log, bt.balance + return bt -def strategy(direction, schema): +def sample_options_strategy(direction, schema): test_strat = Strategy(schema) leg1 = StrategyLeg("leg_1", schema, option_type=Type.CALL, direction=direction) leg1.entry_filter = ((schema.contract == "SPX170317C00300000") & @@ -83,12 +114,3 @@ def strategy(direction, schema): test_strat.add_legs([leg1, leg2]) return test_strat - - -def portfolio(): - VOO = Stock('VOO', 0.4) - TUR = Stock('TUR', 0.1) - RSX = Stock('RSX', 0.5) - - stocks = [VOO, TUR, RSX] - return stocks diff --git a/backtester/test/conftest.py b/backtester/test/conftest.py index d51a562..8803969 100644 --- a/backtester/test/conftest.py +++ b/backtester/test/conftest.py @@ -3,20 +3,47 @@ import os import pytest from backtester.datahandler import HistoricalOptionsData, TiingoData +from backtester.enums import Stock TEST_DIR = os.path.abspath(os.path.dirname(__file__)) -SAMPLE_DATA_STOCKS = os.path.join(TEST_DIR, 'backtester', 'test_data', 'test_data_stocks.csv') -SAMPLE_DATA_OPTIONS = os.path.join(TEST_DIR, 'backtester', 'test_data', 'test_data.csv') +SAMPLE_DATA_STOCKS = os.path.join(TEST_DIR, 'test_data', 'test_data_stocks.csv') +IVY_PORTFOLIO_DATA = os.path.join(TEST_DIR, 'test_data', 'ivy_portfolio.csv') +SAMPLE_DATA_OPTIONS = os.path.join(TEST_DIR, 'test_data', 'test_data.csv') + +# DataHandler fixtures @pytest.fixture(scope='module') -def sample_datahandler_stocks(): +def sample_stocks_datahandler(): data = TiingoData(SAMPLE_DATA_STOCKS) return data @pytest.fixture(scope='module') -def sample_datahandler_options(): +def ivy_portfolio_datahandler(): + data = TiingoData(IVY_PORTFOLIO_DATA) + return data + + +@pytest.fixture(scope='module') +def sample_options_datahandler(): data = HistoricalOptionsData(SAMPLE_DATA_OPTIONS) return data + + +# Stock Porfolio fixtures + + +@pytest.fixture(scope='module') +def ivy_portfolio(): + return [Stock('VTI', 0.2), Stock('VEU', 0.2), Stock('BND', 0.2), Stock('VNQ', 0.2), Stock('DBC', 0.2)] + + +@pytest.fixture(scope='module') +def sample_stock_portfolio(): + VOO = Stock('VOO', 0.4) + TUR = Stock('TUR', 0.1) + RSX = Stock('RSX', 0.5) + + return [VOO, TUR, RSX] diff --git a/backtester/test/backtester/test_data/test_data.csv b/backtester/test/test_data/test_data.csv similarity index 100% rename from backtester/test/backtester/test_data/test_data.csv rename to backtester/test/test_data/test_data.csv diff --git a/backtester/test/backtester/test_data/test_data_stocks.csv b/backtester/test/test_data/test_data_stocks.csv similarity index 100% rename from backtester/test/backtester/test_data/test_data_stocks.csv rename to backtester/test/test_data/test_data_stocks.csv