Implied Volatility in Matlab - matlab

I'm trying to calculate the implied volatility using the Black-Scholes formula in Matlab (2012b), but somehow have problems with some strike prices.
For instance blsimpv(1558,1440,0.0024,(1/12),116.4) will return NaN.
I thought it probably would be some problem with the function and therefore searched the internet for some other matlab scripts and customized it to my custom needs, but unfortunately I'm still not able to return a valid implied volatility.
function sigma=impvol(C,S,K,r,T)
%F=S*exp((r).*T);
%G=C.*exp(r.*T);
%alpha=log(F./K)./sqrt(T);
%beta=0.5*sqrt(T);
%a=beta.*(F+K);
%b=sqrt(2*pi)*(0.5*(F-K)-G);
%c=alpha.*(F-K);
%disc=max(0,b.^2-4*a.*c);
%sigma0=(-b+sqrt(disc))./(2*a);
i=-1000;
while i<=5000
sigma0=i/1000;
sigma=NewtonMethod(sigma0);
if sigma<=10 && sigma>=-10
fprintf('This is sigma %f',sigma)
end
i=i+1;
end
end
function s1=NewtonMethod(s0)
s1=s0;
count=0;
f=#(x) call(S,K,r,x,T)-C;
fprime=#(x) call_vega(S,K,r,x,T);
max_count=1e4;
while max(abs(f(s1)))>1e-7 && count<max_count
count=count+1;
s0=s1;
s1=s0-f(s0)./fprime(s0);
end
end
end
function d=d1(S,K,r,sigma,T)
d=(log(S./K)+(r+sigma.^2*0.5).*(T))./(sigma.*sqrt(T));
end
function d=d2(S,K,r,sigma,T)
d=(log(S./K)+(r-sigma.^2*0.5).*(T))./(sigma.*sqrt(T));
end
function p=Phi(x)
p=0.5*(1.+erf(x/sqrt(2)));
end
function p=PhiPrime(x)
p=exp(-0.5*x.^2)/sqrt(2*pi);
end
function c=call(S,K,r,sigma,T)
c=S.*Phi(d1(S,K,r,sigma,T))-K.*exp(-r.*(T)).*Phi(d2(S,K,r,sigma,T));
end
function v=call_vega(S,K,r,sigma,T)
v=S.*PhiPrime(d1(S,K,r,sigma,T)).*sqrt(T);
end
Running impvol(116.4,1558,1440,0.0024,(1/12)) will however unfortunately return the value 'Inf'. There somehow is a problem with the Newton-Rhapson method not converging but I am kind of clueless how to solve this. Does anyone know how to solve this problem or know some other way how to calculate the implied volatility?
Thank u in advance already for your help!
Kind regards,
Henk

I would definitely suggest this code: Fast Matrixwise Black-Scholes Implied Volatility
It is able to compute the entire surface in one shot and - my experience - I found it much more reliable than blsimpv() or impvol() which are other functions implemented in matlab.

Newton-Rhapson method does not work well for implied volatility. You should use the bisection method (not sure how it is used in Matlab). It is described in http://en.wikipedia.org/wiki/Bisection_method. For completeness, it works this way:
1) Pick an arbitrary high (impossible) volatility like high=200%/year.
2) Pick lowest possible volatility (low=0%).
2a) Calculate option premium for 0% volatility, if actual premium is lower than that, it means negative volatility (which is "impossible").
3) While implied volatility is not found:
3.1) If "high" and "low" are very near (e.g. equal up to 5th decimal), either one is your implied volatility. If not...
3.2) Calculate average between "high" and "low". avg=(high+low)/2
3.3) Calculate option premium for avg volatility.
3.4) If actual premium is higher then p(avg), make min=avg, because implied volatility must lie between avg and max.
3.4a) If actual premium is lower than p(avg), make max=avg, because implied vol must lie between min and avg.
The main disvantage of bisect is that you have to pick a maximum value, so your function won't find implied volatilities bigger than that. But something like 200%/year should be high enough for real-world usage.
I use yet another method more like Newton's method, hence not limited to a range, since vega is a derivative, but with a "linearization" fix to avoid hunting and failure due to small vegas:
def implied_volatility(type, premium, S, K, r, s_dummy, t):
if S <= 0.000001 or K <= 0.000001 or t <= 0.000001 or premium <= 0.000001:
return 0.0
s = 0.35
for cycle in range(0, 120):
ext_premium = type(S, K, r, s, t)
if abs(premium - ext_premium) < 0.005:
return s
ext_vega = type.vega(S, K, r, s, t)
# print S, K, r, s, t, premium, ext_premium, ext_vega
if ext_vega < 0.0000001:
# Avoids zero division if stuck
ext_vega = 0.0000001
s_new = s - (ext_premium - premium) / ext_vega
if abs(s_new - s) > 1.00:
# estimated s is too different from previous;
# it is better to go linearly, since
# vega is too small to give a good guess
if s_new > s:
s += 1.0
else:
s -= 1.0
else:
s = s_new
if s < 0.0:
# No volatility < 0%
s = 0.0001
if s > 99.99:
# No point calculating volatilities > 9999%/year
return 100.0
return 0.0
Still, I think that bisect is your best bet.

I created a simple function that conducts a sort of trial and error calculation if the output from blsimpv is NaN. This slows down the computation time significantly for me but it always gives me a desirable result.
The function is shown to be used below
BSIVC(t,i)= blsimpv(S(t,i),K,r,tau(t),HestonCiter(t,i))
if isnan(BSIVC(t,i));
BSIVC(t,i)= secondIVcalc(HestonCiter(t,i),S(t,i),K,r,q,tau(t))
end
The function itself is described below:
function IV= secondIVcalc(HestonC,S,K,r,q,T)
lowCdif = 1;
a=0;
while lowCdif>0.0001
a= a+0.00001
lowCdif = HestonC - BSCprice(S,K,r,q,a,T);
end
IV= a;
end
Please note that BSCprice is not an in-built function in matlab.
Just to make the code clearer-
BSCprice is of the format BSCprice(Underlying Asset Price, Strike Price, interest rate, dividend yield, implied vol, time to maturity).

Related

scipy integrate.quad return an incorrect value

i use scipy integrate.quad to calc cdf of normal distribution:
def nor(delta, mu, x):
return 1 / (math.sqrt(2 * math.pi) * delta) * np.exp(-np.square(x - mu) / (2 * np.square(delta)))
delta = 0.1
mu = 0
t = np.arange(4.0, 10.0, 1)
nor_int = lambda t: integrate.quad(lambda x: nor(delta, mu, x), -np.inf, t)
nor_int_vec = np.vectorize(nor_int)
s = nor_int_vec(t)
for i in zip(s[0],s[1]):
print i
while it print as follows:
(1.0000000000000002, 1.2506543424265854e-08)
(1.9563704110140217e-11, 3.5403445591955275e-11)
(1.0000000000001916, 1.2616577562700088e-08)
(1.0842532749783998e-34, 1.9621183122960244e-34)
(4.234531567162006e-09, 7.753407284370446e-09)
(1.0000000000001334, 1.757986959115912e-10)
for some x, it return a value approximate to zero, it should be return 1.
can somebody tell me what is wrong?
Same reason as in why does quad return both zeros when integrating a simple Gaussian pdf at a very small variance? but seeing as I can't mark it as a duplicate, here goes:
You are integrating a function with tight localization (at scale delta) over a very large (in fact infinite) interval. The integration routine can simply miss the part of the interval where the function is substantially different from 0, judging it to be 0 instead. Some guidance is required. The parameter points can be used to this effect (see the linked question) but since quad over an infinite interval does not support it, the interval has to be manually split, like so:
for t in range(4, 10):
int1 = integrate.quad(lambda x: nor(delta, mu, x), -np.inf, mu - 10*delta)[0]
int2 = integrate.quad(lambda x: nor(delta, mu, x), mu - 10*delta, t)[0]
print(int1 + int2)
This prints 1 or nearly 1 every time. I picked mu-10*delta as a point to split on, figuring most of the function lies to the right of it, no matter what mu and delta are.
Notes:
Use np.sqrt etc; there is usually no reason for put math functions in NumPy code. The NumPy versions are available and are vectorized.
Applying np.vectorize to quad is not doing anything besides making the code longer and slightly harder to read. Use a normal Python loop or list comprehension. See NumPy vectorization with integration

How do I use eps in this situation where I am taking differences of cumulative sums

First, the data:
orig = reshape([0.0000000000000000 0.3480000000000000 0.7570000000000000 1.3009999999999999 2.8300000000000001 4.7519999999999998 5.2660000000000000 5.8120000000000003 14.3360000000000000 15.3390000000000000 ],[10 1])
change = reshape([0.0000000000000000 0.3480000000000000 0.0000000000000000 0.9530000000000000 1.5290000000000001 1.9219999999999997 0.5140000000000002 0.5460000000000003 0.0000000000000000 9.5270000000000010 ],[10 1])
change = cumsum(change)
orig is a vector of seconds elapsed. change is a vector derived by taking differences between (some) elements of orig. The cumulative sum of change has some elements actually equal to the corresponding element in orig.
However, due to precision issues:
diff = orig - change
gives
diff =
0
0
0.409
0
0
0
0
0
8.524
-1.77635683940025e-15
It seems that if I run the following command:
diff(abs(diff) <= eps(orig)) = 0
then this sets entries which should be zero, but are not due to precision issues, to be zero.
My question is, is this the correct way to do it? Why is the comparison <= instead of <? Should the statement be:
diff(abs(diff) < k*eps(orig)) = 0
for some k > 1 to give some tolerance? If so, how would one pick k?
In case it is necessary to know how change is derived from orig, the following alternate example also shows this behaviour:
orig = reshape([0.0000000000000000 0.3480000000000000 0.7570000000000000 1.3009999999999999 2.8300000000000001 4.7519999999999998 5.2660000000000000 5.8120000000000003 14.3360000000000000 15.3390000000000000 ],[10 1])
change = orig - [0; orig(1:end-1)]
change = cumsum(change)
diff = orig - change
The following statement will be true only if the "almost zero" happens because 1 bit is offseted.
abs(diff) <= eps(orig)
1 bit is a ridiculously high precision to ask, a precision that most likely you can not achieve. Generally, you need to define your treshold yourself, such as
abs(diff) <= 1e-12
You also ask how to choose this value. Answer: there is no way we can tell you that. Its algorithm, application, unit, computer, [...] specific.
You are computing distance between particles? Maybe you need a smaller tolerance. You are doing economic profit calculus? 1e-12 is then a decimal you won't get in cash, for sure. Use 1e-4 instead. Or are you using an algorithm that does numerical approximations? Then you need a higher tolerance. How much tolerance you are OK with is, and will always be, a user choice.
Note: you need to be aware of the types you are using to set this minimum threshold right. MATLAB uses double as default, but if you are using other types, them this threshold is too strict. As an alternative, you can use
abs(diff) <= 100*eps(class(diff))
If your data type is not fixed/known.

Simulating the exponential function with pertubation

for some simulations, I need to make use of an approximation of the exponential function. Now, the problem that I have is that:
function s=expone(N,k)
s=0
for j=1:k
s=s+(exp(-N+j*log(N)-log(factorial(j))));
end
end
is a pretty stable, in the sense that it is almost 1 for k large enough. However, as soon as N is bigger than 200, it quickly drops to zero. How can I improve that, I need large N. I cannot really change the mathematical why of writing this, since I have an additional pertubation, my final code will look something lie:
function s=expone(N,k)
s=0
for j=1:k
s=s+(exp(-N+j*log(N)-log(factorial(j))))*pertubation(N,k);
end
end
THe pertubation is between 0 and 1, so that's no problem, but the prefactor seems not to work for N>200. Can anyone help?
Thanks a lot!
The function log(x) - x has positive and negative part
Graphic in Wolframalpha
while x - log(x!) is negative for x>= 0
Graphic in Wolframalpha
So the problem arise when (N - log(N) ) is much greater than (j - log(j) ). So the solution is to choose a j much bigger than N . Exp(negative) tends to zero
for example expone(20,1) = 7.1907e-05 but expone(20,20) = 0.5591 and expone (20,50) = 1.000
As conclusion, if you want to work with N big, j should be bigger, and as an extra tip you may want to change you function to avoid for loops:
function s = expone(N,k)
j = 1:k;
s = sum ((exp(-N+j*log(N)-log(factorial(j)))));
end

Primitive root of a number

I have tried to implement the algorithm described in here to find primitive roots for a prime number.
It works for small prime numbers, however as I try big numbers, it doesn't return correct answers anymore.
I then notice that a^(p-1)/pi tends to be a big number, it returns inf in MATLAB, so I thought factorizing (p-1) could help, but I am failing to see how.
I wrote a small piece of code in MATLABand here it is.
clear all
clc
%works with prime =23,31,37,etc.
prime=761; %doesn't work for this value
F=factor(prime-1); % the factors of prime-1
for i = 2: prime-1
a=i;
tag =1;
for j= 1 :prime-1
if (isprime(j))
p_i = j;
if(mod(a^((prime-1)/p_i),prime)== 1)
tag=0;
break
else
tag = tag +1;
end
end
end
if (tag > 1 )
a %it should only print the primitive root
break
end
end
Any input is welcome.
Thanks
What Matlab does in this case is it calculates a^((p-1)/p) before taking the modulus. As a^((p-1)/p) quite quickly becomes too large to handle, Matlab seems to resolve this by turning it into a floating point number, losing some resolution and yielding the wrong result when you take the modulus.
As mentioned by #rayreng, you could use an arbitrary precision toolbox to resolve this.
Alternatively, you could split the exponentiation into parts, taking the modulus at each stage. This should be faster, as it is less memory intensive. You could dump this in a function and just call that.
% Calculates a^b mod c
i = 0;
result = 1;
while i < b
result = mod(result*a, c);
i = i + 1;
end

Stable vector of probabilities

I'm trying to make a program in scilab (hopefully the same applies to matlab) to get the time where a stable vector is found, I mean, after making several times the product vector and matrix the result will became stable thus wont' change.
I think the best way to do this is with a recursive function so I coded the following:
function [R]=vector_stable(v,m,i)
V=v*m;
if(V == v) then
R=i;
abort;
else
vector_stable(V,m,i+1);
end
endfunction
Let me explain that a little a bit, V is product of initial vector and the matrix, if the result is the same as the vector parameter then must return the time when this happened, if is not, it will call the same function with the result as the first parameter. However i'm getting the following error
-->R=vector_stable(V,M,0)
!--error 18
: Too many names.
Is my function correct? Can you help me please?
Your function doesn't look right. This might be more like it:
function [R] = vector_stable(v, M, i)
V = v*M;
if(norm(V - v) < 0.001)
R = i;
return;
else
R = vector_stable(V,m,i+1);
end
return
end
Probably stable does not mean "does not change" but "converges"? Then you cannot test for equality V==v for terminating the loop. You could look at the relative difference between the two vectors and terminate if it gets less than, e.g. 1% or 0.1%.
Do you get the error also when you (for testing purposes) terminate if i==10 instead of V==v?