Quantitative Finance & Derivatives
Kirk Spread Option Approximation for Nonzero-Strike Crack and Spark Spreads
Price two-asset commodity spread calls MAX(F1 - F2 - K, 0) in Excel and Arte Sheets using Kirk’s discounted-strike denominator weight and composite spread volatility.
Spot why Margrabe volatility fails when the spread strike K is nonzero
A zero-strike exchange option (K = 0) has an exact Margrabe volatility SQRT(sig1^2 - 2*rho*sig1*sig2 + sig2^2). Once a power plant spark spread or refinery crack spread adds a nonzero fixed conversion strike K > 0 in MAX(F1 - F2 - K, 0), the sum F2 + K*EXP(-r*T) is no longer pure lognormal. Kirk (1995) approximates F2 + K*EXP(-r*T) as a single lognormal denominator with weight w2 = F2 / (F2 + K*EXP(-r*T)).
Set F1 = 92 (B2, power futures), F2 = 75 (B3, gas input cost), K = 10 (B4, conversion fee), r = 0.04 (B5), T = 0.5 (B6), sig1 = 0.30 (B7), sig2 = 0.25 (B8), and correlation rho = 0.80 (B9). By hand, F2_adj = 75 + 10*EXP(-0.02) = 84.8020, so w2 = 75 / 84.8020 = 0.8844. Plugging w2*sig2 into the variance law yields composite spread volatility 0.1810, compared with unweighted Margrabe 0.1803.
| Component | Kirk (1995) Expression | Sample Output |
|---|---|---|
| Adjusted Second Leg F2_adj | F2 + K * EXP(-r * T) | 84.8020 |
| Second-Leg Weight w2 | F2 / F2_adj | 0.8844 |
| Kirk Composite Volatility | SQRT(sig1^2 - 2*rho*sig1*sig2*w2 + (sig2*w2)^2) | 0.1810 |
| Kirk Spread Call Value | EXP(-r*T) * (F1 * N(d1) - F2_adj * N(d2)) | 8.8137 |
Composite Kirk Volatility Formula
Guard T <= 0 or degenerate variance first, then scale sig2 by w2 = F2 / (F2 + K*EXP(-r*T)) inside LET.
=LET(f2_val, B3, k_str, B4, r_rf, B5, t_yr, B6, s_1, B7, s_2, B8, rho_c, B9, f2_adj, f2_val + k_str * EXP(-r_rf * t_yr), w_2, f2_val / f2_adj, var_k, s_1^2 - 2 * rho_c * s_1 * s_2 * w_2 + (s_2 * w_2)^2, IF(OR(t_yr <= 0, var_k <= 0), "Invalid Spread Vol", ROUND(SQRT(var_k), 4)))Kirk Spread Call Price Formula & Collaborative Review Check
Combine Kirk volatility with the Abramowitz-Stegun 26.2.17 normal CDF polynomial so the sheet runs identically without add-ins. On a shared trading desk workbook where an analyst has the suggester role, inspect any proposed change to correlation rho before accepting: dropping B9 from 0.80 to 0.40 widens composite volatility from 0.1810 to 0.2930 and lifts the call price from 8.8137 to 11.2193 because decoupled legs widen spread dispersion.
=LET(f1_val, B2, f2_val, B3, k_str, B4, r_rf, B5, t_yr, B6, s_1, B7, s_2, B8, rho_c, B9, f2_adj, f2_val + k_str * EXP(-r_rf * t_yr), w_2, f2_val / f2_adj, var_k, s_1^2 - 2 * rho_c * s_1 * s_2 * w_2 + (s_2 * w_2)^2, IF(OR(t_yr <= 0, var_k <= 0), "Invalid Spread Vol", LET(sig_k, SQRT(var_k), sig_t, sig_k * SQRT(t_yr), d_1, (LN(f1_val / f2_adj) + 0.5 * var_k * t_yr) / sig_t, d_2, d_1 - sig_t, t_1, 1 / (1 + 0.2316419 * ABS(d_1)), p_1, 0.3989422804014327 * EXP(-0.5 * d_1^2) * t_1 * (0.31938153 + t_1 * (-0.356563782 + t_1 * (1.781477937 + t_1 * (-1.821255978 + 1.330274429 * t_1)))), nd_1, IF(d_1 >= 0, 1 - p_1, p_1), t_2, 1 / (1 + 0.2316419 * ABS(d_2)), p_2, 0.3989422804014327 * EXP(-0.5 * d_2^2) * t_2 * (0.31938153 + t_2 * (-0.356563782 + t_2 * (1.781477937 + t_2 * (-1.821255978 + 1.330274429 * t_2)))), nd_2, IF(d_2 >= 0, 1 - p_2, p_2), ROUND(EXP(-r_rf * t_yr) * (f1_val * nd_1 - f2_adj * nd_2), 4))))Try the example
Use the original values or change them to test your understanding.