use positive::Positive;
use rust_decimal::Decimal;
use std::fmt::Display;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum FindOptimalSide {
Upper,
Lower,
All,
Range(Positive, Positive),
Deltable(Positive),
Center,
DeltaRange(Decimal, Decimal),
}
impl Display for FindOptimalSide {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FindOptimalSide::Upper => write!(f, "Upper"),
FindOptimalSide::Lower => write!(f, "Lower"),
FindOptimalSide::All => write!(f, "All"),
FindOptimalSide::Range(start, end) => write!(f, "Range: {start} - {end}"),
FindOptimalSide::Deltable(threshold) => write!(f, "Deltable: {threshold}"),
FindOptimalSide::Center => write!(f, "Center"),
FindOptimalSide::DeltaRange(min, max) => write!(f, "DeltaRange: {min} - {max}"),
}
}
}
pub enum OptimizationCriteria {
Ratio,
Area,
}
pub(crate) fn calculate_price_range(
start_price: Positive,
end_price: Positive,
step: Positive,
) -> Vec<Positive> {
let mut range = Vec::new();
let mut current_price = start_price;
range.push(current_price);
while current_price <= end_price {
current_price += step;
range.push(current_price);
}
range
}
#[cfg(test)]
mod tests_strategies_utils {
use super::*;
use approx::assert_relative_eq;
use positive::{Positive, pos_or_panic};
#[test]
fn test_find_optimal_side_variants() {
let upper = FindOptimalSide::Upper;
let lower = FindOptimalSide::Lower;
let all = FindOptimalSide::All;
let range = FindOptimalSide::Range(Positive::HUNDRED, pos_or_panic!(200.0));
assert!(matches!(upper, FindOptimalSide::Upper));
assert!(matches!(lower, FindOptimalSide::Lower));
assert!(matches!(all, FindOptimalSide::All));
assert!(matches!(range, FindOptimalSide::Range(_, _)));
}
#[test]
fn test_optimization_criteria_variants() {
let ratio = OptimizationCriteria::Ratio;
let area = OptimizationCriteria::Area;
assert!(matches!(ratio, OptimizationCriteria::Ratio));
assert!(matches!(area, OptimizationCriteria::Area));
}
#[test]
fn test_calculate_price_range_basic() {
let start = Positive::HUNDRED;
let end = pos_or_panic!(110.0);
let step = Positive::TWO;
let range = calculate_price_range(start, end, step);
assert_eq!(range.len(), 7);
assert_eq!(range[0], Positive::HUNDRED);
assert_eq!(range[1], pos_or_panic!(102.0));
assert_eq!(range[2], pos_or_panic!(104.0));
assert_eq!(range[3], pos_or_panic!(106.0));
assert_eq!(range[4], pos_or_panic!(108.0));
assert_eq!(range[5], pos_or_panic!(110.0));
assert_eq!(range[6], pos_or_panic!(112.0));
}
#[test]
fn test_calculate_price_range_single_step() {
let start = Positive::HUNDRED;
let end = Positive::HUNDRED;
let step = Positive::ONE;
let range = calculate_price_range(start, end, step);
assert_eq!(range.len(), 2);
assert_eq!(range[0], Positive::HUNDRED);
}
#[test]
fn test_calculate_price_range_large_step() {
let start = Positive::HUNDRED;
let end = pos_or_panic!(110.0);
let step = pos_or_panic!(20.0);
let range = calculate_price_range(start, end, step);
assert_eq!(range.len(), 2);
assert_eq!(range[0], Positive::HUNDRED);
}
#[test]
fn test_calculate_price_range_fractional_step() {
let start = Positive::ONE;
let end = Positive::TWO;
let step = pos_or_panic!(0.3);
let range = calculate_price_range(start, end, step);
assert_eq!(range.len(), 5);
assert_relative_eq!(range[0].to_f64(), 1.0, epsilon = 1e-16);
assert_relative_eq!(range[1].to_f64(), 1.3, epsilon = 1e-16);
assert_relative_eq!(range[2].to_f64(), 1.6, epsilon = 1e-16);
assert_relative_eq!(range[3].to_f64(), 1.9, epsilon = 1e-16);
}
#[test]
fn test_calculate_price_range_empty() {
let start = Positive::HUNDRED;
let end = pos_or_panic!(90.0);
let step = Positive::ONE;
let range = calculate_price_range(start, end, step);
assert_eq!(range.len(), 1);
}
}