A simple building block for creating strategies, whose underlying assets or strategies are rebalanced to fixed weights, on a fixed rebalancing schedule.
This section will import relevant internal and external libraries, as well as setting up the platform environment.
Learn more: for information on setting up the environment, see Environment setup.
import pandas as pdimport datetime as dtmimport seaborn as snssns.set(rc={'figure.figsize': (18, 6)})import sigtech.framework as sigend_date = dtm.date(2021, 11, 30)sig.init(env_date=end_date)
Building block overview
Overview of the BasketStrategy class.
sig.BasketStrategy?
Rebalancing frequencies
Although it is possible to build custom rebalancing schedules, the majority of common schedules are already supported.
The following AVAILABLE_REBALANCE_FREQS object can help with finding the correct string to supply to a BasketStrategy, and other strategies more generally, to achieve the desired rebalancing behaviour.
freqs = sig.BasketStrategy.AVAILABLE_REBALANCE_FREQSfrequencies = [f for f indir(freqs)ifnot f.startswith('_')]frequency_strings =sorted([f for f in frequencies if f == f.upper()])frequency_strings
Any of the above attributes are methods that take an input and return the correct string format. The thursdays method is an example:
freqs.thursdays?
Signature: freqs.thursdays(n=1)
Docstring:
Rebalance every ``n`` weeks on Thursdays.
:param n: Number of weeks, 1 by default.
:return: Rebalance string value.
File: /opt/conda/envs/sig-env/lib/python3.7/site-packages/sigtech/framework/schedules/schedule.py
Type: method
The input n is how often, in terms of weeks, to rebalance.
So freqs.thursdays(2) returns the string to input when a strategy should rebalance every second Thursday.
The following cell cycles through some of these methods and shows sample output (the strings to supply as the rebalancing_frequency argument):
Based on the above, choose a start date from the beginning of 2021.
start_date = dtm.date(2021, 1, 3)
Create a basket that allocates 70% to the CL contract and 40% to the NG contract, rebalancing on Wednesday every three weeks. There is no requirement for weights to sum to 100% and short positions are also possible.
If the scheduled rebalancing day falls on a market holiday, then rebalance on the following business day.
weights = [0.7,0.4]# list of names of constituents, not constituent objectsconstituent_names = [cl.name, ng.name]rebalance_frequency = freqs.wednesdays(3)# '3W-WED'rebalance_bdc ='FOLLOWING'
The BasketStrategy building block is also able to take strategies as constituents. The following example creates two RollingFutureStrategy objects trading crude oil, a 'front month' version and a 'back month' version, and then constructs a basket from these two strategies.
The relevant information needed to construct a RollingFutureStrategy (RFS) is obtained directly from the relevant FuturesContractGroup.
# utilise a longer historystart_date = dtm.date(2018, 1, 3)cl_group = cl.group()contract_code = cl_group.contract_codecontract_sector = cl_group.contract_sector# specifics of how rolling is to be donerolling_rule ='front'front_offset ='-4:-2'
rf_front = sig.RollingFutureStrategy( currency=currency, start_date=start_date, contract_code=contract_code, contract_sector=contract_sector, rolling_rule=rolling_rule, front_offset=front_offset,# determine which contract to trade contract_offset=0,)rf_front.build()
rf_back = sig.RollingFutureStrategy( currency=currency, start_date=start_date, contract_code=contract_code, contract_sector=contract_sector, rolling_rule=rolling_rule, front_offset=front_offset,# determine which contract to trade contract_offset=1,)rf_back.build()
Comparison of holdings for the two rolling futures strategies.
As expected, whilst the 'front' version of the strategy is rolling out of Gs and into Hs, the back version is rolling out of Hs and into Js.
Create a dollar neutral basket that goes long the back month version of the RFS and short the front month version, rebalancing at the end of each month.
The default value of rebalance_bdc is taken as no argument is supplied. This results in the preceding business day being used to rebalance if that dictated by the schedule falls on a market holiday.
The history can be fetched and plotted directly by calling history or the PlotWrapper. It can be used to create an interactive performance plot.
term_basket.plot.performance()
Basket of baskets
A basket strategy, like any strategy built within the SigTech platform, is itself a Strategy, i.e. it inherits from the sig.Strategy class. This means it is possible to create a BasketStrategy that allocates amongst other BasketStrategy objects.
This example creates a basket that allocates between the crude term structure basket built above and a EURO STOXX 50 (VG) Rolling Futures Strategy.
The same rolling rules as above are used with a contract_offset of zero (the front quarterly contract is held).
group = sig.obj.get('VG INDEX FUTURES GROUP')rfs = sig.RollingFutureStrategy( currency=currency, start_date=start_date, contract_code=group.contract_code, contract_sector=group.contract_sector, rolling_rule=rolling_rule, front_offset=front_offset, contract_offset=0,)rfs.build()
The intersecting_business_days parameter can be used to ensure that the intersection of the business days used for each strategy is used for the resulting strategy.
It is easy to check that this is true for baskets_basket.
ClickExpand All in the following tree plot to view a visualisation of how the top level basket is trading both a lower level basket and a Rolling Futures Strategy. It also shows the lower level constituents that each of these strategies is holding.
BasketStrategy admits a parameter called rebalance_dates, which takes the form of a list of dtm.date or dtm.datetime objects and superseded rebalance_frequency in determining the dates on which the strategy should rebalance.
The example below creates a custom schedule using the SchedulePeriodic class and then passes this to BasketStrategy
start_date = dtm.date(2018, 7, 2)
_end_date = dtm.date(2019, 12, 31)
weekly_fridays = sig.SchedulePeriodic(
start_date=start_date,
end_date=_end_date,
holidays=cl.exchange().holidays,
frequency=freqs.fridays(1)
).all_data_dates()
schedule = [d for d in weekly_fridays if d.month in [3, 6, 9, 12]]
schedule[:5]
One of the main reasons for this difference is that the number of units to trade, or sizing, is done using the previous day's closing price for both of the futures contracts that underlie the basket. This is to more accurately simulate the experience of a portfolio manager with access to EOD prices.
The instant_sizing parameter can be used to execute orders on the same day that they are sized.
The SizeVaryingBasket building block is a basket strategy with the additional functionality to support the handling of fund subscriptions and redemptions.
from sigtech.framework.strategies.basket_strategies import SizeVaryingBasketStrategySizeVaryingBasketStrategy?
This example uses the front and back month CL rolling futures strategies from above, together with a dictionary of 'subs' and 'reds'. There is an option to trigger rebalancing at a subscription or redemption event, which is used in this case. The initial_cash is also altered.
In addition to the usual portfolio_table, the timeline method of a strategy's PlotWrapper is an excellent way to get an overview of the details of a strategy.
Selecting the period 01/02/2021 - 15/02/2021, displaying 'POSITIONS' and selecting all boxes except 'Flatten' clearly displays the cash inflow and the associated _add_cash method being called.
Note: clicking on the top level strategy displays the substrategies.