MATLAB Examples

Natural Gas Storage Valuation: 2. Intrinsic & Spread-Option Valuation

This is the second script of 4 in the Natural Gas Storage Valuation case study. This script prices a natural gas storage contract using the Intrinsic & Spread Option valuation methods. Please see the Readme document for more information on each valuation method and script1_DataPrep for more information on how the data was prepared for this analysis.

Contents

Define Storage Facility, Contract & Valuation Parameters

These are parameters that define the storage facility, its constraints and costs. These are defined in "defineStorageParameters.m"

type defineStorageParameters

defineStorageParameters;

% Add path to helper functions
addpath Utilities
%% Define Storage Facility, Contract & Valuation Parameters
% These are parameters that define the storage facility, its constraints
% and costs.

% Copyright 2014 The MathWorks, Inc.

%% Facility Parameters

facility.vMax = 1e6; % MMBtu (Maximum gas volume for facility)
facility.vMin = 0; % MMBtu (Minimum gas volume for facility)
facility.cI = .01; % $/MMBtu (cost of injection)
facility.cW = .01; % $/MMBtu (cost of withdrawal)
% Ratchet Constraints
facility.iMax = [               0  8197; 
                 facility.vMax*.8  8197; 
                 facility.vMax*.9  6000; 
                 facility.vMax        0]; % MMBtu/day (max injection rate)
facility.wMax = [               0  6000; 
                 facility.vMax*.2 14000; 
                 facility.vMax*.4 16393;
                 facility.vMax    16393]; % MMBtu/day (max withdrawal rate)
             
%% Contract parameters
contract.startDate = '1 Jun 2014';
contract.endDate = '1 Jun 2015';
contract.startVolume = 0; % Contract initial volume
contract.endVolume = 0; % Contract terminal volume

%% Valuation Parameters
valuation.valDate = datenum('15 May 2014');% Valuation date
valuation.discRate = .03; % Valuation discount rate
valuation.liquiditySpread = .02; % Liquidity spread (Bid - Ask)
valuation.minLotSize = 1;
valuation.nTrials = 100; % Number of Monte Carlo trials

% Add initial forward Curve to valuation parameters
load Data\FwdCurveHistory
ind1 = datenum(valuation.valDate) == obsDate;
ind2 = expDate >= datenum(contract.startDate) & ...
       expDate < datenum(contract.endDate);
valuation.startCurve = fwdPrice(ind1, ind2);
valuation.expDates = expDate(ind2); % Expiration dates
clear ind1 ind2 expDate obsDate fwdPrice

Visualize Forward Curve

The forward curve on the valuation date determines the intrinsic value of storage

plot(valuation.expDates, valuation.startCurve, 'o-'); grid on;
ylabel('Forward price ($/MMBtu)'); datetick;
title(sprintf('Forward curve on %s', datestr(valuation.valDate)));

Compute Ratchet Parameters

The daily injection and withdrawal limits are functions of the working gas volume. Since the storage optimization is done with monthly contracts, the monthly injection and withdrawal limit function needs to be calculated. In addition we compute a piece-wise linear approximation of the function for the optimization solver.

[facility.injMax, facility.witMax, facility.injCoef, facility.witCoef] = ...
    generateIWLimitArrays(facility,'linear',6,1000);

plotIWLimits(facility);

Perform Intrinsic Valuation

Run a linear program to select optimal injection (long) and withdrawal (short) positions in forwards that maximize storage value and meet physical constraints.

bid = valuation.startCurve' - valuation.liquiditySpread/2;
ask = valuation.startCurve' + valuation.liquiditySpread/2;

[intrPos, intrValue] = optimizeStorageIntr(facility, bid, ask, contract, valuation);

figure(1); clf
plotResults(intrPos(:,1), intrPos(:,2), valuation.expDates, facility,...
            contract, 30*ones(size(bid)));

fprintf('Intrinsic Storage Value: $%0.2fk\n\n', intrValue/1e3);
Optimization terminated.
Storage Schedule ------------------------------------------------------------
     Month      Inj       With      Vol      InjLim    WitLim
    _______    ______    ______    ______    ______    ______
    'Jun14'         0         0         0    245.91         0
    'Jul14'         0         0         0    245.91         0
    'Aug14'    245.91         0    245.91    245.91         0
    'Sep14'    245.59         0     491.5    245.91    245.91
    'Oct14'    245.26         0    736.76    245.91    420.88
    'Nov14'     201.8         0    938.56    203.16    484.15
    'Dec14'         0     111.8    826.76     51.84    491.79
    'Jan15'         0    488.97    337.78    142.95    490.64
    'Feb15'         0    337.78     0.001    245.91    337.74
    'Mar15'         0         0     0.001    245.91     0.001
    'Apr15'         0         0     0.001    245.91     0.001
    'May15'         0         0     0.001    245.91     0.001
Intrinsic Storage Value: $96.59k

Intrinsic Valuation with Minimum Lot Sizes

Perform the same intrinsic valuation with lot sizes that are a multiple of 10000. This turns the problem into an integer-linear program. As expected, notice that the optimal solution produces a slightly lower value than the intrinsic valuation.

val = valuation; val.minLotSize = 10000;

[intrLPos, istorageValue] = optimizeStorageIntr(facility, bid, ask, contract, val);

figure(2)
plotResults(intrLPos(:,1), intrLPos(:,2), valuation.expDates, facility,...
            contract, 30*ones(size(bid)));

fprintf('Intrinsic Storage Value with Lot Sizes: $%0.4fk\n\n', istorageValue/1e3);
LP:                Optimal objective value is -9.585680.                                            

Heuristics:        Found 1 solution using rss.                                                      
                   Upper bound is -9.479961.                                                        
                   Relative gap is 1.00%.                                                          

Branch and Bound:

   nodes     total   num int        integer       relative                                          
explored  time (s)  solution           fval        gap (%)                                         
      19      0.02         2  -9.513454e+00   1.402418e-01                                          
      36      0.02         2  -9.513454e+00   0.000000e+00                                          

Optimal solution found.

Intlinprog stopped because the objective value is within a gap tolerance of the
optimal value; options.TolGapAbs = 0 (the default value). The intcon variables
are integer within tolerance, options.TolInteger = 1e-05 (the default value).

Storage Schedule ------------------------------------------------------------
     Month     Inj    With    Vol    InjLim    WitLim
    _______    ___    ____    ___    ______    ______
    'Jun14'      0      0       0    245.91         0
    'Jul14'     20      0      20    245.91         0
    'Aug14'    240      0     260    245.91        20
    'Sep14'    240      0     500    245.91       260
    'Oct14'    240      0     740    245.91    424.67
    'Nov14'    200      0     940    201.37    484.49
    'Dec14'      0    130     810    50.625    491.79
    'Jan15'      0    480     330    155.45    489.86
    'Feb15'      0    330       0    245.91       330
    'Mar15'      0      0       0    245.91         0
    'Apr15'      0      0       0    245.91         0
    'May15'      0      0       0    245.91         0
Intrinsic Storage Value with Lot Sizes: $95.1345k

Basket of Spread Options Strategy

Instead of simply taking positions in forwards that capture none of the volatility of forward curve movements, one can take positions in a basket of calendar spread options.

optVol = load('Data\OptionVol'); % opt ivTerm
stats = load('Data\CurveStats'); % m sigma vol cor
valOpt = mergestruct(valuation, optVol, stats);

[optPos, optValue, SO] = optimizeStorageSO(facility, contract, valOpt, 'gross');

figure(3);
plotSpreadResults(optPos, valuation.expDates, facility, contract, 30*ones(size(bid)));

fprintf('Spread Option Storage Value: $%0.2fk\n', optValue/1e3);
fprintf(' Discounted Intrinsic Value: $%0.2fk\n\n', SO.intrValue/1e3);
Storage Schedule ------------------------------------------------------------
     Month      Inj       With      Vol      InjLim    WitLim
    _______    ______    ______    ______    ______    ______
    'Jun14'    245.91         0    245.91    245.91         0
    'Jul14'    245.59         0     491.5    245.91    245.91
    'Aug14'         0     0.646    490.85    245.91    420.88
    'Sep14'    91.382         0    582.23    245.91    420.58
    'Oct14'    245.15         0    827.38    245.14     455.7
    'Nov14'    141.65         0    969.03    142.48    490.66
    'Dec14'         0    80.495    888.53    26.132    491.79
    'Jan15'         0    397.46    491.07    93.941    491.79
    'Feb15'         0    153.29    337.78    245.91    420.68
    'Mar15'         0    92.316    245.47    245.91    337.74
    'Apr15'     0.121         0    245.59    245.91    245.47
    'May15'         0    245.59         0    245.91    245.59
Spread Option Storage Value: $280.27k
 Discounted Intrinsic Value: $142.68k

Storage Cash Flows & Sensitivities

The first chart shows the cash flows that arise from monetizing the spread option storage value. One can capture the computed value by selling the optimal basket of spread options for the computed fair price. Then as those options are exercised, oen would take opposing positions in the forward curves and inject/withdraw gas as necessary. The chart shows cash flows that would arise given which spread options are in-the-money on the valuation date. The second chart shows the overall storage value's sensitivities to the forward prices and volatilities

figure(4);
spreadOptCashFlows(optPos, bid', ask', valuation, facility, SO, 1);

figure(5);
subplot(2,1,1); bar(SO.sens(:,1)); xlabel('Forward'); ylabel('$/MMBtu'); legend('Delta');
title('Sensitivity of Storage Value to Forwards'); set(gca,'YGrid', 'on');
subplot(2,2,3); bar(SO.sens(:,2)); xlabel('Forward'); ylabel('$/MMBtu'); legend('Gamma'); set(gca,'YGrid', 'on');
subplot(2,2,4); bar(SO.sens(:,3)); xlabel('Forward'); ylabel('$/MMBtu'); legend('Vega'); set(gca,'YGrid', 'on');