Skip to main content

rustyqlib/utils/
build_cli.rs

1use 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
81/// Handle the "build" subcommand.
82pub 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    // We measure the time of the operation
87    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    // Save or do something with output_file if needed
94}
95
96/// Handle the "file" subcommand.
97pub 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
108/// Handle the "dir" subcommand.
109pub 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        // Read the directory
118        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                // Construct the corresponding output file path
133                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
147/// Handle the "interactive" subcommand.
148pub 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        // Prompt user
154        print!("> ");
155        io::stdout().flush().expect("Failed to flush stdout");
156
157        // Read user input
158        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
215/// Helper function to measure the time taken by a closure.
216fn 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}