use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; #[derive(Clone, Debug)] pub struct SupertrendData { pub band_value: f64, pub signal: Option, // BUY or SELL pub area: String, // UP or DOWN pub close_time: i64, } impl SupertrendData { fn new() -> SupertrendData { let a = SupertrendData { band_value: 0.0, signal: None, area: String::new(), close_time: 0, }; a } } #[derive(Clone, Debug)] struct TrueRangeData { tr_value: f64, close_time: i64, } impl TrueRangeData { fn new() -> TrueRangeData { let a = TrueRangeData { tr_value: 0.0, close_time: 0, }; a } } #[derive(Clone, Debug)] struct ATRData { atr_value: f64, close_time: i64, } impl ATRData { fn new() -> ATRData { let a = ATRData { atr_value: 0.0, close_time: 0, }; a } } // Implementation from TradingView Script (SuperTrend by KivancOzbilgic) pub async fn supertrend( symbol: &String, input_rt_data: &Vec<(String, Vec)>, atr_period: usize, multiplier: f64, is_original_method: bool, ) -> Option> { let symbol_search_result = input_rt_data.iter().position(|x| x.0 == *symbol); match symbol_search_result { Some(T) => { if input_rt_data[T].1.len() >= atr_period && atr_period >= 2 { // making True Range let mut true_range_vec: Vec = Vec::new(); let mut tr_data = TrueRangeData::new(); let mut tr_val_hl = 0.0; // High - Low let mut tr_val_hcp = 0.0; // Abs(High - Close_previous) let mut tr_val_lcp = 0.0; // Abs(Low - Close_previous) let window_vec = input_rt_data[T].1.windows(2); tr_data.tr_value = input_rt_data[T].1.first().unwrap().high_price - input_rt_data[T].1.first().unwrap().low_price; tr_data.close_time = input_rt_data[T].1.first().unwrap().close_time; true_range_vec.push(tr_data.clone()); for buffer in window_vec { tr_val_hl = buffer[1].high_price - buffer[1].low_price; tr_val_hcp = (buffer[1].high_price - buffer[0].close_price).abs(); tr_val_lcp = (buffer[1].low_price - buffer[0].close_price).abs(); tr_data.tr_value = tr_val_hl.max(tr_val_hcp.max(tr_val_lcp)); tr_data.close_time = buffer[1].close_time; true_range_vec.push(tr_data.clone()); } // making Average True Range let mut average_true_range_vec: Vec = Vec::new(); let mut atr_data = ATRData::new(); if is_original_method == true { // original calculation of ATR let mut first_value = 0.0; let mut first_vec = &true_range_vec[0..atr_period]; for element in first_vec { first_value += element.tr_value; } first_value /= (atr_period) as f64; atr_data.atr_value = first_value; atr_data.close_time = first_vec.last().unwrap().close_time; average_true_range_vec.push(atr_data.clone()); let mut temp_prev_atr_value = first_value; for element in &true_range_vec[atr_period..] { atr_data.atr_value = ((temp_prev_atr_value * ((atr_period - 1) as f64)) + element.tr_value) / atr_period as f64; atr_data.close_time = element.close_time; average_true_range_vec.push(atr_data.clone()); temp_prev_atr_value = atr_data.atr_value; } } else { // Calculation of ATR from SMA True Range with atr_period let window = true_range_vec.windows(atr_period); let mut sum = 0.0; for buffer in window { for element in buffer { sum += element.tr_value; } atr_data.atr_value = sum / atr_period as f64; atr_data.close_time = buffer.last().unwrap().close_time; average_true_range_vec.push(atr_data.clone()); sum = 0.0; } } // making Supertrend #[derive(Clone)] struct BandData { basic_upperband: f64, basic_lowerband: f64, close_time: i64, } let mut supertrend_vec: Vec = Vec::new(); let mut supertrend_data = SupertrendData::new(); let mut band_vec: Vec = Vec::new(); let mut band_data = BandData { basic_upperband: 0.0, basic_lowerband: 0.0, close_time: 0, }; let closetime_search_result = input_rt_data[T].1.iter().position(|x| { x.close_time == average_true_range_vec.first().unwrap().close_time }); let mut rt_data = &input_rt_data[T].1[closetime_search_result.unwrap()..]; let zipped = rt_data.iter().zip(average_true_range_vec); for element in zipped { band_data.basic_upperband = ((element.0.high_price + element.0.low_price) / 2.0) - (multiplier * element.1.atr_value); band_data.basic_lowerband = ((element.0.high_price + element.0.low_price) / 2.0) + (multiplier * element.1.atr_value); band_data.close_time = element.1.close_time; band_vec.push(band_data.clone()); } let mut zipped = rt_data.iter().zip(band_vec); let first_element = zipped.next().unwrap(); let mut prev_close_price = first_element.0.close_price; let mut final_upperband = 0.0; let mut final_lowerband = 0.0; let mut prev_final_upperband = first_element.1.basic_upperband; let mut prev_final_lowerband = first_element.1.basic_lowerband; let mut trend = 1; // 1 means up trend, -1 means down trend let mut prev_trend = 1; for element in zipped { // set final upperband if prev_close_price > prev_final_upperband { final_upperband = prev_final_upperband.max(element.1.basic_upperband); } else { final_upperband = element.1.basic_upperband; } // set final lowerband if prev_close_price < prev_final_lowerband { final_lowerband = prev_final_lowerband.min(element.1.basic_lowerband); } else { final_lowerband = element.1.basic_lowerband; } // set supertrend if trend == -1 && element.0.close_price > prev_final_lowerband { supertrend_data.area = String::from("UP"); trend = 1; } else if trend == 1 && element.0.close_price < prev_final_upperband { supertrend_data.area = String::from("DOWN"); trend = -1; } if supertrend_data.area.contains("UP") { supertrend_data.band_value = final_upperband; } else { supertrend_data.band_value = final_lowerband; } if trend == 1 && prev_trend == -1 { supertrend_data.signal = Some(String::from("BUY")); } else if trend == -1 && prev_trend == 1 { supertrend_data.signal = Some(String::from("SELL")); } else { supertrend_data.signal = None; } supertrend_data.close_time = element.1.close_time; supertrend_vec.push(supertrend_data.clone()); prev_close_price = element.0.close_price; prev_final_upperband = final_upperband; prev_final_lowerband = final_lowerband; prev_trend = trend; } Some(supertrend_vec) } else { None } } None => None, } }