Commodities Momentum Strategy
Contents
Commodities Momentum Strategy#
Understand this code: Read our step-by-step explanation of this notebook: guided walkthrough.
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. ## Environment
[ ]:
import sigtech.framework as sig
import datetime as dtm
import pandas as pd
import numpy as np
import seaborn as sns
sns.set(rc = {'figure.figsize': (18, 6)})
env = sig.init(env_date=dtm.date(2022, 5, 1))
Define the investment universe#
[ ]:
# Dictionary of assets and their ContractCode
assets = {
'Corn Futures (C)': 'C ',
'Soybean Futures (S)': 'S ',
'Sugar Futures (SB)': 'SB',
'Cotton Futures (CT)': 'CT',
'Live Cattle Futures (LC)': 'LC',
'Soybean Oil Futures (BO)': 'BO',
'Gas Oil Futures (QS)': 'QS',
'Heating Oil Futures (HO)': 'HO',
}
[ ]:
start_date = dtm.date(2015, 1, 4)
[ ]:
def create_rfs(contract_code: str, start_date: dtm.date) -> str:
'''Creates RollingFutureStrategy objects and returns the name of the object.'''
try:
rfs = sig.RollingFutureStrategy(
# The contract_code can be found in the Market Data Browser under ContractCode
contract_code = contract_code,
# The contract_sector can be found in the Market Data Browser under BBGMarketSector
contract_sector = 'COMDTY',
# Specify the currency the strategy will be denominated in
currency = 'USD',
# The behaviour of the rolling between contracts, F_0 means adjusted front contract
rolling_rule = 'F_0',
# Start date of the strategy
start_date = start_date,
)
return rfs.name
except:
pass
[ ]:
BASKETS = {
asset: create_rfs(contract_code, start_date)
for asset, contract_code in assets.items()
}
list(BASKETS.keys())
Create the strategy#
[ ]:
def generate_signals_momentum(assets: list[str], days_window: int = 63) -> pd.DataFrame:
'''Generates a dataframe of signals for provided list of strategies'''
# List to store signals for all assets
frames = []
# Loop through all provided assets
for asset in assets:
# Get the performance of the RollingFutureStrategy as a time series
ts_data = sig.obj.get(asset).history()
# Convert prices to percentage change
pct_change_ts = ts_data.pct_change()
# Create a rolling mean of the percentage price change over
# a backward looking window of 63 days
pct_change_mean_window_ts = pct_change_ts.rolling(days_window).mean()
# Convert the percentage numbers to +1 or -1 if it's higher or lower
# than zero, i.e. only maintain the sign of the percentage change.
mom_signal = np.sign(pct_change_mean_window_ts)
# Add the signal for this asset to the list of stored signals
frames.append(mom_signal.to_frame(asset))
# Convert the list to a DataFrame and remove all NaN values
all_signals = pd.concat(frames, axis=1).dropna()
return all_signals
[ ]:
signal_mom = generate_signals_momentum(assets = list(BASKETS.values()))
signal_mom.head()
Construct the portfolio#
[ ]:
signal_obj = sig.signal_library.from_ts(signal_mom)
[ ]:
mom_strat = sig.SignalStrategy(
# Start date of strategy
start_date = start_date,
# Currency in which the strategy is denominated in
currency = 'USD',
# Reference to the signal object which was created above
signal_name = signal_obj.name,
# The rebalance frequency of the strategy. The strategy will only
# switch positions (buying or selling) of the underlying assets on
# the rebalance dates, e.g. EOM is end-of-month. This means that
# it is only the signals at the end-of-month that is relevant to
# this strategy, and all the signals in between will not have an
# impact on buying/selling of the underlying assets.
rebalance_frequency = 'EOM',
# Allocation function for the strategy, in this case the allocation function
# normalizes the number of short and long positions, i.e. holds the same number
# of long and short positions at any given time.
allocation_function = sig.signal_library.allocation.normalize_weights,
# The reference name of the strategy
ticker = f'Momentum Strategy'
)
Generate the performance report#
[ ]:
sig.PerformanceReport(mom_strat.history()).report()
[ ]:
pnl, weight = mom_strat.analytics.pnl_breakdown(levels=1)
pnl.plot();