73 lines
2.4 KiB
Rust
73 lines
2.4 KiB
Rust
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<i8> {
|
||
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
|
||
}
|
||
}
|