Skip to content

Commit 36467bb

Browse files
committed
fixed bug in binomical tree
1 parent 8787fcb commit 36467bb

File tree

6 files changed

+72
-16
lines changed

6 files changed

+72
-16
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,4 @@ bincode = "1.3.1"
3636
strum = "0.25"
3737
strum_macros = "0.25"
3838
ndarray = "0.15"
39+
assert_approx_eq = "1.1.0"

src/equity/binomial.rs

+49-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use ndarray::Array2;
99
pub fn npv(option: &&EquityOption) -> f64 {
1010
assert!(option.volatility >= 0.0);
1111
assert!(option.time_to_maturity() >= 0.0);
12-
assert!(option.current_price.value >= 0.0);
12+
assert!(option.underlying_price.value >= 0.0);
1313
let num_steps = 1000;
1414

1515
let dt = option.time_to_maturity() / num_steps as f64;
@@ -25,31 +25,33 @@ pub fn npv(option: &&EquityOption) -> f64 {
2525
// Calculate option prices at the final time step (backward induction)
2626
let multiplier = if option.option_type == OptionType::Call { 1.0 } else { -1.0 };
2727
for j in 0..=num_steps {
28-
let spot_price_j = option.current_price.value * u.powi(num_steps as i32 - j as i32) * d.powi(j as i32);
28+
let spot_price_j = option.underlying_price.value * u.powi(num_steps as i32 - j as i32) * d.powi(j as i32);
2929
tree[[j,num_steps]] = (multiplier*(spot_price_j - option.strike_price)).max(0.0);
3030
}
3131

3232
match option.style {
3333
ContractStyle::European => {
3434
for i in (0..num_steps).rev() {
3535
for j in 0..=i {
36-
let spot_price_i = option.current_price.value * u.powi(i as i32 - j as i32) * d.powi(j as i32);
36+
let spot_price_i = option.underlying_price.value * u.powi(i as i32 - j as i32) * d.powi(j as i32);
3737
let discounted_option_price = discount_factor * (p * tree[[ j,i+1]] + (1.0 - p) * tree[[ j + 1,i+1]]);
3838
//tree[[j,i]] = (multiplier*(spot_price_i - option.strike_price)).max(discounted_option_price);
3939
tree[[j,i]] = discounted_option_price;
4040
}
4141
}
42+
4243
}
4344
ContractStyle::American => {
4445
println!("American");
4546
for i in (0..num_steps).rev() {
4647
for j in 0..=i {
47-
let spot_price_i = option.current_price.value * u.powi(i as i32 - j as i32) * d.powi(j as i32);
48+
let spot_price_i = option.underlying_price.value * u.powi(i as i32 - j as i32) * d.powi(j as i32);
4849
//let intrinsic_value = (multiplier*(spot_price_i - option.strike_price)).max(0.0);
4950
let discounted_option_price = discount_factor * (p * tree[[ j,i+1]] + (1.0 - p) * tree[[ j + 1,i+1]]);
5051
tree[[j,i]] = (multiplier*(spot_price_i - option.strike_price)).max(discounted_option_price);
5152
}
5253
}
54+
5355
}
5456
_ => {
5557
panic!("Invalid option style");
@@ -58,4 +60,46 @@ pub fn npv(option: &&EquityOption) -> f64 {
5860

5961

6062
return tree[[0,0]];
61-
}
63+
}
64+
65+
// Write a unit test for the binomial tree model
66+
67+
#[cfg(test)]
68+
mod tests {
69+
use assert_approx_eq::assert_approx_eq;
70+
use super::*;
71+
use crate::core::utils::{Contract,MarketData};
72+
use crate::core::trade::{OptionType,Transection};
73+
use crate::core::utils::{ContractStyle};
74+
use crate::equity::vanila_option::{EquityOption};
75+
76+
use chrono::{NaiveDate};
77+
use crate::core::traits::Instrument;
78+
79+
80+
#[test]
81+
fn test_binomial_tree() {
82+
let mut data = Contract {
83+
action: "PV".to_string(),
84+
market_data: Some(MarketData {
85+
underlying_price: 100.0,
86+
strike_price: 100.0,
87+
volatility: Some(0.3),
88+
option_price: Some(10.0),
89+
risk_free_rate: Some(0.05),
90+
dividend: Some(0.0),
91+
maturity: "2024-01-01".to_string(),
92+
option_type: "C".to_string(),
93+
simulation: None
94+
}),
95+
pricer: "Binomial".to_string(),
96+
asset: "".to_string(),
97+
style: Some("European".to_string()),
98+
rate_data: None
99+
};
100+
let mut option = EquityOption::from_json(&data);
101+
option.valuation_date = NaiveDate::from_ymd(2023, 11, 06);
102+
let npv = option.npv();
103+
assert_approx_eq!(npv, 5.058163, 1e-6);
104+
}
105+
}

src/equity/blackscholes.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,17 @@ pub fn npv(bsd_option: &&EquityOption) -> f64 {
1818
assert!(bsd_option.time_to_maturity() >= 0.0);
1919
assert!(bsd_option.underlying_price.value >= 0.0);
2020
if bsd_option.option_type == OptionType::Call {
21+
2122
let option_price = bsd_option.underlying_price.value()
2223
* N(bsd_option.d1())
2324
* exp(-bsd_option.dividend_yield * bsd_option.time_to_maturity())
2425
- bsd_option.strike_price
2526
* exp(-bsd_option.risk_free_rate * bsd_option.time_to_maturity())
2627
* N(bsd_option.d2());
28+
let a = N(bsd_option.d1());
29+
let b = N(bsd_option.d2());
30+
println!("a = {:?} b = {:?}",a,b);
31+
println!("{:?}",bsd_option);
2732
return option_price;
2833
} else {
2934
let option_price = -bsd_option.underlying_price.value()
@@ -152,6 +157,7 @@ impl EquityOption {
152157
}
153158
pub fn option_pricing() {
154159
println!("Welcome to the Black-Scholes Option pricer.");
160+
print!(">>");
155161
println!(" What is the current price of the underlying asset?");
156162
print!(">>");
157163
let mut curr_price = String::new();
@@ -199,6 +205,7 @@ pub fn option_pricing() {
199205
println!("{:?}", expiry.trim());
200206
let _d = expiry.trim();
201207
let future_date = NaiveDate::parse_from_str(&_d, "%Y-%m-%d").expect("Invalid date format");
208+
//println!("{:?}", future_date);
202209
println!("Dividend yield on this stock:");
203210
print!(">>");
204211
let mut div = String::new();
@@ -211,7 +218,7 @@ pub fn option_pricing() {
211218
// rates: vec![0.01,0.02,0.05,0.07,0.08,0.1,0.11,0.12]
212219
//};
213220
let date = vec![0.01,0.02,0.05,0.1,0.5,1.0,2.0,3.0];
214-
let rates = vec![0.05,0.05,0.06,0.07,0.08,0.9,0.9,0.10];
221+
let rates = vec![0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05];
215222
let ts = YieldTermStructure::new(date,rates);
216223
let curr_quote = Quote::new( curr_price.trim().parse::<f64>().unwrap());
217224
let mut option = EquityOption {
@@ -231,17 +238,18 @@ pub fn option_pricing() {
231238
style: ContractStyle::European,
232239
valuation_date: Local::today().naive_local(),
233240
};
241+
println!("{:?}", option.time_to_maturity());
234242
option.set_risk_free_rate();
235243
println!("Theoretical Price ${}", option.npv());
236244
println!("Premium at risk ${}", option.get_premium_at_risk());
237-
println!("Delata {}", option.delta());
245+
println!("Delta {}", option.delta());
238246
println!("Gamma {}", option.gamma());
239247
println!("Vega {}", option.vega() * 0.01);
240248
println!("Theta {}", option.theta() * (1.0 / 365.0));
241249
println!("Rho {}", option.rho() * 0.01);
242-
let mut div1 = String::new();
250+
let mut wait = String::new();
243251
io::stdin()
244-
.read_line(&mut div)
252+
.read_line(&mut wait)
245253
.expect("Failed to read line");
246254
}
247255
pub fn implied_volatility() {

src/equity/vanila_option.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ impl EquityOption {
6464
let underlying_quote = Quote::new(market_data.underlying_price);
6565
//TODO: Add term structure
6666
let date = vec![0.01, 0.02, 0.05, 0.1, 0.5, 1.0, 2.0, 3.0];
67-
let rates = vec![0.05, 0.055, 0.06, 0.07, 0.07, 0.08, 0.1, 0.1];
67+
let rates = vec![0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05];
6868
let ts = YieldTermStructure::new(date, rates);
6969
let option_type = &market_data.option_type;
7070
let side: trade::OptionType;
@@ -79,8 +79,10 @@ impl EquityOption {
7979

8080
let risk_free_rate = Some(market_data.risk_free_rate).unwrap();
8181
let dividend = Some(market_data.dividend).unwrap();
82-
let option_price = Quote::new(match Some(market_data.option_price) {
83-
Some(x) => x.unwrap(),
82+
let mut op = 0.0;
83+
84+
let option_price = Quote::new(match market_data.option_price {
85+
Some(x) => x,
8486
None => 0.0,
8587
});
8688
//let volatility = Some(market_data.volatility);

src/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ fn main() {
123123
let interactive_matches = matches.subcommand_matches("interactive");
124124
match matches.subcommand(){
125125
("build",Some(build_matches)) => {
126+
126127
let input_file = build_matches.value_of("input").unwrap();
127128
let output_file = build_matches.value_of("output").unwrap();
128129
let mut file = File::open(input_file).expect("Failed to open JSON file");

src/utils/parse_json.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ pub fn parse_contract(mut file: &mut File,output_filename: &str) {
8686
file.read_to_string(&mut contents)
8787
.expect("Failed to read JSON file");
8888

89-
let list_contracts: utils::Contracts = serde_json::from_str(&contents).expect("Failed to deserialize JSON");
89+
let list_contracts: Contracts = serde_json::from_str(&contents).expect("Failed to deserialize JSON");
9090

9191
//let data: utils::Contract = serde_json::from_str(&contents).expect("Failed to deserialize JSON");
9292
//let mut output: String = String::new();
@@ -104,18 +104,18 @@ pub fn parse_contract(mut file: &mut File,output_filename: &str) {
104104
pub fn process_contract(data: utils::Contract) -> String {
105105
//println!("Processing {:?}",data);
106106
let date = vec![0.01,0.02,0.05,0.1,0.5,1.0,2.0,3.0];
107-
let rates = vec![0.05,0.05,0.06,0.07,0.08,0.9,0.9,0.10];
107+
let rates = vec![0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05];
108108
let ts = YieldTermStructure::new(date,rates);
109109

110110

111111
if data.action=="PV" && data.asset=="EQ"{
112112
//let market_data = data.market_data.clone().unwrap();
113113
let option = EquityOption::from_json(&data);
114114

115-
let contract_output = utils::ContractOutput{pv:option.npv(),delta:option.delta(),gamma:option.gamma(),vega:option.vega(),theta:option.theta(),rho:option.rho(), error: None };
115+
let contract_output = ContractOutput{pv:option.npv(),delta:option.delta(),gamma:option.gamma(),vega:option.vega(),theta:option.theta(),rho:option.rho(), error: None };
116116
println!("Theoretical Price ${}", contract_output.pv);
117117
println!("Delta ${}", contract_output.delta);
118-
let combined_ = utils::CombinedContract{
118+
let combined_ = CombinedContract{
119119
contract: data,
120120
output:contract_output
121121
};

0 commit comments

Comments
 (0)