← All notesPortfolio

Disclaimer: Data may be delayed, incomplete or inaccurate. For education and research only. Use at your own risk. Not financial advice.

November 2026 · 6 min read

Trading FCPO calendar spreads as a mean-reverting process

A calendar spread is the price difference between two delivery months of the same contract, such as the near and third month of FCPO. Outright palm oil prices trend with weather, biodiesel policy and crude oil. The spread between two months is pulled back by something more mechanical: storage costs and the cost of carrying oil from one month to the next. That pull is what a mean-reversion model tries to capture.

The Ornstein–Uhlenbeck model

The Ornstein–Uhlenbeck (OU) process is the standard model for something that wanders but keeps getting pulled back to a level:

dS = κ (θ − S) dt + σ dW

Fitting it with one regression

On daily data, an OU process is simply an AR(1) regression of today's spread on yesterday's:

St = a + b · St−1 + ε

From the slope and intercept you get every OU parameter: κ = −ln b, θ = a / (1 − b), and the long-run standard deviation σeq = sd(ε) / √(1 − b²). If b is not between 0 and 1, the spread is not mean-reverting in that window, and the honest answer is not to trade it.

I fit this on a rolling window, typically 60 days, so the fair level can drift as the market changes.

The z-score is the signal

z = (S − θ) / σeq

A z-score of +2 means the spread is two long-run standard deviations above fair value. The simple rule is to sell the spread when z is high, buy it when z is low, and exit when it comes back near zero.

Why half-life matters more than the entry level

The half-life, ln 2 / κ, is how many days it takes for half of a deviation to disappear. This is the filter I find most useful:

Adding an ADF stationarity test as a second gate on entries removes more false signals. Exits are left ungated, because you always want to be able to get out.

Things that break the model

Try it with your own data in the Calendar Spread Analyser, or read the full Python version, with a Bayesian hedge ratio and an HTML report, on GitHub.

For education only. Not trading advice.