use serde_json::Value; use crate::binance::models::Kline; use super::{Signal, Strategy, param_f64, param_i64, atr}; pub struct SupertrendStrategy { atr_period: usize, multiplier: f64, profit_pct: f64, stop_loss_pct: f64, } impl SupertrendStrategy { pub fn new(params: &Value) -> Self { Self { atr_period: param_i64(params, "atr_period", 10) as usize, multiplier: param_f64(params, "multiplier", 3.0), profit_pct: param_f64(params, "profit_target_pct", 3.0), stop_loss_pct: param_f64(params, "stop_loss_pct", 2.0), } } fn calc_direction(&self, candles: &[Kline]) -> Vec { let n = candles.len(); let atr_vals = atr(candles, self.atr_period); let mut upper = vec![0.0f64; n]; let mut lower = vec![0.0f64; n]; let mut dir = vec![1i8; n]; for i in 1..n { let hl2 = (candles[i].high + candles[i].low) / 2.0; let a = atr_vals[i]; let ub = hl2 + self.multiplier * a; let lb = hl2 - self.multiplier * a; upper[i] = if ub < upper[i-1] || candles[i-1].close > upper[i-1] { ub } else { upper[i-1] }; lower[i] = if lb > lower[i-1] || candles[i-1].close < lower[i-1] { lb } else { lower[i-1] }; let prev_dir = dir[i-1]; dir[i] = if candles[i].close > upper[i] { 1 } else if candles[i].close < lower[i] { -1 } else { prev_dir }; } dir } } impl Strategy for SupertrendStrategy { fn generate_signal(&self, candles: &[Kline]) -> Signal { let n = candles.len(); if n < self.min_candles() { return Signal::Hold; } let dir = self.calc_direction(candles); let curr = dir[n - 2]; // son kapanmış mum let prev = dir[n - 3]; if prev == -1 && curr == 1 { Signal::Buy } else if prev == 1 && curr == -1 { Signal::Sell } else { Signal::Hold } } fn sell_price(&self, buy_price: f64) -> f64 { buy_price * (1.0 + (self.profit_pct + 0.2) / 100.0) } fn stop_price(&self, buy_price: f64) -> f64 { if self.stop_loss_pct > 0.0 { buy_price * (1.0 - self.stop_loss_pct / 100.0) } else { 0.0 } } fn min_candles(&self) -> usize { self.atr_period * 3 + 5 } }