Sample weights by time decay.
(
triple_barrier_events: &[(NaiveDateTime, NaiveDateTime, f64)],
close: &[(NaiveDateTime, f64)],
decay: f64,
)
| 97 | |
| 98 | /// Sample weights by time decay. |
| 99 | pub fn get_weights_by_time_decay( |
| 100 | triple_barrier_events: &[(NaiveDateTime, NaiveDateTime, f64)], |
| 101 | close: &[(NaiveDateTime, f64)], |
| 102 | decay: f64, |
| 103 | ) -> Result<Vec<(NaiveDateTime, f64)>, String> { |
| 104 | if triple_barrier_events |
| 105 | .iter() |
| 106 | .any(|(t_in, t1, _)| t_in.and_utc().timestamp() == 0 || t1.and_utc().timestamp() == 0) |
| 107 | { |
| 108 | return Err("NaN values in triple_barrier_events, delete nans".into()); |
| 109 | } |
| 110 | let close_index: Vec<NaiveDateTime> = close.iter().map(|(ts, _)| *ts).collect(); |
| 111 | |
| 112 | // num concurrent events per bar |
| 113 | let mut conc: Vec<(NaiveDateTime, usize)> = Vec::new(); |
| 114 | for ts in &close_index { |
| 115 | let mut count = 0usize; |
| 116 | for (start, end, _) in triple_barrier_events { |
| 117 | if *ts >= *start && *ts <= *end { |
| 118 | count += 1; |
| 119 | } |
| 120 | } |
| 121 | conc.push((*ts, count.max(1))); |
| 122 | } |
| 123 | |
| 124 | // average uniqueness per event |
| 125 | let mut av_uniqueness = Vec::new(); |
| 126 | for (start, end, _) in triple_barrier_events { |
| 127 | let vals: Vec<f64> = conc |
| 128 | .iter() |
| 129 | .filter(|(ts, _)| *ts >= *start && *ts <= *end) |
| 130 | .map(|(_, c)| 1.0 / (*c as f64)) |
| 131 | .collect(); |
| 132 | let avg = |
| 133 | if vals.is_empty() { 0.0 } else { vals.iter().sum::<f64>() / (vals.len() as f64) }; |
| 134 | av_uniqueness.push((*start, avg)); |
| 135 | } |
| 136 | |
| 137 | // sort by time for decay application |
| 138 | av_uniqueness.sort_by_key(|(ts, _)| *ts); |
| 139 | let mut decay_w: Vec<(NaiveDateTime, f64)> = Vec::new(); |
| 140 | let mut cum = 0.0; |
| 141 | for (ts, val) in &av_uniqueness { |
| 142 | cum += *val; |
| 143 | decay_w.push((*ts, cum)); |
| 144 | } |
| 145 | if let Some((_, last)) = decay_w.last().cloned() { |
| 146 | let denom = last; |
| 147 | let slope = |
| 148 | if decay >= 0.0 { (1.0 - decay) / denom } else { 1.0 / ((decay + 1.0) * denom) }; |
| 149 | let constant = 1.0 - slope * denom; |
| 150 | for (_, w) in decay_w.iter_mut() { |
| 151 | *w = constant + slope * *w; |
| 152 | if *w < 0.0 { |
| 153 | *w = 0.0; |
| 154 | } |
| 155 | } |
| 156 | } |