Tian (1993)
Tian (1993) using R, Matlab, VBA, C++, Python code with/out Greeks for valuation of vanilla and compound options
The Tian (1993) model has been important for the development of lattice valuation technology. There is some evidence to suggest it has smoother convergence relative to peers. Joshi (2009) compares different trees and examines the performance of each. He outlined how the parameter inputs of u and d are influenced by varying model choices. Since the Tian binomial tree uses a backward inductive pricing process, the pricing starts from the nodes at the maturity. Shang and Byrne (2020) reviewed the model performance here:
VBA, Python and R Implementation based on Espen Haug and Robert McDonald
Tian (1993) with Greeks
Tian (1993) binomial model with Greek Parameter Sensitivities using Python code ( Google Colab ). Please see Tienuss Github.
Compound Options using Tian (1993)
A Tian (1993) Binomial tree is estimated to estimate compound options based on Geske (1979)
The Valuation of Compound Options using Geske (1979) with the R Derivmkts package
The Tian (1993)
The Tian (1993) model has attracted much attention and there is some evidence to suggest it has smoother convergence to true relative to Cox, Ross and Rubinstein. Joshi (2009) compares different trees and examines the performance of each. He outlined how the parameter inputs of u and d are influenced by varying model choices. In turn he examined the speed and accuracy of varying tree/lattice specifications.
The Tian (1993) Binomial Model using C++
// Tian (1993) based on Haug
#include <stdio.h>
#include <math.h>
#include <iostream>
#include <algorithm>
#include <iostream>
#include <vector>
#include<iomanip>
#include <string>
#include <time.h>
//using namespace System;
using namespace std;
// Function for binomial tree
double Binomial(int n, double S, double K, double r, double q, double v, double T, char PutCall, char OpStyle) {
int i, j;
double dt, u, d, p;
// new for Tian
double M, H;
int z;
// Quantities for the tree
dt = T / n;
M = exp((r-q)*dt);
H = exp(v*v*dt);
u = M*H / 2 * ((H + 1) + sqrt(H*H + 2 * H - 3));
d = M*H / 2 * ((H + 1) - sqrt(H*H + 2 * H - 3));
p = (M - d) / (u - d);
if (PutCall == 'C')
{
z = 1;
}
else if (PutCall == 'P')
{
z = -1;
}
vector<double> OptionValue;
//resize the column
OptionValue.resize(n + 1);
for (i = 0; i <= n; i++) {
OptionValue[i] = max(z*(S*pow(u, i)*pow(d, n - i) - K), 0.0);
}
// Backward recursion through the tree
for (j = n - 1; j >= 0; j--)
for (i = 0; i <= j; i++) {
if (OpStyle == 'E')
OptionValue[i] = exp(-r*dt)*(p*(OptionValue[i + 1]) + (1.0 - p)*(OptionValue[i]));
else {
OptionValue[i] = max(z*(S*pow(u, i)*pow(d, j - i) - K), exp(-r*dt)*(p*(OptionValue[i + 1]) + (1.0 - p)*(OptionValue[i])));
//OptionValue[i] = max(z*(S*pow(u, (2 * i - j)) - K), exp(-r*dt)*(p*(OptionValue[i + 1]) + (1.0 - p)*(OptionValue[i])));
}
}
// Return the option price
return OptionValue[0];
}
int main() {
//double S, K, T, v, r;
//char PutCall, OpStyle;
//int n;
clock_t start_time, end_time;
start_time = clock();
int n = 1500; // Number of steps
double S = 100.0; // Spot Price
double K = 100.0; // Strike Price
double T = 3; // Years to maturity
double r = 0.03; // Risk Free Rate
double q = 0.07; //double q =
double v = 0.20;
char PutCall = 'C';
char OpStyle = 'A';
cout << setprecision(10);
cout << "The binomial price is " << Binomial(n, S, K, r, q, v, T, PutCall, OpStyle) << endl;
end_time = clock();
cout << " " << start_time << " " << end_time << " " << (end_time - start_time) << endl;
cout << " " << start_time << " " << end_time << " " << (end_time - start_time) / (double)CLOCKS_PER_SEC << " seconds" << endl;
//system("PAUSE");
}
The Tian (1993) Binomial Model using Python
# adapted from
# https://github.com/tienusss/Option_Calculations
# Import packages
import numpy as np
# Defined functions
def TianBinomial(OutputFlag, AmeEurFlag, CallPutFlag, S, X, T, r, c, v, n):
# This functions calculates the implied volatility of American and European options
# This code is based on "The complete guide to Option Pricing Formulas" by Espen Gaarder Haug (2007)
# Translated from a VBA code
# OutputFlag:
# "P" Returns the options price
# "d" Returns the options delta
# "a" Returns an array containing the option value, delta and gamma
# AmeEurFlag:
# "a" Returns the American option value
# "e" Returns the European option value
# CallPutFlag:
# "C" Returns the call value
# "P" Returns the put value
# S is the share price at time t
# X is the strike price
# T is the time to maturity in years (days/365)
# r is the risk-free interest rate
# c is the cost of carry rate
# v is the volatility
# n determines the stepsize
# Creates a list with values from 0 up to n (which will be used to determine to exercise or not)
n_list = np.arange(0, (n + 1), 1)
# Checks if the input option is a put or a call, if not it returns an error
if CallPutFlag == 'C':
z = 1
elif CallPutFlag == 'P':
z = -1
else:
return 'Call or put not defined'
# Calculates the stepsize in years
dt = T / n
# CRR parameter outputs
# The up and down factors
# u = np.exp(v*np.sqrt(dt))
# d = 1./u
# p = (np.exp((c)*dt)-d) / (u-d)
# Tian Parameters outputs
H = np.exp((v**2)*dt)
u = 0.5 * np.exp(c * dt) * H * (H + 1 + np.sqrt(H**2 + 2*H -3)) # up movement
d = 0.5 * np.exp(c * dt) * H * (H + 1 - np.sqrt(H**2 + 2*H -3)) # down movement
p = (np.exp(c * dt) - d) / (u - d)
df = np.exp(-r * dt)
# Creates the most right column of the tree
max_pay_off_list = []
for i in n_list:
i = i.astype('int')
max_pay_off = np.maximum(0, z * (S * u ** i * d ** (n - i) - X))
max_pay_off_list.append(max_pay_off)
# The binominal tree
for j in np.arange(n - 1, 0 - 1, -1):
for i in np.arange(0, j + 1, 1):
i = i.astype(int) # Need to be converted to a integer
if AmeEurFlag == 'e':
max_pay_off_list[i] = (p * max_pay_off_list[i + 1] + (1 - p) * max_pay_off_list[i]) * df
elif AmeEurFlag == 'a':
max_pay_off_list[i] = np.maximum((z * (S * u ** i * d ** (j - i) - X)),
(p * max_pay_off_list[i + 1] + (1 - p) * max_pay_off_list[i]) * df)
if j == 2:
gamma = ((max_pay_off_list[2] - max_pay_off_list[1]) / (S * u ** 2 - S * u * d) - (
max_pay_off_list[1] - max_pay_off_list[0]) / (S * u * d - S * d ** 2)) / (
0.5 * (S * u ** 2 - S * d ** 2))
if j == 1:
delta = ((max_pay_off_list[1] - max_pay_off_list[0])) / (S * u - S * d)
price = max_pay_off_list[0]
# Put all the variables in the list
variable_list = [delta, gamma, price]
# Return values
if OutputFlag == 'P':
return price
elif OutputFlag == 'd':
return delta
elif OutputFlag == 'g':
return gamma
elif OutputFlag == 'a':
return variable_list
else:
return 'Indicate if you want to return P, d, g or a'
S = 100
X = 100
T = 4/12
r = 0.05
c = 0.05
v = 0.3
n = 97
Eur_call_result = TianBinomial('P', 'e', 'C', S, X, T, r, c, v, n)
American_call_result = TianBinomial('P', 'a', 'C', S, X, T, r, c, v, n)
Eur_put_result = TianBinomial('P', 'e', 'P', S, X, T, r, c, v, n)
American_put_result = TianBinomial('P', 'a', 'P', S, X, T, r, c, v, n)
#Print the output of the results
print('The price of the European call option is equal to ' +str(Eur_call_result))
print('The price of the American call option is equal to ' +str(American_call_result))
print('The price of the European put option is equal to ' +str(Eur_put_result))
print('The price of the American put option is equal to ' +str(American_put_result))