indexes_rs/v2/obv/
main.rs1use crate::v2::obv::types::{OBVConfig, OBVError, OBVInput, OBVOutput, OBVState};
2
3#[derive(Default)]
13pub struct OBV {
14 state: OBVState,
15}
16
17impl OBV {
18 pub fn new() -> Self {
20 Self::with_config(OBVConfig::default())
21 }
22
23 pub fn with_config(config: OBVConfig) -> Self {
25 Self { state: OBVState::new(config) }
26 }
27
28 pub fn calculate(&mut self, input: OBVInput) -> Result<OBVOutput, OBVError> {
30 self.validate_input(&input)?;
32
33 let flow_direction = if self.state.is_first {
34 self.state.previous_close = Some(input.close);
36 self.state.cumulative_obv = input.volume;
37 self.state.is_first = false;
38 0.0
39 } else {
40 let prev_close = self.state.previous_close.unwrap();
41 let direction = self.determine_flow_direction(input.close, prev_close);
42
43 match direction {
45 d if d > 0.0 => {
46 self.state.cumulative_obv += input.volume;
48 }
49 d if d < 0.0 => {
50 self.state.cumulative_obv -= input.volume;
52 }
53 _ => {
54 }
56 }
57
58 self.state.previous_close = Some(input.close);
59 direction
60 };
61
62 Ok(OBVOutput {
63 obv: self.state.cumulative_obv,
64 flow_direction,
65 })
66 }
67
68 pub fn calculate_batch(&mut self, inputs: &[OBVInput]) -> Result<Vec<OBVOutput>, OBVError> {
70 inputs.iter().map(|input| self.calculate(*input)).collect()
71 }
72
73 pub fn reset(&mut self) {
75 self.state = OBVState::new(self.state.config);
76 }
77
78 pub fn get_state(&self) -> &OBVState {
80 &self.state
81 }
82
83 pub fn set_state(&mut self, state: OBVState) {
85 self.state = state;
86 }
87
88 fn validate_input(&self, input: &OBVInput) -> Result<(), OBVError> {
91 if input.volume < 0.0 {
92 return Err(OBVError::NegativeVolume);
93 }
94
95 if !input.close.is_finite() {
96 return Err(OBVError::InvalidPrice);
97 }
98
99 Ok(())
100 }
101
102 fn determine_flow_direction(&self, current_close: f64, previous_close: f64) -> f64 {
103 if current_close > previous_close {
104 1.0 } else if current_close < previous_close {
106 -1.0 } else {
108 0.0 }
110 }
111}
112
113pub fn calculate_obv_simple(close_prices: &[f64], volumes: &[f64]) -> Result<Vec<f64>, OBVError> {
115 if close_prices.len() != volumes.len() {
116 return Err(OBVError::InvalidInput("Close prices and volumes must have same length".to_string()));
117 }
118
119 if close_prices.is_empty() {
120 return Ok(Vec::new());
121 }
122
123 let mut obv_calculator = OBV::new();
124 let mut results = Vec::with_capacity(close_prices.len());
125
126 for (close, volume) in close_prices.iter().zip(volumes.iter()) {
127 let input = OBVInput { close: *close, volume: *volume };
128 let output = obv_calculator.calculate(input)?;
129 results.push(output.obv);
130 }
131
132 Ok(results)
133}