use std::collections::VecDeque;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum FundingDirection {
Positive,
Negative,
Neutral,
}
impl FundingDirection {
pub fn from_rate(rate: f64) -> Self {
if rate > 0.0 {
FundingDirection::Positive
} else if rate < 0.0 {
FundingDirection::Negative
} else {
FundingDirection::Neutral
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FundingVolatility {
pub std_dev: f64,
pub cv: f64,
pub is_high: bool,
pub percentile: f64,
}
pub fn calculate_funding_volatility(rates: &[f64]) -> f64 {
if rates.len() <= 1 {
return 0.0;
}
let mean = rates.iter().sum::<f64>() / rates.len() as f64;
let variance = rates.iter()
.map(|&r| (r - mean).powi(2))
.sum::<f64>() / (rates.len() - 1) as f64;
variance.sqrt()
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FundingMomentum {
pub direction: FundingDirection,
pub strength: f64,
pub rate_of_change: f64,
pub is_accelerating: bool,
}
pub fn calculate_funding_momentum(rates: &[f64]) -> f64 {
if rates.len() <= 1 {
return 0.0;
}
let n = rates.len() as f64;
let indices: Vec<f64> = (0..rates.len()).map(|i| i as f64).collect();
let sum_x: f64 = indices.iter().sum();
let sum_y: f64 = rates.iter().sum();
let sum_xy: f64 = indices.iter().zip(rates.iter()).map(|(&x, &y)| x * y).sum();
let sum_xx: f64 = indices.iter().map(|&x| x * x).sum();
let slope = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x);
slope
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FundingCycle {
pub period_hours: usize,
pub strength: f64,
pub is_significant: bool,
pub current_phase: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FundingAnomaly {
pub is_anomaly: bool,
pub deviation: f64,
pub direction: FundingDirection,
pub potential_cause: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FundingArbitrageOpportunity {
pub is_arbitrage: bool,
pub direction: FundingDirection,
pub annualized_yield: f64,
pub payment_per_contract: f64,
}
pub fn calculate_funding_arbitrage(funding_rate: f64, price: f64) -> FundingArbitrageOpportunity {
let direction = FundingDirection::from_rate(funding_rate);
let is_arbitrage = funding_rate.abs() > 0.0001; let annualized_yield = funding_rate.abs() * 3.0 * 365.25; let payment_per_contract = funding_rate.abs() * price;
FundingArbitrageOpportunity {
is_arbitrage,
direction,
annualized_yield,
payment_per_contract,
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FundingPriceCorrelation {
pub coefficient: f64,
pub is_significant: bool,
pub relationship: String,
pub optimal_lag: i32,
}
impl FundingPriceCorrelation {
pub fn calculate(funding_rates: &[f64], prices: &[f64]) -> Self {
if funding_rates.len() != prices.len() || funding_rates.is_empty() {
return Self {
coefficient: 0.0,
is_significant: false,
relationship: "Unknown".to_string(),
optimal_lag: 0,
};
}
let n = funding_rates.len() as f64;
let sum_x: f64 = funding_rates.iter().sum();
let sum_y: f64 = prices.iter().sum();
let sum_xy: f64 = funding_rates.iter().zip(prices.iter()).map(|(&x, &y)| x * y).sum();
let sum_xx: f64 = funding_rates.iter().map(|&x| x * x).sum();
let sum_yy: f64 = prices.iter().map(|&y| y * y).sum();
let numerator = n * sum_xy - sum_x * sum_y;
let denominator = ((n * sum_xx - sum_x * sum_x) * (n * sum_yy - sum_y * sum_y)).sqrt();
let coefficient = if denominator != 0.0 {
numerator / denominator
} else {
0.0
};
let is_significant = coefficient.abs() > 0.5;
let relationship = if coefficient > 0.7 {
"Positive".to_string()
} else if coefficient < -0.7 {
"Negative".to_string()
} else {
"Weak".to_string()
};
Self {
coefficient,
is_significant,
relationship,
optimal_lag: 0, }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OpenInterestData {
pub current_oi: f64,
pub change: OpenInterestChange,
pub long_short_ratio: f64,
pub is_at_high: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OpenInterestChange {
pub absolute_change: f64,
pub percentage_change: f64,
pub usd_value_change: f64,
pub is_increasing: bool,
pub is_decreasing: bool,
}
impl OpenInterestChange {
pub fn new(prev_oi: f64, curr_oi: f64, price: f64) -> Self {
let absolute_change = curr_oi - prev_oi;
let percentage_change = if prev_oi > 0.0 {
absolute_change / prev_oi
} else {
0.0
};
let usd_value_change = absolute_change * price;
Self {
absolute_change,
percentage_change,
usd_value_change,
is_increasing: absolute_change > 0.0,
is_decreasing: absolute_change < 0.0,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LiquidationData {
pub total_amount: f64,
pub long_liquidations: f64,
pub short_liquidations: f64,
pub impact: LiquidationImpact,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LiquidationImpact {
pub liquidation_percentage: f64,
pub usd_value: f64,
pub price_impact: f64,
pub is_significant: bool,
}
impl LiquidationImpact {
pub fn new(liquidation_amount: f64, open_interest: f64, price: f64, price_impact: f64) -> Self {
let liquidation_percentage = if open_interest > 0.0 {
liquidation_amount / open_interest
} else {
0.0
};
let usd_value = liquidation_amount * price;
let is_significant = liquidation_percentage > 0.05 || price_impact.abs() > 0.01;
Self {
liquidation_percentage,
usd_value,
price_impact,
is_significant,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BasisIndicator {
pub basis: f64,
pub annualized_basis: f64,
pub basis_amount: f64,
pub is_widening: bool,
}
pub fn calculate_basis_indicator(spot_price: f64, futures_price: f64, days_to_expiry: f64) -> BasisIndicator {
let basis_amount = futures_price - spot_price;
let basis = basis_amount / spot_price;
let annualized_basis = basis * (365.0 / days_to_expiry);
BasisIndicator {
basis,
annualized_basis,
basis_amount,
is_widening: false, }
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FundingPrediction {
pub expected_rate: f64,
pub direction: FundingDirection,
pub confidence: f64,
pub horizon_hours: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FundingPredictionConfig {
pub lookback_periods: usize,
pub volatility_weight: f64,
pub momentum_weight: f64,
pub basis_weight: f64,
pub correlation_weight: f64,
}
impl Default for FundingPredictionConfig {
fn default() -> Self {
Self {
lookback_periods: 48,
volatility_weight: 0.2,
momentum_weight: 0.3,
basis_weight: 0.3,
correlation_weight: 0.2,
}
}
}
pub trait FundingPredictionModel {
fn add_observation(&mut self, rate: f64);
fn predict(&self) -> FundingPrediction;
fn get_volatility(&self) -> f64;
fn get_momentum(&self) -> f64;
fn detect_funding_cycle(&self) -> FundingCycle;
fn detect_anomaly(&self) -> FundingAnomaly;
fn correlation_with(&self, other: &dyn FundingPredictionModel) -> f64;
}
pub struct FundingRatePredictor {
config: FundingPredictionConfig,
rates: VecDeque<f64>,
}
impl FundingRatePredictor {
pub fn new(config: FundingPredictionConfig) -> Self {
let capacity = config.lookback_periods;
Self {
config,
rates: VecDeque::with_capacity(capacity),
}
}
}
impl FundingPredictionModel for FundingRatePredictor {
fn add_observation(&mut self, rate: f64) {
if self.rates.len() >= self.config.lookback_periods {
self.rates.pop_front();
}
self.rates.push_back(rate);
}
fn predict(&self) -> FundingPrediction {
if self.rates.is_empty() {
return FundingPrediction {
expected_rate: 0.0,
direction: FundingDirection::Neutral,
confidence: 0.0,
horizon_hours: 8,
};
}
let rates: Vec<f64> = self.rates.iter().copied().collect();
let momentum = calculate_funding_momentum(&rates);
let volatility = calculate_funding_volatility(&rates);
let last_rate = *self.rates.back().unwrap();
let expected_rate = last_rate + momentum;
let direction = FundingDirection::from_rate(expected_rate);
let confidence = 0.5 + 0.3 * (momentum.abs() / (volatility + 0.0001)).min(1.0);
FundingPrediction {
expected_rate,
direction,
confidence,
horizon_hours: 8,
}
}
fn get_volatility(&self) -> f64 {
let rates: Vec<f64> = self.rates.iter().copied().collect();
calculate_funding_volatility(&rates)
}
fn get_momentum(&self) -> f64 {
let rates: Vec<f64> = self.rates.iter().copied().collect();
calculate_funding_momentum(&rates)
}
fn detect_funding_cycle(&self) -> FundingCycle {
FundingCycle {
period_hours: 8, strength: 0.7,
is_significant: true,
current_phase: 0.5,
}
}
fn detect_anomaly(&self) -> FundingAnomaly {
if self.rates.len() < 2 {
return FundingAnomaly {
is_anomaly: false,
deviation: 0.0,
direction: FundingDirection::Neutral,
potential_cause: "Insufficient data".to_string(),
};
}
let rates: Vec<f64> = self.rates.iter().copied().collect();
let mean = rates.iter().sum::<f64>() / rates.len() as f64;
let std_dev = calculate_funding_volatility(&rates);
let last_rate = *self.rates.back().unwrap();
let deviation = if std_dev > 0.0 {
(last_rate - mean) / std_dev
} else {
0.0
};
let is_anomaly = deviation.abs() > 3.0; let direction = FundingDirection::from_rate(last_rate);
FundingAnomaly {
is_anomaly,
deviation: deviation.abs(),
direction,
potential_cause: if is_anomaly {
"Significant market event".to_string()
} else {
"Normal market conditions".to_string()
},
}
}
fn correlation_with(&self, _other: &dyn FundingPredictionModel) -> f64 {
0.5
}
}