 ## Introduction

The WTI-Brent spread is the difference between the prices of two types of crude oil: West Texas Intermediate (WTI) on the long side and Brent Crude (Brent) on the short side. For years, the price difference between the two has only been a few dollars on average. As both oils are very similar, their spread shows signs of strong predictability and usually oscillates around some average value. Therefore, it is possible to use deviations from the fair spread value to bet on convergence back to fair value. Here we present a trading strategy based on the price deviations of the spread.

## Method

We use the WTI and Brent crude oil price their CFDs traded in Oanda exchange.

def Initialize(self):
# import the spot price data


The spread is defined as the difference between WTI price and Brent price. Next, we need to calculate the moving average of the spread series using the the SimpleMovingAverage indicator. As the indicator uses the price difference instead of the price series, we need to manually initialize the indicator with the history request.

self.SpreadSMA = SimpleMovingAverage(20)
hist = self.History([self.wti, self.b], 252, Resolution.Daily).unstack(level=0)['close'].ffill().dropna()
wti_hist = hist[self.wti.ID.ToString()]
b_hist = hist[self.b.ID.ToString()]


To get the fair value of the spread, we perform the linear regression between WTI and Brent price over the last one year history price.

$P_{Brent}=\beta \cdot P_{WTI}+\alpha$

Then the fair value of the spread is

$Fair \ value = (1-\beta)\cdot P_{WTI}-\alpha$

# linear regression to decide the fair value
self.regr = linear_model.LinearRegression()
self.regr.fit(wti_hist.values.reshape(-1, 1), b_hist.values.reshape(-1, 1))


def OnData(self, data):
if not (data.ContainsKey(self.wti) and data.ContainsKey(self.b)):
return

fair_value = data[self.wti].Price - self.regr.predict(np.array([data[self.wti].Price]).reshape(1, -1))

self.SetHoldings(self.wti, -0.5)
self.SetHoldings(self.b, 0.5)

self.SetHoldings(self.wti, 0.5)
self.SetHoldings(self.b, -0.5)

if self.Portfolio[self.wti].IsShort and self.Portfolio[self.b].IsLong and spread < fair_value:
self.Liquidate()

if self.Portfolio[self.wti].IsLong and self.Portfolio[self.b].IsShort and spread > fair_value:
self.Liquidate()


We also set up a scheduled linear regression model re-training every month start.

def Initialize(self):
# Recalibrate model every month
self.Train(self.DateRules.MonthStart(), self.TimeRules.At(0,0), self.MyTrainingMethod)

def MyTrainingMethod(self):
hist = self.History([self.wti, self.b], 252, Resolution.Daily).unstack(level=0)['close'].ffill().dropna()
wti_hist = hist[self.wti.ID.ToString()]
b_hist = hist[self.b.ID.ToString()]

# linear regression to decide the fair value
self.regr.fit(wti_hist.values.reshape(-1, 1), b_hist.values.reshape(-1, 1))


spreadPlot = Chart("Spread Plot")