use crate::indicators::*;
use crate::errors::Result;
use chrono::{DateTime, FixedOffset, TimeZone};
use std::collections::VecDeque;
#[test]
fn test_funding_direction_from_rate_standard() {
let direction = FundingDirection::from_rate(0.0001);
assert_eq!(direction, FundingDirection::Positive);
let direction = FundingDirection::from_rate(-0.0001);
assert_eq!(direction, FundingDirection::Negative);
let direction = FundingDirection::from_rate(0.0);
assert_eq!(direction, FundingDirection::Neutral);
}
#[test]
fn test_funding_volatility_calculation_standard() {
let rates = vec![0.0001, 0.0001, 0.0001, 0.0001, 0.0001];
let volatility = calculate_funding_volatility(&rates);
assert_eq!(volatility, 0.0);
let rates = vec![0.0001, 0.0002, 0.0003, 0.0002, 0.0001];
let volatility = calculate_funding_volatility(&rates);
assert!(volatility > 0.0); }
#[test]
fn test_funding_momentum_calculation_standard() {
let rates = vec![0.0001, 0.0002, 0.0003, 0.0004, 0.0005];
let momentum = calculate_funding_momentum(&rates);
assert!(momentum > 0.0);
let rates = vec![0.0005, 0.0004, 0.0003, 0.0002, 0.0001];
let momentum = calculate_funding_momentum(&rates);
assert!(momentum < 0.0); }
#[test]
fn test_funding_arbitrage_calculation_standard() {
let funding_rate = 0.0005;
let price = 50000.0;
let opportunity = calculate_funding_arbitrage(funding_rate, price);
assert!(opportunity.is_arbitrage);
assert_eq!(opportunity.direction, FundingDirection::Positive);
let funding_rate = -0.0005;
let price = 50000.0;
let opportunity = calculate_funding_arbitrage(funding_rate, price);
assert!(opportunity.is_arbitrage);
assert_eq!(opportunity.direction, FundingDirection::Negative);
}
#[test]
fn test_basis_indicator_calculation_standard() {
let spot_price = 50000.0;
let futures_price = 50500.0;
let days_to_expiry = 30.0;
let basis = calculate_basis_indicator(spot_price, futures_price, days_to_expiry);
assert!(basis.basis > 0.0); assert!(basis.annualized_basis > 0.0);
let spot_price = 50000.0;
let futures_price = 49500.0;
let days_to_expiry = 30.0;
let basis = calculate_basis_indicator(spot_price, futures_price, days_to_expiry);
assert!(basis.basis < 0.0); assert!(basis.annualized_basis < 0.0);
}
#[test]
fn test_funding_prediction_config() {
let default_config = FundingPredictionConfig::default();
assert_eq!(default_config.lookback_periods, 48);
let sum = default_config.volatility_weight +
default_config.momentum_weight +
default_config.basis_weight +
default_config.correlation_weight;
assert!((sum - 1.0).abs() < 0.0001);
let custom_config = FundingPredictionConfig {
lookback_periods: 24,
volatility_weight: 0.1,
momentum_weight: 0.2,
basis_weight: 0.3,
correlation_weight: 0.4,
};
assert_eq!(custom_config.lookback_periods, 24);
assert_eq!(custom_config.volatility_weight, 0.1);
assert_eq!(custom_config.momentum_weight, 0.2);
assert_eq!(custom_config.basis_weight, 0.3);
assert_eq!(custom_config.correlation_weight, 0.4);
}
#[test]
fn test_funding_rate_predictor() {
let config = FundingPredictionConfig {
lookback_periods: 10,
volatility_weight: 0.25,
momentum_weight: 0.25,
basis_weight: 0.25,
correlation_weight: 0.25,
};
let mut predictor = FundingRatePredictor::new(config);
let prediction = predictor.predict();
assert_eq!(prediction.expected_rate, 0.0);
assert_eq!(prediction.direction, FundingDirection::Neutral);
assert_eq!(prediction.confidence, 0.0);
for i in 0..10 {
predictor.add_observation(0.0001 * i as f64);
}
let prediction = predictor.predict();
assert!(prediction.expected_rate > 0.0009); assert_eq!(prediction.direction, FundingDirection::Positive);
assert!(prediction.confidence > 0.5);
let volatility = predictor.get_volatility();
assert!(volatility > 0.0);
let momentum = predictor.get_momentum();
assert!(momentum > 0.0);
let mut predictor = FundingRatePredictor::new(config);
for i in 0..10 {
predictor.add_observation(0.001 - 0.0001 * i as f64);
}
let prediction = predictor.predict();
assert!(prediction.expected_rate < 0.0001); assert_eq!(prediction.direction, FundingDirection::Positive);
let mut predictor = FundingRatePredictor::new(config);
for i in 0..10 {
predictor.add_observation(if i % 2 == 0 { 0.0001 } else { -0.0001 });
}
let prediction = predictor.predict();
assert!(prediction.confidence < 0.7); }
#[test]
fn test_funding_rate_predictor_correlation() {
let config = FundingPredictionConfig::default();
let mut predictor1 = FundingRatePredictor::new(config.clone());
let mut predictor2 = FundingRatePredictor::new(config.clone());
for i in 0..10 {
let rate = 0.0001 * i as f64;
predictor1.add_observation(rate);
predictor2.add_observation(rate);
}
let correlation = predictor1.correlation_with(&predictor1);
assert!((correlation - 1.0).abs() < 0.0001);
let correlation = predictor1.correlation_with(&predictor2);
assert!((correlation - 1.0).abs() < 0.0001);
let mut predictor3 = FundingRatePredictor::new(config);
for i in 0..10 {
let rate = 0.0001 * (9 - i) as f64;
predictor3.add_observation(rate);
}
let correlation = predictor1.correlation_with(&predictor3);
assert!(correlation < 0.0);
let predictor4 = FundingRatePredictor::new(config);
let correlation = predictor1.correlation_with(&predictor4);
assert_eq!(correlation, 0.0);
}
#[test]
fn test_funding_rate_predictor_anomaly_detection() {
let config = FundingPredictionConfig {
lookback_periods: 10,
volatility_weight: 0.25,
momentum_weight: 0.25,
basis_weight: 0.25,
correlation_weight: 0.25,
};
let mut predictor = FundingRatePredictor::new(config);
for _ in 0..9 {
predictor.add_observation(0.0001);
}
predictor.add_observation(0.0001);
let anomaly = predictor.detect_anomaly();
assert!(!anomaly.is_anomaly);
predictor.add_observation(0.0002);
let anomaly = predictor.detect_anomaly();
assert!(!anomaly.is_anomaly);
predictor.add_observation(0.001); let anomaly = predictor.detect_anomaly();
assert!(anomaly.is_anomaly);
assert!(anomaly.deviation > 3.0); assert_eq!(anomaly.direction, FundingDirection::Positive);
predictor.add_observation(-0.001); let anomaly = predictor.detect_anomaly();
assert!(anomaly.is_anomaly);
assert!(anomaly.deviation > 3.0);
assert_eq!(anomaly.direction, FundingDirection::Negative);
}
#[test]
fn test_funding_rate_predictor_cycle_detection() {
let config = FundingPredictionConfig {
lookback_periods: 24,
volatility_weight: 0.25,
momentum_weight: 0.25,
basis_weight: 0.25,
correlation_weight: 0.25,
};
let mut predictor = FundingRatePredictor::new(config);
for i in 0..24 {
let hour = i % 8;
let rate = match hour {
0 => 0.0003, 1 => 0.0002,
2 => 0.0001,
3 => 0.0,
4 => -0.0001, 5 => 0.0,
6 => 0.0001,
7 => 0.0002,
_ => unreachable!(),
};
predictor.add_observation(rate);
}
let cycle = predictor.detect_funding_cycle();
assert_eq!(cycle.period_hours, 8);
assert!(cycle.strength > 0.5); assert!(cycle.is_significant);
let mut predictor = FundingRatePredictor::new(config);
for i in 0..24 {
let rate = 0.0001 * ((i as f64 * 0.123).sin() + (i as f64 * 0.456).cos());
predictor.add_observation(rate);
}
let cycle = predictor.detect_funding_cycle();
assert!(cycle.strength < 0.7); }
#[test]
fn test_open_interest_change() {
let prev_oi = 1000.0;
let curr_oi = 1100.0;
let price = 50000.0;
let change = OpenInterestChange::new(prev_oi, curr_oi, price);
assert_eq!(change.absolute_change, 100.0);
assert_eq!(change.percentage_change, 0.1); assert_eq!(change.usd_value_change, 100.0 * 50000.0);
assert!(change.is_increasing);
assert!(!change.is_decreasing);
let prev_oi = 1000.0;
let curr_oi = 900.0;
let price = 50000.0;
let change = OpenInterestChange::new(prev_oi, curr_oi, price);
assert_eq!(change.absolute_change, -100.0);
assert_eq!(change.percentage_change, -0.1); assert_eq!(change.usd_value_change, -100.0 * 50000.0);
assert!(!change.is_increasing);
assert!(change.is_decreasing);
let prev_oi = 1000.0;
let curr_oi = 1000.0;
let price = 50000.0;
let change = OpenInterestChange::new(prev_oi, curr_oi, price);
assert_eq!(change.absolute_change, 0.0);
assert_eq!(change.percentage_change, 0.0);
assert_eq!(change.usd_value_change, 0.0);
assert!(!change.is_increasing);
assert!(!change.is_decreasing);
let prev_oi = 0.0;
let curr_oi = 1000.0;
let price = 50000.0;
let change = OpenInterestChange::new(prev_oi, curr_oi, price);
assert_eq!(change.absolute_change, 1000.0);
assert_eq!(change.percentage_change, 0.0); assert_eq!(change.usd_value_change, 1000.0 * 50000.0);
assert!(change.is_increasing);
assert!(!change.is_decreasing);
}
#[test]
fn test_liquidation_impact() {
let liquidation_amount = 1000.0;
let open_interest = 10000.0;
let price = 50000.0;
let price_impact = -0.02;
let impact = LiquidationImpact::new(liquidation_amount, open_interest, price, price_impact);
assert_eq!(impact.liquidation_percentage, 0.1); assert_eq!(impact.usd_value, liquidation_amount * price);
assert_eq!(impact.price_impact, price_impact);
assert!(impact.is_significant);
let liquidation_amount = 100.0;
let open_interest = 10000.0;
let price = 50000.0;
let price_impact = -0.001;
let impact = LiquidationImpact::new(liquidation_amount, open_interest, price, price_impact);
assert_eq!(impact.liquidation_percentage, 0.01); assert_eq!(impact.usd_value, liquidation_amount * price);
assert_eq!(impact.price_impact, price_impact);
assert!(!impact.is_significant);
let liquidation_amount = 1000.0;
let open_interest = 0.0;
let price = 50000.0;
let price_impact = -0.02;
let impact = LiquidationImpact::new(liquidation_amount, open_interest, price, price_impact);
assert_eq!(impact.liquidation_percentage, 0.0); assert_eq!(impact.usd_value, liquidation_amount * price);
assert_eq!(impact.price_impact, price_impact);
assert!(impact.is_significant); }
#[test]
fn test_funding_price_correlation() {
let funding_rates = vec![0.0001, 0.0002, 0.0003, 0.0004, 0.0005];
let prices = vec![50000.0, 50100.0, 50200.0, 50300.0, 50400.0];
let correlation = FundingPriceCorrelation::calculate(&funding_rates, &prices);
assert!(correlation.coefficient > 0.9); assert!(correlation.is_significant);
assert_eq!(correlation.relationship, "Positive");
let funding_rates = vec![0.0001, 0.0002, 0.0003, 0.0004, 0.0005];
let prices = vec![50400.0, 50300.0, 50200.0, 50100.0, 50000.0];
let correlation = FundingPriceCorrelation::calculate(&funding_rates, &prices);
assert!(correlation.coefficient < -0.9); assert!(correlation.is_significant);
assert_eq!(correlation.relationship, "Negative");
let funding_rates = vec![0.0001, 0.0002, 0.0001, 0.0002, 0.0001];
let prices = vec![50000.0, 50100.0, 50000.0, 50100.0, 50000.0];
let correlation = FundingPriceCorrelation::calculate(&funding_rates, &prices);
assert!(correlation.coefficient.abs() < 0.5); assert!(!correlation.is_significant);
assert_eq!(correlation.relationship, "Weak");
let funding_rates: Vec<f64> = Vec::new();
let prices: Vec<f64> = Vec::new();
let correlation = FundingPriceCorrelation::calculate(&funding_rates, &prices);
assert_eq!(correlation.coefficient, 0.0);
assert!(!correlation.is_significant);
assert_eq!(correlation.relationship, "Unknown");
let funding_rates = vec![0.0001, 0.0002, 0.0003];
let prices = vec![50000.0, 50100.0, 50200.0, 50300.0, 50400.0];
let correlation = FundingPriceCorrelation::calculate(&funding_rates, &prices);
assert_eq!(correlation.coefficient, 0.0);
assert!(!correlation.is_significant);
assert_eq!(correlation.relationship, "Unknown");
}
#[test]
fn test_as_any_trait() {
let config = FundingPredictionConfig::default();
let predictor = FundingRatePredictor::new(config);
let any = predictor.as_any();
let downcast = any.downcast_ref::<FundingRatePredictor>();
assert!(downcast.is_some());
let wrong_downcast = any.downcast_ref::<String>();
assert!(wrong_downcast.is_none());
}