1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! Volume Weighted Average Price (VWAP) indicator.
use super::{IndicatorError, Result};
/// Calculate Volume Weighted Average Price (VWAP).
///
/// VWAP is the average price weighted by volume. It's commonly used as a trading benchmark.
/// Formula: VWAP = Σ(Typical Price × Volume) / Σ(Volume)
/// where Typical Price = (High + Low + Close) / 3
///
/// # Arguments
///
/// * `highs` - High prices
/// * `lows` - Low prices
/// * `closes` - Close prices
/// * `volumes` - Trading volumes
///
/// # Returns
///
/// Vector of cumulative VWAP values.
///
/// # Example
///
/// ```
/// use finance_query::indicators::vwap;
///
/// let highs = vec![102.0, 104.0, 103.0, 105.0];
/// let lows = vec![100.0, 101.0, 100.5, 102.0];
/// let closes = vec![101.0, 103.0, 102.0, 104.0];
/// let volumes = vec![1000.0, 1200.0, 900.0, 1500.0];
///
/// let result = vwap(&highs, &lows, &closes, &volumes).unwrap();
/// assert_eq!(result.len(), 4);
/// ```
pub fn vwap(
highs: &[f64],
lows: &[f64],
closes: &[f64],
volumes: &[f64],
) -> Result<Vec<Option<f64>>> {
if highs.is_empty() {
return Err(IndicatorError::InsufficientData { need: 1, got: 0 });
}
if highs.len() != lows.len() || highs.len() != closes.len() || highs.len() != volumes.len() {
return Err(IndicatorError::InvalidPeriod(
"All arrays must have the same length".to_string(),
));
}
let mut result = Vec::with_capacity(highs.len());
let mut pv_sum = 0.0;
let mut volume_sum = 0.0;
for i in 0..closes.len() {
// Typical Price = (H + L + C) / 3
let typical_price = (highs[i] + lows[i] + closes[i]) / 3.0;
pv_sum += typical_price * volumes[i];
volume_sum += volumes[i];
if volume_sum > 0.0 {
result.push(Some(pv_sum / volume_sum));
} else {
result.push(None);
}
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vwap_basic() {
let highs = vec![102.0, 104.0, 103.0, 105.0];
let lows = vec![100.0, 101.0, 100.5, 102.0];
let closes = vec![101.0, 103.0, 102.0, 104.0];
let volumes = vec![1000.0, 1200.0, 900.0, 1500.0];
let result = vwap(&highs, &lows, &closes, &volumes).unwrap();
assert_eq!(result.len(), 4);
// All values should exist
for val in &result {
assert!(val.is_some());
}
// VWAP should be reasonable (between low and high ranges)
for vwap_val in result.iter().flatten() {
assert!(vwap_val > &0.0);
}
}
#[test]
fn test_vwap_empty() {
let highs: Vec<f64> = vec![];
let lows: Vec<f64> = vec![];
let closes: Vec<f64> = vec![];
let volumes: Vec<f64> = vec![];
let result = vwap(&highs, &lows, &closes, &volumes);
assert!(result.is_err());
}
#[test]
fn test_vwap_mismatched_lengths() {
let highs = vec![102.0, 104.0];
let lows = vec![100.0];
let closes = vec![101.0, 103.0];
let volumes = vec![1000.0, 1200.0];
let result = vwap(&highs, &lows, &closes, &volumes);
assert!(result.is_err());
}
}