I think I have come up with a C# solution to calculate the implied volatility. I post the C# code below.
However when I input the current values for GOOG where impliedvolatility is known: 0.187 by looking in TOS and using the strikeprice 785.
This function returns a callprice(BlackScholesCallPrice) of 17.13 which is not correct. The TOS shows a callprice at: 13.5.
I wonder what can skew it so much?
Another test with below code is if I change the impliedvolatility to: 0.147, then this returns about the same current call price.
So 0.147 vs 0.187(TOS)
That might be a correct estimate as calculation is not exactly the same as TOS?
private void Form1_Load(object sender, EventArgs e)
{
double stockprice = 786.90;
double exerciseprice = 785;
double impliedvolatility = 0.187; //Assumed implied volatility
double riskfreerate = 0.02;
double dividendyield = 0.0020;
double daystoexpiration = 26;
double BlackScholesCallPrice = 0; //We know that the price is: 13.5 (Adjust impliedvolatility until the price is correct)
double PutPricePutCallParity = 0;
double PutOptionDelta = 0;
calculateimpliedvolatility(stockprice, exerciseprice, impliedvolatility, riskfreerate,
dividendyield, daystoexpiration, out BlackScholesCallPrice, out PutPricePutCallParity, out PutOptionDelta);
MessageBox.Show(BlackScholesCallPrice.ToString() + "," + PutPricePutCallParity.ToString() + "," + PutOptionDelta);
}
void calculateimpliedvolatility(double stockprice, double exerciseprice, double impliedvolatility, double riskfreerate,
double dividendyield, double daystoexpiration, out double BlackScholesCallPrice, out double PutPricePutCallParity, out double PutOptionDelta)
{
System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
//(LN($B$1/$B$5) + ($B$3 - $B$4 + 0,5*($B$2^2))*($B$6/365))/($B$2*($B$6/365)^0,5)
double d1 = (Math.Log(stockprice / exerciseprice) + (riskfreerate - dividendyield + 0.5 * (Math.Pow(impliedvolatility, 2))) * (daystoexpiration / 365)) / ((impliedvolatility * Math.Pow((daystoexpiration / 365), 0.5)));
//E1 - $B$2*($B$6/365)^0,5
double d2 = d1 - impliedvolatility * Math.Pow((daystoexpiration / 365), 0.5);
//NORMSDIST(E1)
double Nd1 = NORMSDIST(d1);
//NORMSDIST(E2)
double Nd2 = NORMSDIST(d2);
//$B$1*EXP(-$B$4*($B$6/365))*E5 - EXP(-$B$3*($B$6/365))*$B$5*E6
BlackScholesCallPrice = stockprice * Math.Exp(-dividendyield * (daystoexpiration / 365)) * Nd1 -
Math.Exp(-riskfreerate * (daystoexpiration / 365)) * exerciseprice * Nd2;
//$E$9 - EXP(-$B$4*($B$6/365))*$B$1 + $B$5 * EXP(-$B$3*($B$6/365))
PutPricePutCallParity = BlackScholesCallPrice - Math.Exp(-dividendyield * (daystoexpiration / 365)) * stockprice + exerciseprice * Math.Exp(-riskfreerate * (daystoexpiration / 365));
//-NORMSDIST(-E1)
PutOptionDelta = -NORMSDIST(-d1);
}
private static double erf(double x)
{
//A&S formula 7.1.26
double a1 = 0.254829592;
double a2 = -0.284496736;
double a3 = 1.421413741;
double a4 = -1.453152027;
double a5 = 1.061405429;
double p = 0.3275911;
x = Math.Abs(x);
double t = 1 / (1 + p * x);
//Direct calculation using formula 7.1.26 is absolutely correct
//But calculation of nth order polynomial takes O(n^2) operations
//return 1 - (a1 * t + a2 * t * t + a3 * t * t * t + a4 * t * t * t * t + a5 * t * t * t * t * t) * Math.Exp(-1 * x * x);
//Horner's method, takes O(n) operations for nth order polynomial
return 1 - ((((((a5 * t + a4) * t) + a3) * t + a2) * t) + a1) * t * Math.Exp(-1 * x * x);
}
public static double NORMSDIST(double z)
{
double sign = 1;
if (z < 0) sign = -1;
return 0.5 * (1.0 + sign * erf(Math.Abs(z) / Math.Sqrt(2)));
}