Finance Functions Bonds & Options
July 7, 2019
[1]: def duration(per_chg_price, chg_yld):
'''
Summary: Calculate the duration of a bond.
PARA per_chg_price: The percentage change in bond price.
PARA type: float
PARA chg_yld: The yield change in percent.
PARA type: float
'''
return -(per_chg_price / chg_yld)
#EXAMPLE
price_change = -.05
yield_change = .01
duration(price_change, yield_change)
[1]: 5.0
[2]: def absolute_yield_spread(yield_higher, yield_lower):
'''
Summary: Calculate the absolute yield spread given two bonds.
PARA yield_higher: The yield on the higher-yield bond.
PARA type: float
PARA yield_lower: The yield on the lower-yield bond.
PARA type: float
'''
return (yield_higher - yield_lower)
#EXAMPLE
y_h = .0675
1
y_l = .0650
absolute_yield_spread(y_h, y_l)
[2]: 0.0025000000000000022
[3]: def relative_yield_spread(absolute_yield, yield_benchmark):
'''
Summary: Calculate the relative yield spread of a given bond.
PARA absolute_yield: The the absolute yield spread for two given bonds.
PARA type: float
PARA yield_benchmark: The yield of the bond that we are defining as the␣
,→ benchmark, the one we want to compare.
PARA type: float
'''
return absolute_yield / yield_benchmark
#EXAMPLE
y_h = .0675
y_l = .0650
y_a = absolute_yield_spread(y_h, y_l)
relative_yield_spread(y_a, y_l)
[3]: 0.03846153846153849
[4]: def yield_ratio(subject_yield, benchmark_yield):
'''
Summary: Calculate the yield ratio, given two bonds.
PARA subject_yield: The yield of the subject bond.
PARA type: float
PARA benchmark_yield: The yield of the bond that we are defining as the␣
,→ benchmark, the one we want to compare.
PARA type: float
'''
return subject_yield / benchmark_yield
#EXAMPLE
y_h = .0675
y_l = .0650
yield_ratio(y_h, y_l)
[4]: 1.0384615384615385
2
[5]: def after_tax_yield(taxable_yield, marginal_tax_rate):
'''
Summary: Calculate the after-tax yield on a taxable security.
PARA taxable_yield: The taxable yield of a bond.
PARA type: float
PARA marginal_tax_rate: The investor's marginal tax rate.
PARA type: float
'''
return taxable_yield * (1 - marginal_tax_rate)
#EXAMPLE
yld_tax = .10
mrg_tax = .40
after_tax_yield(yld_tax, mrg_tax)
[5]: 0.06
[7]: def taxable_equivalent_yield(tax_free_yield, marginal_tax_rate):
'''
Summary: Calculate the taxable-equivalent yield for a given bond.
PARA tax_free_yield: The tax-free yield of a bond.
PARA type: float
PARA marginal_tax_rate: The investor's marginal tax rate.
PARA type: float
'''
return tax_free_yield / (1 - marginal_tax_rate)
#EXAMPLE
yld_tax = .045
mrg_tax = .350
taxable_equivalent_yield(yld_tax, mrg_tax)
[7]: 0.06923076923076922
[8]: def effective_duration(bond_dec, bond_inc, bond_int, yld_delta):
'''
Summary: Calculate the effective duration of a bond, given a change in␣
,→yield.
PARA bond_dec: The value of a bond is the yield decreases by the␣
,→ yield_delta.
3
PARA type: float
PARA bond_inc: The value of a bond is the yield increases by the␣
,→yield_delta.
PARA type: float
PARA bond_int: The inital bond price.
PARA type: float
PARA yld_delta: The change in yield to get bond_dec & bond_inc
PARA type: float
'''
return (bond_inc - bond_dec) / (2 * bond_int * yld_delta)
#EXAMPLE
bond_inc = 952.30
bond_dec = 866.80
bond_int = 908.00
delt_yld = .005
effective_duration(bond_dec, bond_inc, bond_int, delt_yld)
[8]: 9.416299559471366
[9]: def pct_change_bond(effective_duration, yld_chg):
'''
Summary: Calculate the percentage change in bond price.
PARA effective_duration: The effective duration of the bond.
PARA type: float
PARA yld_chg: The percentage change in yield.
PARA type: float
'''
return -effective_duration * yld_chg
#EXAMPLE
bond_inc = 952.30
bond_dec = 866.80
bond_int = 908.00
delt_yld = .005
ed = effective_duration(bond_dec, bond_inc, bond_int, delt_yld)
pct_yld = .003
pct_change_bond(ed, pct_yld)
4
[9]: -0.028248898678414097
[12]: import numpy as np
def portfolio_duration(bonds, durations):
'''
Summary: Calculate the duration of a portfolio given a list of bond prices,␣
,→and a list of durations for each bonds.
PARA bonds: A list of bonds at their market value prices
PARA type: float
PARA durations: A list of durations for each bond in the bonds list
PARA type: float
'''
# calculate the weights
np_bonds = np.array(bonds)
np_weights = np_bonds / np_bonds.sum()
np_durations = np.array(durations)
return np.dot(np_weights, np_durations)
#EXAMPLE
bond = [6000, 4000]
dura = [8.5, 4.0]
portfolio_duration(bond, dura)
[12]: 6.699999999999999
[15]: def return_impact(mod_duration, spread, spread_type, convexity):
'''
Summary: Calculate the return impact for a bond, using either a large or␣
,→small spread.
PARA mod_duration: The modified duration of the bond.
PARA type: float
PARA spread: The change in spread
PARA type: float
PARA spread_type: Defines the spread as either a small spread change or␣
,→large spread change. Options are 'large' or 'small'
PARA type: string
PARA convexity: The spread convexity
5
PARA type: float
'''
if spread_type == 'small':
return -mod_duration * spread
else:
return (-mod_duration * spread) + (.5 * convexity * (spread ** 2))
#EXAMPLE
duration = 6.4
convexity = 50
spread = -.0075
# small example
spread_type = 'small'
display(return_impact(duration, spread, spread_type, convexity))
# large example
spread_type = 'large'
display(return_impact(duration, spread, spread_type, convexity))
0.048
0.04940625
[20]: def option_moneyness(option_type, strike_price, selling_price):
'''
Summary: Returns whether a put option or call option is in the money, at␣
,→the money, or out the money.
PARA option_type: Defines whether the option is a 'call' or 'put' option.
PARA type: string
PARA strike_price: The strike price of the option.
PARA type: float
PARA selling_price: The current selling price of the option
PARA type: float
'''
6
# describe the conditions for a call option
if option_type == 'call':
if (selling_price - strike_price) > 0:
return ('in-the-money', selling_price - strike_price)
elif (selling_price - strike_price) < 0:
return ('out-of-the-money', selling_price - strike_price)
elif(selling_price == strike_price):
return ('at-the-money', 0)
# describe the condtions for a put option
else:
if (strike_price - selling_price) > 0:
return ('in-the-money', strike_price - selling_price)
elif (strike_price - selling_price) < 0:
return ('out-of-the-money', strike_price - selling_price)
elif(selling_price == strike_price):
return ('at-the-money', 0)
# EXAMPLE
selling_price = 37.00
strike_price = 40.00
# call example
opt_type = 'call'
display(option_moneyness(opt_type, strike_price, selling_price))
# put example
opt_type = 'put'
display(option_moneyness(opt_type, strike_price, selling_price))
('out-of-the-money', -3.0)
('in-the-money', 3.0)
[21]: def exp_date_payoff(exercise_price, index_price, contracts):
'''
Summary: Returns the expiration date payoff for a option.
PARA exercise_price: The exercise price of the option.
PARA type: float
7
PARA index_price: The index price at expiration.
PARA type: float
PARA contracts: The contract multiplier
PARA type: int
'''
return (index_price - exercise_price) * contracts
# EXAMPLE
i_price = 962.00
e_price = 950.00
cont = 250
exp_date_payoff(e_price, i_price, cont)
[21]: 3000.0
[22]: def option_intrinsic_value(option_type, strike_price, selling_price):
'''
Summary: Returns the intrinsic value of either a put or call option.
PARA option_type: Defines whether the option is a 'call' or 'put' option.
PARA type: string
PARA strike_price: The strike price of the option.
PARA type: float
PARA selling_price: The current selling price of the option
PARA type: float
'''
# calculate the intrinsic value of a call option
if option_type == 'call':
return max([0, (selling_price - strike_price)])
# calculate the intrinsic value of a put option
else:
return max([0, (strike_price - selling_price)])
# EXAMPLE
selling_price = 55.00
strike_price = 50.00
8
# call example
opt_type = 'call'
display(option_intrinsic_value(opt_type, strike_price, selling_price))
# put example
opt_type = 'put'
display(option_intrinsic_value(opt_type, strike_price, selling_price))
5.0
[29]: def min_max_option(option_type, option_style, risk_free_rate, strike_price,␣
,→selling_price, time_frame):
'''
Summary: Returns the maximum and minimum value for a european or american␣
,→call/put.
PARA option_type: Defines whether the option is a 'call' or 'put' option.
PARA type: string
PARA option_style: Defines whether the option is an 'american' or␣
,→'european' option.
PARA type: string
PARA risk_free_rate: The current risk-free rate.
PARA type: float
PARA selling_price: The current selling price of the option.
PARA type: float
PARA strike_price: The strike price of the option.
PARA type: float
PARA time_frame: The time frame of the option, for example a 4 month option␣
,→ would be 4.
PARA type: float
'''
if option_type == 'call':
if option_style == 'american' or option_style == 'european':
max_opt_1 = 0
9
max_opt_2 = (selling_price - strike_price / (1 + risk_free_rate)␣
,→ **(time_frame / 12))
minimum_value = max([max_opt_1, max_opt_2])
maximum_value = selling_price
return (minimum_value, maximum_value)
else:
if option_style == 'american':
max_opt_1 = 0
max_opt_2 = strike_price - selling_price
minimum_value = max([max_opt_1, max_opt_2])
maximum_value = strike_price
return (minimum_value, maximum_value)
else:
max_opt_1 = 0
max_opt_2 = (strike_price / (1 + risk_free_rate) **(time_frame /␣
,→12)) - selling_price
minimum_value = max([max_opt_1, max_opt_2])
maximum_value = (strike_price / (1 + risk_free_rate) **(time_frame /
,→ 12))
return (minimum_value, maximum_value)
# EXAMPLE - PUTS
selling_price = 63.00
strike_price = 65.00
time_frame = 4
rfr = .05
# put example
opt_type = 'put'
opt_styl = 'american'
display(min_max_option(opt_type, opt_styl, rfr, strike_price, selling_price,␣
,→time_frame))
# put example
opt_type = 'put'
opt_styl = 'european'
10
display(min_max_option(opt_type, opt_styl, rfr, strike_price, selling_price,␣
,→time_frame))
# EXAMPLE - CALLS
selling_price = 68.00
strike_price = 65.00
time_frame = 3
rfr = .05
# call example
opt_type = 'call'
opt_styl = 'american'
display(min_max_option(opt_type, opt_styl, rfr, strike_price, selling_price,␣
,→time_frame))
# call example
opt_type = 'call'
opt_styl = 'european'
display(min_max_option(opt_type, opt_styl, rfr, strike_price, selling_price,␣
,→time_frame))
(2.0, 65.0)
(0.9514295424027992, 63.9514295424028)
(3.788024417500182, 68.0)
(3.788024417500182, 68.0)
[36]: def put_call_parity(calc = 'none', put = 0, call = 0, strike_price = 0,␣
,→stock_price = 0, risk_free_rate = 0, time_frame = 0):
'''
Summary: Define the put-call parity formula, which returns the price of a␣
,→call given a put and vice verse.
PARA calc: Calculate either the value of a 'call' given a put or calculate␣
,→a 'put' given a call.
PARA type: string
PARA put: The price of the put.
PARA type: float
PARA call: The price of the call
PARA type: float
11
PARA exercise_price: The current selling price of the option.
PARA type: float
PARA stock_price: The current price of the stock
PARA type: float
PARA risk_free_rate: The current risk-free rate.
PARA type: float
PARA time_frame: The time frame of the option, for example a 4 month option␣
,→would be 4.
PARA type: int
'''
if calc == 'put':
put = call - stock_price + (strike_price / ((1 + risk_free_rate) **␣
,→(time_frame / 12)))
return put
else:
call = stock_price + put - (strike_price / ((1 + risk_free_rate) **␣
,→ (time_frame / 12)))
return call
#EXAMPLE
stock = 52.00
rfr = .05
time = 3
exercise = 50.00
put = 1.50
calc_type = 'call'
put_call_parity(calc = calc_type,
put = put,
strike_price = exercise,
stock_price = stock,
risk_free_rate = rfr,
time_frame =time)
[36]: 4.106172628846302
[37]: def fixed_rate_pay(swap_fixed_rate, libor, principal, num_days):
'''
Summary: Define the put-call parity formula, which returns the price of a␣
,→call given a put and vice verse.
12
PARA calc: Calculate either the value of a 'call' given a put or calculate␣
,→ a 'put' given a call.
PARA type: string
PARA swap_fixed_rate: The swap's fixed rate.
PARA type: float
PARA libor: The libor rate
PARA type: float
PARA principal: The principal of the swap
PARA type: float
PARA num_days: The number of days until payment
PARA type: int
'''
return (swap_fixed_rate - libor) * (num_days / 360) * (principal)
#EXAMPLE
swap = .06
libor = .04
principal = 1000000
days = 90
fixed_rate_pay(swap, libor, principal, days)
[37]: 4999.999999999999
13