Skip to main content

kestrel_chartkit/indicator/
params.rs

1//! Typed registry parameter values.
2//!
3//! [`super::registry::build`]/[`super::registry::build_checked`] accept `HashMap<String, f64>`,
4//! which cannot express boolean flags, enum selections, free text, timestamps, timeframes, or
5//! price sources. [`ParamValue`] gives each of those a typed representation;
6//! [`super::registry::build_typed`] validates them and flattens the numeric-compatible ones down
7//! to the existing `f64` parameter map so it can reuse the full validated `build_checked` matching
8//! logic unchanged.
9
10use std::collections::HashMap;
11
12#[cfg(feature = "serde")]
13use serde::{Deserialize, Serialize};
14
15use crate::model::Source;
16use crate::session::SessionConfig;
17use crate::timeframe::Timeframe;
18
19/// A single typed registry parameter value.
20#[derive(Debug, Clone, PartialEq)]
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22pub enum ParamValue {
23    Float(f64),
24    Int(i64),
25    Bool(bool),
26    Enum(String),
27    Text(String),
28    Timestamp(i64),
29    Timeframe(Timeframe),
30    /// A trading session window (see [`crate::session::SessionConfig`]).
31    Session(SessionConfig),
32    /// A provider-neutral instrument/symbol identifier, distinct from free-form [`ParamValue::Text`].
33    Symbol(String),
34    Source(Source),
35}
36
37impl ParamValue {
38    /// Flattens this value to `f64` where that is a lossless, well-defined conversion (numeric
39    /// and boolean values). Returns `None` for values with no meaningful scalar form (`Enum`,
40    /// `Text`, `Timeframe`, `Source`).
41    pub fn as_f64(&self) -> Option<f64> {
42        match self {
43            ParamValue::Float(v) => Some(*v),
44            ParamValue::Int(v) => Some(*v as f64),
45            ParamValue::Bool(v) => Some(if *v { 1.0 } else { 0.0 }),
46            ParamValue::Timestamp(v) => Some(*v as f64),
47            ParamValue::Enum(_)
48            | ParamValue::Text(_)
49            | ParamValue::Timeframe(_)
50            | ParamValue::Session(_)
51            | ParamValue::Symbol(_)
52            | ParamValue::Source(_) => None,
53        }
54    }
55
56    /// A short, stable type name used in error messages.
57    pub fn type_name(&self) -> &'static str {
58        match self {
59            ParamValue::Float(_) => "float",
60            ParamValue::Int(_) => "int",
61            ParamValue::Bool(_) => "bool",
62            ParamValue::Enum(_) => "enum",
63            ParamValue::Text(_) => "text",
64            ParamValue::Timestamp(_) => "timestamp",
65            ParamValue::Timeframe(_) => "timeframe",
66            ParamValue::Session(_) => "session",
67            ParamValue::Symbol(_) => "symbol",
68            ParamValue::Source(_) => "source",
69        }
70    }
71}
72
73impl From<f64> for ParamValue {
74    fn from(value: f64) -> Self {
75        ParamValue::Float(value)
76    }
77}
78
79impl From<bool> for ParamValue {
80    fn from(value: bool) -> Self {
81        ParamValue::Bool(value)
82    }
83}
84
85/// A parameter map accepting typed values instead of only `f64`.
86pub type TypedParams = HashMap<String, ParamValue>;
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    #[test]
93    fn test_as_f64_conversions() {
94        assert_eq!(ParamValue::Float(1.5).as_f64(), Some(1.5));
95        assert_eq!(ParamValue::Int(3).as_f64(), Some(3.0));
96        assert_eq!(ParamValue::Bool(true).as_f64(), Some(1.0));
97        assert_eq!(ParamValue::Bool(false).as_f64(), Some(0.0));
98        assert_eq!(ParamValue::Timestamp(1_000).as_f64(), Some(1_000.0));
99        assert_eq!(ParamValue::Enum("fast".to_string()).as_f64(), None);
100        assert_eq!(ParamValue::Text("note".to_string()).as_f64(), None);
101        assert_eq!(ParamValue::Timeframe(Timeframe::Minute(5)).as_f64(), None);
102        assert_eq!(ParamValue::Source(Source::Close).as_f64(), None);
103        assert_eq!(ParamValue::Session(SessionConfig::default()).as_f64(), None);
104        assert_eq!(ParamValue::Symbol("EURUSD".to_string()).as_f64(), None);
105    }
106}