Rolling Futures Basket#

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#

This notebook showcases how a basket containing rolling futures can be constructed in the SigTech platform.

Environment#

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

[ ]:
import sigtech.framework as sig

import datetime as dtm
import pandas as pd
import seaborn as sns

sns.set(rc={'figure.figsize': (18, 6)})

env = sig.init(env_date=dtm.datetime(2020, 6, 1, 11, 0))

Universe#

To create the universe, a helper function is defined in order to create multiple of different RollingFutureStrategy objects, where the asset and its parameters will be passed in as arguments.

[ ]:
def create_rfs(
    asset: str,
    params: dict[str, str]
) -> str:
    return sig.RollingFutureStrategy(
        currency='USD',
        start_date=dtm.date(2010, 1, 15),
        contract_code=asset,
        contract_sector=params['contract_sector'],
        rolling_rule=params['rolling_rule'],
        monthly_roll_days=params['monthly_roll_days'],
    ).name

The universe for this strategy consists of four rolling future strategies with relevant parameters as per below.

[ ]:
rfs_dict = {
    'LH': { # Lean Hogs
        'contract_sector': 'COMDTY',
        'rolling_rule': 'bcom',
        'monthly_roll_days': '3,5'
    },
    'ES': { # S&P 500
        'contract_sector': 'COMDTY',
        'rolling_rule': 'quarterly',
        'monthly_roll_days': '2,3'
    },
    'GC': { # Gold
        'contract_sector': 'COMDTY',
        'rolling_rule': 'quarterly',
        'monthly_roll_days': '3,5'
    },
    'TY': { # US 10Y Bond Futures
        'contract_sector': 'COMDTY',
        'rolling_rule': 'quarterly',
        'monthly_roll_days': '3,5'
    },
}

universe = [
    create_rfs(asset, ast_param)
    for asset, ast_param in rfs_dict.items()
]

A DataFrame for the returns are now calculated below.

[ ]:
df = pd.concat({rfs: sig.obj.get(rfs).history() for rfs in universe}, axis=1).dropna()

df.head()

Strategy#

Two signal objects are now calculated. One for an equally weighted basket and the second for an inverse vol scaling.

[ ]:
# Calculate signal
vol_scaled = df.pct_change().rolling(252).std().dropna() ** -1
equal_weight = pd.DataFrame(index=vol_scaled.index,
                            columns=vol_scaled.columns, data=1)

# Create platform signal object
vol_scaled_signal = sig.signal_library.from_ts(vol_scaled)
equal_weight_signal = sig.signal_library.from_ts(equal_weight)

vol_scaled_signal.history().head()

Portfolio#

These signals are now converted to two strategies. An allocation function is applied to normalize the weights.

[ ]:
vol_scaled_st = sig.SignalStrategy(
    currency='USD',
    start_date=dtm.date(2011, 1, 6),
    signal_name=vol_scaled_signal.name,
    rebalance_frequency='3M',
    allocation_function=sig.signal_library.allocation.normalize_weights,
)

equally_weighted_st = sig.SignalStrategy(
    currency='USD',
    start_date=dtm.date(2011, 1, 6),
    signal_name=equal_weight_signal.name,
    rebalance_frequency='3M',
    allocation_function=sig.signal_library.allocation.normalize_weights,
)

The performance statistics can also be evaluated for the strategies.

Performance#

Here we will show the performance of the portfolio.

[ ]:
sig.PerformanceReport(pd.concat({
    'Inverse vol scaled':  vol_scaled_st.history(),
    'Equally weighted':  equally_weighted_st.history()
}, axis=1), cash=sig.CashIndex.from_currency('USD')).report()