Quant BuffetRelax, Not Over Thinking

Forecasting Crude Oil Prices

Log in to collect

Academic paper

Forecasting Crude Oil Prices: Does Global Financial Uncertainty Matter?

AuthorsYong Ma; shuaibing li; Mingtao Zhou

Institute
  • Hunan University of Finance and Economics
  • ?Hunan University - School of Finance and Statistics
  • ?Hunan University, School of Finance and Statistics
  • Hunan University

Strategy in a nutshell

Invest in crude oil derivatives (futures or spot). Use a univariate regression with GFU as predictor to forecast next-month oil returns. Buy or short monthly based on GFU signals. Portfolio: single position, monthly rebalancing. Suitable for investors with risk aversion coefficient of 3.

Economic rationale

GFU is a robust alternative data predictor for crude oil returns. It improves forecasting accuracy, remains statistically and economically significant, and enriches existing literature on commodity market timing strategies.

Backtest performance

Annualised return4.65%
Volatility19.38%
Beta-0.206
Sharpe ratio0.24
Win rate31%

Full Python code

from AlgorithmImports import *
import data_tools
import statsmodels.api as sm
# endregion

class ForecastingCrudeOilPrices(QCAlgorithm):

def Initialize(self):
self.SetStartDate(2000, 1, 1)
self.SetCash(100000)

leverage:int = 3
self.oil:Symbol = self.AddData(data_tools.QuantpediaFutures, 'CME_CL1', Resolution.Daily).Symbol
self.Securities[self.oil].SetLeverage(leverage)

self.gfu:Symbol = self.AddData(data_tools.GFU, 'GFU', Resolution.Daily).Symbol

# self.test_size:float = 0.2

self.min_period:int = 12
self.price_storage:Dict[Symbol, List] = {}
for symbol in [self.oil, self.gfu]:
    self.price_storage[symbol] = []

def OnData(self, data: Slice) -> None:
rebalance = False

# store daily price
if self.gfu in data and data[self.gfu]:
    self.price_storage[self.gfu].append(data[self.gfu].Value)
    self.price_storage[self.oil].append(self.Securities[self.oil].Price)
    rebalance = True

# rebalance once new GFU data arrived
if rebalance:
    last_update_date = [c.get_last_update_date() for c in [data_tools.QuantpediaFutures, data_tools.GFU]]
    if all(self.Securities[symbol].GetLastData() and last_update_date[i] > self.Time.date() for i, symbol in enumerate([self.oil, self.gfu])):
        if all(len(self.price_storage[symbol]) >= self.min_period for symbol in [self.oil, self.gfu]):
            oil_prices:np.ndarray = np.array(self.price_storage[self.oil])
            x:np.ndarray = np.array(self.price_storage[self.gfu])[1:]
            y:np.ndarray = np.log(oil_prices[1:] / oil_prices[:-1]) # oil monthly log returns

            # run regression
            model = data_tools.multiple_linear_regression(x[:-1], y[1:])
            predicted_oil_return:float = model.predict([1, x[-1]])[0]

            # test_size:int = int(len(self.price_storage[self.oil]) * self.test_size)
            # model = data_tools.multiple_linear_regression(x[:-1][:-test_size], y[1:][:-test_size])
            # predicted_oil_return:float = model.predict(sm.add_constant(x[-test_size:]))[-1] if test_size > 1 else model_.predict([1, x[-test_size:]])[-1][0]

            # trade based on prediction
            if predicted_oil_return > 0:
                self.SetHoldings(self.oil, 1)
            else:
                # self.Liquidate(self.oil)
                self.SetHoldings(self.oil, -1)
    else:
        self.Liquidate()
        return