pub struct Timer { /* private fields */ }
Implementations§
Source§impl Timer
impl Timer
Sourcepub fn new() -> Timer
pub fn new() -> Timer
Examples found in repository?
examples/demo.rs (line 45)
20fn run_canonical_benchmark() {
21 let paths = 1_000_000;
22 let steps = 1;
23 let s0 = 100.0;
24 let k = 100.0;
25 let r = 0.01;
26 let sigma = 0.2;
27 let t = 1.0;
28 let seed = 42;
29
30 let cfg = McConfig {
31 paths,
32 steps,
33 s0,
34 r,
35 sigma,
36 t,
37 seed,
38 use_antithetic: true,
39 use_control_variate: true,
40 payoff: Payoff::EuropeanCall { k },
41 greeks: GreeksConfig::NONE,
42 epsilon: None,
43 };
44
45 let mut timer = Timer::new();
46 timer.start();
47 let (price, variance) = mc_price_option_gbm(&cfg).expect("Valid configuration");
48 let elapsed = timer.elapsed_ms() / 1000.0;
49 let paths_per_sec = paths as f64 / elapsed;
50 let stderr = variance.sqrt();
51
52 let current_dir = std::env::current_dir().expect("Failed to get current directory");
53 let output_filename = current_dir.join("bench").join("rust_canonical.csv");
54 let mut file = std::fs::File::create(&output_filename).expect("Could not create file");
55 std::io::Write::write_all(&mut file, b"paths,price,stderr,paths_per_sec\n").expect("Could not write header");
56 let line = format!("{}, {:.8}, {:.6}, {:.6}\n", paths, price, stderr, paths_per_sec);
57 std::io::Write::write_all(&mut file, line.as_bytes()).expect("Could not write data");
58
59 println!("Rust benchmark results written to {}", output_filename.display());
60}
61
62fn run_demo_mode() {
63 println!("Running fast-sde Monte Carlo Demo\n");
64
65 let paths = 100_000;
66 let steps = 252; // For Asian option, need multiple steps
67 let s0 = 100.0;
68 let k = 100.0;
69 let r = 0.01;
70 let sigma = 0.2;
71 let t = 1.0;
72 let h = 120.0; // Barrier level
73
74 let cfg_european_call = McConfig {
75 paths,
76 steps,
77 s0,
78 r,
79 sigma,
80 t,
81 seed: 12345,
82 use_antithetic: true,
83 use_control_variate: false,
84 payoff: Payoff::EuropeanCall { k },
85 greeks: GreeksConfig::DELTA | GreeksConfig::VEGA | GreeksConfig::RHO | GreeksConfig::GAMMA,
86 epsilon: Some(0.001 * s0), // 0.1% of spot for finite difference
87 };
88
89 let cfg_asian_call = McConfig {
90 paths,
91 steps,
92 s0,
93 r,
94 sigma,
95 t,
96 seed: 12345,
97 use_antithetic: true,
98 use_control_variate: false, // Control variate for Asian is more complex, disable for now
99 payoff: Payoff::AsianCall { k },
100 greeks: GreeksConfig::NONE,
101 epsilon: None,
102 };
103
104 let cfg_barrier_call_up_and_out = McConfig {
105 paths,
106 steps,
107 s0,
108 r,
109 sigma,
110 t,
111 seed: 12345,
112 use_antithetic: true,
113 use_control_variate: false, // Control variate for barrier is complex, disable for now
114 payoff: Payoff::BarrierCallUpAndOut { k, h },
115 greeks: GreeksConfig::NONE,
116 epsilon: None,
117 };
118
119 let cfg_barrier_put_up_and_out = McConfig {
120 paths,
121 steps,
122 s0,
123 r,
124 sigma,
125 t,
126 seed: 12345,
127 use_antithetic: true,
128 use_control_variate: false, // Control variate for barrier is complex, disable for now
129 payoff: Payoff::BarrierPutUpAndOut { k, h },
130 greeks: GreeksConfig::NONE,
131 epsilon: None,
132 };
133
134 // --- European Call Pricing ---
135 println!("--- European Call Pricing ---");
136
137 // Benchmark MC Price for European Call
138 let mut timer = Timer::new();
139 timer.start();
140 let (mc_price_european, _variance_european) = mc_price_option_gbm(&cfg_european_call).expect("Valid configuration");
141 let price_time_european = timer.elapsed_ms();
142 println!("MC Price (European Call): {} ({} ms)", mc_price_european, price_time_european);
143 let analytic_price_european = bs_analytic::bs_call_price(cfg_european_call.s0, k, cfg_european_call.r, cfg_european_call.sigma, cfg_european_call.t);
144 println!("Analytic Price (European Call): {}", analytic_price_european);
145 let abs_error_price_european = (mc_price_european - analytic_price_european).abs();
146 let rel_error_price_european = abs_error_price_european / analytic_price_european;
147 println!("Absolute Error (Price): {}", abs_error_price_european);
148 println!("Relative Error (Price): {}\n", rel_error_price_european);
149
150 // Benchmark MC Delta (Pathwise) for European Call
151 timer.start();
152 let mc_delta_european = mc_delta_european_call_gbm_pathwise(&cfg_european_call);
153 let delta_time_european = timer.elapsed_ms();
154 println!("MC Delta (European Call Pathwise): {} ({} ms)", mc_delta_european, delta_time_european);
155 let analytic_delta_european = bs_analytic::bs_call_delta(cfg_european_call.s0, k, cfg_european_call.r, cfg_european_call.sigma, cfg_european_call.t);
156 println!("Analytic Delta (European Call): {}", analytic_delta_european);
157 let abs_error_delta_european = (mc_delta_european - analytic_delta_european).abs();
158 let rel_error_delta_european = abs_error_delta_european / analytic_delta_european;
159 println!("Absolute Error (Delta): {}", abs_error_delta_european);
160 println!("Relative Error (Delta): {}\n", rel_error_delta_european);
161
162 // Benchmark MC Vega (Pathwise) for European Call
163 timer.start();
164 let mc_vega_european = mc_vega_european_call_gbm_pathwise(&cfg_european_call);
165 let vega_time_european = timer.elapsed_ms();
166 println!("MC Vega (European Call Pathwise): {} ({} ms)", mc_vega_european, vega_time_european);
167 let analytic_vega_european = bs_analytic::bs_call_vega(cfg_european_call.s0, k, cfg_european_call.r, cfg_european_call.sigma, cfg_european_call.t);
168 println!("Analytic Vega (European Call): {}", analytic_vega_european);
169 let abs_error_vega_european = (mc_vega_european - analytic_vega_european).abs();
170 let rel_error_vega_european = abs_error_vega_european / analytic_vega_european;
171 println!("Absolute Error (Vega): {}", abs_error_vega_european);
172 println!("Relative Error (Vega): {}\n", rel_error_vega_european);
173
174 // Benchmark MC Rho (Pathwise) for European Call
175 timer.start();
176 let mc_rho_european = mc_rho_european_call_gbm_pathwise(&cfg_european_call);
177 let rho_time_european = timer.elapsed_ms();
178 println!("MC Rho (European Call Pathwise): {} ({} ms)", mc_rho_european, rho_time_european);
179 let analytic_rho_european = bs_analytic::bs_call_rho(cfg_european_call.s0, k, cfg_european_call.r, cfg_european_call.sigma, cfg_european_call.t);
180 println!("Analytic Rho (European Call): {}", analytic_rho_european);
181 let abs_error_rho_european = (mc_rho_european - analytic_rho_european).abs();
182 let rel_error_rho_european = abs_error_rho_european / analytic_rho_european;
183 println!("Absolute Error (Rho): {}", abs_error_rho_european);
184 println!("Relative Error (Rho): {}\n", rel_error_rho_european);
185
186 // Benchmark MC Gamma (Finite Difference) for European Call
187 timer.start();
188 let mc_gamma_european = mc_gamma_european_call_gbm_finite_diff_batched(&cfg_european_call);
189 let gamma_time_european = timer.elapsed_ms();
190 println!("MC Gamma (European Call Finite Diff): {} ({} ms)", mc_gamma_european, gamma_time_european);
191 let analytic_gamma_european = bs_analytic::bs_call_gamma(cfg_european_call.s0, k, cfg_european_call.r, cfg_european_call.sigma, cfg_european_call.t);
192 println!("Analytic Gamma (European Call): {}", analytic_gamma_european);
193 let abs_error_gamma_european = (mc_gamma_european - analytic_gamma_european).abs();
194 let rel_error_gamma_european = abs_error_gamma_european / analytic_gamma_european;
195 println!("Absolute Error (Gamma): {}", abs_error_gamma_european);
196 println!("Relative Error (Gamma): {}\n", rel_error_gamma_european);
197
198 // --- Asian Call Pricing ---
199 println!("--- Asian Call Pricing ---");
200
201 // For Asian option, we need to simulate paths to get average price
202 // No simple analytic solution for Asian option under GBM to compare directly
203
204 let mut timer_asian = Timer::new();
205 timer_asian.start();
206 let (mc_price_asian, _variance_asian) = mc_price_option_gbm(&cfg_asian_call).expect("Valid configuration");
207 let price_time_asian = timer_asian.elapsed_ms();
208 println!("MC Price (Asian Call): {} ({} ms)\n", mc_price_asian, price_time_asian);
209 let elapsed_sec_price_asian = price_time_asian as f64 / 1000.0;
210 println!("Throughput: {:.2} paths/sec\n", cfg_asian_call.paths as f64 / elapsed_sec_price_asian);
211
212 // --- Barrier Call Up and Out Pricing ---
213 println!("--- Barrier Call Up and Out Pricing ---");
214 let mut timer_barrier_call = Timer::new();
215 timer_barrier_call.start();
216 let (mc_price_barrier_call, _variance_barrier_call) = mc_price_option_gbm(&cfg_barrier_call_up_and_out).expect("Valid configuration");
217 let price_time_barrier_call = timer_barrier_call.elapsed_ms();
218 println!("MC Price (Barrier Call Up and Out): {} ({} ms)\n", mc_price_barrier_call, price_time_barrier_call);
219 let elapsed_sec_price_barrier_call = price_time_barrier_call as f64 / 1000.0;
220 println!("Throughput: {:.2} paths/sec\n", cfg_barrier_call_up_and_out.paths as f64 / elapsed_sec_price_barrier_call);
221
222 // --- Barrier Put Up and Out Pricing ---
223 println!("--- Barrier Put Up and Out Pricing ---");
224 let mut timer_barrier_put = Timer::new();
225 timer_barrier_put.start();
226 let (mc_price_barrier_put, _variance_barrier_put) = mc_price_option_gbm(&cfg_barrier_put_up_and_out).expect("Valid configuration");
227 let price_time_barrier_put = timer_barrier_put.elapsed_ms();
228 println!("MC Price (Barrier Put Up and Out): {} ({} ms)\n", mc_price_barrier_put, price_time_barrier_put);
229 let elapsed_sec_price_barrier_put = price_time_barrier_put as f64 / 1000.0;
230 println!("Throughput: {:.2} paths/sec\n", cfg_barrier_put_up_and_out.paths as f64 / elapsed_sec_price_barrier_put);
231
232 // --- CSV Output ---
233 // For CSV output, we need to generate paths with the new Payoff structure
234 let path_data_for_csv: Vec<(f64, f64, f64)> = (0..paths)
235 .into_par_iter()
236 .map(|i| {
237 let mut rng = rng::seed_rng_from_u64(cfg_european_call.seed + i as u64);
238 let mut path_prices = Vec::with_capacity(steps + 1);
239 path_prices.push(s0);
240
241 let mut current_s = s0;
242 let dt = t / steps as f64;
243 let sqrt_dt = dt.sqrt();
244
245 for _ in 0..steps {
246 let z = rng::get_normal_draw(&mut rng);
247 current_s *= ((r - 0.5 * sigma * sigma) * dt + sigma * sqrt_dt * z).exp();
248 path_prices.push(current_s);
249 }
250
251 let payoff_european = cfg_european_call.payoff.calculate(&path_prices);
252 let delta_path_european = if *path_prices.last().unwrap() > k { *path_prices.last().unwrap() / s0 } else { 0.0 };
253 (*path_prices.last().unwrap(), payoff_european, delta_path_european)
254 })
255 .collect();
256
257 let paths_csv_filename = "results/paths.csv";
258 match output::write_paths_to_csv(paths_csv_filename, &path_data_for_csv) {
259 Ok(_) => println!("Path data written to {}", paths_csv_filename),
260 Err(e) => eprintln!("Error writing path data: {}", e),
261 }
262
263 // Collect summary data into owned Strings
264 let mc_price_european_str = mc_price_european.to_string();
265 let analytic_price_european_str = analytic_price_european.to_string();
266 let abs_error_price_european_str = abs_error_price_european.to_string();
267 let rel_error_price_european_str = rel_error_price_european.to_string();
268 let price_time_european_str = price_time_european.to_string();
269
270 let mc_delta_european_str = mc_delta_european.to_string();
271 let analytic_delta_european_str = analytic_delta_european.to_string();
272 let abs_error_delta_european_str = abs_error_delta_european.to_string();
273 let rel_error_delta_european_str = rel_error_delta_european.to_string();
274 let delta_time_european_str = delta_time_european.to_string();
275
276 let mc_price_asian_str = mc_price_asian.to_string();
277 let price_time_asian_str = price_time_asian.to_string();
278
279 let mc_price_barrier_call_str = mc_price_barrier_call.to_string();
280 let price_time_barrier_call_str = price_time_barrier_call.to_string();
281
282 let mc_price_barrier_put_str = mc_price_barrier_put.to_string();
283 let price_time_barrier_put_str = price_time_barrier_put.to_string();
284
285 let summary_data = vec![
286 ("metric", "value"),
287 ("mc_price_european", &mc_price_european_str),
288 ("analytic_price_european", &analytic_price_european_str),
289 ("abs_error_price_european", &abs_error_price_european_str),
290 ("rel_error_price_european", &rel_error_price_european_str),
291 ("price_time_ms_european", &price_time_european_str),
292 ("mc_delta_european", &mc_delta_european_str),
293 ("analytic_delta_european", &analytic_delta_european_str),
294 ("abs_error_delta_european", &abs_error_delta_european_str),
295 ("rel_error_delta_european", &rel_error_delta_european_str),
296 ("delta_time_ms_european", &delta_time_european_str),
297 ("mc_price_asian", &mc_price_asian_str),
298 ("price_time_ms_asian", &price_time_asian_str),
299 ("mc_price_barrier_call", &mc_price_barrier_call_str),
300 ("price_time_ms_barrier_call", &price_time_barrier_call_str),
301 ("mc_price_barrier_put", &mc_price_barrier_put_str),
302 ("price_time_ms_barrier_put", &price_time_barrier_put_str),
303 ];
304
305 // Write summary to CSV
306 let summary_csv_filename = "results/summary.csv";
307 match output::write_summary_to_csv(summary_csv_filename, &summary_data) {
308 Ok(_) => println!("Summary data written to {}", summary_csv_filename),
309 Err(e) => eprintln!("Error writing summary data: {}", e),
310 }
311}
Sourcepub fn start(&mut self)
pub fn start(&mut self)
Examples found in repository?
examples/demo.rs (line 46)
20fn run_canonical_benchmark() {
21 let paths = 1_000_000;
22 let steps = 1;
23 let s0 = 100.0;
24 let k = 100.0;
25 let r = 0.01;
26 let sigma = 0.2;
27 let t = 1.0;
28 let seed = 42;
29
30 let cfg = McConfig {
31 paths,
32 steps,
33 s0,
34 r,
35 sigma,
36 t,
37 seed,
38 use_antithetic: true,
39 use_control_variate: true,
40 payoff: Payoff::EuropeanCall { k },
41 greeks: GreeksConfig::NONE,
42 epsilon: None,
43 };
44
45 let mut timer = Timer::new();
46 timer.start();
47 let (price, variance) = mc_price_option_gbm(&cfg).expect("Valid configuration");
48 let elapsed = timer.elapsed_ms() / 1000.0;
49 let paths_per_sec = paths as f64 / elapsed;
50 let stderr = variance.sqrt();
51
52 let current_dir = std::env::current_dir().expect("Failed to get current directory");
53 let output_filename = current_dir.join("bench").join("rust_canonical.csv");
54 let mut file = std::fs::File::create(&output_filename).expect("Could not create file");
55 std::io::Write::write_all(&mut file, b"paths,price,stderr,paths_per_sec\n").expect("Could not write header");
56 let line = format!("{}, {:.8}, {:.6}, {:.6}\n", paths, price, stderr, paths_per_sec);
57 std::io::Write::write_all(&mut file, line.as_bytes()).expect("Could not write data");
58
59 println!("Rust benchmark results written to {}", output_filename.display());
60}
61
62fn run_demo_mode() {
63 println!("Running fast-sde Monte Carlo Demo\n");
64
65 let paths = 100_000;
66 let steps = 252; // For Asian option, need multiple steps
67 let s0 = 100.0;
68 let k = 100.0;
69 let r = 0.01;
70 let sigma = 0.2;
71 let t = 1.0;
72 let h = 120.0; // Barrier level
73
74 let cfg_european_call = McConfig {
75 paths,
76 steps,
77 s0,
78 r,
79 sigma,
80 t,
81 seed: 12345,
82 use_antithetic: true,
83 use_control_variate: false,
84 payoff: Payoff::EuropeanCall { k },
85 greeks: GreeksConfig::DELTA | GreeksConfig::VEGA | GreeksConfig::RHO | GreeksConfig::GAMMA,
86 epsilon: Some(0.001 * s0), // 0.1% of spot for finite difference
87 };
88
89 let cfg_asian_call = McConfig {
90 paths,
91 steps,
92 s0,
93 r,
94 sigma,
95 t,
96 seed: 12345,
97 use_antithetic: true,
98 use_control_variate: false, // Control variate for Asian is more complex, disable for now
99 payoff: Payoff::AsianCall { k },
100 greeks: GreeksConfig::NONE,
101 epsilon: None,
102 };
103
104 let cfg_barrier_call_up_and_out = McConfig {
105 paths,
106 steps,
107 s0,
108 r,
109 sigma,
110 t,
111 seed: 12345,
112 use_antithetic: true,
113 use_control_variate: false, // Control variate for barrier is complex, disable for now
114 payoff: Payoff::BarrierCallUpAndOut { k, h },
115 greeks: GreeksConfig::NONE,
116 epsilon: None,
117 };
118
119 let cfg_barrier_put_up_and_out = McConfig {
120 paths,
121 steps,
122 s0,
123 r,
124 sigma,
125 t,
126 seed: 12345,
127 use_antithetic: true,
128 use_control_variate: false, // Control variate for barrier is complex, disable for now
129 payoff: Payoff::BarrierPutUpAndOut { k, h },
130 greeks: GreeksConfig::NONE,
131 epsilon: None,
132 };
133
134 // --- European Call Pricing ---
135 println!("--- European Call Pricing ---");
136
137 // Benchmark MC Price for European Call
138 let mut timer = Timer::new();
139 timer.start();
140 let (mc_price_european, _variance_european) = mc_price_option_gbm(&cfg_european_call).expect("Valid configuration");
141 let price_time_european = timer.elapsed_ms();
142 println!("MC Price (European Call): {} ({} ms)", mc_price_european, price_time_european);
143 let analytic_price_european = bs_analytic::bs_call_price(cfg_european_call.s0, k, cfg_european_call.r, cfg_european_call.sigma, cfg_european_call.t);
144 println!("Analytic Price (European Call): {}", analytic_price_european);
145 let abs_error_price_european = (mc_price_european - analytic_price_european).abs();
146 let rel_error_price_european = abs_error_price_european / analytic_price_european;
147 println!("Absolute Error (Price): {}", abs_error_price_european);
148 println!("Relative Error (Price): {}\n", rel_error_price_european);
149
150 // Benchmark MC Delta (Pathwise) for European Call
151 timer.start();
152 let mc_delta_european = mc_delta_european_call_gbm_pathwise(&cfg_european_call);
153 let delta_time_european = timer.elapsed_ms();
154 println!("MC Delta (European Call Pathwise): {} ({} ms)", mc_delta_european, delta_time_european);
155 let analytic_delta_european = bs_analytic::bs_call_delta(cfg_european_call.s0, k, cfg_european_call.r, cfg_european_call.sigma, cfg_european_call.t);
156 println!("Analytic Delta (European Call): {}", analytic_delta_european);
157 let abs_error_delta_european = (mc_delta_european - analytic_delta_european).abs();
158 let rel_error_delta_european = abs_error_delta_european / analytic_delta_european;
159 println!("Absolute Error (Delta): {}", abs_error_delta_european);
160 println!("Relative Error (Delta): {}\n", rel_error_delta_european);
161
162 // Benchmark MC Vega (Pathwise) for European Call
163 timer.start();
164 let mc_vega_european = mc_vega_european_call_gbm_pathwise(&cfg_european_call);
165 let vega_time_european = timer.elapsed_ms();
166 println!("MC Vega (European Call Pathwise): {} ({} ms)", mc_vega_european, vega_time_european);
167 let analytic_vega_european = bs_analytic::bs_call_vega(cfg_european_call.s0, k, cfg_european_call.r, cfg_european_call.sigma, cfg_european_call.t);
168 println!("Analytic Vega (European Call): {}", analytic_vega_european);
169 let abs_error_vega_european = (mc_vega_european - analytic_vega_european).abs();
170 let rel_error_vega_european = abs_error_vega_european / analytic_vega_european;
171 println!("Absolute Error (Vega): {}", abs_error_vega_european);
172 println!("Relative Error (Vega): {}\n", rel_error_vega_european);
173
174 // Benchmark MC Rho (Pathwise) for European Call
175 timer.start();
176 let mc_rho_european = mc_rho_european_call_gbm_pathwise(&cfg_european_call);
177 let rho_time_european = timer.elapsed_ms();
178 println!("MC Rho (European Call Pathwise): {} ({} ms)", mc_rho_european, rho_time_european);
179 let analytic_rho_european = bs_analytic::bs_call_rho(cfg_european_call.s0, k, cfg_european_call.r, cfg_european_call.sigma, cfg_european_call.t);
180 println!("Analytic Rho (European Call): {}", analytic_rho_european);
181 let abs_error_rho_european = (mc_rho_european - analytic_rho_european).abs();
182 let rel_error_rho_european = abs_error_rho_european / analytic_rho_european;
183 println!("Absolute Error (Rho): {}", abs_error_rho_european);
184 println!("Relative Error (Rho): {}\n", rel_error_rho_european);
185
186 // Benchmark MC Gamma (Finite Difference) for European Call
187 timer.start();
188 let mc_gamma_european = mc_gamma_european_call_gbm_finite_diff_batched(&cfg_european_call);
189 let gamma_time_european = timer.elapsed_ms();
190 println!("MC Gamma (European Call Finite Diff): {} ({} ms)", mc_gamma_european, gamma_time_european);
191 let analytic_gamma_european = bs_analytic::bs_call_gamma(cfg_european_call.s0, k, cfg_european_call.r, cfg_european_call.sigma, cfg_european_call.t);
192 println!("Analytic Gamma (European Call): {}", analytic_gamma_european);
193 let abs_error_gamma_european = (mc_gamma_european - analytic_gamma_european).abs();
194 let rel_error_gamma_european = abs_error_gamma_european / analytic_gamma_european;
195 println!("Absolute Error (Gamma): {}", abs_error_gamma_european);
196 println!("Relative Error (Gamma): {}\n", rel_error_gamma_european);
197
198 // --- Asian Call Pricing ---
199 println!("--- Asian Call Pricing ---");
200
201 // For Asian option, we need to simulate paths to get average price
202 // No simple analytic solution for Asian option under GBM to compare directly
203
204 let mut timer_asian = Timer::new();
205 timer_asian.start();
206 let (mc_price_asian, _variance_asian) = mc_price_option_gbm(&cfg_asian_call).expect("Valid configuration");
207 let price_time_asian = timer_asian.elapsed_ms();
208 println!("MC Price (Asian Call): {} ({} ms)\n", mc_price_asian, price_time_asian);
209 let elapsed_sec_price_asian = price_time_asian as f64 / 1000.0;
210 println!("Throughput: {:.2} paths/sec\n", cfg_asian_call.paths as f64 / elapsed_sec_price_asian);
211
212 // --- Barrier Call Up and Out Pricing ---
213 println!("--- Barrier Call Up and Out Pricing ---");
214 let mut timer_barrier_call = Timer::new();
215 timer_barrier_call.start();
216 let (mc_price_barrier_call, _variance_barrier_call) = mc_price_option_gbm(&cfg_barrier_call_up_and_out).expect("Valid configuration");
217 let price_time_barrier_call = timer_barrier_call.elapsed_ms();
218 println!("MC Price (Barrier Call Up and Out): {} ({} ms)\n", mc_price_barrier_call, price_time_barrier_call);
219 let elapsed_sec_price_barrier_call = price_time_barrier_call as f64 / 1000.0;
220 println!("Throughput: {:.2} paths/sec\n", cfg_barrier_call_up_and_out.paths as f64 / elapsed_sec_price_barrier_call);
221
222 // --- Barrier Put Up and Out Pricing ---
223 println!("--- Barrier Put Up and Out Pricing ---");
224 let mut timer_barrier_put = Timer::new();
225 timer_barrier_put.start();
226 let (mc_price_barrier_put, _variance_barrier_put) = mc_price_option_gbm(&cfg_barrier_put_up_and_out).expect("Valid configuration");
227 let price_time_barrier_put = timer_barrier_put.elapsed_ms();
228 println!("MC Price (Barrier Put Up and Out): {} ({} ms)\n", mc_price_barrier_put, price_time_barrier_put);
229 let elapsed_sec_price_barrier_put = price_time_barrier_put as f64 / 1000.0;
230 println!("Throughput: {:.2} paths/sec\n", cfg_barrier_put_up_and_out.paths as f64 / elapsed_sec_price_barrier_put);
231
232 // --- CSV Output ---
233 // For CSV output, we need to generate paths with the new Payoff structure
234 let path_data_for_csv: Vec<(f64, f64, f64)> = (0..paths)
235 .into_par_iter()
236 .map(|i| {
237 let mut rng = rng::seed_rng_from_u64(cfg_european_call.seed + i as u64);
238 let mut path_prices = Vec::with_capacity(steps + 1);
239 path_prices.push(s0);
240
241 let mut current_s = s0;
242 let dt = t / steps as f64;
243 let sqrt_dt = dt.sqrt();
244
245 for _ in 0..steps {
246 let z = rng::get_normal_draw(&mut rng);
247 current_s *= ((r - 0.5 * sigma * sigma) * dt + sigma * sqrt_dt * z).exp();
248 path_prices.push(current_s);
249 }
250
251 let payoff_european = cfg_european_call.payoff.calculate(&path_prices);
252 let delta_path_european = if *path_prices.last().unwrap() > k { *path_prices.last().unwrap() / s0 } else { 0.0 };
253 (*path_prices.last().unwrap(), payoff_european, delta_path_european)
254 })
255 .collect();
256
257 let paths_csv_filename = "results/paths.csv";
258 match output::write_paths_to_csv(paths_csv_filename, &path_data_for_csv) {
259 Ok(_) => println!("Path data written to {}", paths_csv_filename),
260 Err(e) => eprintln!("Error writing path data: {}", e),
261 }
262
263 // Collect summary data into owned Strings
264 let mc_price_european_str = mc_price_european.to_string();
265 let analytic_price_european_str = analytic_price_european.to_string();
266 let abs_error_price_european_str = abs_error_price_european.to_string();
267 let rel_error_price_european_str = rel_error_price_european.to_string();
268 let price_time_european_str = price_time_european.to_string();
269
270 let mc_delta_european_str = mc_delta_european.to_string();
271 let analytic_delta_european_str = analytic_delta_european.to_string();
272 let abs_error_delta_european_str = abs_error_delta_european.to_string();
273 let rel_error_delta_european_str = rel_error_delta_european.to_string();
274 let delta_time_european_str = delta_time_european.to_string();
275
276 let mc_price_asian_str = mc_price_asian.to_string();
277 let price_time_asian_str = price_time_asian.to_string();
278
279 let mc_price_barrier_call_str = mc_price_barrier_call.to_string();
280 let price_time_barrier_call_str = price_time_barrier_call.to_string();
281
282 let mc_price_barrier_put_str = mc_price_barrier_put.to_string();
283 let price_time_barrier_put_str = price_time_barrier_put.to_string();
284
285 let summary_data = vec![
286 ("metric", "value"),
287 ("mc_price_european", &mc_price_european_str),
288 ("analytic_price_european", &analytic_price_european_str),
289 ("abs_error_price_european", &abs_error_price_european_str),
290 ("rel_error_price_european", &rel_error_price_european_str),
291 ("price_time_ms_european", &price_time_european_str),
292 ("mc_delta_european", &mc_delta_european_str),
293 ("analytic_delta_european", &analytic_delta_european_str),
294 ("abs_error_delta_european", &abs_error_delta_european_str),
295 ("rel_error_delta_european", &rel_error_delta_european_str),
296 ("delta_time_ms_european", &delta_time_european_str),
297 ("mc_price_asian", &mc_price_asian_str),
298 ("price_time_ms_asian", &price_time_asian_str),
299 ("mc_price_barrier_call", &mc_price_barrier_call_str),
300 ("price_time_ms_barrier_call", &price_time_barrier_call_str),
301 ("mc_price_barrier_put", &mc_price_barrier_put_str),
302 ("price_time_ms_barrier_put", &price_time_barrier_put_str),
303 ];
304
305 // Write summary to CSV
306 let summary_csv_filename = "results/summary.csv";
307 match output::write_summary_to_csv(summary_csv_filename, &summary_data) {
308 Ok(_) => println!("Summary data written to {}", summary_csv_filename),
309 Err(e) => eprintln!("Error writing summary data: {}", e),
310 }
311}
Sourcepub fn elapsed_ms(&self) -> f64
pub fn elapsed_ms(&self) -> f64
Examples found in repository?
examples/demo.rs (line 48)
20fn run_canonical_benchmark() {
21 let paths = 1_000_000;
22 let steps = 1;
23 let s0 = 100.0;
24 let k = 100.0;
25 let r = 0.01;
26 let sigma = 0.2;
27 let t = 1.0;
28 let seed = 42;
29
30 let cfg = McConfig {
31 paths,
32 steps,
33 s0,
34 r,
35 sigma,
36 t,
37 seed,
38 use_antithetic: true,
39 use_control_variate: true,
40 payoff: Payoff::EuropeanCall { k },
41 greeks: GreeksConfig::NONE,
42 epsilon: None,
43 };
44
45 let mut timer = Timer::new();
46 timer.start();
47 let (price, variance) = mc_price_option_gbm(&cfg).expect("Valid configuration");
48 let elapsed = timer.elapsed_ms() / 1000.0;
49 let paths_per_sec = paths as f64 / elapsed;
50 let stderr = variance.sqrt();
51
52 let current_dir = std::env::current_dir().expect("Failed to get current directory");
53 let output_filename = current_dir.join("bench").join("rust_canonical.csv");
54 let mut file = std::fs::File::create(&output_filename).expect("Could not create file");
55 std::io::Write::write_all(&mut file, b"paths,price,stderr,paths_per_sec\n").expect("Could not write header");
56 let line = format!("{}, {:.8}, {:.6}, {:.6}\n", paths, price, stderr, paths_per_sec);
57 std::io::Write::write_all(&mut file, line.as_bytes()).expect("Could not write data");
58
59 println!("Rust benchmark results written to {}", output_filename.display());
60}
61
62fn run_demo_mode() {
63 println!("Running fast-sde Monte Carlo Demo\n");
64
65 let paths = 100_000;
66 let steps = 252; // For Asian option, need multiple steps
67 let s0 = 100.0;
68 let k = 100.0;
69 let r = 0.01;
70 let sigma = 0.2;
71 let t = 1.0;
72 let h = 120.0; // Barrier level
73
74 let cfg_european_call = McConfig {
75 paths,
76 steps,
77 s0,
78 r,
79 sigma,
80 t,
81 seed: 12345,
82 use_antithetic: true,
83 use_control_variate: false,
84 payoff: Payoff::EuropeanCall { k },
85 greeks: GreeksConfig::DELTA | GreeksConfig::VEGA | GreeksConfig::RHO | GreeksConfig::GAMMA,
86 epsilon: Some(0.001 * s0), // 0.1% of spot for finite difference
87 };
88
89 let cfg_asian_call = McConfig {
90 paths,
91 steps,
92 s0,
93 r,
94 sigma,
95 t,
96 seed: 12345,
97 use_antithetic: true,
98 use_control_variate: false, // Control variate for Asian is more complex, disable for now
99 payoff: Payoff::AsianCall { k },
100 greeks: GreeksConfig::NONE,
101 epsilon: None,
102 };
103
104 let cfg_barrier_call_up_and_out = McConfig {
105 paths,
106 steps,
107 s0,
108 r,
109 sigma,
110 t,
111 seed: 12345,
112 use_antithetic: true,
113 use_control_variate: false, // Control variate for barrier is complex, disable for now
114 payoff: Payoff::BarrierCallUpAndOut { k, h },
115 greeks: GreeksConfig::NONE,
116 epsilon: None,
117 };
118
119 let cfg_barrier_put_up_and_out = McConfig {
120 paths,
121 steps,
122 s0,
123 r,
124 sigma,
125 t,
126 seed: 12345,
127 use_antithetic: true,
128 use_control_variate: false, // Control variate for barrier is complex, disable for now
129 payoff: Payoff::BarrierPutUpAndOut { k, h },
130 greeks: GreeksConfig::NONE,
131 epsilon: None,
132 };
133
134 // --- European Call Pricing ---
135 println!("--- European Call Pricing ---");
136
137 // Benchmark MC Price for European Call
138 let mut timer = Timer::new();
139 timer.start();
140 let (mc_price_european, _variance_european) = mc_price_option_gbm(&cfg_european_call).expect("Valid configuration");
141 let price_time_european = timer.elapsed_ms();
142 println!("MC Price (European Call): {} ({} ms)", mc_price_european, price_time_european);
143 let analytic_price_european = bs_analytic::bs_call_price(cfg_european_call.s0, k, cfg_european_call.r, cfg_european_call.sigma, cfg_european_call.t);
144 println!("Analytic Price (European Call): {}", analytic_price_european);
145 let abs_error_price_european = (mc_price_european - analytic_price_european).abs();
146 let rel_error_price_european = abs_error_price_european / analytic_price_european;
147 println!("Absolute Error (Price): {}", abs_error_price_european);
148 println!("Relative Error (Price): {}\n", rel_error_price_european);
149
150 // Benchmark MC Delta (Pathwise) for European Call
151 timer.start();
152 let mc_delta_european = mc_delta_european_call_gbm_pathwise(&cfg_european_call);
153 let delta_time_european = timer.elapsed_ms();
154 println!("MC Delta (European Call Pathwise): {} ({} ms)", mc_delta_european, delta_time_european);
155 let analytic_delta_european = bs_analytic::bs_call_delta(cfg_european_call.s0, k, cfg_european_call.r, cfg_european_call.sigma, cfg_european_call.t);
156 println!("Analytic Delta (European Call): {}", analytic_delta_european);
157 let abs_error_delta_european = (mc_delta_european - analytic_delta_european).abs();
158 let rel_error_delta_european = abs_error_delta_european / analytic_delta_european;
159 println!("Absolute Error (Delta): {}", abs_error_delta_european);
160 println!("Relative Error (Delta): {}\n", rel_error_delta_european);
161
162 // Benchmark MC Vega (Pathwise) for European Call
163 timer.start();
164 let mc_vega_european = mc_vega_european_call_gbm_pathwise(&cfg_european_call);
165 let vega_time_european = timer.elapsed_ms();
166 println!("MC Vega (European Call Pathwise): {} ({} ms)", mc_vega_european, vega_time_european);
167 let analytic_vega_european = bs_analytic::bs_call_vega(cfg_european_call.s0, k, cfg_european_call.r, cfg_european_call.sigma, cfg_european_call.t);
168 println!("Analytic Vega (European Call): {}", analytic_vega_european);
169 let abs_error_vega_european = (mc_vega_european - analytic_vega_european).abs();
170 let rel_error_vega_european = abs_error_vega_european / analytic_vega_european;
171 println!("Absolute Error (Vega): {}", abs_error_vega_european);
172 println!("Relative Error (Vega): {}\n", rel_error_vega_european);
173
174 // Benchmark MC Rho (Pathwise) for European Call
175 timer.start();
176 let mc_rho_european = mc_rho_european_call_gbm_pathwise(&cfg_european_call);
177 let rho_time_european = timer.elapsed_ms();
178 println!("MC Rho (European Call Pathwise): {} ({} ms)", mc_rho_european, rho_time_european);
179 let analytic_rho_european = bs_analytic::bs_call_rho(cfg_european_call.s0, k, cfg_european_call.r, cfg_european_call.sigma, cfg_european_call.t);
180 println!("Analytic Rho (European Call): {}", analytic_rho_european);
181 let abs_error_rho_european = (mc_rho_european - analytic_rho_european).abs();
182 let rel_error_rho_european = abs_error_rho_european / analytic_rho_european;
183 println!("Absolute Error (Rho): {}", abs_error_rho_european);
184 println!("Relative Error (Rho): {}\n", rel_error_rho_european);
185
186 // Benchmark MC Gamma (Finite Difference) for European Call
187 timer.start();
188 let mc_gamma_european = mc_gamma_european_call_gbm_finite_diff_batched(&cfg_european_call);
189 let gamma_time_european = timer.elapsed_ms();
190 println!("MC Gamma (European Call Finite Diff): {} ({} ms)", mc_gamma_european, gamma_time_european);
191 let analytic_gamma_european = bs_analytic::bs_call_gamma(cfg_european_call.s0, k, cfg_european_call.r, cfg_european_call.sigma, cfg_european_call.t);
192 println!("Analytic Gamma (European Call): {}", analytic_gamma_european);
193 let abs_error_gamma_european = (mc_gamma_european - analytic_gamma_european).abs();
194 let rel_error_gamma_european = abs_error_gamma_european / analytic_gamma_european;
195 println!("Absolute Error (Gamma): {}", abs_error_gamma_european);
196 println!("Relative Error (Gamma): {}\n", rel_error_gamma_european);
197
198 // --- Asian Call Pricing ---
199 println!("--- Asian Call Pricing ---");
200
201 // For Asian option, we need to simulate paths to get average price
202 // No simple analytic solution for Asian option under GBM to compare directly
203
204 let mut timer_asian = Timer::new();
205 timer_asian.start();
206 let (mc_price_asian, _variance_asian) = mc_price_option_gbm(&cfg_asian_call).expect("Valid configuration");
207 let price_time_asian = timer_asian.elapsed_ms();
208 println!("MC Price (Asian Call): {} ({} ms)\n", mc_price_asian, price_time_asian);
209 let elapsed_sec_price_asian = price_time_asian as f64 / 1000.0;
210 println!("Throughput: {:.2} paths/sec\n", cfg_asian_call.paths as f64 / elapsed_sec_price_asian);
211
212 // --- Barrier Call Up and Out Pricing ---
213 println!("--- Barrier Call Up and Out Pricing ---");
214 let mut timer_barrier_call = Timer::new();
215 timer_barrier_call.start();
216 let (mc_price_barrier_call, _variance_barrier_call) = mc_price_option_gbm(&cfg_barrier_call_up_and_out).expect("Valid configuration");
217 let price_time_barrier_call = timer_barrier_call.elapsed_ms();
218 println!("MC Price (Barrier Call Up and Out): {} ({} ms)\n", mc_price_barrier_call, price_time_barrier_call);
219 let elapsed_sec_price_barrier_call = price_time_barrier_call as f64 / 1000.0;
220 println!("Throughput: {:.2} paths/sec\n", cfg_barrier_call_up_and_out.paths as f64 / elapsed_sec_price_barrier_call);
221
222 // --- Barrier Put Up and Out Pricing ---
223 println!("--- Barrier Put Up and Out Pricing ---");
224 let mut timer_barrier_put = Timer::new();
225 timer_barrier_put.start();
226 let (mc_price_barrier_put, _variance_barrier_put) = mc_price_option_gbm(&cfg_barrier_put_up_and_out).expect("Valid configuration");
227 let price_time_barrier_put = timer_barrier_put.elapsed_ms();
228 println!("MC Price (Barrier Put Up and Out): {} ({} ms)\n", mc_price_barrier_put, price_time_barrier_put);
229 let elapsed_sec_price_barrier_put = price_time_barrier_put as f64 / 1000.0;
230 println!("Throughput: {:.2} paths/sec\n", cfg_barrier_put_up_and_out.paths as f64 / elapsed_sec_price_barrier_put);
231
232 // --- CSV Output ---
233 // For CSV output, we need to generate paths with the new Payoff structure
234 let path_data_for_csv: Vec<(f64, f64, f64)> = (0..paths)
235 .into_par_iter()
236 .map(|i| {
237 let mut rng = rng::seed_rng_from_u64(cfg_european_call.seed + i as u64);
238 let mut path_prices = Vec::with_capacity(steps + 1);
239 path_prices.push(s0);
240
241 let mut current_s = s0;
242 let dt = t / steps as f64;
243 let sqrt_dt = dt.sqrt();
244
245 for _ in 0..steps {
246 let z = rng::get_normal_draw(&mut rng);
247 current_s *= ((r - 0.5 * sigma * sigma) * dt + sigma * sqrt_dt * z).exp();
248 path_prices.push(current_s);
249 }
250
251 let payoff_european = cfg_european_call.payoff.calculate(&path_prices);
252 let delta_path_european = if *path_prices.last().unwrap() > k { *path_prices.last().unwrap() / s0 } else { 0.0 };
253 (*path_prices.last().unwrap(), payoff_european, delta_path_european)
254 })
255 .collect();
256
257 let paths_csv_filename = "results/paths.csv";
258 match output::write_paths_to_csv(paths_csv_filename, &path_data_for_csv) {
259 Ok(_) => println!("Path data written to {}", paths_csv_filename),
260 Err(e) => eprintln!("Error writing path data: {}", e),
261 }
262
263 // Collect summary data into owned Strings
264 let mc_price_european_str = mc_price_european.to_string();
265 let analytic_price_european_str = analytic_price_european.to_string();
266 let abs_error_price_european_str = abs_error_price_european.to_string();
267 let rel_error_price_european_str = rel_error_price_european.to_string();
268 let price_time_european_str = price_time_european.to_string();
269
270 let mc_delta_european_str = mc_delta_european.to_string();
271 let analytic_delta_european_str = analytic_delta_european.to_string();
272 let abs_error_delta_european_str = abs_error_delta_european.to_string();
273 let rel_error_delta_european_str = rel_error_delta_european.to_string();
274 let delta_time_european_str = delta_time_european.to_string();
275
276 let mc_price_asian_str = mc_price_asian.to_string();
277 let price_time_asian_str = price_time_asian.to_string();
278
279 let mc_price_barrier_call_str = mc_price_barrier_call.to_string();
280 let price_time_barrier_call_str = price_time_barrier_call.to_string();
281
282 let mc_price_barrier_put_str = mc_price_barrier_put.to_string();
283 let price_time_barrier_put_str = price_time_barrier_put.to_string();
284
285 let summary_data = vec![
286 ("metric", "value"),
287 ("mc_price_european", &mc_price_european_str),
288 ("analytic_price_european", &analytic_price_european_str),
289 ("abs_error_price_european", &abs_error_price_european_str),
290 ("rel_error_price_european", &rel_error_price_european_str),
291 ("price_time_ms_european", &price_time_european_str),
292 ("mc_delta_european", &mc_delta_european_str),
293 ("analytic_delta_european", &analytic_delta_european_str),
294 ("abs_error_delta_european", &abs_error_delta_european_str),
295 ("rel_error_delta_european", &rel_error_delta_european_str),
296 ("delta_time_ms_european", &delta_time_european_str),
297 ("mc_price_asian", &mc_price_asian_str),
298 ("price_time_ms_asian", &price_time_asian_str),
299 ("mc_price_barrier_call", &mc_price_barrier_call_str),
300 ("price_time_ms_barrier_call", &price_time_barrier_call_str),
301 ("mc_price_barrier_put", &mc_price_barrier_put_str),
302 ("price_time_ms_barrier_put", &price_time_barrier_put_str),
303 ];
304
305 // Write summary to CSV
306 let summary_csv_filename = "results/summary.csv";
307 match output::write_summary_to_csv(summary_csv_filename, &summary_data) {
308 Ok(_) => println!("Summary data written to {}", summary_csv_filename),
309 Err(e) => eprintln!("Error writing summary data: {}", e),
310 }
311}
Auto Trait Implementations§
impl Freeze for Timer
impl RefUnwindSafe for Timer
impl Send for Timer
impl Sync for Timer
impl Unpin for Timer
impl UnwindSafe for Timer
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
Converts
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
Converts
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
Source§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
The inverse inclusion map: attempts to construct
self
from the equivalent element of its
superset. Read moreSource§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
Checks if
self
is actually part of its subset T
(and can be converted to it).Source§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
Use with care! Same as
self.to_subset
but without any property checks. Always succeeds.Source§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
The inclusion map: converts
self
to the equivalent element of its superset.