ta_benchmarks/
catalogue_cases.rs1use fast_ta::inventory::{FunctionGroup, INDICATOR_CATALOGUE};
4use std::collections::BTreeSet;
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8pub enum CaseKind {
9 Sma,
10 Bbands,
11 Rsi,
12 Macd,
13 Atr,
14 Adx,
15 HtDcPhase,
16 CdlDoji,
17 CdlEngulfing,
18 Cdl3WhiteSoldiers,
19 LinearReg,
20 TypPrice,
21 Obv,
22 Sin,
23 Add,
24}
25
26#[derive(Clone, Copy, Debug, PartialEq, Eq)]
28pub struct CaseSpec {
29 pub kind: CaseKind,
30 pub id: &'static str,
31 pub family: &'static str,
32 pub definition: &'static str,
33 pub parameters: &'static str,
34 pub output_kind: &'static str,
35 pub output_arity: usize,
36}
37
38include!(concat!(env!("OUT_DIR"), "/catalogue_cases.rs"));
39
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
42pub struct DefinitionCoverage {
43 pub name: &'static str,
44 pub family: &'static str,
45 pub measured: bool,
46}
47
48#[derive(Clone, Debug, PartialEq, Eq)]
50pub struct MeasurementCoverage {
51 pub implemented_count: usize,
52 pub measured_count: usize,
53 pub unmeasured_count: usize,
54 pub definitions: Vec<DefinitionCoverage>,
55}
56
57impl MeasurementCoverage {
58 pub fn measured_percent(&self) -> f64 {
60 if self.implemented_count == 0 {
61 0.0
62 } else {
63 self.measured_count as f64 * 100.0 / self.implemented_count as f64
64 }
65 }
66
67 pub fn measured_by_family(&self) -> Vec<(&'static str, usize, usize)> {
69 FunctionGroup::ALL
70 .iter()
71 .map(|group| {
72 let family = group.as_str();
73 let implemented = self
74 .definitions
75 .iter()
76 .filter(|definition| definition.family == family)
77 .count();
78 let measured = self
79 .definitions
80 .iter()
81 .filter(|definition| definition.family == family && definition.measured)
82 .count();
83 (family, measured, implemented)
84 })
85 .collect()
86 }
87}
88
89pub fn measurement_coverage() -> Result<MeasurementCoverage, String> {
91 let mut measured_names = BTreeSet::new();
92 for case in MATRIX {
93 if !measured_names.insert(case.id) {
94 return Err(format!("duplicate representative case {:?}", case.id));
95 }
96 let definition = INDICATOR_CATALOGUE.definition(case.id).ok_or_else(|| {
97 format!(
98 "representative case {:?} is not in the Indicator Catalogue",
99 case.id
100 )
101 })?;
102 if !definition.is_implemented() {
103 return Err(format!(
104 "representative case {:?} is not implemented",
105 case.id
106 ));
107 }
108 if definition.group.as_str() != case.family {
109 return Err(format!(
110 "representative case {:?} has family {:?}, expected {:?}",
111 case.id,
112 case.family,
113 definition.group.as_str()
114 ));
115 }
116 }
117
118 let definitions = INDICATOR_CATALOGUE
119 .implemented_definitions()
120 .map(|definition| DefinitionCoverage {
121 name: definition.name,
122 family: definition.group.as_str(),
123 measured: measured_names.contains(definition.name),
124 })
125 .collect::<Vec<_>>();
126 let measured_count = definitions
127 .iter()
128 .filter(|definition| definition.measured)
129 .count();
130 let implemented_count = definitions.len();
131 Ok(MeasurementCoverage {
132 implemented_count,
133 measured_count,
134 unmeasured_count: implemented_count - measured_count,
135 definitions,
136 })
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn representative_cases_are_an_executable_subset_of_the_catalogue() {
145 let coverage = measurement_coverage().unwrap();
146 assert_eq!(coverage.implemented_count, 161);
147 assert_eq!(coverage.measured_count, MATRIX.len());
148 assert_eq!(coverage.unmeasured_count, 161 - MATRIX.len());
149 assert!(coverage
150 .measured_by_family()
151 .iter()
152 .all(|(_, measured, implemented)| *measured > 0 && measured <= implemented));
153 }
154}