rustyqlib/utils/
build_cli.rs1use std::time::Instant;
2use clap::{Arg, ArgMatches, Command};
3use std::fs::File;
4use crate::utils::parse_contracts;
5use std::fs;
6use std::path::Path;
7use std::io;
8use std::io::Write;
9use crate::equity::blackscholes;
10use crate::equity::montecarlo;
11pub fn build_cli() -> Command {
12 Command::new("RustyQLib Quant Library for Option Pricing")
13 .version("0.0.2")
14 .author("Siddharth Singh <siddharth_qs@outlook.com>")
15 .about("Pricing and risk management of financial derivatives")
16 .subcommand(
17 Command::new("build")
18 .about("Building the curve / Vol surface")
19 .arg(
20 Arg::new("input")
21 .short('i')
22 .long("input")
23 .value_name("FILE")
24 .help("Input financial contracts to use in construction")
25 .required(true),
26 )
27 .arg(
28 Arg::new("output")
29 .short('o')
30 .long("output")
31 .value_name("FILE")
32 .help("Output file name")
33 .required(true),
34 ),
35 )
36 .subcommand(
37 Command::new("file")
38 .about("Pricing a single contract")
39 .arg(
40 Arg::new("input")
41 .short('i')
42 .long("input")
43 .value_name("FILE")
44 .help("Pricing a single contract")
45 .required(true),
46 )
47 .arg(
48 Arg::new("output")
49 .short('o')
50 .long("output")
51 .value_name("FILE")
52 .help("Output file name")
53 .required(true),
54 ),
55 )
56 .subcommand(
57 Command::new("dir")
58 .about("Pricing all contracts in a directory")
59 .arg(
60 Arg::new("input")
61 .short('i')
62 .long("input")
63 .value_name("DIR")
64 .help("Pricing all contracts in a directory")
65 .required(true),
66 )
67 .arg(
68 Arg::new("output")
69 .short('o')
70 .long("output")
71 .value_name("DIR")
72 .help("Output priced contracts to a directory")
73 .required(true),
74 ),
75 )
76 .subcommand(
77 Command::new("interactive").about("Interactive mode"),
78 )
79}
80
81pub fn handle_build(matches: &ArgMatches) {
83 let input_file = matches.get_one::<String>("input").unwrap();
84 let output_file = matches.get_one::<String>("output").unwrap();
85
86 measure_time("build_curve", || {
88 let mut file = File::open(input_file).expect("Failed to open JSON file");
89 parse_contracts::build_curve(&mut file, output_file);
90 println!("(Stub) build_curve from {}", input_file);
91 });
92
93 }
95
96pub fn handle_file(matches: &ArgMatches) {
98 let input_file = matches.get_one::<String>("input").unwrap();
99 let output_file = matches.get_one::<String>("output").unwrap();
100
101 measure_time("parse_contract (single file)", || {
102 let mut file = File::open(input_file).expect("Failed to open JSON file");
103 parse_contracts::parse_contract(&mut file, output_file);
104 println!("(Stub) parse_contract from {}", input_file);
105 });
106}
107
108pub fn handle_dir(matches: &ArgMatches) {
110 let input_dir = matches.get_one::<String>("input").unwrap();
111 let output_dir = matches.get_one::<String>("output").unwrap();
112
113 let input_path = Path::new(input_dir);
114 let output_path = Path::new(output_dir);
115
116 measure_time("parse_contract (directory)", || {
117 let files = fs::read_dir(input_path).expect("Failed to read input directory");
119
120 for file_result in files {
121 let dir_entry = file_result.expect("Failed to read entry");
122 let path = dir_entry.path();
123
124 let is_contract_file = path.is_file()
125 && matches!(
126 path.extension().and_then(|s| s.to_str()).map(|e| e.to_lowercase()).as_deref(),
127 Some("json") | Some("xml")
128 );
129 if is_contract_file {
130 let mut file = File::open(&path).expect("Failed to open contract file");
131
132 let output_file_path = output_path.join(
134 path.file_name().expect("Failed to get file name"),
135 );
136
137 parse_contracts::parse_contract(&mut file, output_file_path.to_str().unwrap());
138 println!(
139 "(Stub) parse_contract from {:?} -> {:?}",
140 path, output_file_path
141 );
142 }
143 }
144 });
145}
146
147pub fn handle_interactive() {
149 println!("Welcome to Option pricing CLI");
150 loop {
151 println!("Do you want to price an option (1), calculate implied volatility (2), or exit (3)?");
152
153 print!("> ");
155 io::stdout().flush().expect("Failed to flush stdout");
156
157 let mut input = String::new();
159 io::stdin()
160 .read_line(&mut input)
161 .expect("Failed to read line");
162
163 let selection: u8 = match input.trim().parse() {
164 Ok(num) => num,
165 Err(_) => {
166 eprintln!("Please enter a valid number!");
167 continue;
168 }
169 };
170
171 match selection {
172 1 => {
173 println!("Do you want to use the Black-Scholes (1) or Monte-Carlo (2) model?");
174 print!("> ");
175 io::stdout().flush().expect("Failed to flush stdout");
176
177 let mut model_input = String::new();
178 io::stdin()
179 .read_line(&mut model_input)
180 .expect("Failed to read line");
181
182 let model_num: u8 = match model_input.trim().parse() {
183 Ok(num) => num,
184 Err(_) => {
185 eprintln!("Please enter a valid number!");
186 continue;
187 }
188 };
189
190 match model_num {
191 1 => {
192 blackscholes::option_pricing();
193 println!("(Stub) blackscholes::option_pricing()");
194 }
195 2 => {
196 montecarlo::option_pricing();
197 println!("(Stub) montecarlo::option_pricing()");
198 }
199 _ => println!("You gave a wrong number! Accepted arguments are 1 and 2."),
200 }
201 }
202 2 => {
203 blackscholes::implied_volatility();
204 println!("(Stub) blackscholes::implied_volatility()");
205 }
206 3 => {
207 println!("Exiting interactive mode...");
208 break;
209 }
210 _ => println!("You gave a wrong number! Accepted arguments are 1, 2, or 3."),
211 }
212 }
213}
214
215fn measure_time<F: FnOnce()>(label: &str, f: F) {
217 let start_time = Instant::now();
218 f();
219 let elapsed_time = start_time.elapsed();
220 println!("Time taken for {}: {:?}", label, elapsed_time);
221}