kestrel_chartkit/evaluation/
exporter.rs1use crate::model::MarketRegime;
2use crate::signal::{CompositeSignal, TriggerAction};
3use std::collections::HashMap;
4
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, PartialEq)]
10#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
11pub struct FeatureRecord {
12 pub timestamp: i64,
13 pub symbol: String,
14 pub regime: MarketRegime,
15 pub indicator_values: HashMap<String, f64>,
16 pub subscores: HashMap<String, f64>,
17 pub trigger: TriggerAction,
18 pub score: f64,
19 pub agreement: f64,
20}
21
22#[derive(Debug, Clone, Default)]
24pub struct FeatureExporter;
25
26impl FeatureExporter {
27 pub fn new() -> Self {
28 Self
29 }
30
31 pub fn export_record(
33 &self,
34 timestamp: i64,
35 symbol: &str,
36 regime: MarketRegime,
37 indicator_values: HashMap<String, f64>,
38 signal: &CompositeSignal,
39 ) -> FeatureRecord {
40 let mut subscores = HashMap::new();
41 for sub in &signal.per_indicator {
42 subscores.insert(sub.indicator.clone(), sub.score);
43 }
44
45 FeatureRecord {
46 timestamp,
47 symbol: symbol.to_string(),
48 regime,
49 indicator_values,
50 subscores,
51 trigger: signal.trigger,
52 score: signal.score,
53 agreement: signal.agreement,
54 }
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61 use crate::signal::PermissionGrade;
62
63 #[test]
64 fn test_feature_exporter() {
65 let exporter = FeatureExporter::new();
66 let signal = CompositeSignal {
67 score: 0.85,
68 direction: crate::signal::SignalDirection::Bullish,
69 permission: PermissionGrade::ClearToTrade,
70 trigger: TriggerAction::Buy,
71 target_zone: None,
72 invalidation_zone: None,
73 setup_duration: None,
74 risk_plan: None,
75 heat_score: 0.5,
76 sr_zones: Vec::new(),
77 per_indicator: Vec::new(),
78 reasons: Vec::new(),
79 explanation: "test".to_string(),
80 agreement: 0.9,
81 regime: MarketRegime::BullishExpansion,
82 };
83
84 let rec = exporter.export_record(
85 1000,
86 "BTCUSD",
87 MarketRegime::BullishExpansion,
88 HashMap::from([("rsi".to_string(), 65.0)]),
89 &signal,
90 );
91
92 assert_eq!(rec.symbol, "BTCUSD");
93 assert_eq!(rec.indicator_values["rsi"], 65.0);
94 }
95}