Skip to content

Commit

Permalink
Modified interfaces to QuantTradingSystem and BacktestTradingSession …
Browse files Browse the repository at this point in the history
…to allow long/short specification.
  • Loading branch information
mhallsmoore committed Jun 23, 2020
1 parent ee8f094 commit 2034114
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 30 deletions.
75 changes: 61 additions & 14 deletions qstrader/system/qts.py
Expand Up @@ -13,6 +13,9 @@
from qstrader.portcon.order_sizer.dollar_weighted import (
DollarWeightedCashBufferedOrderSizer
)
from qstrader.portcon.order_sizer.long_short import (
LongShortLeveragedOrderSizer
)


class QuantTradingSystem(object):
Expand All @@ -36,8 +39,9 @@ class QuantTradingSystem(object):
The alpha model used within the portfolio construction.
risk_model : `AlphaModel`, optional
An optional risk model used within the portfolio construction.
cash_buffer_percentage : `float`, optional
The percentage of the portfolio to retain in cash.
long_only : `Boolean`, optional
Whether to invoke the long only order sizer or allow
long/short leveraged portfolios. Defaults to long/short leveraged.
submit_orders : `Boolean`, optional
Whether to actually submit generated orders. Defaults to no submission.
"""
Expand All @@ -49,21 +53,65 @@ def __init__(
broker_portfolio_id,
data_handler,
alpha_model,
*args,
risk_model=None,
cash_buffer_percentage=0.05,
submit_orders=False
long_only=False,
submit_orders=False,
**kwargs
):
self.universe = universe
self.broker = broker
self.broker_portfolio_id = broker_portfolio_id
self.data_handler = data_handler
self.alpha_model = alpha_model
self.risk_model = risk_model
self.cash_buffer_percentage = cash_buffer_percentage
self.long_only = long_only
self.submit_orders = submit_orders
self._initialise_models()
self._initialise_models(**kwargs)

def _create_order_sizer(self, **kwargs):
"""
Depending upon whether the quant trading system has been
set to be long only, determine the appropriate order sizing
mechanism.
def _initialise_models(self):
Returns
-------
`OrderSizer`
The order sizing mechanism for the portfolio construction.
"""
if self.long_only:
if 'cash_buffer_percentage' not in kwargs:
raise ValueError(
'Long only portfolio specified for Quant Trading System '
'but no cash buffer percentage supplied.'
)
cash_buffer_percentage = kwargs['cash_buffer_percentage']

order_sizer = DollarWeightedCashBufferedOrderSizer(
self.broker,
self.broker_portfolio_id,
self.data_handler,
cash_buffer_percentage=cash_buffer_percentage
)
else:
if 'gross_leverage' not in kwargs:
raise ValueError(
'Long/short leveraged portfolio specified for Quant '
'Trading System but no gross leverage percentage supplied.'
)
gross_leverage = kwargs['gross_leverage']

order_sizer = LongShortLeveragedOrderSizer(
self.broker,
self.broker_portfolio_id,
self.data_handler,
gross_leverage=gross_leverage
)

return order_sizer

def _initialise_models(self, **kwargs):
"""
Initialise the various models for the quantitative
trading strategy. This includes the portfolio
Expand All @@ -72,16 +120,15 @@ def _initialise_models(self):
TODO: Add TransactionCostModel
TODO: Ensure this is dynamically generated from config.
"""
# Portfolio Construction
order_sizer = DollarWeightedCashBufferedOrderSizer(
self.broker,
self.broker_portfolio_id,
self.data_handler,
cash_buffer_percentage=self.cash_buffer_percentage
)
# Determine the appropriate order sizing mechanism
order_sizer = self._create_order_sizer(**kwargs)

# TODO: Allow optimiser to be generated from config
optimiser = FixedWeightPortfolioOptimiser(
data_handler=self.data_handler
)

# Generate the portfolio construction
self.portfolio_construction_model = PortfolioConstructionModel(
self.broker,
self.broker_portfolio_id,
Expand Down
62 changes: 46 additions & 16 deletions qstrader/trading/backtest.py
Expand Up @@ -54,8 +54,9 @@ class BacktestTradingSession(TradingSession):
The ID of the portfolio being used for the backtest.
portfolio_name : `str`, optional
The name of the portfolio being used for the backtest.
cash_buffer_percentage : `float`, optional
The percentage of the portfolio to retain in cash.
long_only : `Boolean`, optional
Whether to invoke the long only order sizer or allow
long/short leveraged portfolios. Defaults to long/short leveraged.
fee_model : `FeeModel` class instance, optional
The optional FeeModel derived subclass to use for transaction cost estimates.
burn_in_dt : `pd.Timestamp`, optional
Expand All @@ -76,7 +77,7 @@ def __init__(
account_name=DEFAULT_ACCOUNT_NAME,
portfolio_id=DEFAULT_PORTFOLIO_ID,
portfolio_name=DEFAULT_PORTFOLIO_NAME,
cash_buffer_percentage=0.05,
long_only=False,
fee_model=ZeroFeeModel(),
burn_in_dt=None,
data_handler=None,
Expand All @@ -93,7 +94,7 @@ def __init__(
self.account_name = account_name
self.portfolio_id = portfolio_id
self.portfolio_name = portfolio_name
self.cash_buffer_percentage = cash_buffer_percentage
self.long_only = long_only
self.fee_model = fee_model
self.burn_in_dt = burn_in_dt

Expand All @@ -114,7 +115,7 @@ def __init__(
)
self.rebalance_schedule = self._create_rebalance_event_times()

self.qts = self._create_quant_trading_system()
self.qts = self._create_quant_trading_system(**kwargs)
self.equity_curve = []
self.target_allocations = []

Expand Down Expand Up @@ -256,7 +257,7 @@ def _create_rebalance_event_times(self):
)
return rebalancer.rebalances

def _create_quant_trading_system(self):
def _create_quant_trading_system(self, **kwargs):
"""
Creates the quantitative trading system with the provided
alpha model.
Expand All @@ -269,16 +270,45 @@ def _create_quant_trading_system(self):
`QuantTradingSystem`
The quantitative trading system.
"""
qts = QuantTradingSystem(
self.universe,
self.broker,
self.portfolio_id,
self.data_handler,
self.alpha_model,
self.risk_model,
self.cash_buffer_percentage,
submit_orders=True
)
if self.long_only:
if 'cash_buffer_percentage' not in kwargs:
raise ValueError(
'Long only portfolio specified for Quant Trading System '
'but no cash buffer percentage supplied.'
)
cash_buffer_percentage = kwargs['cash_buffer_percentage']

qts = QuantTradingSystem(
self.universe,
self.broker,
self.portfolio_id,
self.data_handler,
self.alpha_model,
self.risk_model,
long_only=self.long_only,
cash_buffer_percentage=cash_buffer_percentage,
submit_orders=True
)
else:
if 'gross_leverage' not in kwargs:
raise ValueError(
'Long/short leveraged portfolio specified for Quant '
'Trading System but no gross leverage percentage supplied.'
)
gross_leverage = kwargs['gross_leverage']

qts = QuantTradingSystem(
self.universe,
self.broker,
self.portfolio_id,
self.data_handler,
self.alpha_model,
self.risk_model,
long_only=self.long_only,
gross_leverage=gross_leverage,
submit_orders=True
)

return qts

def _update_equity_curve(self, dt):
Expand Down
1 change: 1 addition & 0 deletions tests/integration/trading/test_backtest_e2e.py
Expand Up @@ -34,6 +34,7 @@ def test_backtest_sixty_forty_no_corp_actions(etf_filepath):
portfolio_id='000001',
rebalance='weekly',
rebalance_weekday='WED',
long_only=True,
cash_buffer_percentage=0.05
)
backtest.run(results=False)
Expand Down

0 comments on commit 2034114

Please sign in to comment.