Maximum Smoothness Forward Curve

A typical characteristic of energy commodities such as electricity and natural gas is that delivery takes place over a period in time, not on a single date. Listed futures contracts cover standardized periods, such as “Week”, “Month”, “Quarter”, “Season” or “Year”. The forward curve is an essential tool for pricing non-standard OTC contracts having any settlement period.

An example of such standard energy market contracts can be found in the package data set powfutures130513.

powfutures130513
#>    Include Contract      Start        End Closing
#> 1     TRUE   W21-13 2013-05-20 2013-05-26   33.65
#> 2     TRUE   W22-13 2013-05-27 2013-06-02   35.77
#> 3     TRUE   W23-13 2013-06-03 2013-06-09   36.58
#> 4     TRUE   W24-13 2013-06-10 2013-06-16   35.93
#> 5     TRUE   W25-13 2013-06-17 2013-06-23   33.14
#> 6     TRUE   W26-13 2013-06-24 2013-06-30   34.16
#> 7    FALSE  MJUN-13 2013-06-01 2013-06-30   35.35
#> 8     TRUE  MJUL-13 2013-07-01 2013-07-31   33.14
#> 9     TRUE  MAUG-13 2013-08-01 2013-08-31   35.72
#> 10    TRUE  MSEP-13 2013-09-01 2013-09-30   38.41
#> 11    TRUE  MOCT-13 2013-10-01 2013-10-31   38.81
#> 12    TRUE  MNOV-13 2013-11-01 2013-11-30   40.94
#> 13   FALSE    Q3-13 2013-07-01 2013-09-30   35.72
#> 14    TRUE    Q4-13 2013-10-01 2013-12-31   40.53
#> 15    TRUE    Q1-14 2014-01-01 2014-03-31   42.40
#> 16    TRUE    Q2-14 2014-04-01 2014-06-30   33.39
#> 17    TRUE    Q3-14 2014-07-01 2014-09-30   31.78
#> 18    TRUE    Q4-14 2014-10-01 2014-12-31   38.25
#> 19    TRUE    Q1-15 2015-01-01 2015-03-31   40.73
#> 20    TRUE    Q2-15 2015-04-01 2015-06-30   32.64
#> 21    TRUE    Q3-15 2015-07-01 2015-09-30   30.87
#> 22    TRUE    Q4-15 2015-10-01 2015-12-31   37.22
#> 23   FALSE   CAL-14 2014-01-01 2014-12-31   36.43
#> 24   FALSE   CAL-15 2015-01-01 2015-12-31   35.12
#> 25    TRUE   CAL-16 2016-01-01 2016-12-31   34.10
#> 26   FALSE   CAL-17 2017-01-01 2017-12-31   35.22
#> 27   FALSE   CAL-18 2018-01-01 2018-12-31   36.36
#> 28   FALSE   CAL-19 2019-01-01 2019-12-31   37.44
#> 29   FALSE   CAL-20 2020-01-01 2020-12-31   38.58
#> 30   FALSE   CAL-21 2021-01-01 2021-12-31   39.73
#> 31   FALSE   CAL-22 2022-01-01 2022-12-31   40.93
#> 32   FALSE   CAL-23 2023-01-01 2023-12-31   42.15

The data set contains closing prices for a set of synthetic futures contracts on the trading date 2013-05-13. It can be used to create an instance of the MSFC class with:

fwd_npri <- msfc(tdate = as.Date("2013-05-13"),
                  include = powfutures130513$Include,
                  contract = powfutures130513$Contract,
                  sdate = powfutures130513$Start,
                  edate = powfutures130513$End,
                  f = powfutures130513$Closing)

In addition to the arguments from the list of contracts, the user may also provide a prior function to the calculation. This is relevant for markets with strong seasonality, such as power markets. The default value for the msfc() function is prior = 0, but the user can provide any vector expressing a belief regarding the market to be combined with the observed prices. We create a new MSFC instance, this time including a simple seasonal prior collected from the package data set powpriors130513:

fwd_wpri <- msfc(tdate = as.Date("2013-05-13"),
                  include = powfutures130513$Include,
                  contract = powfutures130513$Contract,
                  sdate = powfutures130513$Start,
                  edate = powfutures130513$End,
                  f = powfutures130513$Closing,
                  prior = powpriors130513$mod.prior)

Available methods for the objects fwd_npri and fwd_wpri are plot(), summary() and show(). The effect from the prior curve can be visually inspected by comparing the two plots:

plot(fwd_npri, title = "Forward curve without prior")

plot(fwd_wpri, title = "Forward curve with prior")

As shown in the plots, the shorter contracts close in time to the trading date clearly reflect a seasonal pattern. This is typical in power markets, where weather and calendar effects have strong influence on transacted volume and price formation. On a longer horizon however, this information is not observable in market prices, as the quoted contracts cover longer time spans. This is where price data may be supplemented with prior knowledge in order to create a representation of the market consistent with both the underlying fundamentals and the listed contracts.

In etrm, the forward curve is calculated with the function \(f(t) = \lambda(t) + \epsilon(t)\) where \(\lambda(t)\) is the prior supplied by the user and \(\epsilon(t)\) is an adjustment function taking the observed prices into account. The msfc() function finds the smoothest possible adjustment function \(\epsilon(t)\) by minimizing the mean squared value of a spline function, while ensuring that the average value of the curve \(f(t)\) is equal to contract prices used in the calculation for the respective time intervals. The number of polynomials used in the spline along with head(prior) and computed prices based on the curve are available with the summary() method:

summary(fwd_npri)
#> $Description
#> [1] "MSFC of length 1329 built with 41 polynomials at trade date 2013-05-13"
#> 
#> $PriorFunc
#> [1] 0 0 0 0 0 0
#> 
#> $BenchSheet
#>    Include Contract       From         To Price  Comp
#> 1     TRUE   W21-13 2013-05-20 2013-05-26 33.65 33.65
#> 2     TRUE   W22-13 2013-05-27 2013-06-02 35.77 35.77
#> 3     TRUE   W23-13 2013-06-03 2013-06-09 36.58 36.58
#> 4     TRUE   W24-13 2013-06-10 2013-06-16 35.93 35.93
#> 5     TRUE   W25-13 2013-06-17 2013-06-23 33.14 33.14
#> 6     TRUE   W26-13 2013-06-24 2013-06-30 34.16 34.16
#> 8     TRUE  MJUL-13 2013-07-01 2013-07-31 33.14 33.14
#> 9     TRUE  MAUG-13 2013-08-01 2013-08-31 35.72 35.72
#> 10    TRUE  MSEP-13 2013-09-01 2013-09-30 38.41 38.41
#> 11    TRUE  MOCT-13 2013-10-01 2013-10-31 38.81 38.81
#> 12    TRUE  MNOV-13 2013-11-01 2013-11-30 40.94 40.94
#> 14    TRUE    Q4-13 2013-10-01 2013-12-31 40.53 40.53
#> 15    TRUE    Q1-14 2014-01-01 2014-03-31 42.40 42.40
#> 16    TRUE    Q2-14 2014-04-01 2014-06-30 33.39 33.39
#> 17    TRUE    Q3-14 2014-07-01 2014-09-30 31.78 31.78
#> 18    TRUE    Q4-14 2014-10-01 2014-12-31 38.25 38.25
#> 19    TRUE    Q1-15 2015-01-01 2015-03-31 40.73 40.73
#> 20    TRUE    Q2-15 2015-04-01 2015-06-30 32.64 32.64
#> 21    TRUE    Q3-15 2015-07-01 2015-09-30 30.87 30.87
#> 22    TRUE    Q4-15 2015-10-01 2015-12-31 37.22 37.22
#> 25    TRUE   CAL-16 2016-01-01 2016-12-31 34.10 34.10

For comparison, the calculation including prior:

summary(fwd_wpri)
#> $Description
#> [1] "MSFC of length 1329 built with 41 polynomials at trade date 2013-05-13"
#> 
#> $PriorFunc
#> [1] 30.10842 30.16396 30.19572 30.16144 29.06268 28.93272
#> 
#> $BenchSheet
#>    Include Contract       From         To Price  Comp
#> 1     TRUE   W21-13 2013-05-20 2013-05-26 33.65 33.65
#> 2     TRUE   W22-13 2013-05-27 2013-06-02 35.77 35.77
#> 3     TRUE   W23-13 2013-06-03 2013-06-09 36.58 36.58
#> 4     TRUE   W24-13 2013-06-10 2013-06-16 35.93 35.93
#> 5     TRUE   W25-13 2013-06-17 2013-06-23 33.14 33.14
#> 6     TRUE   W26-13 2013-06-24 2013-06-30 34.16 34.16
#> 8     TRUE  MJUL-13 2013-07-01 2013-07-31 33.14 33.14
#> 9     TRUE  MAUG-13 2013-08-01 2013-08-31 35.72 35.72
#> 10    TRUE  MSEP-13 2013-09-01 2013-09-30 38.41 38.41
#> 11    TRUE  MOCT-13 2013-10-01 2013-10-31 38.81 38.81
#> 12    TRUE  MNOV-13 2013-11-01 2013-11-30 40.94 40.94
#> 14    TRUE    Q4-13 2013-10-01 2013-12-31 40.53 40.53
#> 15    TRUE    Q1-14 2014-01-01 2014-03-31 42.40 42.40
#> 16    TRUE    Q2-14 2014-04-01 2014-06-30 33.39 33.39
#> 17    TRUE    Q3-14 2014-07-01 2014-09-30 31.78 31.78
#> 18    TRUE    Q4-14 2014-10-01 2014-12-31 38.25 38.25
#> 19    TRUE    Q1-15 2015-01-01 2015-03-31 40.73 40.73
#> 20    TRUE    Q2-15 2015-04-01 2015-06-30 32.64 32.64
#> 21    TRUE    Q3-15 2015-07-01 2015-09-30 30.87 30.87
#> 22    TRUE    Q4-15 2015-10-01 2015-12-31 37.22 37.22
#> 25    TRUE   CAL-16 2016-01-01 2016-12-31 34.10 34.10

The forward curve values can be extracted along with daily prices for the contracts used in the calculation with the show() method:

head(show(fwd_npri), 15)[1:8]
#>          Date     MSFC W21-13 W22-13 W23-13 W24-13 W25-13 W26-13
#> 1  2013-05-13 29.54905     NA     NA     NA     NA     NA     NA
#> 2  2013-05-14 29.96485     NA     NA     NA     NA     NA     NA
#> 3  2013-05-15 30.38054     NA     NA     NA     NA     NA     NA
#> 4  2013-05-16 30.79674     NA     NA     NA     NA     NA     NA
#> 5  2013-05-17 31.21359     NA     NA     NA     NA     NA     NA
#> 6  2013-05-18 31.63077     NA     NA     NA     NA     NA     NA
#> 7  2013-05-19 32.04745     NA     NA     NA     NA     NA     NA
#> 8  2013-05-20 32.46234  33.65     NA     NA     NA     NA     NA
#> 9  2013-05-21 32.87354  33.65     NA     NA     NA     NA     NA
#> 10 2013-05-22 33.27815  33.65     NA     NA     NA     NA     NA
#> 11 2013-05-23 33.67247  33.65     NA     NA     NA     NA     NA
#> 12 2013-05-24 34.05208  33.65     NA     NA     NA     NA     NA
#> 13 2013-05-25 34.41187  33.65     NA     NA     NA     NA     NA
#> 14 2013-05-26 34.74600  33.65     NA     NA     NA     NA     NA
#> 15 2013-05-27 35.05074     NA  35.77     NA     NA     NA     NA

We have excluded columns from the data frame for the sake of presentation. Further details regarding the calculation such as spline coefficients and knot points can be found in the slots:

slotNames(fwd_npri)
#> [1] "Name"        "TradeDate"   "BenchSheet"  "Polynomials" "PriorFunc"  
#> [6] "Results"     "SplineCoef"  "KnotPoints"  "CalcDat"