use rs_backtester::strategies::Strategy;
use rs_backtester::datas::Data;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum SignalStrength {
Strong,
Medium,
Weak,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TradingSignal {
pub position: f64,
pub strength: SignalStrength,
}
impl TradingSignal {
pub fn new(position: f64, strength: SignalStrength) -> Self {
Self {
position,
strength,
}
}
pub fn is_long(&self) -> bool {
self.position > 0.0
}
pub fn is_short(&self) -> bool {
self.position < 0.0
}
pub fn is_neutral(&self) -> bool {
self.position == 0.0
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FundingAwareConfig {
pub funding_threshold: f64,
pub funding_weight: f64,
pub use_funding_direction: bool,
pub use_funding_prediction: bool,
}
impl Default for FundingAwareConfig {
fn default() -> Self {
Self {
funding_threshold: 0.0001, funding_weight: 0.5, use_funding_direction: true,
use_funding_prediction: true,
}
}
}
pub trait HyperliquidStrategy {
fn funding_config(&self) -> &FundingAwareConfig;
fn set_funding_config(&mut self, config: FundingAwareConfig);
fn process_funding(&self, funding_rate: f64) -> TradingSignal;
fn combine_signals(&self, base_signal: f64, funding_signal: &TradingSignal) -> f64;
}
pub fn funding_arbitrage_strategy(data: Data, threshold: f64) -> Strategy {
let strategy = Strategy {
name: format!("Funding Arbitrage (threshold: {})", threshold),
choices: Vec::new(),
indicator: None,
};
strategy
}
pub fn enhanced_sma_cross(
data: Data,
fast_period: usize,
slow_period: usize,
funding_config: FundingAwareConfig,
) -> Strategy {
let strategy = Strategy {
name: format!("Enhanced SMA Cross ({}, {})", fast_period, slow_period),
choices: Vec::new(),
indicator: None,
};
strategy
}
pub struct FundingArbitrageStrategy {
threshold: f64,
funding_config: FundingAwareConfig,
}
impl FundingArbitrageStrategy {
pub fn new(threshold: f64) -> Self {
Self {
threshold,
funding_config: FundingAwareConfig::default(),
}
}
}
impl HyperliquidStrategy for FundingArbitrageStrategy {
fn funding_config(&self) -> &FundingAwareConfig {
&self.funding_config
}
fn set_funding_config(&mut self, config: FundingAwareConfig) {
self.funding_config = config;
}
fn process_funding(&self, funding_rate: f64) -> TradingSignal {
if funding_rate.abs() <= self.threshold {
return TradingSignal::new(0.0, SignalStrength::Weak);
}
let position = if funding_rate > 0.0 { 1.0 } else { -1.0 };
let strength = if funding_rate.abs() > self.threshold * 2.0 {
SignalStrength::Strong
} else {
SignalStrength::Medium
};
TradingSignal::new(position, strength)
}
fn combine_signals(&self, base_signal: f64, funding_signal: &TradingSignal) -> f64 {
if funding_signal.is_neutral() {
return base_signal;
}
let weight = match funding_signal.strength {
SignalStrength::Strong => self.funding_config.funding_weight,
SignalStrength::Medium => self.funding_config.funding_weight * 0.7,
SignalStrength::Weak => self.funding_config.funding_weight * 0.3,
};
let combined = base_signal * (1.0 - weight) + funding_signal.position * weight;
if combined > 0.3 {
1.0
} else if combined < -0.3 {
-1.0
} else {
0.0
}
}
}
pub struct EnhancedSmaStrategy {
fast_period: usize,
slow_period: usize,
funding_config: FundingAwareConfig,
}
impl EnhancedSmaStrategy {
pub fn new(fast_period: usize, slow_period: usize) -> Self {
Self {
fast_period,
slow_period,
funding_config: FundingAwareConfig::default(),
}
}
fn calculate_sma(&self, data: &[f64], period: usize) -> f64 {
if data.len() < period {
return 0.0;
}
let sum: f64 = data[data.len() - period..].iter().sum();
sum / period as f64
}
}
impl HyperliquidStrategy for EnhancedSmaStrategy {
fn funding_config(&self) -> &FundingAwareConfig {
&self.funding_config
}
fn set_funding_config(&mut self, config: FundingAwareConfig) {
self.funding_config = config;
}
fn process_funding(&self, funding_rate: f64) -> TradingSignal {
if !self.funding_config.use_funding_direction ||
funding_rate.abs() <= self.funding_config.funding_threshold {
return TradingSignal::new(0.0, SignalStrength::Weak);
}
let position = if funding_rate > 0.0 { 1.0 } else { -1.0 };
let strength = if funding_rate.abs() > self.funding_config.funding_threshold * 2.0 {
SignalStrength::Medium
} else {
SignalStrength::Weak
};
TradingSignal::new(position, strength)
}
fn combine_signals(&self, base_signal: f64, funding_signal: &TradingSignal) -> f64 {
if funding_signal.is_neutral() {
return base_signal;
}
if (base_signal > 0.0 && funding_signal.is_long()) ||
(base_signal < 0.0 && funding_signal.is_short()) {
return base_signal;
}
let weight = match funding_signal.strength {
SignalStrength::Strong => self.funding_config.funding_weight,
SignalStrength::Medium => self.funding_config.funding_weight * 0.5,
SignalStrength::Weak => self.funding_config.funding_weight * 0.2,
};
base_signal * (1.0 - weight)
}
}