1use optionstratlib_core::model::Positive;
2use optionstratlib_core::utils::TimeFrame;
3use rust_decimal::Decimal;
4use serde::{Deserialize, Serialize};
5use std::fmt::{Display, Formatter};
6
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
9#[cfg_attr(feature = "schema", derive(utoipa::ToSchema))]
10pub enum WalkType {
11 Brownian {
13 dt: Positive,
15 drift: Decimal,
17 volatility: Positive,
19 },
20
21 GeometricBrownian {
23 dt: Positive,
25 drift: Decimal,
27 volatility: Positive,
29 },
30
31 LogReturns {
33 dt: Positive,
35 expected_return: Decimal,
37 volatility: Positive,
39 autocorrelation: Option<Decimal>,
41 },
42
43 MeanReverting {
45 dt: Positive,
47 volatility: Positive,
49 speed: Positive,
51 mean: Positive,
53 },
54
55 JumpDiffusion {
57 dt: Positive,
59 drift: Decimal,
61 volatility: Positive,
63 intensity: Positive,
65 jump_mean: Decimal,
67 jump_volatility: Positive,
69 },
70
71 Garch {
73 dt: Positive,
75 drift: Decimal,
77 volatility: Positive,
79 alpha: Positive,
81 beta: Positive,
83 },
84
85 Heston {
87 dt: Positive,
89 drift: Decimal,
91 volatility: Positive,
93 kappa: Positive,
95 theta: Positive,
97 xi: Positive,
99 rho: Decimal,
101 },
102
103 Custom {
105 dt: Positive,
107 drift: Decimal,
109 volatility: Positive,
111 vov: Positive,
113 vol_speed: Positive,
115 vol_mean: Positive,
117 },
118
119 Telegraph {
121 dt: Positive,
123 drift: Decimal,
125 volatility: Positive,
127 lambda_up: Positive,
129 lambda_down: Positive,
131 vol_multiplier_up: Option<Positive>,
133 vol_multiplier_down: Option<Positive>,
135 },
136
137 Historical {
149 timeframe: TimeFrame,
151 prices: Vec<Positive>,
153
154 symbol: Option<String>,
163 },
164}
165
166#[derive(Debug, Clone)]
176pub struct WalkPath {
177 pub prices: Vec<Positive>,
180
181 pub vols: Option<Vec<Positive>>,
184}
185
186impl WalkType {
187 #[must_use]
196 pub fn volatility(&self) -> Option<Positive> {
197 match self {
198 WalkType::Brownian { volatility, .. }
199 | WalkType::GeometricBrownian { volatility, .. }
200 | WalkType::LogReturns { volatility, .. }
201 | WalkType::MeanReverting { volatility, .. }
202 | WalkType::JumpDiffusion { volatility, .. }
203 | WalkType::Garch { volatility, .. }
204 | WalkType::Heston { volatility, .. }
205 | WalkType::Custom { volatility, .. }
206 | WalkType::Telegraph { volatility, .. } => Some(*volatility),
207 WalkType::Historical { .. } => None,
208 }
209 }
210}
211
212impl Display for WalkType {
213 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
214 match self {
215 WalkType::Brownian {
216 dt,
217 drift,
218 volatility,
219 } => write!(
220 f,
221 "Brownian {{ dt: {dt}, drift: {drift}, volatility: {volatility} }}"
222 ),
223 WalkType::GeometricBrownian {
224 dt,
225 drift,
226 volatility,
227 } => write!(
228 f,
229 "GeometricBrownian {{ dt: {dt}, drift: {drift}, volatility: {volatility} }}"
230 ),
231 WalkType::LogReturns {
232 dt,
233 expected_return,
234 volatility,
235 autocorrelation,
236 } => write!(
237 f,
238 "LogReturns {{ dt: {dt}, expected_return: {expected_return}, volatility: {volatility}, autocorrelation: {autocorrelation:?} }}"
239 ),
240 WalkType::MeanReverting {
241 dt,
242 volatility,
243 speed,
244 mean,
245 } => write!(
246 f,
247 "MeanReverting {{ dt: {dt}, volatility: {volatility}, speed: {speed}, mean: {mean} }}"
248 ),
249 WalkType::JumpDiffusion {
250 dt,
251 drift,
252 volatility,
253 intensity,
254 jump_mean,
255 jump_volatility,
256 } => write!(
257 f,
258 "JumpDiffusion {{ dt: {dt}, drift: {drift}, volatility: {volatility}, intensity: {intensity}, jump_mean: {jump_mean}, jump_volatility: {jump_volatility} }}"
259 ),
260 WalkType::Garch {
261 dt,
262 drift,
263 volatility,
264 alpha,
265 beta,
266 } => write!(
267 f,
268 "Garch {{ dt: {dt}, drift: {drift}, volatility: {volatility}, alpha: {alpha}, beta: {beta} }}"
269 ),
270 WalkType::Heston {
271 dt,
272 drift,
273 volatility,
274 kappa,
275 theta,
276 xi,
277 rho,
278 } => write!(
279 f,
280 "Heston {{ dt: {dt}, drift: {drift}, volatility: {volatility}, kappa: {kappa}, theta: {theta}, xi: {xi}, rho: {rho} }}"
281 ),
282 WalkType::Custom {
283 dt,
284 drift,
285 volatility,
286 vov,
287 vol_speed,
288 vol_mean,
289 } => write!(
290 f,
291 "Custom {{ dt: {dt}, drift: {drift}, volatility: {volatility}, vov: {vov}, vol_speed: {vol_speed}, vol_mean: {vol_mean} }}"
292 ),
293 WalkType::Telegraph {
294 dt,
295 drift,
296 volatility,
297 lambda_up,
298 lambda_down,
299 vol_multiplier_up,
300 vol_multiplier_down,
301 } => write!(
302 f,
303 "Telegraph {{ dt: {dt}, drift: {drift}, volatility: {volatility}, lambda_up: {lambda_up}, lambda_down: {lambda_down}, vol_multiplier_up: {vol_multiplier_up:?}, vol_multiplier_down: {vol_multiplier_down:?} }}"
304 ),
305 WalkType::Historical {
306 timeframe,
307 prices,
308 symbol,
309 } => write!(
310 f,
311 "Historical {{ timeframe: {timeframe}, prices: {prices:?}, symbol: {symbol:?} }}"
312 ),
313 }
314 }
315}
316
317#[cfg(test)]
318mod tests_walk_type {
319 use super::*;
320 use optionstratlib_core::pos_or_panic;
321
322 use rust_decimal_macros::dec;
323
324 #[test]
325 fn test_brownian_creation() {
326 let walk = WalkType::Brownian {
327 dt: pos_or_panic!(0.01),
328 drift: dec!(0.05),
329 volatility: pos_or_panic!(0.2),
330 };
331
332 if let WalkType::Brownian {
333 dt,
334 drift,
335 volatility,
336 } = walk
337 {
338 assert_eq!(dt, pos_or_panic!(0.01));
339 assert_eq!(drift, dec!(0.05));
340 assert_eq!(volatility, pos_or_panic!(0.2));
341 } else {
342 panic!("Expected Brownian variant");
343 }
344 }
345
346 #[test]
347 fn test_geometric_brownian_creation() {
348 let walk = WalkType::GeometricBrownian {
349 dt: pos_or_panic!(0.01),
350 drift: dec!(0.05),
351 volatility: pos_or_panic!(0.2),
352 };
353
354 if let WalkType::GeometricBrownian {
355 dt,
356 drift,
357 volatility,
358 } = walk
359 {
360 assert_eq!(dt, pos_or_panic!(0.01));
361 assert_eq!(drift, dec!(0.05));
362 assert_eq!(volatility, pos_or_panic!(0.2));
363 } else {
364 panic!("Expected GeometricBrownian variant");
365 }
366 }
367
368 #[test]
369 fn test_log_returns_creation() {
370 let walk = WalkType::LogReturns {
371 dt: pos_or_panic!(0.01),
372 expected_return: dec!(0.05),
373 volatility: pos_or_panic!(0.2),
374 autocorrelation: Some(dec!(0.1)),
375 };
376
377 if let WalkType::LogReturns {
378 dt,
379 expected_return,
380 volatility,
381 autocorrelation,
382 } = walk
383 {
384 assert_eq!(dt, pos_or_panic!(0.01));
385 assert_eq!(expected_return, dec!(0.05));
386 assert_eq!(volatility, pos_or_panic!(0.2));
387 assert_eq!(autocorrelation, Some(dec!(0.1)));
388 } else {
389 panic!("Expected LogReturns variant");
390 }
391 }
392
393 #[test]
394 fn test_mean_reverting_creation() {
395 let walk = WalkType::MeanReverting {
396 dt: pos_or_panic!(0.01),
397 volatility: pos_or_panic!(0.2),
398 speed: pos_or_panic!(0.1),
399 mean: Positive::HUNDRED,
400 };
401
402 if let WalkType::MeanReverting {
403 dt,
404 volatility,
405 speed,
406 mean,
407 } = walk
408 {
409 assert_eq!(dt, pos_or_panic!(0.01));
410 assert_eq!(volatility, pos_or_panic!(0.2));
411 assert_eq!(speed, pos_or_panic!(0.1));
412 assert_eq!(mean, Positive::HUNDRED);
413 } else {
414 panic!("Expected MeanReverting variant");
415 }
416 }
417
418 #[test]
419 fn test_jump_diffusion_creation() {
420 let walk = WalkType::JumpDiffusion {
421 dt: pos_or_panic!(0.01),
422 drift: dec!(0.05),
423 volatility: pos_or_panic!(0.2),
424 intensity: Positive::ONE,
425 jump_mean: dec!(-0.05),
426 jump_volatility: pos_or_panic!(0.3),
427 };
428
429 if let WalkType::JumpDiffusion {
430 dt,
431 drift,
432 volatility,
433 intensity,
434 jump_mean,
435 jump_volatility,
436 } = walk
437 {
438 assert_eq!(dt, pos_or_panic!(0.01));
439 assert_eq!(drift, dec!(0.05));
440 assert_eq!(volatility, pos_or_panic!(0.2));
441 assert_eq!(intensity, Positive::ONE);
442 assert_eq!(jump_mean, dec!(-0.05));
443 assert_eq!(jump_volatility, pos_or_panic!(0.3));
444 } else {
445 panic!("Expected JumpDiffusion variant");
446 }
447 }
448
449 #[test]
450 fn test_garch_creation() {
451 let walk = WalkType::Garch {
452 dt: pos_or_panic!(0.01),
453 drift: dec!(0.05),
454 volatility: pos_or_panic!(0.2),
455 alpha: pos_or_panic!(0.1),
456 beta: pos_or_panic!(0.8),
457 };
458
459 if let WalkType::Garch {
460 dt,
461 drift,
462 volatility,
463 alpha,
464 beta,
465 } = walk
466 {
467 assert_eq!(dt, pos_or_panic!(0.01));
468 assert_eq!(drift, dec!(0.05));
469 assert_eq!(volatility, pos_or_panic!(0.2));
470 assert_eq!(alpha, pos_or_panic!(0.1));
471 assert_eq!(beta, pos_or_panic!(0.8));
472 } else {
473 panic!("Expected Garch variant");
474 }
475 }
476
477 #[test]
478 fn test_heston_creation() {
479 let walk = WalkType::Heston {
480 dt: pos_or_panic!(0.01),
481 drift: dec!(0.05),
482 volatility: pos_or_panic!(0.2),
483 kappa: Positive::TWO,
484 theta: pos_or_panic!(0.04),
485 xi: pos_or_panic!(0.3),
486 rho: dec!(-0.7),
487 };
488
489 if let WalkType::Heston {
490 dt,
491 drift,
492 volatility,
493 kappa,
494 theta,
495 xi,
496 rho,
497 } = walk
498 {
499 assert_eq!(dt, pos_or_panic!(0.01));
500 assert_eq!(drift, dec!(0.05));
501 assert_eq!(volatility, pos_or_panic!(0.2));
502 assert_eq!(kappa, Positive::TWO);
503 assert_eq!(theta, pos_or_panic!(0.04));
504 assert_eq!(xi, pos_or_panic!(0.3));
505 assert_eq!(rho, dec!(-0.7));
506 } else {
507 panic!("Expected Heston variant");
508 }
509 }
510
511 #[test]
512 fn test_custom_creation() {
513 let walk = WalkType::Custom {
514 dt: pos_or_panic!(0.01),
515 drift: dec!(0.05),
516 volatility: pos_or_panic!(0.2),
517 vov: pos_or_panic!(0.25),
518 vol_speed: pos_or_panic!(0.15),
519 vol_mean: pos_or_panic!(0.3),
520 };
521
522 if let WalkType::Custom {
523 dt,
524 drift,
525 volatility,
526 vov,
527 vol_speed,
528 vol_mean,
529 } = walk
530 {
531 assert_eq!(dt, pos_or_panic!(0.01));
532 assert_eq!(drift, dec!(0.05));
533 assert_eq!(volatility, pos_or_panic!(0.2));
534 assert_eq!(vov, pos_or_panic!(0.25));
535 assert_eq!(vol_speed, pos_or_panic!(0.15));
536 assert_eq!(vol_mean, pos_or_panic!(0.3));
537 } else {
538 panic!("Expected Custom variant");
539 }
540 }
541
542 #[test]
543 fn test_telegraph_creation() {
544 let walk = WalkType::Telegraph {
545 dt: pos_or_panic!(0.01),
546 drift: dec!(0.05),
547 volatility: pos_or_panic!(0.2),
548 lambda_up: pos_or_panic!(0.5),
549 lambda_down: pos_or_panic!(0.3),
550 vol_multiplier_up: Some(pos_or_panic!(1.2)),
551 vol_multiplier_down: Some(pos_or_panic!(0.8)),
552 };
553
554 if let WalkType::Telegraph {
555 dt,
556 drift,
557 volatility,
558 lambda_up,
559 lambda_down,
560 vol_multiplier_up,
561 vol_multiplier_down,
562 } = walk
563 {
564 assert_eq!(dt, pos_or_panic!(0.01));
565 assert_eq!(drift, dec!(0.05));
566 assert_eq!(volatility, pos_or_panic!(0.2));
567 assert_eq!(lambda_up, pos_or_panic!(0.5));
568 assert_eq!(lambda_down, pos_or_panic!(0.3));
569 assert_eq!(vol_multiplier_up, Some(pos_or_panic!(1.2)));
570 assert_eq!(vol_multiplier_down, Some(pos_or_panic!(0.8)));
571 } else {
572 panic!("Expected Telegraph variant");
573 }
574 }
575
576 #[test]
577 fn test_display_brownian() {
578 let walk = WalkType::Brownian {
579 dt: pos_or_panic!(0.01),
580 drift: dec!(0.05),
581 volatility: pos_or_panic!(0.2),
582 };
583
584 let display = format!("{walk}");
585 assert!(display.contains("Brownian"));
586 assert!(display.contains("dt: 0.01"));
587 assert!(display.contains("drift: 0.05"));
588 assert!(display.contains("volatility: 0.2"));
589 }
590
591 #[test]
592 fn test_display_geometric_brownian() {
593 let walk = WalkType::GeometricBrownian {
594 dt: pos_or_panic!(0.01),
595 drift: dec!(0.05),
596 volatility: pos_or_panic!(0.2),
597 };
598
599 let display = format!("{walk}");
600 assert!(display.contains("GeometricBrownian"));
601 assert!(display.contains("dt: 0.01"));
602 assert!(display.contains("drift: 0.05"));
603 assert!(display.contains("volatility: 0.2"));
604 }
605
606 #[test]
607 fn test_display_log_returns() {
608 let walk = WalkType::LogReturns {
609 dt: pos_or_panic!(0.01),
610 expected_return: dec!(0.05),
611 volatility: pos_or_panic!(0.2),
612 autocorrelation: Some(dec!(0.1)),
613 };
614
615 let display = format!("{walk}");
616 assert!(display.contains("LogReturns"));
617 assert!(display.contains("dt: 0.01"));
618 assert!(display.contains("expected_return: 0.05"));
619 assert!(display.contains("volatility: 0.2"));
620 assert!(display.contains("autocorrelation: Some(0.1)"));
621 }
622
623 #[test]
624 fn test_display_mean_reverting() {
625 let walk = WalkType::MeanReverting {
626 dt: pos_or_panic!(0.01),
627 volatility: pos_or_panic!(0.2),
628 speed: pos_or_panic!(0.1),
629 mean: Positive::HUNDRED,
630 };
631
632 let display = format!("{walk}");
633 assert!(display.contains("MeanReverting"));
634 assert!(display.contains("dt: 0.01"));
635 assert!(display.contains("volatility: 0.2"));
636 assert!(display.contains("speed: 0.1"));
637 assert!(display.contains("mean: 100"));
638 }
639
640 #[test]
641 fn test_display_jump_diffusion() {
642 let walk = WalkType::JumpDiffusion {
643 dt: pos_or_panic!(0.01),
644 drift: dec!(0.05),
645 volatility: pos_or_panic!(0.2),
646 intensity: Positive::ONE,
647 jump_mean: dec!(-0.05),
648 jump_volatility: pos_or_panic!(0.3),
649 };
650
651 let display = format!("{walk}");
652 assert!(display.contains("JumpDiffusion"));
653 assert!(display.contains("dt: 0.01"));
654 assert!(display.contains("drift: 0.05"));
655 assert!(display.contains("volatility: 0.2"));
656 assert!(display.contains("intensity: 1"));
657 assert!(display.contains("jump_mean: -0.05"));
658 assert!(display.contains("jump_volatility: 0.3"));
659 }
660
661 #[test]
662 fn test_display_garch() {
663 let walk = WalkType::Garch {
664 dt: pos_or_panic!(0.01),
665 drift: dec!(0.05),
666 volatility: pos_or_panic!(0.2),
667 alpha: pos_or_panic!(0.1),
668 beta: pos_or_panic!(0.8),
669 };
670
671 let display = format!("{walk}");
672 assert!(display.contains("Garch"));
673 assert!(display.contains("dt: 0.01"));
674 assert!(display.contains("drift: 0.05"));
675 assert!(display.contains("volatility: 0.2"));
676 assert!(display.contains("alpha: 0.1"));
677 assert!(display.contains("beta: 0.8"));
678 }
679
680 #[test]
681 fn test_display_heston() {
682 let walk = WalkType::Heston {
683 dt: pos_or_panic!(0.01),
684 drift: dec!(0.05),
685 volatility: pos_or_panic!(0.2),
686 kappa: Positive::TWO,
687 theta: pos_or_panic!(0.04),
688 xi: pos_or_panic!(0.3),
689 rho: dec!(-0.7),
690 };
691
692 let display = format!("{walk}");
693 assert!(display.contains("Heston"));
694 assert!(display.contains("dt: 0.01"));
695 assert!(display.contains("drift: 0.05"));
696 assert!(display.contains("volatility: 0.2"));
697 assert!(display.contains("kappa: 2"));
698 assert!(display.contains("theta: 0.04"));
699 assert!(display.contains("xi: 0.3"));
700 assert!(display.contains("rho: -0.7"));
701 }
702
703 #[test]
704 fn test_display_custom() {
705 let walk = WalkType::Custom {
706 dt: pos_or_panic!(0.01),
707 drift: dec!(0.05),
708 volatility: pos_or_panic!(0.2),
709 vov: pos_or_panic!(0.25),
710 vol_speed: pos_or_panic!(0.15),
711 vol_mean: pos_or_panic!(0.3),
712 };
713
714 let display = format!("{walk}");
715 assert!(display.contains("Custom"));
716 assert!(display.contains("dt: 0.01"));
717 assert!(display.contains("drift: 0.05"));
718 assert!(display.contains("volatility: 0.2"));
719 assert!(display.contains("vov: 0.25"));
720 assert!(display.contains("vol_speed: 0.15"));
721 assert!(display.contains("vol_mean: 0.3"));
722 }
723
724 #[test]
725 fn test_display_telegraph() {
726 let walk = WalkType::Telegraph {
727 dt: pos_or_panic!(0.01),
728 drift: dec!(0.05),
729 volatility: pos_or_panic!(0.2),
730 lambda_up: pos_or_panic!(0.5),
731 lambda_down: pos_or_panic!(0.3),
732 vol_multiplier_up: Some(pos_or_panic!(1.2)),
733 vol_multiplier_down: Some(pos_or_panic!(0.8)),
734 };
735
736 let display = format!("{walk}");
737 assert!(display.contains("Telegraph"));
738 assert!(display.contains("dt: 0.01"));
739 assert!(display.contains("drift: 0.05"));
740 assert!(display.contains("volatility: 0.2"));
741 assert!(display.contains("lambda_up: 0.5"));
742 assert!(display.contains("lambda_down: 0.3"));
743 assert!(display.contains("vol_multiplier_up: Some(1.2)"));
744 assert!(display.contains("vol_multiplier_down: Some(0.8)"));
745 }
746
747 #[test]
748 fn test_log_returns_without_autocorrelation() {
749 let walk = WalkType::LogReturns {
750 dt: pos_or_panic!(0.01),
751 expected_return: dec!(0.05),
752 volatility: pos_or_panic!(0.2),
753 autocorrelation: None,
754 };
755
756 if let WalkType::LogReturns {
757 dt,
758 expected_return,
759 volatility,
760 autocorrelation,
761 } = walk
762 {
763 assert_eq!(dt, pos_or_panic!(0.01));
764 assert_eq!(expected_return, dec!(0.05));
765 assert_eq!(volatility, pos_or_panic!(0.2));
766 assert_eq!(autocorrelation, None);
767 } else {
768 panic!("Expected LogReturns variant");
769 }
770
771 let display = format!("{walk}");
772 assert!(display.contains("LogReturns"));
773 assert!(display.contains("autocorrelation: None"));
774 }
775
776 #[test]
777 fn test_clone() {
778 let walk = WalkType::Brownian {
779 dt: pos_or_panic!(0.01),
780 drift: dec!(0.05),
781 volatility: pos_or_panic!(0.2),
782 };
783
784 let cloned = walk;
785
786 if let WalkType::Brownian {
787 dt,
788 drift,
789 volatility,
790 } = cloned
791 {
792 assert_eq!(dt, pos_or_panic!(0.01));
793 assert_eq!(drift, dec!(0.05));
794 assert_eq!(volatility, pos_or_panic!(0.2));
795 } else {
796 panic!("Expected Brownian variant");
797 }
798 }
799
800 #[test]
801 fn test_clone_equality() {
802 let walk = WalkType::Brownian {
803 dt: pos_or_panic!(0.01),
804 drift: dec!(0.05),
805 volatility: pos_or_panic!(0.2),
806 };
807
808 let cloned = walk.clone();
809
810 assert_eq!(format!("{walk:?}"), format!("{:?}", cloned));
812 }
813}
814
815#[cfg(test)]
816mod tests_serialize {
817 use super::*;
818 use optionstratlib_core::pos_or_panic;
819
820 use rust_decimal_macros::dec;
821 use serde_json::{from_str, to_string};
822
823 #[test]
824 fn test_brownian_serialization() {
825 let walk_type = WalkType::Brownian {
826 dt: pos_or_panic!(0.0027), drift: dec!(0.05),
828 volatility: pos_or_panic!(0.2),
829 };
830
831 let json = to_string(&walk_type).unwrap();
832
833 assert!(json.contains("\"Brownian\""));
835 assert!(json.contains("\"dt\""));
836 assert!(json.contains("\"drift\""));
837 assert!(json.contains("\"volatility\""));
838
839 assert!(json.contains("0.0027"));
841 assert!(json.contains("0.05"));
842 assert!(json.contains("0.2"));
843
844 let deserialized: WalkType = from_str(&json).unwrap();
846 assert_eq!(walk_type, deserialized);
847 }
848
849 #[test]
850 fn test_geometric_brownian_serialization() {
851 let walk_type = WalkType::GeometricBrownian {
852 dt: pos_or_panic!(0.0192), drift: dec!(0.07),
854 volatility: pos_or_panic!(0.25),
855 };
856
857 let json = to_string(&walk_type).unwrap();
858
859 assert!(json.contains("\"GeometricBrownian\""));
860 assert!(json.contains("\"dt\""));
861 assert!(json.contains("\"drift\""));
862 assert!(json.contains("\"volatility\""));
863
864 assert!(json.contains("0.0192"));
865 assert!(json.contains("0.07"));
866 assert!(json.contains("0.25"));
867
868 let deserialized: WalkType = from_str(&json).unwrap();
869 assert_eq!(walk_type, deserialized);
870 }
871
872 #[test]
873 fn test_log_returns_serialization() {
874 let walk_type = WalkType::LogReturns {
875 dt: pos_or_panic!(0.0833), expected_return: dec!(0.06),
877 volatility: pos_or_panic!(0.18),
878 autocorrelation: Some(dec!(0.1)),
879 };
880
881 let json = to_string(&walk_type).unwrap();
882
883 assert!(json.contains("\"LogReturns\""));
884 assert!(json.contains("\"dt\""));
885 assert!(json.contains("\"expected_return\""));
886 assert!(json.contains("\"volatility\""));
887 assert!(json.contains("\"autocorrelation\""));
888
889 assert!(json.contains("0.0833"));
890 assert!(json.contains("0.06"));
891 assert!(json.contains("0.18"));
892 assert!(json.contains("0.1"));
893
894 let deserialized: WalkType = from_str(&json).unwrap();
895 assert_eq!(walk_type, deserialized);
896 }
897
898 #[test]
899 fn test_log_returns_with_none_autocorrelation() {
900 let walk_type = WalkType::LogReturns {
901 dt: pos_or_panic!(0.0833),
902 expected_return: dec!(0.06),
903 volatility: pos_or_panic!(0.18),
904 autocorrelation: None,
905 };
906
907 let json = to_string(&walk_type).unwrap();
908
909 assert!(json.contains("\"autocorrelation\":null"));
911
912 let deserialized: WalkType = from_str(&json).unwrap();
913 assert_eq!(walk_type, deserialized);
914 }
915
916 #[test]
917 fn test_mean_reverting_serialization() {
918 let walk_type = WalkType::MeanReverting {
919 dt: pos_or_panic!(0.0027),
920 volatility: pos_or_panic!(0.3),
921 speed: pos_or_panic!(0.5),
922 mean: Positive::HUNDRED,
923 };
924
925 let json = to_string(&walk_type).unwrap();
926
927 assert!(json.contains("\"MeanReverting\""));
928 assert!(json.contains("\"dt\""));
929 assert!(json.contains("\"volatility\""));
930 assert!(json.contains("\"speed\""));
931 assert!(json.contains("\"mean\""));
932
933 assert!(json.contains("0.0027"));
934 assert!(json.contains("0.3"));
935 assert!(json.contains("0.5"));
936 assert!(json.contains("100"));
937
938 let deserialized: WalkType = from_str(&json).unwrap();
939 assert_eq!(walk_type, deserialized);
940 }
941
942 #[test]
943 fn test_jump_diffusion_serialization() {
944 let walk_type = WalkType::JumpDiffusion {
945 dt: pos_or_panic!(0.0027),
946 drift: dec!(0.04),
947 volatility: pos_or_panic!(0.15),
948 intensity: pos_or_panic!(3.0), jump_mean: dec!(-0.05), jump_volatility: pos_or_panic!(0.1),
951 };
952
953 let json = to_string(&walk_type).unwrap();
954
955 assert!(json.contains("\"JumpDiffusion\""));
956 assert!(json.contains("\"dt\""));
957 assert!(json.contains("\"drift\""));
958 assert!(json.contains("\"volatility\""));
959 assert!(json.contains("\"intensity\""));
960 assert!(json.contains("\"jump_mean\""));
961 assert!(json.contains("\"jump_volatility\""));
962
963 assert!(json.contains("0.0027"));
964 assert!(json.contains("0.04"));
965 assert!(json.contains("0.15"));
966 assert!(json.contains("3"));
967 assert!(json.contains("-0.05"));
968 assert!(json.contains("0.1"));
969
970 let deserialized: WalkType = from_str(&json).unwrap();
971 assert_eq!(walk_type, deserialized);
972 }
973
974 #[test]
975 fn test_garch_serialization() {
976 let walk_type = WalkType::Garch {
977 dt: pos_or_panic!(0.0027),
978 drift: dec!(0.03),
979 volatility: pos_or_panic!(0.2),
980 alpha: pos_or_panic!(0.1),
981 beta: pos_or_panic!(0.8),
982 };
983
984 let json = to_string(&walk_type).unwrap();
985
986 assert!(json.contains("\"Garch\""));
987 assert!(json.contains("\"dt\""));
988 assert!(json.contains("\"drift\""));
989 assert!(json.contains("\"volatility\""));
990 assert!(json.contains("\"alpha\""));
991 assert!(json.contains("\"beta\""));
992
993 let deserialized: WalkType = from_str(&json).unwrap();
994 assert_eq!(walk_type, deserialized);
995 }
996
997 #[test]
998 fn test_heston_serialization() {
999 let walk_type = WalkType::Heston {
1000 dt: pos_or_panic!(0.0027),
1001 drift: dec!(0.05),
1002 volatility: pos_or_panic!(0.2),
1003 kappa: pos_or_panic!(1.5),
1004 theta: pos_or_panic!(0.04),
1005 xi: pos_or_panic!(0.3),
1006 rho: dec!(-0.7),
1007 };
1008
1009 let json = to_string(&walk_type).unwrap();
1010
1011 assert!(json.contains("\"Heston\""));
1012 assert!(json.contains("\"dt\""));
1013 assert!(json.contains("\"drift\""));
1014 assert!(json.contains("\"volatility\""));
1015 assert!(json.contains("\"kappa\""));
1016 assert!(json.contains("\"theta\""));
1017 assert!(json.contains("\"xi\""));
1018 assert!(json.contains("\"rho\""));
1019
1020 let deserialized: WalkType = from_str(&json).unwrap();
1021 assert_eq!(walk_type, deserialized);
1022 }
1023
1024 #[test]
1025 fn test_custom_serialization() {
1026 let walk_type = WalkType::Custom {
1027 dt: pos_or_panic!(0.0027),
1028 drift: dec!(0.05),
1029 volatility: pos_or_panic!(0.2),
1030 vov: pos_or_panic!(0.1),
1031 vol_speed: pos_or_panic!(0.5),
1032 vol_mean: pos_or_panic!(0.2),
1033 };
1034
1035 let json = to_string(&walk_type).unwrap();
1036
1037 assert!(json.contains("\"Custom\""));
1038 assert!(json.contains("\"dt\""));
1039 assert!(json.contains("\"drift\""));
1040 assert!(json.contains("\"volatility\""));
1041 assert!(json.contains("\"vov\""));
1042 assert!(json.contains("\"vol_speed\""));
1043 assert!(json.contains("\"vol_mean\""));
1044
1045 let deserialized: WalkType = from_str(&json).unwrap();
1046 assert_eq!(walk_type, deserialized);
1047 }
1048
1049 #[test]
1050 fn test_telegraph_serialization() {
1051 let walk_type = WalkType::Telegraph {
1052 dt: pos_or_panic!(0.0027),
1053 drift: dec!(0.05),
1054 volatility: pos_or_panic!(0.2),
1055 lambda_up: pos_or_panic!(0.5),
1056 lambda_down: pos_or_panic!(0.3),
1057 vol_multiplier_up: Some(pos_or_panic!(1.2)),
1058 vol_multiplier_down: Some(pos_or_panic!(0.8)),
1059 };
1060
1061 let json = to_string(&walk_type).unwrap();
1062
1063 assert!(json.contains("\"Telegraph\""));
1064 assert!(json.contains("\"dt\""));
1065 assert!(json.contains("\"drift\""));
1066 assert!(json.contains("\"volatility\""));
1067 assert!(json.contains("\"lambda_up\""));
1068 assert!(json.contains("\"lambda_down\""));
1069 assert!(json.contains("\"vol_multiplier_up\""));
1070 assert!(json.contains("\"vol_multiplier_down\""));
1071
1072 let deserialized: WalkType = from_str(&json).unwrap();
1073 assert_eq!(walk_type, deserialized);
1074 }
1075
1076 #[test]
1077 fn test_historical_serialization() {
1078 let walk_type = WalkType::Historical {
1079 timeframe: TimeFrame::Day,
1080 prices: vec![
1081 Positive::HUNDRED,
1082 pos_or_panic!(101.5),
1083 pos_or_panic!(99.8),
1084 pos_or_panic!(102.3),
1085 pos_or_panic!(103.1),
1086 ],
1087 symbol: None,
1088 };
1089
1090 let json = to_string(&walk_type).unwrap();
1091
1092 assert!(json.contains("\"Historical\""));
1093 assert!(json.contains("\"timeframe\""));
1094 assert!(json.contains("\"prices\""));
1095 assert!(json.contains("Day"));
1096 assert!(json.contains("100"));
1097 assert!(json.contains("101.5"));
1098 assert!(json.contains("99.8"));
1099 assert!(json.contains("102.3"));
1100 assert!(json.contains("103.1"));
1101
1102 let deserialized: WalkType = from_str(&json).unwrap();
1103 assert_eq!(walk_type, deserialized);
1104 }
1105
1106 #[test]
1107 fn test_deserialize_from_json_string() {
1108 let json = r#"{
1109 "GeometricBrownian": {
1110 "dt": 0.0027,
1111 "drift": 0.06,
1112 "volatility": 0.22
1113 }
1114 }"#;
1115
1116 let walk_type: WalkType = from_str(json).unwrap();
1117
1118 match walk_type {
1119 WalkType::GeometricBrownian {
1120 dt,
1121 drift,
1122 volatility,
1123 } => {
1124 assert_eq!(dt, pos_or_panic!(0.0027));
1125 assert_eq!(drift, dec!(0.06));
1126 assert_eq!(volatility, pos_or_panic!(0.22));
1127 }
1128 _ => panic!("Wrong variant deserialized"),
1129 }
1130 }
1131
1132 #[test]
1133 fn test_negative_decimal_values() {
1134 let walk_type = WalkType::JumpDiffusion {
1135 dt: pos_or_panic!(0.0027),
1136 drift: dec!(-0.03), volatility: pos_or_panic!(0.15),
1138 intensity: Positive::TWO,
1139 jump_mean: dec!(-0.1), jump_volatility: pos_or_panic!(0.05),
1141 };
1142
1143 let json = to_string(&walk_type).unwrap();
1144
1145 assert!(json.contains("-0.03"));
1146 assert!(json.contains("-0.1"));
1147
1148 let deserialized: WalkType = from_str(&json).unwrap();
1149 assert_eq!(walk_type, deserialized);
1150 }
1151}