• No results found

Conclusions and Further Research

In our attempt to solve the vexing valuation problem for natural gas storage, we deploy new methods within stochastic optimization within the structure of an operationally acute model coupled with intuitive price centric policies.

The model we describe is purposefully structured to accurately account for the various operating characteristics of real storage facilities. This includes the explicit modeling of operational properties taking into account the variations in pressure within the storage facility and the energy costs associated with the use of the compressor during injection. Both of these operational components are usually absent within other models. However, like the Approximate Dynamic Programming approach discussed in Chapter 2, our model lacks the full evolution of the forward curve, where we confine our decisions to the current spot price and month ahead forward price. Further research could be pursued through navigating the computational challenges of natural gas storage as a high dimensional stochastic optimization problem.

CHAPTER 7: Conclusion and Further Research

94

Opportunity for further work within our framing of the natural gas storage valuation problem would involve the further development of the operational policies. We acknowledge that industry professionals who are responsible for making the operational decisions for natural gas storage employ indicators outside of the price evolution of the natural gas market. These may include gross production reports regarding natural gas, total volume within U.S. storage, and weather, all of which would offer a richer and more complex experience with the storage valuation problem.

An exciting aspect of this work is the opportunity to utilize a new approach when solving the storage valuation problem. The Knowledge Gradient is still a new field of research within stochastic optimization with new methods and extensions rapidly appearing. One of these emerging ideas is the Knowledge Gradient with Non- Parametric estimation which embarked on its maiden voyage upon our goal to determine an accurate and computationally efficient method for valuating natural gas storage. Through the use of KGNP, policies were obtained that displayed comparable results to the value of gas storage reported by the natural gas industry. However, the creativity within the policy development aspects of this work was stunted by the dimensional limitation of the KGNP algorithm which could only cope with the manipulation of two parameters. Should the KGNP increase its dimension threshold, it will be an even greater asset in solving stochastic optimization problems such as this one.

CHAPTER 7: Conclusion and Further Research

95

Based on observing appropriate responses to various testing, as well as receiving comparable results to current industry valuations of natural gas storage, we have added to the literature an instrument that successfully deploys the strict use of natural gas prices in policy development for the operational decisions surrounding a natural gas storage facility.

Appendix: Code

96

Appendix: Code

//Natural Gas Storage Model and Pricing Simulations //By Jennifer Schoppe with Professor Hugo Simao

import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.Random; import java.util.StringTokenizer; public class ModelSimTesting {

//function will read in simulated price data that was computed in MatLab

public static double[] readExternalFile(String fname) { int numRecs = 0; // the return variable double[]prices_array = new double[2175]; int i = 1;

try {

// setup buffered reader to read in the file one record at a time File filePtr = new File(fname);

FileReader fileReader = new FileReader(filePtr);

BufferedReader bufReader = new BufferedReader(fileReader); // read the file, record by record

i++;

String nextRec = bufReader.readLine();

while (nextRec != null) {

// parse record: three fields, separated by 1 or more tabs StringTokenizer st = new StringTokenizer(nextRec, "\t"); // make sure that the parsed record did contain two fields

if (st.countTokens() == 1) { double spot = Double.parseDouble(st.nextToken().trim()); prices_array[numRecs] = spot; numRecs++; } else {

// issue an error message

System.out.println("Cannot parse rec # "+ i +": "+ nextRec);

System.out.flush(); }

// get the next record i++;

nextRec = bufReader.readLine(); }

// close the input files bufReader.close(); fileReader.close(); }

catch (IOException e) {

System.out.println("Caught exception " + e + " while reading rec #" + i + " of file " + fname);

System.out.println("Exiting!"); System.out.flush();

System.exit(-1); }

Appendix: Code

97

return prices_array;

}

public static double[][] pairs(double step){

//produces array of all possible choices for the percents

double [][] pairs = new double [2][361]; int count = 0;

double upper = 1; for(int i =0; i<19;i++){

double lower = 1; for(int j = 0;j<19;j++){

pairs [0][count] = upper; pairs [1][count] = lower; count++;

lower = lower + step; }

upper = upper + step; }

return pairs;

}

public static double[][] simulate(double[]prices_array) { double [][] prices = new double [2][360];

ArrayList<Double> spot = new ArrayList<Double>(); ArrayList<Double> ExpS = new ArrayList<Double>();

for (int i = 0; i<prices_array.length; i++){

spot.add(prices_array[i]); }

//mean reversion speed calculated using MATLAB regression

double alpha = .0063;

//Performing Exponential Smoothing

int N = 2175; double Beta = .002;

//initialize starting smoothing price and price vector

double E = 0;

for (int i = 0; i< 5; i++ ){ double e = spot.get(i);

E = E + e; }

ExpS.add(E);

for (int i = 1; i < spot.size(); i++) {

ExpS.add(spot.get(i-1)*Beta + (1-Beta)*ExpS.get(i-1)); }

//Spot Price Simulation

double dt = 1;

for (int x = 0;x<12;x++){

for (int y =0; y<30;y++){

Random generator = new Random();

double dz = generator.nextGaussian(); //Volatility calculation //volatility window int m = 360; //year int n = spot.size(); double Sum = 0;

for(int i = n-m; i<n; i++){ double sum = 0;

for(int j = n-m; j<n; j++){

sum = sum + (Math.log(spot.get(j))- Math.log(spot.get(j-1)));

Appendix: Code

98

}

Sum = Sum + Math.pow((Math.log(spot.get(i))- Math.log(spot.get(i-1))-(1/m)*sum),2); }

double sig = Math.sqrt((1/(double)(m-1))*Sum); double sigHat = sig*Math.sqrt(2*alpha)/Math.sqrt(1-

Math.exp(-2*alpha));

int g = spot.size();

double S_old = (spot.get(g-1)); double E_old = (ExpS.get(g-1));

double dxt = alpha*(Math.log(E_old)-Math.log(S_old))*dt +

sigHat*dz; spot.add(S_old + dxt);

prices[0][x*30+y] = S_old + dxt;

ExpS.add((S_old+dxt)*Beta + (1-Beta)*E_old);

double delta = 30-y;

double mu = Math.log(S_old)*Math.exp(-alpha*delta) + Math.log(E_old)*(1-Math.exp(-alpha*delta)); double sd = (Math.pow(sigHat,2)/(2*alpha))*(1-Math.exp(- 2*alpha*delta)); double z = generator.nextGaussian(); double y_t = mu + sd*z; prices[1][x*30+y] = Math.exp(y_t); } } return prices; }

public static double[]CapChange(double Spot, double Forward, double rateI, double rateW, double Imax, double Wmax, double commitment, double contracts, double

history,double upper, double lower) {

//Output: spot injection/withdrawal and forward months injection/withdrawal decision

double[]amount = new double[2];

//decision for spot

double x = 0;

double y = commitment;

double change = 100*((Spot-history)/history);

//for gain in price value

if (change>0) { if (change>= upper){ x = -rateW; } else { x = 0; } } else { if(Math.abs(change)>=lower) { x = rateI; } else { x = 0; } } double z = 0;

//constraints for spot decision

double check1 = 0; double check2 = 0; while(check1==0 || check2==0){ if (x>0){ if (x + y/30 > Imax) { x = Imax - y/30; check1 = 1; } else{

Appendix: Code 99 check1 = 1; } if (y>0){ if((1/rateI)*(x+y/30)>1) { x = rateI - y/30; check2 = 1; } else{ check2 = 1; } } else { if ((1/rateI)*x + (1/rateW)*Math.abs(y/30) >1) { x = (1-(1/rateW)*Math.abs(y/30))*rateI; check2 = 1; } else { check2 = 1; } } } else {

if (Math.abs(x + y/30) > Wmax) {

x = -Wmax - y/30; check1 = 1; } else{ check1 = 1; } if (y>0){ if(((1/rateW)*Math.abs(x)+(1/rateW)*(y/30))>1) { x = -(1-(1/rateI)*y/30)*rateW; check2 = 1; } else{ check2 = 1; } } else { if (Math.abs((1/rateW)*x+(1/rateW)*(y/30)) >1) { x = -rateW*(1+(1/rateW)*(y/30)); check2 = 1; } else { check2 = 1; } } } }

// constraints for forward decision

if (contracts >0) { if (z > 0) { if(contracts + z > Imax) { z = Imax -contracts; } } else { if (Math.abs(z)>Wmax) { z = -Wmax; } } } else { if (z>0) { if (z >Imax) { z = Imax; } } else { if (Math.abs(z+contracts)>Wmax) { z = -(Wmax-Math.abs(contracts));

Appendix: Code 100 } } } amount[0]= x; amount[1]= z; //amount[0]= x; //amount[1]= z; return amount; }

public static double Capacity(double Current, double loss,double s, double f) {

//Output: new capacity of storage facility

double X = 0; if (s>0 && f>0){

X = Current + loss*s + loss*f/30;

return X; } else if (s>0 && f<0){ X = Current + loss*s + f/30; return X; } else if (s==0 && f==0){ X = Current; return X; } else if (s<0 && f>0){ X = Current + s + loss*f/30; return X; } else { X = Current + s + f/30; return X; } }

public static double Pressure(double max, double min, double CapMax, double Cap) {

//Output: pressure within storage facility

//Inputs: max pressure, min pressure, max capacity, current capacity

double p = ((max-min)/CapMax)*Cap + min; return p;

}

public static double InjRate(double max, double min, double CapMax, double Cap) {

//Output: fuel loss rate for injection into storage facility //Inputs: max rate, min rate, max pressure, current pressure

double rate = -((max-min)/CapMax)*Cap + max; return rate;

}

public static double WithRate(double max, double min, double CapMax, double Cap) {

//Output: fuel loss rate for withdrawal from storage facility //Inputs: max rate, min rate, max pressure, current pressure

double rate = ((max-min)/CapMax)*Cap + min; return rate;

}

public static double Compression(double max, double pmax, double p) {

//Output: horsepower of compressor for injection

//Inputs: max horsepower of compressor, max pressure, current pressure

double rate = (max/pmax)*p; return rate;

}

public static double InjSpotCost(double compress, double rate, double amount, double spot) {

//Output: spot injection cost

//Inputs: compression, Injection rate, amount to be injected, spot price

double cost = Math.abs((compress*42.44*60)*((1/rate)*24*amount)

*spot/1000000);

Appendix: Code

101

}

public static double WithSpotCost(double amount) {

//Output: withdrawal cost

//Inputs: withdrawal rate, storage cost, current pressure

double cost = Math.abs(.01*amount*1000000); return cost; //in Bcf

}

public static double InjForCost(double compress, double rate, double amount, double forward) {

//Output: spot injection cost

//Inputs: compression, Injection rate, amount to be injected, spot price

double cost = Math.abs((compress*42.44*60)*((1/rate)*24*(amount/30))

*forward/1000000);

return cost; //in Bcf

}

public static double WithForCost(double amount) {

//Output: withdrawal cost

//Inputs: withdrawal rate, storage cost, current pressure

double cost = Math.abs(.01*(amount/30)*1000000); return cost; //in Bcf

}

public static double Reward(double spot, double forward, double Cost_I_Spot,double

Cost_W_Spot,double Cost_I_Forward,double Cost_W_Forward,double s,double f,double loss){

// Output: daily reward

double R = 0; if (s==0 && f==0){ R = 0; return R; } else if (s>=0 && f<=0){

R = -((loss*spot)*s*1000000 + Cost_I_Spot) +(forward)*Math.abs(f) *1000000 - Cost_W_Forward ;

return R;

}

else if(s>=0 && f>=0) {

R = -((loss*spot)*s*1000000 + Cost_I_Spot +(loss*forward)*f*1000000 + Cost_I_Forward) ;

return R;

}

else if (s<=0 && f>=0){

R = (spot)*Math.abs(s)*1000000 - Cost_W_Spot -((loss*forward) *f*1000000 + Cost_I_Forward) ;

return R;

}

else {

R = (spot)*Math.abs(s)*1000000 - Cost_W_Spot +(forward)*Math.abs(f) *1000000 - Cost_W_Forward ;

return R;

} }

public static double model(double Capacity, double upper, double lower){

// Output: produces Annual Value for Natural Gas String fname =

"\\C:\\Users\\Jennifer\\Documents\\Thesis\\PriceData.txt\\";

double[] prices_array = readExternalFile(fname); double [][] prices = new double [2][360];

prices = simulate(prices_array); FileOutputStream fout;

try

{

Appendix: Code

102

fout = new FileOutputStream

("\\C:\\Users\\Jennifer\\Documents\\Thesis\\Simulations.txt\\");

// Print a line of text

for(int i = 0; i < 360; i++){

new PrintStream(fout).print(prices[0][i] + "\t"); new PrintStream(fout).print(prices[1][i] + "\n");

}

// Close our output stream fout.close();

}

// Catches any error conditions

catch (IOException e)

{

System.err.println ("Unable to write to file"); System.exit(-1);

}

//establish parameters for model

double X_max = Capacity; //maximum level of working gas that can be put in

a storage facility

double rate_I_max = .3; //maximum injection rate for storage facility double rate_I_min = 0; //minimum injection rate for storage facility double rate_W_max = .5; //maximum withdrawal rate for storage facility double rate_W_min = 0; //minimum withdrawal rate for storage facility double p_max = .85; //maximum pressure within the storage facility double p_min = .2; //minimum pressure within the storage facility double compress_max = 25000; //max horsepower for compressor double loss_I = .985; //fuel rate loss for injection

double d = 3500; //depth of storage facility double gamma = 0; //risk free discount factor double history = 0; //for swing trading

//establish initial variables

double[] X = new double[361]; //initial capacity of storage facility double[][] x_t = new double[2][361];//initialization of spot decision double[] max_With = new double[361];//initialization of max Withdrawal

Capacity

double[] max_Inj = new double[361];//initialization of maximum Injection

Capacity

double[] rate_I = new double[361]; //injection rate for storage facility double[] rate_W = new double[361]; //withdrawal rate for storage facility double[] p = new double[361]; //initialization of pressure

double[] cost_I_spot = new double[361]; double[] cost_W_spot = new double[361]; double[] cost_I_forward = new double[361]; double[] cost_W_forward = new double[361];

double[] compress = new double[361];//horsepower of compressor

double[] reward = new double[361]; //initializing reward, daily revenue

//initialize variables

double Spot_t = 0;//initialization of spot price double Forward_t = 0;//initialization of forward price

double contracts = 0; //number of futures contracts to be fulfilled double price = 0; //price of contracts to be fulfilled

double forward_commitment = 0; //amount injected or withdrawn due to

forward contract commitments from prior month

double price_commitment = 0;

double Value = 0; //initializing yearly revenue int Count = 0; //initializing counter

Appendix: Code 103 //day 0 values X[0] = 0; x_t[0][0] = 0; x_t[1][0] = 0; max_With[0] = 0;

max_Inj[0]= X_max - max_With[0]; rate_I[0] = rate_I_max; rate_W[0] = rate_W_min; p[0] = p_min; cost_I_spot[0] = 0; cost_W_spot[0] = 0; cost_I_forward[0] = 0; cost_W_forward[0] = 0; compress[0] = 0; reward[0] = 0;

double[] spot = new double[360]; double[] forward = new double[360]; for (int i = 0; i<360; i++){

spot[i] = prices[0][i]; forward[i] = prices[1][i];

//System.out.print("spot = " + spot[i]+"\t"); //System.out.print("forward = "+forward[i]+ "\n"); }

//loop of year long operation of storage facility

double AnnualProfit = 0;

for (int i = 1; i <= 360; i++) {

//keeps track of where we are in months

if (Count == 31){ contracts = 0; Count = 1; } else { Count++; }

max_With[i] = X[i-1] + x_t[0][i-1]; max_Inj[i] = X_max - max_With[i];

X[i] = Capacity(X[i-1], loss_I, x_t[0][i-1],forward_commitment); p[i] = Pressure(p_max, p_min, X_max, X[i]);

rate_I[i] = InjRate(rate_I_max, rate_I_min, X_max, X[i]); rate_W[i] = WithRate(rate_W_max, rate_W_min, X_max, X[i]); compress[i]= Compression(compress_max, p_max, p[i]);

cost_I_spot[i] = InjSpotCost(compress[i], rate_I[i], x_t[0][i- 1],spot[i-1]);

cost_W_spot[i] = WithSpotCost(x_t[0][i-1]);

cost_I_forward[i] = InjForCost(compress[i], rate_I[i], forward_commitment,price_commitment);

cost_W_forward[i] = WithForCost(forward_commitment}; reward[i] = Reward(spot[i-1], price_commitment,

cost_I_spot[i],cost_W_spot[i], cost_I_forward[i],

cost_W_forward[i], x_t[0][i-1],forward_commitment,loss_I); Value = Value + reward[i];

if (i<4) {

history = 0;

for(int j =0;j<i;j++){

history = history + spot[j]; } history = history/(double)4; } else { history = spot[i-4]; }

double[] decisions = new double[2];

Appendix: Code 104 rate_W[i],max_Inj[i],max_With[i], forward_commitment, contracts, history,upper,lower); x_t[0][i] = decisions[0]; x_t[1][i] = decisions[1]; if (Count <30) { if(x_t[1][i] != 0) { if (contracts*x_t[1][i]<=0){ price_commitment = forward[i-1]; contracts = contracts + x_t[1][i]; } else { price_commitment = (price_commitment* Math.abs(contracts) + forward[i-1]* Math.abs(x_t[1][i]))/Math.abs(contrac ts+x_t[1][i]);

contracts = contracts + x_t[1][i]; } } } else { forward_commitment = contracts/30; }

AnnualProfit = AnnualProfit + reward[i]; }

FileOutputStream fout2;

try

{

// Open an output stream fout2 = new FileOutputStream

("\\C:\\Users\\Jennifer\\Documents\\Thesis\\ModelOutput.txt\\");

for (int i = 0; i < 360; i++){

// Print a line of text

new PrintStream(fout2).print(x_t[0][i]+"\t"); new PrintStream(fout2).print(x_t[1][i]+"\t"); //System.out.print(price_commitment+ "\t"); new PrintStream(fout2).print(X[i]+"\t"); new PrintStream(fout2).print(forward_commitment+ "\t"); new PrintStream(fout2).print(max_Inj[i]+"\t"); new PrintStream(fout2).print(max_With[i]+"\t"); new PrintStream(fout2).print(rate_I[i]+"\t"); new PrintStream(fout2).print(rate_W[i]+"\t"); new PrintStream(fout2).print(cost_I_spot[i]+"\t"); new PrintStream(fout2).print(cost_W_spot[i]+"\t"); new PrintStream(fout2).print(cost_I_forward[i]+"\t"); new PrintStream(fout2).print(cost_W_forward[i]+"\t"); new PrintStream(fout2).print(reward[i]+"\n"); } fout2.close(); }

// Catches any error conditions

catch (IOException e)

{

System.err.println ("Unable to write to file"); System.exit(-1);

Appendix: Code

105

}

//System.out.println("Annual Value = " + Value);

return AnnualProfit; } /* * * * @param args */

public static void main(String[] args) {

// TODO Auto-generated method stub //only needed for testing

double AnnualProfit = model(.01,.015, Capacity);

System.out.println(AnnualProfit); }

Bibliography

106

Bibliography

Barut, A. Emre, and W.B. Powell. Optimal Learning for Sequential Sampling with Non-

Parametric Estimation (Working Paper). 2010.

Brown, Robert G., and Richard F. Meyer. The Fundamental Theorem of Exponential

Smoothing. 1960.

Byers, Joe Wayne. "Commodity Storage Valuation: A Linear Optimization based on traded instruments." 2006.

CASTLE Laboratory. 1997-2009. http://www.castlelab.princeton.edu/ (accessed 1

2010, April).

CMEGroup. 2010. http://www.cmegroup.com/trading/energy/natural-gas/natural-

gas-last-day.html (accessed 04 02, 2010).

Dietert, Jeff A., and David A. Pursell. Underground Natural Gas Storage. Simmons & Company International, 2000.

Energy Information Administration. August 2004.

http://www.eia.doe.gov/oil_gas/info_glance/natural_gas.html (accessed November 3, 2009).

Enstor. "Gas Dailys: Historical Daily Spot Prices 1999-2009." 2010.

Bibliography

107

Eydeland, Alexander, and Krzysztof Wolyniec. Energy and Power Risk Management:

New Developements in Modeling, Pricing, and Hedging. Hoboken: John Wiley &

Sons, 2003.

Frazier, P., W.B. Powell, and S. Dayanik. A Knowledge Gradient Policy for Correlated

Normal Rewards. 2009.

—. A Knowledge Gradient Policy for Sequential Information Collection. 2008.

Gupta, S.S., and K.J. Miescke. Bayesian Look Ahead One-stage Sampling Allocations for

Selection of the Best Population. 1996.

Hardle, Wolfgang, Marlene Muller, Stefan Sperlich, and Axel Werwatz.

Nonparametric and Semiparametric Models. Berlin: Springer, 2004.

Lai, Guoming, Francois Margot, and Nicola Secomandi. An Approximate Dynamic

Programming Approach to Benchmark Practiced-based Heuristics for Natural Gas Storage Valuation. 2008.

Merrill, Arthur. Filtered Waves. New York: Analysis Press, 1977.

Mes, Martijn R.K., W.B. Powell, and P. Frazier. Hierarchical Knowledge Gradient for

Sequential Sampling. 2009.

Nadaraya, E.A. "On Estimating Regression." Theory of Probability and its

Applications, 1964.

Bibliography

108

NaturalGas.org. 2004. http://www.naturalgas.org/naturalgas/storage.asp#depleted

(accessed November 12, 2009).

Powell, W.B., and P. Frazier. ""Optimal Learning"." TutORials in Operations Research, 2008: 213-246.

Powell, Warren B., and Ilya Ryzhov. Optimal Learning. Hoboken: Wiley, 2010.

Puterman, M.L. Markov Decision Processes. Wiley, 1994.

U.S. Department of Energy. 2009.

http://www.energy.gov/energysources/naturalgas.htm (accessed November 22, 2009).

Related documents