diff --git a/src/strategy_team/future_strategy_long.rs b/src/strategy_team/future_strategy_long.rs index 7e7cad2..7ecb868 100644 --- a/src/strategy_team/future_strategy_long.rs +++ b/src/strategy_team/future_strategy_long.rs @@ -162,66 +162,91 @@ pub async fn list_up_for_buy( Ok(()) } -pub async fn list_up_for_sell(alldata: &AllData) -> Result<(), Box> { +pub async fn list_up_for_sell(all_data: &AllData) -> Result<(), Box> { let filled_positions = select_filled_positions().await?; let client = ClientBuilder::new() .timeout(tokio::time::Duration::from_millis(5000)) .build() .unwrap(); - let server_epoch = get_server_epoch().await; + let mut filtered_symbols: HashMap = HashMap::new(); for element in &filled_positions { filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new()); } let server_epoch = get_server_epoch().await; - let stoch_rsis = stoch_rsi(30, 30, 2, 2, &alldata.rt_price_1m_vec, &filtered_symbols).await?; for element in filled_positions { - if let Some(stoch_rsis_vec) = stoch_rsis.get(&element.symbol) { - let mut over_turned = false; - let mut is_sell = false; - if stoch_rsis_vec.len() > 10 { - if stoch_rsis_vec.last().unwrap().close_time > server_epoch - && stoch_rsis_vec[stoch_rsis_vec.len()-1].k < stoch_rsis_vec[stoch_rsis_vec.len()-1].d - && stoch_rsis_vec[stoch_rsis_vec.len()-2].k > stoch_rsis_vec[stoch_rsis_vec.len()-2].d { - // over_turned = true; - } + let opclo_sample_length: usize = 15; // 15 candle samsples + let mut target_profit_percent = 0.0; + if let Some(price_1m_vec) = all_data.rt_price_1m_vec.get(&element.symbol) { + let vec_len = price_1m_vec.len(); + if let Some(candles) = + price_1m_vec.get(vec_len - opclo_sample_length - 2..vec_len - 1) + { + let windows = candles.windows(2); + let mut sum_amplitude_candles = 0.0; + let mut sum_ratio_amp_body = 0.0; + let mut average_amplitude = 0.0; - // TODO: BNB 코인이 있으면 - // let base_qty_to_be_ordered = - // element.base_qty_ordered.round_dp_with_strategy( - // lot_step_size.normalize().scale(), - // RoundingStrategy::ToZero, - // ); - // TODO: BNB 코인이 없으면 - - if !element.current_price.is_zero() { - if element.pure_profit_percent >= 0.3 { - is_sell = true; - } else if element.pure_profit_percent <= -0.3 { - is_sell = true; - } else if server_epoch - element.transact_time >= (600_000) * 1 { - // time up selling - is_sell = true; - } else if over_turned == true { - is_sell = true; - } - - if is_sell == true { - limit_order_close( - &element, - TimeInForce::Gtc, - element.current_price, - element.base_qty_ordered, - &client - ) - .await; - } + for window in windows { + sum_amplitude_candles += ((window.last().unwrap().high_price + - window.last().unwrap().low_price) + * 100.0) + / window.first().unwrap().close_price; } + let average_amplitude = sum_amplitude_candles / opclo_sample_length as f64; // percent unit + let mut amplitude_variance = 0.0; + + let windows = candles.windows(2); + for window in windows { + amplitude_variance += ((((window.last().unwrap().high_price + - window.last().unwrap().low_price) + * 100.0) + / window.first().unwrap().close_price) + - average_amplitude) + .powi(2); + } + amplitude_variance = amplitude_variance / (opclo_sample_length - 1) as f64; + let standard_deviation_amplitude = amplitude_variance.sqrt(); + target_profit_percent = + average_amplitude + (standard_deviation_amplitude * 0.5); } } - } + let mut is_sell = false; + + // TODO: BNB 코인이 있으면 + // let base_qty_to_be_ordered = + // element.base_qty_ordered.round_dp_with_strategy( + // lot_step_size.normalize().scale(), + // RoundingStrategy::ToZero, + // ); + // TODO: BNB 코인이 없으면 + + if !element.current_price.is_zero() && target_profit_percent.is_normal() { + if element.pure_profit_percent >= target_profit_percent * 2.0 { + is_sell = true; + } else if element.pure_profit_percent <= target_profit_percent * -1.0 { + is_sell = true; + } else if server_epoch - element.transact_time >= (600_000) * 1 { + // time up selling + is_sell = true; + } + + if is_sell == true { + limit_order_close( + &element, + TimeInForce::Gtc, + element.current_price, + element.base_qty_ordered, + &client + ) + .await; + } + } + + } + Ok(()) } diff --git a/src/strategy_team/future_strategy_short.rs b/src/strategy_team/future_strategy_short.rs index 8f607a3..e8623da 100644 --- a/src/strategy_team/future_strategy_short.rs +++ b/src/strategy_team/future_strategy_short.rs @@ -162,7 +162,7 @@ pub async fn list_up_for_buy( Ok(()) } -pub async fn list_up_for_sell(alldata: &AllData) -> Result<(), Box> { +pub async fn list_up_for_sell(all_data: &AllData) -> Result<(), Box> { let filled_positions = select_filled_positions().await?; let client = ClientBuilder::new() @@ -175,53 +175,77 @@ pub async fn list_up_for_sell(alldata: &AllData) -> Result<(), Box 10 { - if stoch_rsis_vec.last().unwrap().close_time > server_epoch - && stoch_rsis_vec[stoch_rsis_vec.len()-1].k > stoch_rsis_vec[stoch_rsis_vec.len()-1].d - && stoch_rsis_vec[stoch_rsis_vec.len()-2].k < stoch_rsis_vec[stoch_rsis_vec.len()-2].d { - // over_turned = true; - } + let opclo_sample_length: usize = 15; // 15 candle samsples + let mut target_profit_percent = 0.0; + if let Some(price_1m_vec) = all_data.rt_price_1m_vec.get(&element.symbol) { + let vec_len = price_1m_vec.len(); + if let Some(candles) = + price_1m_vec.get(vec_len - opclo_sample_length - 2..vec_len - 1) + { + let windows = candles.windows(2); + let mut sum_amplitude_candles = 0.0; + let mut sum_ratio_amp_body = 0.0; + let mut average_amplitude = 0.0; - // TODO: BNB 코인이 있으면 - // let base_qty_to_be_ordered = - // element.base_qty_ordered.round_dp_with_strategy( - // lot_step_size.normalize().scale(), - // RoundingStrategy::ToZero, - // ); - // TODO: BNB 코인이 없으면 - - if !element.current_price.is_zero() { - if element.pure_profit_percent >= 0.3 { - is_sell = true; - } else if element.pure_profit_percent <= -0.3 { - is_sell = true; - } else if server_epoch - element.transact_time >= (600_000) * 1 { - // time up selling - is_sell = true; - } else if over_turned == true { - is_sell = true; - } - - if is_sell == true { - limit_order_close( - &element, - TimeInForce::Gtc, - element.current_price, - element.base_qty_ordered, - &client - ) - .await; - } + for window in windows { + sum_amplitude_candles += ((window.last().unwrap().high_price + - window.last().unwrap().low_price) + * 100.0) + / window.first().unwrap().close_price; } + let average_amplitude = sum_amplitude_candles / opclo_sample_length as f64; // percent unit + let mut amplitude_variance = 0.0; + + let windows = candles.windows(2); + for window in windows { + amplitude_variance += ((((window.last().unwrap().high_price + - window.last().unwrap().low_price) + * 100.0) + / window.first().unwrap().close_price) + - average_amplitude) + .powi(2); + } + amplitude_variance = amplitude_variance / (opclo_sample_length - 1) as f64; + let standard_deviation_amplitude = amplitude_variance.sqrt(); + target_profit_percent = + average_amplitude + (standard_deviation_amplitude * 0.5); + } + } + + let mut is_sell = false; + + // TODO: BNB 코인이 있으면 + // let base_qty_to_be_ordered = + // element.base_qty_ordered.round_dp_with_strategy( + // lot_step_size.normalize().scale(), + // RoundingStrategy::ToZero, + // ); + // TODO: BNB 코인이 없으면 + + if !element.current_price.is_zero() && target_profit_percent.is_normal() { + if element.pure_profit_percent >= target_profit_percent * 2.0 { + is_sell = true; + } else if element.pure_profit_percent <= target_profit_percent * -1.0 { + is_sell = true; + } else if server_epoch - element.transact_time >= (600_000) * 1 { + // time up selling + is_sell = true; + } + + if is_sell == true { + limit_order_close( + &element, + TimeInForce::Gtc, + element.current_price, + element.base_qty_ordered, + &client + ) + .await; } } } + Ok(()) }