239 lines
8.9 KiB
Rust
239 lines
8.9 KiB
Rust
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct SupertrendData {
|
|
pub band_value: f64,
|
|
pub signal: Option<String>, // 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<RealtimePriceData>)>,
|
|
atr_period: usize,
|
|
multiplier: f64,
|
|
is_original_method: bool,
|
|
) -> Option<Vec<SupertrendData>> {
|
|
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<TrueRangeData> = 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<ATRData> = 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<SupertrendData> = Vec::new();
|
|
let mut supertrend_data = SupertrendData::new();
|
|
let mut band_vec: Vec<BandData> = 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,
|
|
}
|
|
}
|