示例#1
0
def test_static_universe(assets, dt, expected):
    """
    Checks that the StaticUniverse correctly returns the
    list of assets for a particular datetime.
    """
    universe = StaticUniverse(assets)
    assert universe.get_assets(dt) == expected
示例#2
0
def test_pcm_fixed_weight_optimiser_fixed_alpha_weights_call_end_to_end(
        helpers):
    """
    Tests the full portfolio base class logic for carrying out
    rebalancing.

    TODO: DataHandler is mocked. A non-disk based data source
    should be utilised instead.
    """
    first_dt = pd.Timestamp('2019-01-01 15:00:00', tz=pytz.utc)
    asset_list = ['EQ:SPY', 'EQ:AGG', 'EQ:TLT', 'EQ:GLD']
    initial_funds = 1e6
    account_id = '1234'
    port_id = '1234'
    cash_buffer_perc = 0.05

    exchange = SimulatedExchange(first_dt)
    universe = StaticUniverse(asset_list)

    mock_asset_prices_first = {
        'EQ:SPY': 56.87,
        'EQ:AGG': 219.45,
        'EQ:TLT': 178.33,
        'EQ:GLD': 534.21
    }
    data_handler = Mock()
    data_handler.get_asset_latest_ask_price.side_effect = \
        lambda self, x: mock_asset_prices_first[x]

    broker = SimulatedBroker(first_dt,
                             exchange,
                             data_handler,
                             account_id,
                             initial_funds=initial_funds)
    broker.create_portfolio(port_id, 'Portfolio')
    broker.subscribe_funds_to_portfolio(port_id, initial_funds)

    order_sizer = DollarWeightedCashBufferedOrderSizeGeneration(
        broker, port_id, data_handler, cash_buffer_perc)
    optimiser = FixedWeightPortfolioOptimiser(data_handler)

    alpha_weights = {
        'EQ:SPY': 0.345,
        'EQ:AGG': 0.611,
        'EQ:TLT': 0.870,
        'EQ:GLD': 0.0765
    }
    alpha_model = FixedSignalsAlphaModel(alpha_weights)

    pcm = PortfolioConstructionModel(broker, port_id, universe, order_sizer,
                                     optimiser, alpha_model)

    result_first = pcm(first_dt)
    expected_first = [
        Order(first_dt, 'EQ:AGG', 1390),
        Order(first_dt, 'EQ:GLD', 71),
        Order(first_dt, 'EQ:SPY', 3029),
        Order(first_dt, 'EQ:TLT', 2436)
    ]
    helpers.assert_order_lists_equal(result_first, expected_first)
示例#3
0
def test_backtest_sixty_forty(etf_filepath):
    """
    Ensures that a full end-to-end weekly rebalanced backtested
    trading session with fixed proportion weights produces the
    correct rebalance orders as well as correctly calculated
    market values after a single month's worth of daily
    backtesting.
    """
    os.environ['QSTRADER_CSV_DATA_DIR'] = etf_filepath

    assets = ['EQ:ABC', 'EQ:DEF']
    universe = StaticUniverse(assets)
    signal_weights = {'EQ:ABC': 0.6, 'EQ:DEF': 0.4}
    alpha_model = FixedSignalsAlphaModel(signal_weights)

    start_dt = pd.Timestamp('2019-01-01 00:00:00', tz=pytz.UTC)
    end_dt = pd.Timestamp('2019-01-31 23:59:00', tz=pytz.UTC)

    backtest = BacktestTradingSession(
        start_dt,
        end_dt,
        universe,
        alpha_model,
        portfolio_id='000001',
        rebalance='weekly',
        rebalance_weekday='WED',
        long_only=True,
        cash_buffer_percentage=0.05
    )
    backtest.run(results=False)

    portfolio = backtest.broker.portfolios['000001']

    portfolio_dict = portfolio.portfolio_to_dict()
    expected_dict = {
        'EQ:ABC': {
            'unrealised_pnl': -31121.26203538094,
            'realised_pnl': 0.0,
            'total_pnl': -31121.26203538094,
            'market_value': 561680.8382534103,
            'quantity': 4674
        },
        'EQ:DEF': {
            'unrealised_pnl': 18047.831359406424,
            'realised_pnl': 613.3956570402925,
            'total_pnl': 18661.227016446715,
            'market_value': 376203.80367208034,
            'quantity': 1431.0
        }
    }

    history_df = portfolio.history_to_df().reset_index()
    expected_df = pd.read_csv(os.path.join(etf_filepath, 'sixty_forty_history.dat'))

    pd.testing.assert_frame_equal(history_df, expected_df)
    assert portfolio_dict == expected_dict
示例#4
0
def test_backtest_long_short_leveraged(etf_filepath):
    """
    Ensures that a full end-to-end daily rebalanced backtested
    trading session of a leveraged long short portfolio with
    fixed proportion weights produces the correct rebalance
    orders as well as correctly calculated market values after
    a single month's worth of daily backtesting.
    """
    os.environ['QSTRADER_CSV_DATA_DIR'] = etf_filepath

    assets = ['EQ:ABC', 'EQ:DEF']
    universe = StaticUniverse(assets)
    signal_weights = {'EQ:ABC': 1.0, 'EQ:DEF': -0.7}
    alpha_model = FixedSignalsAlphaModel(signal_weights)

    start_dt = pd.Timestamp('2019-01-01 00:00:00', tz=pytz.UTC)
    end_dt = pd.Timestamp('2019-01-31 23:59:00', tz=pytz.UTC)

    backtest = BacktestTradingSession(
        start_dt,
        end_dt,
        universe,
        alpha_model,
        portfolio_id='000001',
        rebalance='daily',
        long_only=False,
        gross_leverage=2.0
    )
    backtest.run(results=False)

    portfolio = backtest.broker.portfolios['000001']

    portfolio_dict = portfolio.portfolio_to_dict()
    expected_dict = {
        'EQ:ABC': {
            'unrealised_pnl': -48302.832839363175,
            'realised_pnl': -3930.9847615026706,
            'total_pnl': -52233.81760086585,
            'market_value': 1055344.698660986,
            'quantity': 8782.0
        },
        'EQ:DEF': {
            'unrealised_pnl': -42274.737165376326,
            'realised_pnl': -9972.897320721153,
            'total_pnl': -52247.63448609748,
            'market_value': -742417.5692312752,
            'quantity': -2824.0
        }
    }

    history_df = portfolio.history_to_df().reset_index()
    expected_df = pd.read_csv(os.path.join(etf_filepath, 'long_short_history.dat'))

    pd.testing.assert_frame_equal(history_df, expected_df)
    assert portfolio_dict == expected_dict
示例#5
0
def test_backtest_sixty_forty_no_corp_actions(etf_filepath):
    """
    Ensures that a full end-to-end weekly rebalanced backtested
    trading session with fixed proportion weights produces the
    correct rebalance orders as well as correctly calculated
    market values after a single month's worth of daily
    backtesting.
    """
    os.environ['QSTRADER_CSV_DATA_DIR'] = etf_filepath

    assets = ['EQ:ABC', 'EQ:DEF']
    universe = StaticUniverse(assets)
    signal_weights = {'EQ:ABC': 0.6, 'EQ:DEF': 0.4}
    alpha_model = FixedSignalsAlphaModel(signal_weights)

    start_dt = pd.Timestamp('2019-01-01 00:00:00', tz=pytz.UTC)
    end_dt = pd.Timestamp('2019-01-31 23:59:00', tz=pytz.UTC)

    backtest = BacktestTradingSession(start_dt,
                                      end_dt,
                                      universe,
                                      alpha_model,
                                      portfolio_id='000001',
                                      rebalance='weekly',
                                      rebalance_weekday='WED',
                                      cash_buffer_percentage=0.05)
    backtest.run(results=False)

    portfolio = backtest.broker.portfolios['000001']

    portfolio_dict = portfolio.portfolio_to_dict()
    expected_dict = {
        'EQ:ABC': {
            'book_cost': 592802.1002887912,
            'gain': -31121.262035380933,
            'market_value': 561680.8382534103,
            'perc_gain': -5.249856911812527,
            'quantity': 4674.0
        },
        'EQ:DEF': {
            'book_cost': 358155.9723126739,
            'gain': 18047.83135940641,
            'market_value': 376203.80367208034,
            'perc_gain': 5.03909825735098,
            'quantity': 1431.0
        }
    }

    history_df = portfolio.history_to_df().reset_index()
    expected_df = pd.read_csv(os.path.join(etf_filepath, 'history.dat'))

    pd.testing.assert_frame_equal(history_df, expected_df)
    assert portfolio_dict == expected_dict
示例#6
0
from qstrader.asset.equity import Equity
from qstrader.asset.universe.static import StaticUniverse
from qstrader.data.backtest_data_handler import BacktestDataHandler
from qstrader.data.daily_bar_csv import CSVDailyBarDataSource
from qstrader.statistics.tearsheet import TearsheetStatistics
from qstrader.trading.backtest import BacktestTradingSession


if __name__ == "__main__":
    start_dt = pd.Timestamp('2003-09-30 14:30:00', tz=pytz.UTC)
    end_dt = pd.Timestamp('2019-12-31 23:59:00', tz=pytz.UTC)

    # Construct the symbols and assets necessary for the backtest
    strategy_symbols = ['SPY', 'AGG']
    strategy_assets = ['EQ:%s' % symbol for symbol in strategy_symbols]
    strategy_universe = StaticUniverse(strategy_assets)

    # To avoid loading all CSV files in the directory, set the
    # data source to load only those provided symbols
    csv_dir = os.environ.get('QSTRADER_CSV_DATA_DIR')
    data_source = CSVDailyBarDataSource(csv_dir, Equity, csv_symbols=strategy_symbols)
    data_handler = BacktestDataHandler(strategy_universe, data_sources=[data_source])

    # Construct an Alpha Model that simply provides
    # static allocations to a universe of assets
    # In this case 60% SPY ETF, 40% AGG ETF,
    # rebalanced at the end of each month
    strategy_alpha_model = FixedSignalsAlphaModel({'EQ:SPY': 0.6, 'EQ:AGG': 0.4})
    strategy_backtest = BacktestTradingSession(
        start_dt,
        end_dt,
示例#7
0
from qstrader.alpha_model.fixed_signals import FixedSignalsAlphaModel
from qstrader.asset.equity import Equity
from qstrader.asset.universe.static import StaticUniverse
from qstrader.data.backtest_data_handler import BacktestDataHandler
from qstrader.data.daily_bar_csv import CSVDailyBarDataSource
from qstrader.statistics.tearsheet import TearsheetStatistics
from qstrader.trading.backtest import BacktestTradingSession

if __name__ == "__main__":
    start_dt = pd.Timestamp('2004-11-19 14:30:00', tz=pytz.UTC)
    end_dt = pd.Timestamp('2019-12-31 23:59:00', tz=pytz.UTC)

    # Construct the symbol and asset necessary for the backtest
    strategy_symbols = ['GLD']
    strategy_assets = ['EQ:GLD']
    strategy_universe = StaticUniverse(strategy_assets)

    # To avoid loading all CSV files in the directory, set the
    # data source to load only those provided symbols
    csv_dir = os.environ.get('QSTRADER_CSV_DATA_DIR')
    data_source = CSVDailyBarDataSource(csv_dir,
                                        Equity,
                                        csv_symbols=strategy_symbols)
    data_handler = BacktestDataHandler(strategy_universe,
                                       data_sources=[data_source])

    # Construct an Alpha Model that simply provides a fixed
    # signal for the single GLD ETF at 100% allocation
    # with a backtest that does not rebalance
    strategy_alpha_model = FixedSignalsAlphaModel({'EQ:GLD': 1.0})
    strategy_backtest = BacktestTradingSession(start_dt,
示例#8
0
def cli(start_date, end_date, allocations, strat_title, strat_id, tearsheet):
    csv_dir = os.environ.get('QSTRADER_CSV_DATA_DIR', '.')

    start_dt = pd.Timestamp('%s 00:00:00' % start_date, tz=pytz.UTC)

    if end_date is None:
        # Use yesterday's date
        yesterday = (datetime.now() - timedelta(1)).strftime('%Y-%m-%d')
        end_dt = pd.Timestamp('%s 23:59:00' % yesterday, tz=pytz.UTC)
    else:
        end_dt = pd.Timestamp('%s 23:59:00' % end_date, tz=pytz.UTC)

    alloc_dict = obtain_allocations(allocations)

    # Assets and Data Handling
    strategy_assets = list(alloc_dict.keys())
    strategy_symbols = [
        symbol.replace('EQ:', '') for symbol in strategy_assets
    ]
    strategy_universe = StaticUniverse(strategy_assets)
    strategy_data_source = CSVDailyBarDataSource(csv_dir,
                                                 Equity,
                                                 csv_symbols=strategy_symbols)

    strategy_data_handler = BacktestDataHandler(
        strategy_universe, data_sources=[strategy_data_source])

    strategy_assets = alloc_dict.keys()
    strategy_alpha_model = FixedSignalsAlphaModel(alloc_dict)
    strategy_backtest = BacktestTradingSession(
        start_dt,
        end_dt,
        strategy_universe,
        strategy_alpha_model,
        rebalance='end_of_month',
        account_name=strat_title,
        portfolio_id='STATIC001',
        portfolio_name=strat_title,
        long_only=True,
        cash_buffer_percentage=0.01,
        data_handler=strategy_data_handler)
    strategy_backtest.run()

    # Benchmark: 60/40 US Equities/Bonds
    benchmark_symbols = ['SPY', 'AGG']
    benchmark_assets = ['EQ:SPY', 'EQ:AGG']
    benchmark_universe = StaticUniverse(benchmark_assets)

    benchmark_data_source = CSVDailyBarDataSource(
        csv_dir, Equity, csv_symbols=benchmark_symbols)
    benchmark_data_handler = BacktestDataHandler(
        benchmark_universe, data_sources=[benchmark_data_source])

    benchmark_signal_weights = {'EQ:SPY': 0.6, 'EQ:AGG': 0.4}
    benchmark_title = '60/40 US Equities/Bonds'
    benchmark_alpha_model = FixedSignalsAlphaModel(benchmark_signal_weights)
    benchmark_backtest = BacktestTradingSession(
        start_dt,
        end_dt,
        benchmark_universe,
        benchmark_alpha_model,
        rebalance='end_of_month',
        account_name='60/40 US Equities/Bonds',
        portfolio_id='6040EQBD',
        portfolio_name=benchmark_title,
        long_only=True,
        cash_buffer_percentage=0.01,
        data_handler=benchmark_data_handler)
    benchmark_backtest.run()

    output_filename = ('%s_monthly.json' % strat_id).replace('-', '_')
    stats = JSONStatistics(
        equity_curve=strategy_backtest.get_equity_curve(),
        target_allocations=strategy_backtest.get_target_allocations(),
        strategy_id=strat_id,
        strategy_name=strat_title,
        benchmark_curve=benchmark_backtest.get_equity_curve(),
        benchmark_id='6040-us-equitiesbonds',
        benchmark_name=benchmark_title,
        output_filename=output_filename)
    stats.to_file()

    if tearsheet:
        tearsheet = TearsheetStatistics(
            strategy_equity=strategy_backtest.get_equity_curve(),
            benchmark_equity=benchmark_backtest.get_equity_curve(),
            title=strat_title)
        tearsheet.plot_results()
示例#9
0
        start_dt,
        end_dt,
        strategy_universe,
        strategy_alpha_model,
        signals=signals,
        rebalance='end_of_month',
        long_only=True,
        cash_buffer_percentage=0.01,
        burn_in_dt=burn_in_dt,
        data_handler=strategy_data_handler)
    strategy_backtest.run()

    # Construct benchmark assets (buy & hold SPY)
    benchmark_symbols = ['SPY']
    benchmark_assets = ['EQ:SPY']
    benchmark_universe = StaticUniverse(benchmark_assets)
    benchmark_data_source = CSVDailyBarDataSource(
        csv_dir, Equity, csv_symbols=benchmark_symbols)
    benchmark_data_handler = BacktestDataHandler(
        benchmark_universe, data_sources=[benchmark_data_source])

    # Construct a benchmark Alpha Model that provides
    # 100% static allocation to the SPY ETF, with no rebalance
    benchmark_alpha_model = FixedSignalsAlphaModel({'EQ:SPY': 1.0})
    benchmark_backtest = BacktestTradingSession(
        burn_in_dt,
        end_dt,
        benchmark_universe,
        benchmark_alpha_model,
        rebalance='buy_and_hold',
        long_only=True,