Oil Future Spread Matched Roll Dates#

New user? The best way to learn SigTech is to follow the steps in our user guide.

Unrestricted data: This notebook only uses data in our core offer to all users. Go ahead and run it.

Changes will not be saved: Edits made to SigTech’s example notebooks like this will only persist for the duration of your session. When you restart the research environment, this notebook will have been restored to its original state, and any changes you have made will have been lost. To make permanent changes, copy and paste the content to a new notebook in one of your workspaces.

Introduction#

The purpose of thise notebook is to show how to create a spread between two futures instrument traded on different exchanges but with similar underlying asset.

Environment#

This section will import relevant internal and external libraries, as well as setting up the platform environment.

[ ]:
import sigtech.framework as sig
from sigtech.framework.analytics.performance.performance_report import View

import datetime as dtm
import seaborn as sns

sns.set(rc = {'figure.figsize': (18, 6)})
env = sig.init(env_date=dtm.date(2024, 2, 22))
# First we set the calendars for both exchanges
env[sig.config.TM_CALENDAR] = 'NYM (T) CALENDAR,ICE_EU_IPE(T) CALENDAR'

Universe#

[ ]:
cl = sig.obj.get('CLU21 COMDTY')
[ ]:
co =  sig.obj.get('COU21 COMDTY')
[ ]:
cl.group_name, cl.exchange_code, cl.expiry_date, cl.contract_size, cl.currency # cl.asset_description,
[ ]:
co.group_name, co.exchange_code, co.expiry_date, co.contract_size, co.currency # co.asset_description,

Strategy#

Custom Tables#

The custom table reflects what contract is being held at each calendar month. [0] refers to current year and [1] to the following year.

[ ]:
#                Jan.    Feb.    Mar.     Apr.    May.    Jun.    Jul.    Aug.    Sep.    Oct.    Nov.   Dec.
custom_table =  ['M[0]', 'M[0]', 'M[0]', 'U[0]', 'U[0]', 'U[0]', 'Z[0]', 'Z[1]', 'Z[1]', 'H[1]', 'H[1]','H[1]']

start_date = dtm.date(2017, 6, 4)

Matching Roll Dates#

There are two options when setting RollingFuturesStrategy roll dates. In order to roll at the same time monthly_roll_days will allow the strategy to roll on an speficif day of the month instead of days before expiry.

[ ]:
def cmdty_future(contract, custom_table, start_date, roll_bday):
    """
        Returns a RollingFutureStrategy with a custom table and
        roll dates set by ``monthly_roll_days``: the n-th
        business day of the calendar month.
    """
    return sig.RollingFutureStrategy(
        currency = 'USD',
        start_date = start_date,
        contract_code = contract,
        contract_sector = 'COMDTY',
        rolling_rule = 'custom',
        front_offset = None,
        monthly_roll_days = roll_bday,  # Nth businessdays
        include_trading_costs = False,  #don't include trading costs
        total_return = False,  # don't include cash accrual,
        custom_roll_table = custom_table
    )
[ ]:
oil = cmdty_future('CL',custom_table,start_date, '15,15')
brent =  cmdty_future('CO',custom_table,start_date, '15,15')

oil.history().plot()
brent.history().plot();

Ensuring both roll calendars match#

[ ]:
oil.rolling_table.tail()
[ ]:
brent.rolling_table.tail()
[ ]:
oil.plot.portfolio_table('TOP_ORDER_PTS',start_dt=oil.start_date, end_dt=oil.start_date+dtm.timedelta(days=5), unit_type='TRADE')
[ ]:
brent.plot.portfolio_table('TOP_ORDER_PTS',start_dt=brent.start_date, end_dt=brent.start_date+dtm.timedelta(days=5), unit_type='TRADE')

Portfolio Construction#

[ ]:
basket = sig.BasketStrategy(
             currency = 'USD',
             start_date = start_date+dtm.timedelta(days=5),
             constituent_names = [oil.name, brent.name],
             weights = [-1, 1],
             rebalance_frequency = '1W',
             include_trading_costs=False,
             total_return=False,
             ticker = 'Long/Short Strategy'
        )

Performance Analytics#

[ ]:
VIEWS = [View.SUMMARY_SINGLE, View.MONTHLY_STATS_HEATMAP, View.ROLLING_PLOTS]
report = sig.PerformanceReport([oil,brent,basket], views=VIEWS).report()