MCPcopy Create free account
hub / github.com/Open-Quant/openquant / average_holding_period

Function average_holding_period

crates/openquant/src/backtest_statistics.rs:34–74  ·  view source on GitHub ↗
(target_positions: &[(NaiveDateTime, f64)])

Source from the content-addressed store, hash-verified

32//! let hurdle = deflated_sharpe_ratio(0.1, &[0.05, 50.0], 500, 0.0, 3.0, true, true)?;
33//! assert!((hurdle - 0.1138).abs() < 1e-4);
34//!
35//! let min_trl = minimum_track_record_length(0.1, 0.0, 0.0, 3.0, 0.05)?;
36//! assert!((min_trl - 272.9).abs() < 0.1);
37//! # Ok(())
38//! # }
39//! ```
40
41use crate::util::stats;
42use crate::util::InputError;
43use chrono::NaiveDateTime;
44use statrs::distribution::{ContinuousCDF, Normal};
45
46const EULER_GAMMA: f64 = 0.5772156649015329_f64;
47
48/// Returns the timestamps at which a position was closed or reversed (AFML Snippet 14.1).
49///
50/// A flattening is a bar where the position goes from non-zero to zero; a flip is a bar
51/// where the position changes sign. The result is sorted, deduplicated, and always ends with
52/// the last timestamp of the input (so an open position is treated as closed there). An
53/// empty input gives an empty result.
54///
55/// `target_positions` is `(timestamp, position)` pairs in increasing time.
56pub fn timing_of_flattening_and_flips(
57 target_positions: &[(NaiveDateTime, f64)],
58) -> Vec<NaiveDateTime> {
59 let mut flattenings = Vec::new();
60 let mut flips = Vec::new();
61 for i in 1..target_positions.len() {
62 let prev = target_positions[i - 1].1;
63 let curr = target_positions[i].1;
64 if curr == 0.0 && prev != 0.0 {
65 flattenings.push(target_positions[i].0);
66 }
67 let mult = curr * prev;
68 if mult < 0.0 {
69 flips.push(target_positions[i].0);
70 }
71 }
72 let mut res = flattenings;
73 res.extend(flips);
74 res.sort();
75 res.dedup();
76 if let Some(last) = target_positions.last()
77 && !res.contains(&last.0)

Callers 2

Calls 2

is_emptyMethod · 0.80
lenMethod · 0.80

Tested by

no test coverage detected