Commodity Vector Autoregression Model#

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 custom strategy fitting a VAR model to a selection of commodity futures’ returns can be constructed in the SigTech Platform. The strategy will long/short a basket of individual commodities depending on the VAR model’s prediction.

Environment#

This section imports the relevant internal and external libraries, and sets up the platform environment.

[ ]:
import numpy as np
import pandas as pd
import datetime as dtm

from typing import Optional

# Import VAR model package
from statsmodels.tsa.api import VAR

import sigtech.framework as sig

env = sig.init(env_date=dtm.date(2024, 2, 22))

Universe#

A basket of rolling commodity futures using the RollingFutureStrategy class.

[ ]:
commodity_contracts = {
    'Corn Futures': 'C ',
    'Soybean Futures': 'S ',
    'Sugar Futures': 'SB',
    'Cotton Futures': 'CT'
}

def create_rfs(contract_code: str,  start_date: dtm.date) -> str:
    """Returns RollingFutureStrategy object."""
    return sig.RollingFutureStrategy(contract_code=contract_code,
                                     contract_sector='COMDTY',
                                     currency='USD',
                                     rolling_rule='F_0',
                                     start_date=start_date)

Strategy#

A custom strategy is created by inheriting from the base sig.Strategy class. All child classes of this base class must define a strategy_initialization method which will schedule the initial decision(s) and set weights via the add_method method.

[ ]:


class CommodityVARStrategy(sig.DailyStrategy): # Strategy Inputs list_of_commodities: Optional[list[str]] = None var_lag: Optional[int] = 1 var_lookback: Optional[int] = 20 def __post_init__(self): super().__post_init__() assert len(self.list_of_commodities) > 1, 'At least two commodities must be provided' rf_start_date = self.start_date - dtm.timedelta(5 * self.var_lag) self.commodities = [create_rfs(x, rf_start_date) for x in self.list_of_commodities] def strategy_initialization(self, dt): """ Set up strategy with initial values. """ for day in self.trade_schedule().data_ts()[1:]: self.add_method(day + pd.Timedelta("12 hours"), self.trade_decision) def trade_decision(self, dt): """ Fit the model and predict next day's returns. """ # Clear all positions for imnt, _ in self.positions.iterate_instruments(dt): self.add_position_target(dt, imnt.name, 0) # We assume execution at close, with close prices and no delay commodity_returns = pd.concat( [ np.log(1 + x.history()[:dt.replace(tzinfo=None)].pct_change().dropna()) for x in self.commodities ], axis=1 ) # Provide VAR model with frequency information commodity_returns.index = commodity_returns.index.to_period("B") # Fit model to the last set of values training_data = commodity_returns[-self.var_lookback:] model = VAR(training_data) results = model.fit(self.var_lag) # Make prediction for next time step next_step = commodity_returns.iloc[-len(self.commodities)*self.var_lag:] predictions = results.forecast(next_step.values, 1) # Trade based on sign of predictions for i, prediction in enumerate(np.sign(predictions)[0]): trade_imnt = self.commodities[i].name self.add_position_target(dt, trade_imnt, prediction, unit_type='WEIGHT')

Portfolio#

Our custom strategy is built and backtested

[ ]:
var_strategy = CommodityVARStrategy(
    currency="USD",
    start_date=dtm.date(2017, 1, 3),
    list_of_commodities=list(commodity_contracts.values()),
    var_lag=1,
    var_lookback=20,
    include_trading_costs=False,
    ticker='Commodity VAR'
)

var_strategy.history().plot(figsize=(15, 8));
[ ]:
var_strategy.inspect.positions_df().plot(figsize=(20,5));

Performance#

Shows some of the statistics and relevant performance metrics available on the platform for the strategy.

[ ]:
sig.PerformanceReport(var_strategy.history(), cash=sig.CashIndex.from_currency('USD')).report()