Black & Scholes

Hello,

Does someone know how to code  Black&Scholes formula with .afl   ? 

It seems that the code provided in the Amibroker library , at :

http://www.amibroker.com/members/library/detail.php?id=447&hilite=EXP

has a bug ,

and it doesn't seems .afl possess a native Black&Scholes() function like most of other T.A language ( easy language ...etc )

1 Like

@Pyhrus,

try this:

// Adapted from source code in many programming languages here:
// http://www.espenhaug.com/black_scholes.html

// Cumulative Normal Distribution
function CND(x)
{
	local a1, a2, a3, a4, a5, l, k, w, Pi;
	
	Pi = 3.141592654; // Pi - Math constant

	a1 = 0.31938153;
	a2 =-0.356563782;
	a3 = 1.781477937;
	a4 = -1.821255978;
	a5 = 1.330274429;

	l = abs(x);
	k = 1.0 / (1.0 + 0.2316419 * l);
	w = 1.0 - 1.0 / sqrt(2 * Pi) * exp(-l*l / 2) * (a1 * k + a2 * (k^2) + a3 * (K^3) + a4 * (K^4) + a5 * (K^5));
	
	if (x < 0 ){
		w = 1.0 - w;
	}
	return w;
}

// BlackScholes function parameters: 
// isCallFlag = True for Call / False for Put
// S = Stock price
// X = Strike price
// T = Years to maturity (DTE /  365) - DTE = Days to expiration
// r = Risk-free rate
// v = Volatility
function BlackScholes(isCallFlag, S, X, T, r, v) 
{

	local d1, d2, result;
	
	d1 = (log(S / X) + (r + v * v / 2.0) * T) / (v * sqrt(T));
	d2 = d1 - v * sqrt(T);

	if (isCallFlag)
		result = (S * CND(d1)-X * exp(-r * T) * CND(d2));
	else
		result = (X * exp(-r * T) * CND(-d2) - S * CND(-d1));
	return result;
}

_TRACE("Call 60 : " + BlackScholes(True, 60, 65, 0.25, 0.01, 0.30));
_TRACE("Put  60 : " + BlackScholes(False, 60, 65, 0.25, 0.01, 0.30));
// Testing the problematic example in the comments section in the AFL Library
// StockPrice = 15; StrikePrice = 20; Timedays = 46; InterestRate = 0.05; VKnown = 0.3;
_TRACE("Call 15 OTM :" + BlackScholes(True, 15, 20, 46/365, 0.05, 0.3));
_TRACE("Call 14 OTM :" + BlackScholes(True, 14, 20, 46/365, 0.05, 0.3));

As said above, I simply adapted code I found with a Google search.
I compared the results to the Python version and they are almost equal: we know that equality is a little fiddly due to rounding and precision issues...

10 Likes

Hi all

I am very grateful to beppe for the code, and it works fine; but if I try it with a variable, I obtain an error, error 6 , in the last IF of function CND (x)

if (x < 0 )
{
w = 1.0 - w;
}
return w;

Error 6 Condition in IF WHILE FOR statements has to be numeric or Boolean, you can not use an array here, please use array subscript operator to access array elements. This hasn't logic for me, because x in the variable in the funtion to be generated ...
may anybody help me and explain?
very grateful for any help
Cesare

here is my code with the error ( the same of beppe, with only the last strings from me)

// Adapted from source code in many programming languages here:
// http://www.espenhaug.com/black_scholes.html

// Cumulative Normal Distribution
function CND(x)
{
	
	local a1, a2, a3, a4, a5, l, k, w, Pi;
	
	Pi = 3.141592654; // Pi - Math constant

	a1 = 0.31938153;
	a2 =-0.356563782;
	a3 = 1.781477937;
	a4 = -1.821255978;
	a5 = 1.330274429;

	l = abs(x);
	k = 1.0 / (1.0 + 0.2316419 * l);
	w = 1.0 - 1.0 / sqrt(2 * Pi) * exp(-l*l / 2) * (a1 * k + a2 * (k^2) + a3 * (K^3) + a4 * (K^4) + a5 * (K^5));
	
	if (x < 0 )
	{
		w = 1.0 - w;
	}
	return w;
}

// BlackScholes function parameters: 
// isCallFlag = True for Call / False for Put
// S = Stock price
// X = Strike price
// T = Years to maturity (DTE /  365) - DTE = Days to expiration
// r = Risk-free rate
// v = Volatility
function BlackScholes(isCallFlag, S, X, T, r, v) 
{

	local d1, d2, result;
	
	d1 = (log(S / X) + (r + v * v / 2.0) * T) / (v * sqrt(T));
	d2 = d1 - v * sqrt(T);

	if (isCallFlag)
		result = (S * CND(d1)-X * exp(-r * T) * CND(d2));
	else
		result = (X * exp(-r * T) * CND(-d2) - S * CND(-d1));
	return result;
}

_TRACE("Call 60 : " + BlackScholes(True, 60, 65, 0.25, 0.01, 0.30));
_TRACE("Put  60 : " + BlackScholes(False, 60, 65, 0.25, 0.01, 0.30));
// Testing the problematic example in the comments section in the AFL Library
// StockPrice = 15; StrikePrice = 20; Timedays = 46; InterestRate = 0.05; VKnown = 0.3;
_TRACE("Call 15 OTM :" + BlackScholes(True, 15, 20, 46/365, 0.05, 0.3));
_TRACE("Call 14 OTM :" + BlackScholes(True, 14, 20, 46/365, 0.05, 0.3));





//zz= under= 2000; st=strike =2050; 
zz = close;  st=strike= Close * 1.05;
dte= 30/365; rate= 0.05; IV= 0.3; 


call001= BlackScholes(True, zz, st, dte, rate, iv) ;
Plot ( C, "c", colorBlack);
Plot ( C+call001, "call", colorBlue);

@cesare,

please read about difference between if-else and IIf here.
Also read about how AFL works and about coding mistakes.

So since you want to deal with arrays replace

if (x < 0 )
{
	w = 1.0 - w;
}

by

result = IIf(x<0, 1-w, w);// IIf() is polymorph function

FYI, IIf() is polymorph function so it may return array or number depending on function arguments.

(Also I replaced l and v by different names since former ones are reserved Low and Volume (array) variables).

So

// Adapted from source code in many programming languages here:
// http://www.espenhaug.com/black_scholes.html
// Cumulative Normal Distribution
function CND(x)
{	
	local a1, a2, a3, a4, a5, absx, k, w, Pi;
	
	Pi = 3.141592654; // Pi - Math constant

	a1 = 0.31938153;
	a2 =-0.356563782;
	a3 = 1.781477937;
	a4 = -1.821255978;
	a5 = 1.330274429;

	absx = abs(x);
	k = 1.0 / (1.0 + 0.2316419 * absx);
	w = 1.0 - 1.0 / sqrt(2 * Pi) * exp(-absx*absx / 2) * (a1 * k + a2 * (k^2) + a3 * (K^3) + a4 * (K^4) + a5 * (K^5));
	
	result = IIf(x<0, 1-w, w);// IIf() is polymorph function
	return result;
}

// BlackScholes function parameters: 
// isCallFlag = True for Call / False for Put
// S = Stock price
// X = Strike price
// T = Years to maturity (DTE /  365) - DTE = Days to expiration
// r = Risk-free rate
// _v = Volatility
function BlackScholes(isCallFlag, S, X, T, r, _v) 
{
	local d1, d2, result;
	
	d1 = (log(S / X) + (r + _v * _v / 2.0) * T) / (_v * sqrt(T));
	d2 = d1 - _v * sqrt(T);

	if (isCallFlag)
		result = (S * CND(d1)-X * exp(-r * T) * CND(d2));
	else
		result = (X * exp(-r * T) * CND(-d2) - S * CND(-d1));
	return result;
}

_TRACE("Call 60 : " + BlackScholes(True, 60, 65, 0.25, 0.01, 0.30));
_TRACE("Put  60 : " + BlackScholes(False, 60, 65, 0.25, 0.01, 0.30));
// Testing the problematic example in the comments section in the AFL Library
// StockPrice = 15; StrikePrice = 20; Timedays = 46; InterestRate = 0.05; VKnown = 0.3;
_TRACE("Call 15 OTM :" + BlackScholes(True, 15, 20, 46/365, 0.05, 0.3));
_TRACE("Call 14 OTM :" + BlackScholes(True, 14, 20, 46/365, 0.05, 0.3));

//zz= under= 2000; st=strike =2050; 
zz = close;  st=strike= Close * 1.05;
dte= 30/365; rate= 0.05; IV= 0.3; 


call001= BlackScholes(True, zz, st, dte, rate, iv) ;
Plot ( C, "c", colorBlack);
Plot ( C+call001, "call", colorBlue);
2 Likes

many many thanks Mr. Fxshart, you have fixed my trouble;
yes I already read 'how AFL works' but I didn't understood enough

If I may ask, the difficult for me was because if I put the variables fixet in the line
zz= under= 2000; st=strike =2050;
all worked fine, but if I put the line with arrays
zz = close; st=strike= Close * 1.05;
the error arised: problably because in this way the "x" variable become an array, and before it was a constant: is this interpretation correct ?
many many thanks for your kindness
Cesare

@cesare, yes that's correct. It is because of x becoming array in that case. Please read the other links about if-else and IIf. It explains everything already plus has examples. Numbered arrays consist of multiple numbers. That's why arrays are not allowed to be used in if-else statements but instead elements of array (single numbers). In if-else statement you can not check for true/false state on entire array at once. You have to pick element by element. Think of it like the beads of the chain of "praying the rosary". There you go through the chain bead by bead (element by element) where the chain would be your array.

20

You can check type of a variable by using typeof operator.

e.g.

x = Close;
printf( "Type of x is: %s", typeof(x) );
3 Likes

I am obliged to you: very clear, very well explained, and the chain image is chosen with inspiration.
I am an old programmer (begun with FORTRAN COBOL BASIC) and sometimes become sidetracked by AFL language: you are very kind to help
best regards
Cesare

BlackScholes Options Pricing AFL functions with some Key Greeks.

/************************************** *************************************************************************************************************************************
Options Pricing and Greeks
Inspired and Adapted from Beppe Post on Amibroker forum. 
// Adapted from source code in many programming languages here:
// http://www.espenhaug.com/black_scholes.html
// Greeks forumlas used form Options Pricing Model excel sheet from http://www.optiontradingtips.com 

Written By  : Yogesh Dalvi
Date        : 10-Sep-2020
Email 		: yogesh.dalvi@gmail.com
twitter		: @dyogesh21
Telegram	: https:/t.me/Dyogesh
*****************************************************************************************************************************************************************************/

//Options Functions

// BlackScholes function parameters: 
// isCallFlag = True for Call / False for Put
// S = Stock price
// X = Strike price
// T = Years to maturity (DTE /  365) - DTE = Days to expiration
// r = Risk-free rate
// v = Volatility
// d = Divident
// mp = Market Price

function OptionPrice(isCallFlag, S, X , T, r, iv,d) 
{

	local d1, d2, result;
	
	d1 = (log(S / X) + (r - d  + (iv * iv * 0.5)) * T) / (iv * sqrt(T));
	d2 = d1 - iv * sqrt(T);

	if (isCallFlag)
		result = ((S * exp(-d * T)*  NormDist(d1)) - (X * exp(-r * T) * NormDist(d2)) );
	else
 		result = ( (X * exp(-r * T) * NormDist(-d2))   - (S * NormDist(-d1) * exp(-d * T) ));
	return result;
}

function OptionDelta(isCallFlag, S, X, T, r, iv,d) 
{

	local d1, d2, result;

	d1 = (log(S / X) + (r - d  + (iv * iv * 0.5)) * T) / (iv * sqrt(T));
	d2 = d1 - iv * sqrt(T);

	
	if (isCallFlag)
		result = NormDist(d1);
	else
 		result = NormDist(d1) - 1;
	return result;
}

function OptionGamma(S, X, T, r, iv,d) 
{

	local d1, d2, nd1,Pi,result;
    Pi = 3.14159265358979;
	d1 = (log(S / X) + (r - d  + (iv * iv * 0.5)) * T) / (iv * sqrt(T));
	d2 = d1 - iv * sqrt(T);
    nd1 = exp(-(d1*d1) / 2) / sqrt(2 * Pi);
    
    result = nd1 / (S * (iv * sqrt(T)));
    return result;
}


function OptionTheta(isCallFlag, S, X, T, r, iv,d) 
{

	local d1, d2,nd1,nd2,Pi,theta,result;
    Pi = 3.14159265358979;
    
	d1 = (log(S / X) + (r - d  + (iv * iv * 0.5)) * T) / (iv * sqrt(T));
	d2 = d1 - iv * sqrt(T);
    nd1 = exp(-(d1*d1) / 2) / sqrt(2 * Pi);
    nd2 = d1 - iv * sqrt(T); 
	

	
	if (isCallFlag)
	{
		theta = - (((S * iv * nd1) / (2 * sqrt(T)))  - (r * X * exp(-r * T) * nd2)) ;
	}
	else
 	{
 		theta = - (((S * iv * nd1) / (2 * sqrt(T)))  + (r * X * exp(-r * T) * (1- nd2))) ;
	}
	
	if (T < 1 / 365) 
		result = theta * T  ;
	else 
		result = theta /  365;	
	
	return result;
}

function OptionVega(S, X, T, r, iv,d) 
{

	local d1, d2, nd1,Pi,result;
    Pi = 3.14159265358979;
	d1 = (log(S / X) + (r - d  + (iv * iv * 0.5)) * T) / (iv * sqrt(T));
	d2 = d1 - iv * sqrt(T);
    nd1 = exp(-(d1*d1) / 2) / sqrt(2 * Pi);
    
    result = 0.01 * S * sqrt(T) * nd1;
    return result;
}

function OptionRho(isCallFlag,S, X, T, r, iv,d) 
{

	local d1, d2, nd1,Pi,result;
    Pi = 3.14159265358979;
	d1 = (log(S / X) + (r - d  + (iv * iv * 0.5)) * T) / (iv * sqrt(T));
	d2 = d1 - iv * sqrt(T);
    nd1 = exp(-(d1*d1) / 2) / sqrt(2 * Pi);
    
    if(iscallFlag)
		result = 0.01 * X * T * exp(-r * T) * NormDist(d2);
	else 
		result = - 0.01 * X * T * exp(-r * T) * (1 -NormDist(d2));	
     
   
}



function OptionIV(isCallFlag, S, X, T, r,mp,d) 
{
	
 local hval,lval,result;	
 
 hval = 3;
 lval = 0;
	
 if(isCallFlag)
 {
	do
	{
	 if(OptionPrice(isCallFlag,S,X,T,r,(hval+lval) / 2 ,d) > mp)	
		hval = (hval + lval) / 2;
	 else 
		lval = (hval + lval) / 2;
			
	} while ((hval - lval) > 0.0001);  
	
	
 }
 else
 {
	do
	{
	 if(OptionPrice(isCallFlag,S,X,T,r,(hval+lval) / 2 ,d) > mp)	
		hval = (hval + lval) / 2;
	 else 
		lval = (hval + lval) / 2;
			
	} while ((hval - lval) > 0.0001) ; 

 }
 
 result = (hval + lval) / 2;
 return result; 

}

5 Likes

Sorry for reviving an old thread but since a limited solution is there for Black & Scholes in the forum thought to share the updated version of the OptionPrice function shared generously by @beppe & @yogeshdalvi I am personally using @yogeshdalvi greek snippet.

/* 	
	https://forum.amibroker.com/t/black-scholes/4890/4
	Inspired and Adapted from Beppe and yogeshdalvi post on Amibroker forum.
	Result calculation based on Dr. Kevin Bracker, kbracker@pittstate.edu
*/

//Options Functions
ExpiryDate = ParamDate( "Expiry Date", "2021-05-27", 2 );
dte = Max( 0, ( DateTimeDiff( vExpiryDate , LastValue( DateTime() ) ) - ( 7.5 * 60 * 60 ) ) / ( 365 * 24 * 60 * 60 ) ); //Since Expiry is at 3:30Pm subtract 7.5 hours
dte1 = LastValue( vDTE * 365 );
isCallFlag = True;// for Call / False for Put
s = 14696; // Stock Price
x = 14900; // Strike Price
t = (15.11/365); // Years to maturity (DTE /  365) - DTE = Days to expiration
iv = (17.45/100); // implied volatility
r = 0; //Risk-free rate

function OptionPrice( isCallFlag, s, x, t, r, iv )
{
    local d1, d2, result;

    d1 = ( log( s / x ) + ( r - d  + ( iv * iv * 0.5 ) ) * t ) / ( iv * sqrt( t ) );
    d2 = d1 - iv * sqrt( T );

    if( isCallFlag )

        result = ( s * NormDist( d1 ) ) - ( ( x / EXP( r * t ) ) * NormDist( d2 ) );
    else
        result	= ( ( s * NormDist( d1 ) ) - ( ( x / EXP( r * t ) ) * NormDist( d2 ) ) + ( x / EXP( r * t ) ) - s );

    return result;
}
call_Prem = Prec(BlackScholes( 1, s, x, t, r, iv ),2) ;
cIntVal = Prec(MAX(s-x,0),2);
cspecprem = Prec(call_prem - cIntVal,2);

put_prem = Prec(BlackScholes( 0, s, x, t, r, iv ),2);
pIntVal = Prec(MAX(x-s,0),2);
pspecprem = Prec(Put_prem - pIntVal,2);
_TRACE( "Call 14900 : " + call_Prem);
_TRACE( "Call Intrinsic Value : " + cIntVal );
_TRACE( "Call Speculative Premium : " + cspecprem );

_TRACE( "Put  14900 : " +  put_prem);
_TRACE( "Put Intrinsic Value : " + pIntVal );
_TRACE( "Put Speculative Premium : " + cspecprem );