Skip to main content

rustyqlib/utils/
parse_contracts.rs

1use serde::{Deserialize, Serialize};
2use std::fs::File;
3use std::fs;
4use byteorder::{ByteOrder, LittleEndian,BigEndian};
5use std::io::Read;
6use chrono::{Datelike, Local, NaiveDate};
7use crate::core::quotes::Quote;
8use crate::core::termstructure::YieldTermStructure;
9use crate::equity::vanila_option::{EquityOption};
10//use crate::core::utils::{dN, N};
11//use super::vanila_option::{EquityOption};
12use crate::equity::utils::{Engine};
13//use crate::cmdty::cmdty_option::{CmdtyOption};
14use crate::core::trade;
15use crate::cmdty::cmdty_option;
16use crate::core::traits::{Instrument, Rates};
17use crate::core::utils::{Contract,CombinedContract, ContractOutput, Contracts, OutputJson,EngineType};
18use crate::core::utils::ContractStyle;
19use crate::core::traits::Greeks;
20use std::io::Write;
21use std::env::temp_dir;
22//use crate::read_csv::read_ts;
23use crate::rates;
24use crate::rates::deposits::Deposit;
25use crate::rates::build_contracts::{build_ir_contracts, build_ir_contracts_from_json, build_term_structure};
26use crate::equity::build_contracts::{build_eq_contracts_from_json};
27use crate::core::vols::VolSurface;
28use crate::equity::handle_equity_contracts::handle_equity_contract;
29
30use rayon::prelude::*;
31use serde_json::Value;
32use crate::core::serialization::{self, Format};
33/// This function saves the output to a file and returns the path to the file.
34pub fn save_to_file<'a>(output_folder: &'a str, subfolder: &'a str, filename: &'a str, output: &'a str) -> String {
35    let mut dir = std::path::PathBuf::from(output_folder);
36    if subfolder.len() > 0 {
37        dir.push(subfolder);
38    }
39    let _dir = dir.as_path();
40    if !_dir.exists() {
41        let _ = fs::create_dir(_dir);
42    }
43    dir.push(filename);
44    let mut file = File::create(&dir).expect("Failed to create file");
45    file.write_all(output.as_bytes()).expect("Failed to write to file");
46    return dir.as_path().to_str().unwrap().to_string();
47}
48
49/// This function different types of curves such as term structure, volatility surface, etc.
50pub fn build_curve(file: &mut File, output_filename: &str) -> () {
51    let mut contents = String::new();
52    file.read_to_string(&mut contents)
53        .expect("Failed to read curve definition file");
54    let format = Format::detect(&contents);
55    let list_contracts: Contracts = serialization::parse(&contents, format)
56        .unwrap_or_else(|e| panic!("Failed to read {format:?} curve definition: {e}"));
57    if list_contracts.contracts.len() == 0 {
58        panic!("No contracts found in JSON file");
59    }
60    else if list_contracts.asset=="EQ"{
61        println!("Building implied volatility surface");
62        let contracts:Vec<Box<EquityOption>> = build_eq_contracts_from_json(list_contracts.contracts);
63        let vol_surface = crate::equity::vol_surface::build_implied_vol_surface(&contracts)
64            .expect("Failed to build implied vol surface");
65        println!("{}", vol_surface);
66        let vol_value = serde_json::to_value(&vol_surface).unwrap();
67        let serialized_vol_surface =
68            serialization::render_value(&vol_value, format, "vol_surface");
69        let filename = format!("vol_surface.{}", format.extension());
70        let out_dir = save_to_file(output_filename, "vol_surface", &filename, &serialized_vol_surface);
71        println!("Volatility surface saved to {}", out_dir);
72    }
73    else if list_contracts.asset=="CO"{
74        //Todo -build commodity vol surface
75        panic!("Commodity contracts not supported");
76    }
77    else if list_contracts.asset=="IR"{
78        let mut contracts:Vec<Box<dyn Rates>> = build_ir_contracts_from_json(list_contracts.contracts);
79        let ts = build_term_structure(contracts);
80        let mut output: String = String::new();
81        for i in 0..ts.date.len(){
82            output.push_str(&format!("{},{},{}\n",ts.date[i],ts.discount_factor[i],ts.rate[i]));
83        }
84
85        let out_dir = save_to_file(output_filename, "term_structure", "term_structure.csv", &output);
86        println!("Term structure saved to {}", out_dir);
87
88    }
89    else{
90        panic!("Asset class not supported");
91    }
92}
93
94/// Price every contract in a document. The input format is detected from
95/// the content (JSON or XML) and the output format from the output file
96/// extension, defaulting to the input format.
97pub fn parse_contract(file: &mut File, output_filename: &str) {
98    let mut contents = String::new();
99    file.read_to_string(&mut contents)
100        .expect("Failed to read contract file");
101
102    let in_format = Format::detect(&contents);
103    let out_format = Format::from_path(output_filename).unwrap_or(in_format);
104
105    let list_contracts: Contracts = serialization::parse(&contents, in_format)
106        .unwrap_or_else(|e| panic!("Failed to read {in_format:?} contracts: {e}"));
107
108    if list_contracts.contracts.is_empty() {
109        println!("No contracts found in the input document");
110        return;
111    }
112    // parallel processing of each contract using rayon
113    let mut output_vec: Vec<_> = list_contracts.contracts.par_iter().enumerate()
114        .map(|(index,data)| (index,process_contract(data)))
115        .collect();
116    output_vec.sort_by_key(|k| k.0);
117
118    let results: Vec<Value> = output_vec.into_iter().map(|(_,v)| v).collect();
119    let output_str = serialization::render_results(&results, out_format);
120    //Write to file
121    let mut file = File::create(output_filename).expect("Failed to create file");
122    file.write_all(output_str.as_bytes()).expect("Failed to write to file");
123}
124pub fn process_contract(data: &Contract) -> serde_json::Value {
125
126    let date =  vec![0.01,0.02,0.05,0.1,0.5,1.0,2.0,3.0];
127    let rates = vec![0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05];
128    let ts = YieldTermStructure::new(date,rates);
129
130    if data.action=="PV" && data.asset=="EQ"{
131        return handle_equity_contract(data);
132
133    }
134    // else if data.action=="PV" && data.asset=="CO"{
135    //     let market_data = data.market_data.clone().unwrap();
136    //     let curr_quote = Quote::new( market_data.underlying_price);
137    //     let option_type = &market_data.option_type;
138    //     let side: trade::OptionType;
139    //     let option_type = match &market_data.option_type {
140    //         Some(x) => x.clone(),
141    //         None => "".to_string(),
142    //     };
143    //     match option_type.trim() {
144    //         "C" | "c" | "Call" | "call" => side = trade::OptionType::Call,
145    //         "P" | "p" | "Put" | "put" => side = trade:: OptionType::Put,
146    //         _ => panic!("Invalide side argument! Side has to be either 'C' or 'P'."),
147    //     }
148    //     let maturity_date = &market_data.maturity;
149    //     let today = Local::today();
150    //     let future_date = NaiveDate::parse_from_str(&maturity_date, "%Y-%m-%d").expect("Invalid date format");
151    //     let duration = future_date.signed_duration_since(today.naive_utc());
152    //     let year_fraction = duration.num_days() as f64 / 365.0;
153    //     let vol = Some(market_data.volatility).unwrap();
154    //
155    //     let sim = market_data.simulation;
156    //     if data.pricer=="Analytical"{
157    //         let mut option: CmdtyOption = CmdtyOption {
158    //             option_type: side,
159    //             transection: trade::Transection::Buy,
160    //             current_price: curr_quote,
161    //             strike_price: market_data.strike_price.unwrap_or(0.0),
162    //             volatility: vol.unwrap(),
163    //             time_to_maturity: year_fraction,
164    //             transection_price: 0.0,
165    //             term_structure: ts,
166    //             engine: cmdty_option::Engine::Black76,
167    //             simulation: Option::from(sim.unwrap_or(10000)),
168    //             time_to_future_maturity: None,
169    //             risk_free_rate: None
170    //         };
171    //         let contract_output = ContractOutput{pv:option.npv(),delta:option.delta(),gamma:option.gamma(),vega:option.vega(),theta:option.theta(),rho:option.rho(), error: None };
172    //         println!("Theoretical Price ${}", contract_output.pv);
173    //         println!("Delta ${}", contract_output.delta);
174    //         let combined_ = CombinedContract{
175    //             contract: data.clone(),
176    //             output:contract_output
177    //         };
178    //         let output_json = serde_json::to_string(&combined_).expect("Failed to generate output");
179    //         return output_json;
180    //
181    //
182    //     }
183    //
184    // }
185    else if data.action=="PV" && data.asset=="IR"{
186        //println!("Processing {:?}",data);
187        let rate_data = data.rate_data.clone().unwrap();
188        let mut start_date_str = rate_data.start_date; // Only for 0M case
189        let mut maturity_date_str = rate_data.maturity_date;
190        let current_date = Local::now().date_naive();
191        let maturity_date = rates::utils::convert_mm_to_date(maturity_date_str);
192        let start_date = rates::utils::convert_mm_to_date(start_date_str);
193        println!("Maturity Date {:?}",maturity_date);
194        let mut deposit = Deposit {
195            start_date: start_date,
196            maturity_date: maturity_date,
197            valuation_date: current_date,
198            notional: rate_data.notional,
199            fix_rate: rate_data.fix_rate,
200            day_count: rates::utils::DayCountConvention::Act360,
201            business_day_adjustment: 0,
202            term_structure: None
203        };
204        match rate_data.day_count.as_str() {
205            "Act360" |"A360" => {
206                deposit.day_count = rates::utils::DayCountConvention::Act360;
207            }
208            "Act365" |"A365" => {
209                deposit.day_count = rates::utils::DayCountConvention::Act365;
210            }
211            "Thirty360" |"30/360" => {
212                deposit.day_count = rates::utils::DayCountConvention::Thirty360;
213            }
214            _ => {}
215        }
216        let df = deposit.get_discount_factor();
217        println!("Discount Factor {:?}",df);
218        return serde_json::Value::String("Work in progress".to_string());
219    }
220    else{
221        panic!("Invalid action");
222    }
223    return serde_json::Value::String("Invalid Action".to_string());
224
225}