use crate::backtest::{
HyperliquidCommission, OrderType, TradingScenario,
CommissionTracker, OrderTypeStrategy
};
use crate::errors::Result;
use chrono::{DateTime, FixedOffset, TimeZone};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
#[test]
fn test_hyperliquid_commission_default() {
let commission = HyperliquidCommission::default();
assert_eq!(commission.maker_rate, 0.0002); assert_eq!(commission.taker_rate, 0.0005); assert!(commission.funding_enabled);
}
#[test]
fn test_hyperliquid_commission_new() {
let commission = HyperliquidCommission::new(0.0001, 0.0003, false);
assert_eq!(commission.maker_rate, 0.0001);
assert_eq!(commission.taker_rate, 0.0003);
assert!(!commission.funding_enabled);
}
#[test]
fn test_calculate_fee() {
let commission = HyperliquidCommission::default();
let trade_value = 10000.0;
let maker_fee = commission.calculate_fee(OrderType::LimitMaker, trade_value);
let market_fee = commission.calculate_fee(OrderType::Market, trade_value);
let limit_taker_fee = commission.calculate_fee(OrderType::LimitTaker, trade_value);
assert_eq!(maker_fee, trade_value * 0.0002);
assert_eq!(market_fee, trade_value * 0.0005);
assert_eq!(limit_taker_fee, trade_value * 0.0005);
}
#[test]
fn test_calculate_scenario_fee() {
let commission = HyperliquidCommission::default();
let trade_value = 10000.0;
let open_market_fee = commission.calculate_scenario_fee(
TradingScenario::OpenPosition, OrderType::Market, trade_value
);
let close_maker_fee = commission.calculate_scenario_fee(
TradingScenario::ClosePosition, OrderType::LimitMaker, trade_value
);
let reduce_taker_fee = commission.calculate_scenario_fee(
TradingScenario::ReducePosition, OrderType::LimitTaker, trade_value
);
let increase_market_fee = commission.calculate_scenario_fee(
TradingScenario::IncreasePosition, OrderType::Market, trade_value
);
assert_eq!(open_market_fee, trade_value * 0.0005);
assert_eq!(close_maker_fee, trade_value * 0.0002);
assert_eq!(reduce_taker_fee, trade_value * 0.0005);
assert_eq!(increase_market_fee, trade_value * 0.0005);
}
#[test]
fn test_to_rs_backtester_commission() {
let commission = HyperliquidCommission::default();
let rs_commission = commission.to_rs_backtester_commission();
assert_eq!(rs_commission.rate, commission.taker_rate);
}
#[test]
fn test_validate_valid_commission() -> Result<()> {
let commission = HyperliquidCommission::default();
let result = commission.validate();
assert!(result.is_ok());
Ok(())
}
#[test]
fn test_validate_invalid_maker_rate() {
let commission = HyperliquidCommission::new(-0.1, 0.0005, true);
let result = commission.validate();
assert!(result.is_err());
if let Err(e) = result {
assert!(e.to_string().contains("Invalid maker rate"));
}
let commission = HyperliquidCommission::new(1.5, 0.0005, true);
let result = commission.validate();
assert!(result.is_err());
if let Err(e) = result {
assert!(e.to_string().contains("Invalid maker rate"));
}
}
#[test]
fn test_validate_invalid_taker_rate() {
let commission = HyperliquidCommission::new(0.0002, -0.1, true);
let result = commission.validate();
assert!(result.is_err());
if let Err(e) = result {
assert!(e.to_string().contains("Invalid taker rate"));
}
let commission = HyperliquidCommission::new(0.0002, 1.5, true);
let result = commission.validate();
assert!(result.is_err());
if let Err(e) = result {
assert!(e.to_string().contains("Invalid taker rate"));
}
}
#[test]
fn test_validate_maker_higher_than_taker() {
let commission = HyperliquidCommission::new(0.0006, 0.0005, true);
let result = commission.validate();
assert!(result.is_err());
if let Err(e) = result {
assert!(e.to_string().contains("Maker rate should typically be lower than taker rate"));
}
}
#[test]
fn test_order_type_strategy_always_market() {
let strategy = OrderTypeStrategy::AlwaysMarket;
for i in 0..10 {
assert_eq!(strategy.get_order_type(i), OrderType::Market);
}
}
#[test]
fn test_order_type_strategy_always_maker() {
let strategy = OrderTypeStrategy::AlwaysMaker;
for i in 0..10 {
assert_eq!(strategy.get_order_type(i), OrderType::LimitMaker);
}
}
#[test]
fn test_order_type_strategy_mixed() {
let strategy = OrderTypeStrategy::Mixed { maker_percentage: 0.5 };
let mut maker_count = 0;
let mut market_count = 0;
for i in 0..1000 {
match strategy.get_order_type(i) {
OrderType::LimitMaker => maker_count += 1,
_ => market_count += 1,
}
}
let maker_ratio = maker_count as f64 / 1000.0;
assert!((maker_ratio - 0.5).abs() < 0.1); }
#[test]
fn test_order_type_strategy_mixed_extremes() {
let strategy = OrderTypeStrategy::Mixed { maker_percentage: 0.0 };
for i in 0..10 {
assert_eq!(strategy.get_order_type(i), OrderType::Market);
}
let strategy = OrderTypeStrategy::Mixed { maker_percentage: 1.0 };
for i in 0..10 {
assert_eq!(strategy.get_order_type(i), OrderType::LimitMaker);
}
}
#[test]
fn test_order_type_strategy_adaptive() {
let strategy = OrderTypeStrategy::Adaptive;
for i in 0..10 {
if i % 2 == 0 {
assert_eq!(strategy.get_order_type(i), OrderType::LimitMaker);
} else {
assert_eq!(strategy.get_order_type(i), OrderType::Market);
}
}
}
#[test]
fn test_commission_tracker_default() {
let tracker = CommissionTracker::default();
assert_eq!(tracker.total_maker_fees, 0.0);
assert_eq!(tracker.total_taker_fees, 0.0);
assert_eq!(tracker.maker_order_count, 0);
assert_eq!(tracker.taker_order_count, 0);
}
#[test]
fn test_commission_tracker_add_commission() {
let mut tracker = CommissionTracker::default();
let timestamp = FixedOffset::east_opt(0).unwrap().timestamp_opt(1640995200, 0).unwrap();
tracker.add_commission(
timestamp,
OrderType::LimitMaker,
10000.0,
2.0,
TradingScenario::OpenPosition
);
assert_eq!(tracker.total_maker_fees, 2.0);
assert_eq!(tracker.maker_order_count, 1);
assert_eq!(tracker.total_taker_fees, 0.0);
assert_eq!(tracker.taker_order_count, 0);
tracker.add_commission(
timestamp,
OrderType::Market,
20000.0,
10.0,
TradingScenario::ClosePosition
);
assert_eq!(tracker.total_maker_fees, 2.0);
assert_eq!(tracker.maker_order_count, 1);
assert_eq!(tracker.total_taker_fees, 10.0);
assert_eq!(tracker.taker_order_count, 1);
tracker.add_commission(
timestamp,
OrderType::LimitTaker,
15000.0,
7.5,
TradingScenario::ReducePosition
);
assert_eq!(tracker.total_maker_fees, 2.0);
assert_eq!(tracker.maker_order_count, 1);
assert_eq!(tracker.total_taker_fees, 17.5);
assert_eq!(tracker.taker_order_count, 2);
}
#[test]
fn test_commission_tracker_total_commission() {
let mut tracker = CommissionTracker::default();
let timestamp = FixedOffset::east_opt(0).unwrap().timestamp_opt(1640995200, 0).unwrap();
tracker.add_commission(
timestamp,
OrderType::LimitMaker,
10000.0,
2.0,
TradingScenario::OpenPosition
);
tracker.add_commission(
timestamp,
OrderType::Market,
20000.0,
10.0,
TradingScenario::ClosePosition
);
assert_eq!(tracker.total_commission(), 12.0);
}
#[test]
fn test_commission_tracker_average_commission_rate() {
let mut tracker = CommissionTracker::default();
let timestamp = FixedOffset::east_opt(0).unwrap().timestamp_opt(1640995200, 0).unwrap();
tracker.add_commission(
timestamp,
OrderType::LimitMaker,
10000.0,
2.0,
TradingScenario::OpenPosition
);
tracker.add_commission(
timestamp,
OrderType::Market,
20000.0,
10.0,
TradingScenario::ClosePosition
);
assert_eq!(tracker.average_commission_rate(), 6.0);
}
#[test]
fn test_commission_tracker_maker_taker_ratio() {
let mut tracker = CommissionTracker::default();
let timestamp = FixedOffset::east_opt(0).unwrap().timestamp_opt(1640995200, 0).unwrap();
tracker.add_commission(
timestamp,
OrderType::LimitMaker,
10000.0,
2.0,
TradingScenario::OpenPosition
);
tracker.add_commission(
timestamp,
OrderType::LimitMaker,
10000.0,
2.0,
TradingScenario::OpenPosition
);
tracker.add_commission(
timestamp,
OrderType::Market,
20000.0,
10.0,
TradingScenario::ClosePosition
);
tracker.add_commission(
timestamp,
OrderType::Market,
20000.0,
10.0,
TradingScenario::ClosePosition
);
tracker.add_commission(
timestamp,
OrderType::LimitTaker,
15000.0,
7.5,
TradingScenario::ReducePosition
);
assert_eq!(tracker.maker_taker_ratio(), 0.4);
}
#[test]
fn test_commission_tracker_empty() {
let tracker = CommissionTracker::default();
assert_eq!(tracker.total_commission(), 0.0);
assert_eq!(tracker.average_commission_rate(), 0.0);
assert_eq!(tracker.maker_taker_ratio(), 0.0);
}