#[cfg(test)]
mod tests {
use super::super::config::*;
use super::super::algorithm::*;
use crate::core::OHLCV;
use proptest::prelude::*;
use chrono::{TimeZone, Utc};
fn arb_ohlcv() -> impl Strategy<Value = OHLCV> {
(
10.0..200.0, 10.0..200.0, 10.0..200.0, 10.0..200.0, 100u64..10_000u64, ).prop_map(|(open, high, low, close, volume)| {
let (high, low) = if high < low { (low, high) } else { (high, low) };
OHLCV {
timestamp: Utc.timestamp_opt(1_600_000_000, 0).unwrap(),
open,
high,
low,
close,
volume,
}
})
}
proptest! {
#[test]
fn test_kmeans_property_valid_data(
(k, n) in (2usize..6, 10usize..50),
data in prop::collection::vec(arb_ohlcv(), 10..50)
) {
prop_assume!(n <= data.len());
let config = KMeansConfig::builder()
.k(k)
.max_iterations(50)
.tolerance(1e-6)
.build()
.unwrap();
let mut kmeans = KMeans::with_config(config);
let result = kmeans.fit(&data).unwrap();
prop_assert_eq!(result.cluster_assignments.len(), data.len());
prop_assert_eq!(result.n_clusters, k);
prop_assert!(result.cluster_assignments.iter().all(|&c| c < k));
let mut cluster_counts = vec![0; k];
for &c in &result.cluster_assignments {
cluster_counts[c] += 1;
}
prop_assert!(cluster_counts.iter().all(|&count| count > 0));
}
#[test]
fn test_kmeans_property_insufficient_data(
k in 2usize..10,
n in 1usize..5
) {
let data: Vec<OHLCV> = (0..n).map(|_| OHLCV {
timestamp: Utc.timestamp_opt(1_600_000_000, 0).unwrap(),
open: 100.0,
high: 105.0,
low: 98.0,
close: 103.0,
volume: 1000,
}).collect();
let config = KMeansConfig::builder()
.k(k)
.max_iterations(10)
.tolerance(1e-6)
.build()
.unwrap();
let mut kmeans = KMeans::with_config(config);
let result = kmeans.fit(&data);
prop_assert!(result.is_err());
}
}
}