FX Hedged Oil Location Spread Strategy#

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

Restricted data: You will only have access to the data used in this notebook if your organisation has specifically purchased it. To check your current data access, view Data. If you would like to access more data, please contact sales@sigtech.com.

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 simple FX hedged oil location spread strategy trading brent futures can be constructed in the SigTech Platform. The strategy will apply a linear regression to the Brent - WTI spread and go long/short to trade mean reversion across the regression.

Environment#

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

[ ]:
import uuid
import numpy as np
import pandas as pd
import seaborn as sns
import datetime as dtm
from sklearn.linear_model import LinearRegression

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

import sigtech.framework as sig
from sigtech.framework.analytics.performance.performance_report import PerformanceReport, View

start_date = dtm.date(2018, 1, 1)

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

Universe#

First, we define our `RollingFutureStrategy <https://guide.sigtech.com/learning-pathways/cross-asset/rolling-future-strategy>`__ to handle the rolling of Brent Crude Oil (contract code CO) and WTI Crude Oil (contract code CL) futures. The rolling_rule is customisable, the front_offset will roll 6 and 5 days before contract expiry, as defined in python notation.

[ ]:
def get_rfs(contract_code: str, rolling_rule : str):
    return sig.RollingFutureStrategy(
        currency="USD",
        start_date=dtm.date(2014, 1, 1),
        contract_code=contract_code,
        contract_sector='COMDTY',
        rolling_rule=rolling_rule,
        front_offset="-6,-5",
    )

Strategy#

First we fit a linear regression on the percentage spread between the prices of the front futures for our two crude oil contracts.

[ ]:
co_rfs = get_rfs('CO', 'front')
cl_rfs = get_rfs('CL', 'front')
df = pd.concat([co_rfs.history(), cl_rfs.history()], axis=1)
df.columns = ['co', 'cl']
df = df.dropna()
[ ]:
df['pct_spread'] = (df['co'] - df['cl'])/df['co']

X = df.index.values.reshape(-1, 1)
model = LinearRegression().fit(X, df['pct_spread'])
df['regressed_pct_spread'] = model.predict(X.astype(float))

df['pct_spread'].plot()
df['regressed_pct_spread'].plot()

Our signal is defined as the Z-score of the spread’s deviation from the fitted model. If the spread diverges more than 0.1 STDs away from our fit, we go long/short on brent crude oil to speculate on mean reversion of the spread.

[ ]:
df['diff_pct_spread'] = df['pct_spread'] - df['regressed_pct_spread']
df['av_diff_pct_spread'] = df['diff_pct_spread'].rolling(15).mean()
df['std_diff_pct_spread'] = df['diff_pct_spread'].rolling(15).mean()
df = df.dropna()

df['z_spread'] = (df['diff_pct_spread'] - df['av_diff_pct_spread']) / df['std_diff_pct_spread']
df['SignalRegressedZSpread'] = np.select([df['z_spread'] > 0.1, df['z_spread'] < -0.1], [-1, 1], 0)

Portfolio#

To build our portoflio, the SignalStrategy building block is used. By default, the identity allocation function is used, which returns the unchanged allocations as given by the signal strategy output.

Our signal will trade third month cotracts are specified by the F_3 rolling_rule.

[ ]:
co_tradable = get_rfs('CO', 'F_3')

signal_df = pd.DataFrame({co_tradable.name: df['SignalRegressedZSpread']}, index=df.index)

ss_regressed_spread = sig.SignalStrategy(
    currency='USD',
    start_date=start_date,
    signal_name=sig.signal_library.from_ts(signal_df).name,
    ticker=f'LOCSPR REGR {str(uuid.uuid4())[:4]}'
)

ss_regressed_spread.history().plot()
[ ]:
ss_regressed_spread.plot.timeline()

The futures contracts are traded in USD, which may present a currency risk. In order to deliver our strategy’s performance in a given currency, we can hedge via the FXForwardHedgingStrategy building block. This goes long 100% notional exposure to the underlying strategy and short 100% notional exposure its currency through FX forwards. The interactive timeline plot is used below to see this in action.

The exposure_rebalance_threshold is the magnitued of percentage slippage allowed between this strategy and its underlying strategy before a rebalance is triggered. The hedge_rebalance_threshold is the percentage slippage allowed between the FX forwards and the total currency notional of the underlying strategy before a rebalance is triggered.

[ ]:
fx_hedged_regression_spread = sig.FXForwardHedgingStrategy(
    currency='GBP',
    strategy_name=ss_regressed_spread.name,
    start_date=start_date,
    hedging_tenor='1M',
    exposure_rebalance_threshold=0.1,
    hedge_rebalance_threshold=0.1
)

fx_hedged_regression_spread.plot.timeline()

Performance#

Our PerformanceReport is a customisable view that shows the statistics and relevant performance metrics for our strategies.

[ ]:
VIEWS = [View.SUMMARY_SINGLE, View.HISTORY, View.ROLLING_PLOTS, View.MONTHLY_STATS_HEATMAP]
CASH = sig.CashIndex.from_currency('USD').history()
report = PerformanceReport([ss_regressed_spread, fx_hedged_regression_spread], CASH, views=VIEWS).report()