From aa9a5a8d17009b7bcba89c3d82821521ca70cb58 Mon Sep 17 00:00:00 2001 From: Siddharth Date: Wed, 25 Oct 2023 02:29:20 -0400 Subject: [PATCH] refactor equity option and added volterm structure --- derivatives/src/core/quotes.rs | 1 + derivatives/src/core/termstructure.rs | 1 + derivatives/src/core/trade.rs | 3 +- derivatives/src/equity/binomial.rs | 4 +- derivatives/src/equity/blackscholes.rs | 75 ++++++++++-------- derivatives/src/equity/build_contracts.rs | 95 +++-------------------- derivatives/src/equity/montecarlo.rs | 16 ++-- derivatives/src/equity/vanila_option.rs | 79 ++++++++++++++++++- derivatives/src/equity/vol_surface.rs | 9 ++- derivatives/src/utils/parse_json.rs | 89 ++------------------- 10 files changed, 158 insertions(+), 214 deletions(-) diff --git a/derivatives/src/core/quotes.rs b/derivatives/src/core/quotes.rs index 30a0f7c..1ddc346 100644 --- a/derivatives/src/core/quotes.rs +++ b/derivatives/src/core/quotes.rs @@ -1,3 +1,4 @@ +#[derive(Debug)] pub struct Quote{ pub value: f64, pub bid: f64, diff --git a/derivatives/src/core/termstructure.rs b/derivatives/src/core/termstructure.rs index c77c484..d03c982 100644 --- a/derivatives/src/core/termstructure.rs +++ b/derivatives/src/core/termstructure.rs @@ -1,4 +1,5 @@ use chrono::{DateTime, Local, NaiveDate}; +#[derive(Debug)] pub struct YieldTermStructure { pub date: Vec, pub rates: Vec diff --git a/derivatives/src/core/trade.rs b/derivatives/src/core/trade.rs index 44b36ed..917b843 100644 --- a/derivatives/src/core/trade.rs +++ b/derivatives/src/core/trade.rs @@ -1,9 +1,10 @@ +#[derive(Debug,Clone)] pub enum Transection { Buy, Sell, } -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Debug,Clone)] pub enum OptionType { Call, Put, diff --git a/derivatives/src/equity/binomial.rs b/derivatives/src/equity/binomial.rs index aba8005..a645b7f 100644 --- a/derivatives/src/equity/binomial.rs +++ b/derivatives/src/equity/binomial.rs @@ -6,11 +6,11 @@ use crate::core::utils::{ContractStyle}; use ndarray::Array2; pub fn npv(option: &&EquityOption) -> f64 { assert!(option.volatility >= 0.0); - assert!(option.time_to_maturity >= 0.0); + assert!(option.time_to_maturity() >= 0.0); assert!(option.current_price.value >= 0.0); let num_steps = 1000; - let dt = option.time_to_maturity / num_steps as f64; + let dt = option.time_to_maturity() / num_steps as f64; let discount_factor = (-option.risk_free_rate * dt).exp(); // Calculate parameters for the binomial tree let u = (option.volatility*dt.sqrt()).exp(); //up movement diff --git a/derivatives/src/equity/blackscholes.rs b/derivatives/src/equity/blackscholes.rs index 53e8d2a..f61d629 100644 --- a/derivatives/src/equity/blackscholes.rs +++ b/derivatives/src/equity/blackscholes.rs @@ -2,6 +2,7 @@ use libm::{exp, log}; use std::f64::consts::{PI, SQRT_2}; use std::{io, thread}; use crate::core::quotes::Quote; +use chrono::{Datelike, Local, NaiveDate}; //use utils::{N,dN}; //use vanila_option::{EquityOption,OptionType}; use crate::core::utils::{ContractStyle, dN, N}; @@ -13,23 +14,23 @@ use super::super::core::traits::{Instrument,Greeks}; use super::super::core::interpolation; pub fn npv(bsd_option: &&EquityOption) -> f64 { - assert!(bsd_option.volatility >= 0.0); - assert!(bsd_option.time_to_maturity >= 0.0); + //assert!(bsd_option.volatility >= 0.0); + assert!(bsd_option.time_to_maturity() >= 0.0); assert!(bsd_option.underlying_price.value >= 0.0); if bsd_option.option_type == OptionType::Call { let option_price = bsd_option.underlying_price.value() * N(bsd_option.d1()) - * exp(-bsd_option.dividend_yield * bsd_option.time_to_maturity) + * exp(-bsd_option.dividend_yield * bsd_option.time_to_maturity()) - bsd_option.strike_price - * exp(-bsd_option.risk_free_rate * bsd_option.time_to_maturity) + * exp(-bsd_option.risk_free_rate * bsd_option.time_to_maturity()) * N(bsd_option.d2()); return option_price; } else { let option_price = -bsd_option.underlying_price.value() * N(-bsd_option.d1()) - * exp(-bsd_option.dividend_yield * bsd_option.time_to_maturity) + * exp(-bsd_option.dividend_yield * bsd_option.time_to_maturity()) + bsd_option.strike_price - * exp(-bsd_option.risk_free_rate * bsd_option.time_to_maturity) + * exp(-bsd_option.risk_free_rate * bsd_option.time_to_maturity()) * N(-bsd_option.d2()); return option_price; } @@ -39,7 +40,7 @@ impl Greeks for EquityOption{ fn delta(&self) -> f64 { let mut delta = N(self.d1()); if self.option_type == OptionType::Call { - delta = delta * exp(-self.dividend_yield * self.time_to_maturity); + delta = delta * exp(-self.dividend_yield * self.time_to_maturity()); } else if self.option_type == OptionType::Put { delta = delta - 1.0; } @@ -48,12 +49,12 @@ impl Greeks for EquityOption{ fn gamma(&self) -> f64 { let gamma = dN(self.d1()); //(St * sigma * math.sqrt(T - t)) - let var_sqrt = self.volatility * (self.time_to_maturity.sqrt()); + let var_sqrt = self.volatility * (self.time_to_maturity().sqrt()); return gamma / (self.current_price.value() * var_sqrt); } fn vega(&self) -> f64 { //St * dN(d1) * math.sqrt(T - t) - let vega = self.current_price.value() * dN(self.d1()) * self.time_to_maturity.sqrt(); + let vega = self.underlying_price.value * dN(self.d1()) * self.time_to_maturity().sqrt(); return vega; } fn theta(&self) -> f64 { @@ -61,19 +62,19 @@ impl Greeks for EquityOption{ if self.option_type == OptionType::Call { //-(St * dN(d1) * sigma / (2 * math.sqrt(T - t)) + r * K * math.exp(-r * (T - t)) * N(d2)) let t1 = -self.current_price.value() * dN(self.d1()) * self.volatility - / (2.0 * self.time_to_maturity.sqrt()); + / (2.0 * self.time_to_maturity().sqrt()); let t2 = (self.risk_free_rate - self.dividend_yield) * self.strike_price - * exp(-(self.risk_free_rate - self.dividend_yield) * self.time_to_maturity) + * exp(-(self.risk_free_rate - self.dividend_yield) * self.time_to_maturity()) * N(self.d2()); theta = t1 + t2; } else if self.option_type == OptionType::Put { //-(St * dN(d1) * sigma / (2 * math.sqrt(T - t)) - r * K * math.exp(-r * (T - t)) * N(d2)) let t1 = -self.current_price.value() * dN(self.d1()) * self.volatility - / (2.0 * self.time_to_maturity.sqrt()); + / (2.0 * self.time_to_maturity().sqrt()); let t2 = (self.risk_free_rate - self.dividend_yield) * self.strike_price - * exp(-(self.risk_free_rate - self.dividend_yield) * self.time_to_maturity) + * exp(-(self.risk_free_rate - self.dividend_yield) * self.time_to_maturity()) * N(self.d2()); theta = t1 - t2; } @@ -85,14 +86,14 @@ impl Greeks for EquityOption{ let mut rho = 0.0; if self.option_type == OptionType::Call { rho = self.strike_price - * self.time_to_maturity - * exp(-(self.risk_free_rate - self.dividend_yield) * self.time_to_maturity) + * self.time_to_maturity() + * exp(-(self.risk_free_rate - self.dividend_yield) * self.time_to_maturity()) * N(self.d2()); } else if self.option_type == OptionType::Put { //put_rho = -K * (T - t) * math.exp(-r * (T - t)) * N(-d2) rho = -self.strike_price - * self.time_to_maturity - * exp(-(self.risk_free_rate - self.dividend_yield) * self.time_to_maturity) + * self.time_to_maturity() + * exp(-(self.risk_free_rate - self.dividend_yield) * self.time_to_maturity()) * N(-self.d2()); } @@ -103,7 +104,7 @@ impl Greeks for EquityOption{ impl EquityOption { pub fn set_risk_free_rate(&mut self){ let model = interpolation::CubicSpline::new(&self.term_structure.date, &self.term_structure.rates); - let r = model.interpolation(self.time_to_maturity); + let r = model.interpolation(self.time_to_maturity()); self.risk_free_rate = r; } pub fn get_premium_at_risk(&self) -> f64 { @@ -122,15 +123,15 @@ impl EquityOption { } pub fn d1(&self) -> f64 { //Black-Scholes-Merton d1 function Parameters - let tmp1 = (self.current_price.value() / self.strike_price).ln() + let tmp1 = (self.underlying_price.value() / self.strike_price).ln() + (self.risk_free_rate - self.dividend_yield + 0.5 * self.volatility.powi(2)) - * self.time_to_maturity; + * self.time_to_maturity(); - let tmp2 = self.volatility * (self.time_to_maturity.sqrt()); + let tmp2 = self.volatility * (self.time_to_maturity().sqrt()); return tmp1 / tmp2; } pub fn d2(&self) -> f64 { - let d2 = self.d1() - self.volatility * self.time_to_maturity.powf(0.5); + let d2 = self.d1() - self.volatility * self.time_to_maturity().powf(0.5); return d2; } pub fn imp_vol(&mut self,option_price:f64) -> f64 { @@ -140,10 +141,18 @@ impl EquityOption { } self.volatility } + pub fn get_imp_vol(&mut self) -> f64 { + for i in 0..100{ + let d_sigma = (self.npv()-self.current_price.value)/self.vega(); + self.volatility -= d_sigma + } + self.volatility + } } pub fn option_pricing() { println!("Welcome to the Black-Scholes Option pricer."); println!("(Step 1/7) What is the current price of the underlying asset?"); + print!(">>"); let mut curr_price = String::new(); io::stdin() .read_line(&mut curr_price) @@ -174,12 +183,13 @@ pub fn option_pricing() { println!("Risk-free rate in %:"); let mut rf = String::new(); io::stdin().read_line(&mut rf).expect("Failed to read line"); - println!("Time to maturity in years"); + println!(" Maturity date in YYYY-MM-DD format:"); + let mut expiry = String::new(); io::stdin() .read_line(&mut expiry) .expect("Failed to read line"); - + let future_date = NaiveDate::parse_from_str(&expiry, "%Y-%m-%d").expect("Invalid date format"); println!("Dividend yield on this stock:"); let mut div = String::new(); io::stdin() @@ -201,7 +211,7 @@ pub fn option_pricing() { current_price: Quote::new(0.0), strike_price: strike.trim().parse::().unwrap(), volatility: vol.trim().parse::().unwrap(), - time_to_maturity: expiry.trim().parse::().unwrap(), + maturity_date: future_date, risk_free_rate: rf.trim().parse::().unwrap(), dividend_yield: div.trim().parse::().unwrap(), transection_price: 0.0, @@ -209,6 +219,7 @@ pub fn option_pricing() { engine: Engine::BlackScholes, simulation: None, style: ContractStyle::European, + valuation_date: Local::today().naive_local(), }; option.set_risk_free_rate(); println!("Theoretical Price ${}", option.npv()); @@ -260,12 +271,12 @@ pub fn implied_volatility() { let mut rf = String::new(); io::stdin().read_line(&mut rf).expect("Failed to read line"); - println!("Time to maturity in years"); + println!(" Maturity date in YYYY-MM-DD format:"); let mut expiry = String::new(); io::stdin() .read_line(&mut expiry) .expect("Failed to read line"); - + let future_date = NaiveDate::parse_from_str(&expiry.trim(), "%Y-%m-%d").expect("Invalid date format"); println!("Dividend yield on this stock:"); let mut div = String::new(); io::stdin() @@ -288,7 +299,7 @@ pub fn implied_volatility() { current_price: Quote::new(0.0), strike_price: strike.trim().parse::().unwrap(), volatility: 0.20, - time_to_maturity: expiry.trim().parse::().unwrap(), + maturity_date: future_date, risk_free_rate: rf.trim().parse::().unwrap(), dividend_yield: div.trim().parse::().unwrap(), transection_price: 0.0, @@ -297,15 +308,11 @@ pub fn implied_volatility() { simulation:sim, //style:Option::from("European".to_string()), style: ContractStyle::European, + valuation_date: Local::today().naive_utc(), }; option.set_risk_free_rate(); println!("Implied Volatility {}%", 100.0*option.imp_vol(option_price.trim().parse::().unwrap())); - // println!("Premium at risk ${}", option.get_premium_at_risk()); - // println!("Delata {}", option.delta()); - // println!("Gamma {}", option.gamma()); - // println!("Vega {}", option.vega() * 0.01); - // println!("Theta {}", option.theta() * (1.0 / 365.0)); - // println!("Rho {}", option.rho() * 0.01); + let mut div1 = String::new(); io::stdin() .read_line(&mut div) diff --git a/derivatives/src/equity/build_contracts.rs b/derivatives/src/equity/build_contracts.rs index 9392edf..16cc8e8 100644 --- a/derivatives/src/equity/build_contracts.rs +++ b/derivatives/src/equity/build_contracts.rs @@ -12,98 +12,25 @@ use crate::rates::utils::{DayCountConvention}; use crate::core::quotes::Quote; use crate::core::utils::{Contract,ContractStyle}; use crate::equity::utils::{Engine}; -use std::collections::HashMap; -pub fn build_eq_contracts(data: Contract)-> Box{ - let market_data = data.market_data.clone().unwrap(); - let underlying_quote = Quote::new( market_data.underlying_price); - let date = vec![0.01,0.02,0.05,0.1,0.5,1.0,2.0,3.0]; - let rates = vec![0.01,0.02,0.05,0.07,0.08,0.1,0.11,0.12]; - let ts = YieldTermStructure::new(date,rates); - let option_type = &market_data.option_type; - let side: trade::OptionType; - match option_type.trim() { - "C" | "c" | "Call" | "call" => side = trade::OptionType::Call, - "P" | "p" | "Put" | "put" => side = trade::OptionType::Put, - _ => panic!("Invalide side argument! Side has to be either 'C' or 'P'."), - } - let maturity_date = &market_data.maturity; - let today = Local::today(); - let future_date = NaiveDate::parse_from_str(&maturity_date, "%Y-%m-%d").expect("Invalid date format"); - let duration = future_date.signed_duration_since(today.naive_utc()); - let year_fraction = duration.num_days() as f64 / 365.0; - let rf = Some(market_data.risk_free_rate).unwrap(); - let div = Some(market_data.dividend).unwrap(); - let price = Some(market_data.option_price).unwrap(); - let option_price = Quote::new(price.unwrap()); - let mut option = EquityOption { - option_type: side, - transection: trade::Transection::Buy, - underlying_price: underlying_quote, - current_price: option_price, - strike_price: market_data.strike_price, - volatility: 0.2, - time_to_maturity: year_fraction, - risk_free_rate: rf.unwrap_or(0.0), - dividend_yield: div.unwrap_or(0.0), - transection_price: 0.0, - term_structure: ts, - engine: Engine::BlackScholes, - simulation: None, - style: ContractStyle::European, - //style: Option::from(data.style.as_ref().unwrap_or(&default_style)).map(|x| &**x), - }; - match data.pricer.trim() { - "Analytical" |"analytical" => { - option.engine = Engine::BlackScholes; - } - "MonteCarlo" |"montecarlo"|"MC" => { - option.engine = Engine::MonteCarlo; - } - "Binomial"|"binomial" => { - option.engine = Engine::Binomial; - } - _ => { - panic!("Invalid pricer");} - } - match data.style.as_ref().unwrap_or(&"European".to_string()).trim() { - "European" |"european" => { - option.style = ContractStyle::European; - } - "American" |"american" => { - option.style = ContractStyle::American; - } - _ => { - option.style = ContractStyle::European;} - } - option.set_risk_free_rate(); - option.volatility = option.imp_vol(option.current_price.value); - return Box::new(option); - } +use std::collections::BTreeMap; pub fn build_eq_contracts_from_json(data: Vec) -> Vec> { - let mut derivatives:Vec> = Vec::new(); - for contract in data { - let eq = build_eq_contracts(contract); - derivatives.push(eq); - } + let derivatives:Vec> = data.iter().map(|x| EquityOption::equityoption_from_json(x.clone())).collect(); return derivatives; } -pub fn build_vol_surface(mut contracts:Vec>) -> VolSurface { - //let mut ts:rates::utils::TermStructure = rates::utils::TermStructure::new(vec![],vec![],vec![], - // rates::utils::DayCountConvention::Act360); - // let mut vol_surface:VolSurface = VolSurface::new(Default::default(), 0.0, spot_date: NaiveDate::from_ymd(, 2020), - // DayCountConvention::Act365); - let mut hash_map:HashMap> = HashMap::new(); - let spot_date = Local::today(); +pub fn build_volatility_surface(mut contracts:Vec>) -> VolSurface { + + let mut vol_tree:BTreeMap> = BTreeMap::new(); + let spot_date = contracts[0].valuation_date; let spot_price = contracts[0].underlying_price.value; for i in 0..contracts.len(){ let mut contract = contracts[i].as_mut(); - let stike = contract.underlying_price.value / contract.strike_price as f64; - let vol = contract.volatility; - let maturity = contract.time_to_maturity; - hash_map.entry(maturity).or_insert(Vec::new()).push((stike,vol)); + let moneyness = contract.underlying_price.value / contract.strike_price as f64; + let volatility = contract.get_imp_vol(); + let maturity = contract.maturity_date; + vol_tree.entry(maturity).or_insert(Vec::new()).push((moneyness,volatility)); } - let vol_surface:VolSurface = VolSurface::new(hash_map, spot_price, spot_date, + let vol_surface:VolSurface = VolSurface::new(vol_tree, spot_price, spot_date, DayCountConvention::Act365); return vol_surface; } \ No newline at end of file diff --git a/derivatives/src/equity/montecarlo.rs b/derivatives/src/equity/montecarlo.rs index 8bb406f..d62797b 100644 --- a/derivatives/src/equity/montecarlo.rs +++ b/derivatives/src/equity/montecarlo.rs @@ -10,6 +10,7 @@ //println!(":?:{}",t); use std::io; +use chrono::{Datelike, Local, NaiveDate}; use libm::exp; //use crate::equity::vanila_option::{Engine, EquityOption, OptionType, Transection}; @@ -40,7 +41,7 @@ pub fn simulate_market(option: &&EquityOption) -> Vec{ for z in path{ let sim_value = option.underlying_price.value() *exp(((option.risk_free_rate - option.dividend_yield - 0.5 * option.volatility.powi(2)) - * option.time_to_maturity)+option.volatility * option.time_to_maturity.sqrt()*z); + * option.time_to_maturity())+option.volatility * option.time_to_maturity().sqrt()*z); market_at_maturity.push(sim_value); } market_at_maturity @@ -49,7 +50,7 @@ pub fn simulate_market(option: &&EquityOption) -> Vec{ pub fn simulate_market_path_wise(option: &&EquityOption) -> Vec{ let M = 1000; let N = 10000; - let dt = option.time_to_maturity/1000.0; + let dt = option.time_to_maturity()/1000.0; let path = RNG::get_matrix_standard_normal(N,M); let mut market_at_maturity:Vec = Vec::new(); for ipath in &path{ @@ -89,7 +90,7 @@ pub fn payoff(market: &Vec, pub fn npv(option: &&EquityOption,path_size: bool) -> f64 { assert!(option.volatility >= 0.0); - assert!(option.time_to_maturity >= 0.0); + assert!(option.time_to_maturity() >= 0.0); assert!(option.underlying_price.value >= 0.0); let mut st = vec![]; if path_size { @@ -104,7 +105,7 @@ pub fn npv(option: &&EquityOption,path_size: bool) -> f64 { let payoff = payoff(&st,&option.strike_price,&option.option_type); let sum_pay:f64 = payoff.iter().sum(); let num_of_simulations = st.len() as f64; - let c0:f64 = (sum_pay / num_of_simulations)*exp(-(option.risk_free_rate)*option.time_to_maturity); + let c0:f64 = (sum_pay / num_of_simulations)*exp(-(option.risk_free_rate)*option.time_to_maturity()); c0 } @@ -147,12 +148,12 @@ pub fn option_pricing() { let mut rf = String::new(); io::stdin().read_line(&mut rf).expect("Failed to read line"); - println!("Time to maturity in years"); + println!("Maturity date in YYYY-MM-DD format:"); let mut expiry = String::new(); io::stdin() .read_line(&mut expiry) .expect("Failed to read line"); - + let future_date = NaiveDate::parse_from_str(&expiry.trim(), "%Y-%m-%d").expect("Invalid date format"); println!("Dividend yield on this stock:"); let mut div = String::new(); io::stdin() @@ -174,7 +175,7 @@ pub fn option_pricing() { current_price: Quote::new(0.01), strike_price: strike.trim().parse::().unwrap(), volatility: vol.trim().parse::().unwrap(), - time_to_maturity: expiry.trim().parse::().unwrap(), + maturity_date: future_date, risk_free_rate: rf.trim().parse::().unwrap(), dividend_yield: div.trim().parse::().unwrap(), transection_price: 0.0, @@ -182,6 +183,7 @@ pub fn option_pricing() { engine: Engine::MonteCarlo, simulation: std::option::Option::Some(10000), style: ContractStyle::European, + valuation_date: Local::today().naive_utc(), }; option.set_risk_free_rate(); println!("Theoretical Price ${}", option.npv()); diff --git a/derivatives/src/equity/vanila_option.rs b/derivatives/src/equity/vanila_option.rs index b60828d..56bef21 100644 --- a/derivatives/src/equity/vanila_option.rs +++ b/derivatives/src/equity/vanila_option.rs @@ -1,3 +1,4 @@ +use chrono::{Datelike, Local, NaiveDate}; use crate::equity::montecarlo; use crate::equity::binomial; use super::super::core::termstructure::YieldTermStructure; @@ -6,7 +7,8 @@ use super::super::core::traits::{Instrument,Greeks}; use super::blackscholes; use crate::equity::utils::{Engine}; use crate::core::trade::{OptionType,Transection}; -use crate::core::utils::{ContractStyle}; +use crate::core::utils::{Contract,ContractStyle}; +use crate::core::trade; impl Instrument for EquityOption { fn npv(&self) -> f64 { @@ -31,6 +33,8 @@ impl Instrument for EquityOption { } } } + +#[derive(Debug)] pub struct EquityOption { pub option_type: OptionType, pub transection: Transection, @@ -54,3 +58,76 @@ impl EquityOption{ time_to_maturity } } +impl EquityOption { + pub fn equityoption_from_json(data: Contract) -> Box { + let market_data = data.market_data.unwrap(); + let underlying_quote = Quote::new(market_data.underlying_price); + //TODO: Add term structure + let date = vec![0.01, 0.02, 0.05, 0.1, 0.5, 1.0, 2.0, 3.0]; + let rates = vec![0.05, 0.055, 0.06, 0.07, 0.07, 0.08, 0.1, 0.1]; + let ts = YieldTermStructure::new(date, rates); + let option_type = &market_data.option_type; + let side: trade::OptionType; + match option_type.trim() { + "C" | "c" | "Call" | "call" => side = trade::OptionType::Call, + "P" | "p" | "Put" | "put" => side = trade::OptionType::Put, + _ => panic!("Invalide side argument! Side has to be either 'C' or 'P'."), + } + let maturity_date = &market_data.maturity; + let today = Local::today(); + let future_date = NaiveDate::parse_from_str(&maturity_date, "%Y-%m-%d").expect("Invalid date format"); + + let risk_free_rate = Some(market_data.risk_free_rate).unwrap(); + let dividend = Some(market_data.dividend).unwrap(); + let option_price = Quote::new(match Some(market_data.option_price) { + Some(x) => x.unwrap(), + None => 0.0, + }); + let mut option = EquityOption { + option_type: side, + transection: trade::Transection::Buy, + underlying_price: underlying_quote, + current_price: option_price, + strike_price: market_data.strike_price, + volatility: 0.2, + maturity_date: future_date, + risk_free_rate: risk_free_rate.unwrap_or(0.0), + dividend_yield: dividend.unwrap_or(0.0), + transection_price: 0.0, + term_structure: ts, + engine: Engine::BlackScholes, + simulation: None, + style: ContractStyle::European, + valuation_date: today.naive_utc(), + }; + match data.pricer.trim() { + "Analytical" | "analytical" => { + option.engine = Engine::BlackScholes; + } + "MonteCarlo" | "montecarlo" | "MC" => { + option.engine = Engine::MonteCarlo; + } + "Binomial" | "binomial" => { + option.engine = Engine::Binomial; + } + _ => { + panic!("Invalid pricer"); + } + } + match data.style.as_ref().unwrap_or(&"European".to_string()).trim() { + "European" | "european" => { + option.style = ContractStyle::European; + } + "American" | "american" => { + option.style = ContractStyle::American; + } + _ => { + option.style = ContractStyle::European; + } + } + option.set_risk_free_rate(); + println!("{:?}", option); + //option.volatility = option.imp_vol(option.current_price.value); + return Box::new(option); + } +} diff --git a/derivatives/src/equity/vol_surface.rs b/derivatives/src/equity/vol_surface.rs index 4acdb29..582f09e 100644 --- a/derivatives/src/equity/vol_surface.rs +++ b/derivatives/src/equity/vol_surface.rs @@ -1,16 +1,16 @@ -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use crate::rates::utils::{DayCountConvention}; use chrono::{NaiveDate}; #[derive(Clone,Debug)] pub struct VolSurface{ - pub term_structure: HashMap>, + pub term_structure: BTreeMap>, pub spot: f64, pub spot_date: NaiveDate, pub day_count: DayCountConvention, } impl VolSurface { - pub fn new(term_structure: HashMap>,spot:f64,spot_date:NaiveDate,day_count:DayCountConvention) -> VolSurface { + pub fn new(term_structure: BTreeMap>,spot:f64,spot_date:NaiveDate,day_count:DayCountConvention) -> VolSurface { VolSurface { term_structure, spot, @@ -19,10 +19,11 @@ impl VolSurface { } } pub fn get_vol(&self,val_date:NaiveDate,maturity_date:NaiveDate,strike:f64)-> f64{ + //TODO: Interpolate Vol Surface 0.0 } pub fn get_year_fraction(&self,val_date:NaiveDate,maturity_date:NaiveDate) -> f64 { - self.day_count.year_fraction(val_date,maturity_date) + self.day_count.get_year_fraction(val_date,maturity_date) } } \ No newline at end of file diff --git a/derivatives/src/utils/parse_json.rs b/derivatives/src/utils/parse_json.rs index e259919..02c659f 100644 --- a/derivatives/src/utils/parse_json.rs +++ b/derivatives/src/utils/parse_json.rs @@ -24,7 +24,7 @@ use std::env::temp_dir; use crate::rates; use crate::rates::deposits::Deposit; use crate::rates::build_contracts::{build_ir_contracts, build_ir_contracts_from_json, build_term_structure}; -use crate::equity::build_contracts::{build_vol_surface, build_eq_contracts_from_json}; +use crate::equity::build_contracts::{build_volatility_surface, build_eq_contracts_from_json}; pub fn build_curve(mut file: &mut File,output_filename: &str)->() { let mut contents = String::new(); file.read_to_string(&mut contents) @@ -35,7 +35,7 @@ pub fn build_curve(mut file: &mut File,output_filename: &str)->() { } else if list_contracts.asset=="EQ"{ let mut contracts:Vec> = build_eq_contracts_from_json(list_contracts.contracts); - let vol_surface = build_vol_surface(contracts); + let vol_surface = build_volatility_surface(contracts); let mut dir = std::path::PathBuf::from(output_filename); dir.push("vol_surface"); @@ -44,13 +44,9 @@ pub fn build_curve(mut file: &mut File,output_filename: &str)->() { let _ = fs::create_dir(vol_dir); } dir.push("vol_surface.csv"); - let mut file = File::create(dir).expect("Failed to create file"); - let mut output: String = String::new(); - /* dump the hash map in csv format */ - for (k, v) in vol_surface.iter() { - output.push_str(&format!("{},{},{}\n",k,v.0,v.1)); - } - file.write_all(output.as_bytes()).expect("Failed to write to file"); + //Todo write Vol Surface to file + println!("{:?}",vol_surface); + } else if list_contracts.asset=="CO"{ @@ -78,16 +74,7 @@ pub fn build_curve(mut file: &mut File,output_filename: &str)->() { panic!("Asset class not supported"); } } -// pub fn build_contracts(data: utils::Contract) -> Box { -// if data.asset=="IR"{ -// let contract = build_ir_contracts(data); -// return contract; -// -// } -// else { -// panic!("Invalid asset"); -// } -// } + pub fn parse_contract(mut file: &mut File,output_filename: &str) { let mut contents = String::new(); @@ -117,69 +104,9 @@ pub fn process_contract(data: utils::Contract) -> String { if data.action=="PV" && data.asset=="EQ"{ - let market_data = data.market_data.clone().unwrap(); - let sim = market_data.simulation; - let curr_quote = Quote::new(market_data.underlying_price); - - let option_type = &market_data.option_type; - let side: trade::OptionType; - match option_type.trim() { - "C" | "c" | "Call" | "call" => side = trade::OptionType::Call, - "P" | "p" | "Put" | "put" => side = trade::OptionType::Put, - _ => panic!("Invalide side argument! Side has to be either 'C' or 'P'."), - } - let maturity_date = &market_data.maturity; - let today = Local::today(); - let future_date = NaiveDate::parse_from_str(&maturity_date, "%Y-%m-%d").expect("Invalid date format"); - let duration = future_date.signed_duration_since(today.naive_utc()); - let year_fraction = duration.num_days() as f64 / 365.0; - let rf = Some(market_data.risk_free_rate).unwrap(); - let div = Some(market_data.dividend).unwrap(); - let vol = Some(market_data.volatility).unwrap(); - let mut option = EquityOption { - option_type: side, - transection: trade::Transection::Buy, - underlying_price: curr_quote.unwrap(), - current_price: Quote::new(0.0), - strike_price: market_data.strike_price, - volatility: vol.unwrap(), - time_to_maturity: year_fraction, - risk_free_rate: rf.unwrap_or(0.0), - dividend_yield: div.unwrap_or(0.0), - transection_price: 0.0, - term_structure: ts, - engine: Engine::BlackScholes, - simulation: Option::from(sim.unwrap_or(10000)), - style: ContractStyle::European, - //style: Option::from(data.style.as_ref().unwrap_or(&default_style)).map(|x| &**x), - }; - - match data.pricer.trim() { - "Analytical" |"analytical" => { - option.engine = Engine::BlackScholes; - } - "MonteCarlo" |"montecarlo"|"MC" => { - option.engine = Engine::MonteCarlo; - } - "Binomial"|"binomial" => { - option.engine = Engine::Binomial; - } - _ => { - panic!("Invalid pricer");} - } - - match data.style.as_ref().unwrap_or(&"European".to_string()).trim() { - "European" |"european" => { - option.style = ContractStyle::European; - } - "American" |"american" => { - option.style = ContractStyle::American; - } - _ => { - option.style = ContractStyle::European;} - } + //let market_data = data.market_data.clone().unwrap(); + let option = EquityOption::equityoption_from_json(data.clone()); - option.set_risk_free_rate(); 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 }; println!("Theoretical Price ${}", contract_output.pv); println!("Delta ${}", contract_output.delta);