From 23066cf0f3a5fe47ee58931437aed8d380f0ebde Mon Sep 17 00:00:00 2001 From: Sik Yoon Date: Sat, 13 Apr 2024 14:04:09 +0900 Subject: [PATCH] Formatting --- src/coex/assets_managing_team.rs | 16 +- src/coex/exchange_team.rs | 22 +- src/coex/order_team.rs | 121 +++++-- src/coin_health_check_team/monitors.rs | 9 +- src/coin_health_check_team/request_candles.rs | 4 +- src/coin_health_check_team/request_others.rs | 37 +- src/initialization.rs | 11 +- src/main.rs | 149 ++++---- src/server_health_check_team.rs | 4 +- src/strategy_team/mod.rs | 30 +- src/strategy_team/strategy_001.rs | 259 ++++++++------ src/strategy_team/strategy_002.rs | 175 ++++++---- src/strategy_team/strategy_003.rs | 144 +++++--- src/strategy_team/strategy_004.rs | 183 ++++++---- src/strategy_team/strategy_005.rs | 183 ++++++---- src/strategy_team/strategy_006.rs | 257 +++++++++----- src/strategy_team/strategy_007.rs | 323 +++++++++++------- src/strategy_team/strategy_008.rs | 312 +++++++++++------ src/strategy_team/strategy_manager.rs | 61 ++-- src/strategy_team/strategy_test.rs | 6 +- .../datapoints/price_data.rs | 44 ++- src/value_estimation_team/indicators/adx.rs | 91 +++-- .../indicators/bollingerband.rs | 52 ++- src/value_estimation_team/indicators/dema.rs | 17 +- src/value_estimation_team/indicators/ema.rs | 4 +- .../indicators/heatmap_volume.rs | 31 +- src/value_estimation_team/indicators/macd.rs | 38 ++- src/value_estimation_team/indicators/mod.rs | 4 +- src/value_estimation_team/indicators/rsi.rs | 8 +- src/value_estimation_team/indicators/sma.rs | 8 +- .../indicators/stoch_rsi.rs | 6 +- .../indicators/supertrend.rs | 78 +++-- src/value_estimation_team/indicators/tema.rs | 32 +- 33 files changed, 1689 insertions(+), 1030 deletions(-) diff --git a/src/coex/assets_managing_team.rs b/src/coex/assets_managing_team.rs index 6e89f8c..9f9d0f8 100644 --- a/src/coex/assets_managing_team.rs +++ b/src/coex/assets_managing_team.rs @@ -6,12 +6,12 @@ use crate::database_control::*; use crate::decimal_funcs::*; use crate::RunningMode::*; use crate::RUNNING_MODE; +use log; use reqwest::Client; use rust_decimal::{prelude::ToPrimitive, Decimal, RoundingStrategy}; use rust_decimal_macros::dec; use serde_json::{Result, Value}; use sqlx::FromRow; -use log; #[derive(Debug, FromRow)] pub struct AchievementEvaluationInfo { @@ -156,7 +156,7 @@ pub async fn add_extra_usdt(extra_usdt: Decimal) { // add additional_usdt into [wallet_simul] let mut update_table_name = String::new(); - unsafe{ + unsafe { if RUNNING_MODE == SIMUL { let update_table_name = String::from("wallet"); let mut value_build = String::from("free + "); @@ -186,7 +186,7 @@ pub async fn update_current_total_usdt() { } else { profit = decimal_sub(decimal_div(free_usdt, initial_usdt), dec!(1)); } - + update_values = vec![ (String::from("current_total_usdt"), free_usdt.to_string()), (String::from("profit"), profit.to_string()), @@ -198,19 +198,19 @@ pub async fn update_current_total_usdt() { ]; } else { let asset_info = select_asset_manage_announcement().await; - + let achievement_evaluation = select_achievement_evaluation().await; let balance = decimal_sub( achievement_evaluation.usdt_profit, achievement_evaluation.invested_usdt, ); let current_total_usdt = decimal_add(asset_info.initial_usdt, balance); - + let profit = decimal_sub( decimal_div(current_total_usdt, asset_info.initial_usdt), dec!(1), ); - + update_values = vec![ ( String::from("current_total_usdt"), @@ -225,7 +225,7 @@ pub async fn update_current_total_usdt() { ]; } } - + let update_table_name = String::from("asset_manage_announcement"); let update_condition = vec![(String::from("id"), String::from("1"))]; update_record3(&update_table_name, &update_values, &update_condition) @@ -541,7 +541,7 @@ pub async fn select_asset_manage_announcement() -> AssetInfo { pub async fn fetch_free_usdt() -> Decimal { let wallet_info = WalletInfo::new(); let select_table_name = { - unsafe{ + unsafe { if RUNNING_MODE == SIMUL || RUNNING_MODE == REAL { String::from("wallet") } else { diff --git a/src/coex/exchange_team.rs b/src/coex/exchange_team.rs index cd04b2e..9585ef9 100644 --- a/src/coex/exchange_team.rs +++ b/src/coex/exchange_team.rs @@ -257,7 +257,9 @@ pub async fn buy_coin( .unwrap(); for element in &filtered_suggested_coin_vec { if is_tradable == true { - if exchange_info_map.contains_key(&element.symbol) && trade_fee_map.contains_key(&element.symbol) { + if exchange_info_map.contains_key(&element.symbol) + && trade_fee_map.contains_key(&element.symbol) + { let exchange_info = exchange_info_map.get(&element.symbol).unwrap(); let lot_step_size = exchange_info.stepsize; let tick_size = exchange_info.ticksize; @@ -375,10 +377,15 @@ pub async fn buy_coin_for_test( let insert_table_name = String::from("buy_ordered_coin_list"); for element in &filtered_suggested_coin_vec { - if exchange_info_map.contains_key(&element.symbol) && trade_fee_map.contains_key(&element.symbol){ + if exchange_info_map.contains_key(&element.symbol) + && trade_fee_map.contains_key(&element.symbol) + { let lot_step_size = exchange_info_map.get(&element.symbol).unwrap().stepsize; let tick_size = exchange_info_map.get(&element.symbol).unwrap().ticksize; - let base_commission_precision = exchange_info_map.get(&element.symbol).unwrap().base_commission_precision; + let base_commission_precision = exchange_info_map + .get(&element.symbol) + .unwrap() + .base_commission_precision; let trade_fee = trade_fee_map.get(&element.symbol).unwrap().takercommission; // buy the suggested coin and transfer it into [buy_ordered_coin_list] @@ -387,8 +394,10 @@ pub async fn buy_coin_for_test( // let order_price = current_price; let order_price = element.suggested_price; - base_qty_ordered = decimal_div(unit_trade_usdt, order_price) - .round_dp_with_strategy(lot_step_size.normalize().scale(), RoundingStrategy::ToZero); + base_qty_ordered = decimal_div(unit_trade_usdt, order_price).round_dp_with_strategy( + lot_step_size.normalize().scale(), + RoundingStrategy::ToZero, + ); base_qty_fee_adjusted = decimal_mul(base_qty_ordered, decimal_sub(dec!(1), trade_fee)) .round_dp_with_strategy(base_commission_precision, RoundingStrategy::ToZero); used_usdt = decimal_mul(base_qty_ordered, order_price) @@ -865,7 +874,8 @@ pub async fn update_profit_percent() { profit_percent = (decimal_sub( decimal_div(element.usdt_profit, element.invested_usdt), dec!(1), - )).round_dp(2) + )) + .round_dp(2) .to_f64() .unwrap() * 100.0; diff --git a/src/coex/order_team.rs b/src/coex/order_team.rs index c546ae6..b905935 100644 --- a/src/coex/order_team.rs +++ b/src/coex/order_team.rs @@ -24,14 +24,14 @@ use crate::value_estimation_team::indicators::supertrend::{supertrend, Supertren use futures::future::try_join_all; use hex::ToHex; use hmac_sha256::HMAC; +use log; use reqwest::{Client, ClientBuilder}; use rust_decimal::{prelude::FromPrimitive, prelude::ToPrimitive, Decimal, RoundingStrategy}; use rust_decimal_macros::dec; use serde_json::Value; use sqlx::FromRow; -use tokio::time::*; use std::collections::HashMap; -use log; +use tokio::time::*; pub enum OrderSide { Buy, @@ -199,7 +199,7 @@ pub async fn limit_order_buy_test( // building URL and API-keys let mut url = String::new(); let mut api_key = String::new(); - unsafe{ + unsafe { if RUNNING_MODE == TEST { url.push_str(URL_TEST); api_key = API_KEY_TESTNET.to_string(); @@ -598,18 +598,26 @@ async fn update_repeat_task( for element in buy_ordered_coin_vec { // build update values update_record_build.clear(); - if coin_price_map.contains_key(&element.symbol) && exchange_info_map.contains_key(&element.symbol) && trade_fee_map.contains_key(&element.symbol) { - price = rust_decimal::prelude::FromPrimitive::from_f64(*coin_price_map.get(&element.symbol).unwrap()).unwrap(); + if coin_price_map.contains_key(&element.symbol) + && exchange_info_map.contains_key(&element.symbol) + && trade_fee_map.contains_key(&element.symbol) + { + price = rust_decimal::prelude::FromPrimitive::from_f64( + *coin_price_map.get(&element.symbol).unwrap(), + ) + .unwrap(); if !price.is_zero() { // to get quote_commission_precision let trade_fee = trade_fee_map.get(&element.symbol).unwrap().takercommission; let lot_step_size = exchange_info_map.get(&element.symbol).unwrap().stepsize; - let quote_commission_precision = exchange_info_map.get(&element.symbol).unwrap().quote_commission_precision; - let base_qty_to_be_ordered = - element.base_qty_fee_adjusted.round_dp_with_strategy( - lot_step_size.normalize().scale(), - RoundingStrategy::ToZero, - ); + let quote_commission_precision = exchange_info_map + .get(&element.symbol) + .unwrap() + .quote_commission_precision; + let base_qty_to_be_ordered = element.base_qty_fee_adjusted.round_dp_with_strategy( + lot_step_size.normalize().scale(), + RoundingStrategy::ToZero, + ); let expected_get_usdt = decimal_mul( decimal_mul(base_qty_to_be_ordered, price).round_dp_with_strategy( quote_commission_precision, @@ -617,9 +625,9 @@ async fn update_repeat_task( ), decimal_sub(dec!(1), trade_fee), ); - + // TODO: sell_count >=1 이면 expected_get_usdt 는 한번만 tradefee만 적용하여 업데이트 할 것. 현재는 수수료를 2번 (매수,매도)를 계산함. 아래 변수에 든 값으로 업데이트 하면 됨 - // let expected_get_usdt = + // let expected_get_usdt = // decimal_mul(base_qty_to_be_ordered, price).round_dp_with_strategy( // quote_commission_precision, // RoundingStrategy::ToZero, @@ -628,7 +636,7 @@ async fn update_repeat_task( / element.used_usdt.to_f64().unwrap()) - 1.0) * 100.0; - pure_profit_percent = (pure_profit_percent * 100.0).round() / 100.0; // Rounding + pure_profit_percent = (pure_profit_percent * 100.0).round() / 100.0; // Rounding update_record_build.push(element.id.to_string()); // id update_record_build.push(price.to_string()); // current_price update_record_build.push(expected_get_usdt.to_string()); //expected_get_usdt @@ -705,9 +713,17 @@ pub async fn limit_order_sell( let mut insert_value_container: Vec = Vec::new(); unsafe { if RUNNING_MODE == SIMUL && buy_ordered_coin.status == "SIMUL" { - if exchange_info_map.contains_key(&buy_ordered_coin.symbol) && trade_fee_map.contains_key(&buy_ordered_coin.symbol) { - let quote_asset_precision = exchange_info_map.get(&buy_ordered_coin.symbol).unwrap().quote_asset_precision; - let trade_fee = trade_fee_map.get(&buy_ordered_coin.symbol).unwrap().takercommission; + if exchange_info_map.contains_key(&buy_ordered_coin.symbol) + && trade_fee_map.contains_key(&buy_ordered_coin.symbol) + { + let quote_asset_precision = exchange_info_map + .get(&buy_ordered_coin.symbol) + .unwrap() + .quote_asset_precision; + let trade_fee = trade_fee_map + .get(&buy_ordered_coin.symbol) + .unwrap() + .takercommission; let get_usdt = decimal_mul(sell_base_quantity, sell_base_price) .round_dp_with_strategy(quote_asset_precision, RoundingStrategy::ToZero); @@ -735,7 +751,8 @@ pub async fn limit_order_sell( dec!(1), ), dec!(100), - ).round_dp(2); + ) + .round_dp(2); insert_value_container.push(pure_profit_percent.to_string()); // pure_profit_percent insert_value_container.push(buy_ordered_coin.maximum_profit_percent.to_string()); // maximum_profit_percent insert_value_container.push(buy_ordered_coin.registerer.to_string()); // registerer @@ -811,9 +828,17 @@ pub async fn limit_order_sell( .push(T.get("status").unwrap().as_str().unwrap().to_string()); // status insert_value_container.push(buy_ordered_coin.used_usdt.to_string()); // used_usdt - if exchange_info_map.contains_key(&buy_ordered_coin.symbol) && trade_fee_map.contains_key(&buy_ordered_coin.symbol) { - let quote_asset_precision = exchange_info_map.get(&buy_ordered_coin.symbol).unwrap().quote_asset_precision; - let trade_fee = trade_fee_map.get(&buy_ordered_coin.symbol).unwrap().takercommission; + if exchange_info_map.contains_key(&buy_ordered_coin.symbol) + && trade_fee_map.contains_key(&buy_ordered_coin.symbol) + { + let quote_asset_precision = exchange_info_map + .get(&buy_ordered_coin.symbol) + .unwrap() + .quote_asset_precision; + let trade_fee = trade_fee_map + .get(&buy_ordered_coin.symbol) + .unwrap() + .takercommission; let get_usdt = rust_decimal::prelude::FromStr::from_str( T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(), ) @@ -859,11 +884,15 @@ pub async fn limit_order_sell( if T.get("status").unwrap().as_str().unwrap() == "FILLED" { let pure_profit_percent = decimal_mul( decimal_sub( - decimal_div(get_usdt_fee_adjusted, buy_ordered_coin.used_usdt), + decimal_div( + get_usdt_fee_adjusted, + buy_ordered_coin.used_usdt, + ), dec!(1), ), dec!(100), - ).round_dp(2); + ) + .round_dp(2); insert_value_container .push(buy_ordered_coin.pure_profit_percent.to_string()); // pure_profit_percent @@ -876,7 +905,8 @@ pub async fn limit_order_sell( insert_value_container.push(buy_ordered_coin.is_long.to_string()); // is_long insert_values.push(insert_value_container.clone()); - insert_records(&insert_table_name, &insert_columns, &insert_values).await; + insert_records(&insert_table_name, &insert_columns, &insert_values) + .await; // delete record in buy_ordered_coin_list let delete_table_name = String::from("buy_ordered_coin_list"); @@ -962,7 +992,9 @@ pub async fn monitoring_filled_sell_order( for element in filled_sell_orders { // build insert value let pure_profit_usdt = decimal_sub(element.get_usdt_fee_adjusted, element.used_usdt); - let pure_profit_percent = decimal_mul(decimal_div(pure_profit_usdt, element.used_usdt), dec!(100)).round_dp(2); + let pure_profit_percent = + decimal_mul(decimal_div(pure_profit_usdt, element.used_usdt), dec!(100)) + .round_dp(2); insert_value_build.clear(); insert_value_build.push(element.symbol.clone()); // symbol insert_value_build.push(server_epoch.to_string()); // soldtime @@ -1164,7 +1196,8 @@ pub async fn cancel_buy_order( // calculate values to be updated if trade_fee_map.contains_key(&order.symbol) { - let trade_fee = trade_fee_map.get(&order.symbol).unwrap().takercommission; + let trade_fee = + trade_fee_map.get(&order.symbol).unwrap().takercommission; let base_qty_ordered = rust_decimal::prelude::FromStr::from_str( T.get("executedQty").unwrap().as_str().unwrap(), ) @@ -1344,9 +1377,15 @@ pub async fn cancel_sell_order( insert_values.push(insert_value_container.clone()); insert_records(&insert_table_name, &insert_columns, &insert_values).await; } else { - if exchange_info_map.contains_key(&order.symbol) && trade_fee_map.contains_key(&order.symbol) { - let quote_asset_precision = exchange_info_map.get(&order.symbol).unwrap().quote_asset_precision; - let trade_fee = trade_fee_map.get(&order.symbol).unwrap().takercommission; + if exchange_info_map.contains_key(&order.symbol) + && trade_fee_map.contains_key(&order.symbol) + { + let quote_asset_precision = exchange_info_map + .get(&order.symbol) + .unwrap() + .quote_asset_precision; + let trade_fee = + trade_fee_map.get(&order.symbol).unwrap().takercommission; if base_qty_executed == base_qty_ordered { // FILLED case // update status FILLED @@ -1371,7 +1410,8 @@ pub async fn cancel_sell_order( dec!(1), ), dec!(100), - ).round_dp(2); + ) + .round_dp(2); let table_name = String::from("sell_ordered_coin_list"); let mut value_build = String::from("\'"); @@ -1422,7 +1462,8 @@ pub async fn cancel_sell_order( dec!(1), ), dec!(100), - ).round_dp(2); + ) + .round_dp(2); let table_name = String::from("sell_ordered_coin_list"); let mut value_build = String::from("\'"); @@ -1756,11 +1797,18 @@ pub async fn query_sell_order( match v { Ok(T) => { - if T.get("status").is_some_and(|a| a.as_str().unwrap() == "FILLED") - || T.get("status").is_some_and(|a| a.as_str().unwrap() == "PARTIALLY_FILLED") + if T.get("status") + .is_some_and(|a| a.as_str().unwrap() == "FILLED") + || T.get("status") + .is_some_and(|a| a.as_str().unwrap() == "PARTIALLY_FILLED") { - if exchange_info_map.contains_key(&order.symbol) && trade_fee_map.contains_key(&order.symbol) { - let quote_asset_precision = exchange_info_map.get(&order.symbol).unwrap().quote_asset_precision; + if exchange_info_map.contains_key(&order.symbol) + && trade_fee_map.contains_key(&order.symbol) + { + let quote_asset_precision = exchange_info_map + .get(&order.symbol) + .unwrap() + .quote_asset_precision; let trade_fee = trade_fee_map.get(&order.symbol).unwrap().takercommission; let get_usdt = rust_decimal::prelude::FromStr::from_str( T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(), @@ -1781,7 +1829,8 @@ pub async fn query_sell_order( let pure_profit_percent = decimal_mul( decimal_sub(decimal_div(get_usdt_fee_adjusted, order.used_usdt), dec!(1)), dec!(100), - ).round_dp(2); + ) + .round_dp(2); let table_name = String::from("sell_ordered_coin_list"); let mut value_build = String::from("\'"); diff --git a/src/coin_health_check_team/monitors.rs b/src/coin_health_check_team/monitors.rs index 13868fb..3feebcb 100644 --- a/src/coin_health_check_team/monitors.rs +++ b/src/coin_health_check_team/monitors.rs @@ -11,9 +11,9 @@ use serde::Deserialize; use serde_json::Value; use sqlx::{Error, FromRow}; use std::borrow::{Borrow, BorrowMut}; +use std::collections::{HashMap, HashSet}; use std::sync::Arc; use tokio::{join, sync::Mutex, time::*}; -use std::collections::{HashMap, HashSet}; #[derive(Debug, FromRow)] struct AllCoinProfitChangeAvgList { @@ -115,13 +115,13 @@ pub async fn collect_valid_usde_trade( // get valid usdt trades let usdt_trades = select_record(&fetch_table_name, &column_name, &condition, &usdt_trades).await?; - + // get banned usdt trades #[derive(Debug, FromRow)] struct Symbols { symbol: String, } - + let table_name = String::from("banned_usdt_trades"); let column_name = String::from("symbol"); let condition = None; @@ -152,7 +152,8 @@ pub async fn collect_valid_usde_trade( .unwrap(); let step_size = value.stepsize; let step_price = decimal_mul(step_size, avg_price); - let unit_trade_usdt = crate::coex::assets_managing_team::get_unit_trade_usdt().await; + let unit_trade_usdt = + crate::coex::assets_managing_team::get_unit_trade_usdt().await; // exclude USDT trades whose step_price is over than 1% of unit_trade_usdt if step_price > decimal_mul(unit_trade_usdt, dec!(0.01)) { diff --git a/src/coin_health_check_team/request_candles.rs b/src/coin_health_check_team/request_candles.rs index 5d401d4..6bc4c67 100644 --- a/src/coin_health_check_team/request_candles.rs +++ b/src/coin_health_check_team/request_candles.rs @@ -6,9 +6,9 @@ use serde::Deserialize; use serde_json::Value; use sqlx::{Error, FromRow}; use std::borrow::{Borrow, BorrowMut}; +use std::collections::HashMap; use std::sync::Arc; use tokio::{join, sync::Mutex, time::*}; -use std::collections::HashMap; #[derive(Debug, Clone)] pub struct CandleData { @@ -166,7 +166,7 @@ async fn de_candle_json2( } if let Some(value) = candle_map.get_mut(&symbol) { - *value = candle_vec; + *value = candle_vec; } else { candle_map.insert(symbol, candle_vec); } diff --git a/src/coin_health_check_team/request_others.rs b/src/coin_health_check_team/request_others.rs index c92f22d..ecdefbf 100644 --- a/src/coin_health_check_team/request_others.rs +++ b/src/coin_health_check_team/request_others.rs @@ -1,17 +1,17 @@ use crate::database_control::*; use hex::ToHex; use hmac_sha256::HMAC; +use log; use reqwest::{Client, ClientBuilder, Response}; use rust_decimal::{prelude::FromPrimitive, prelude::ToPrimitive, Decimal, RoundingStrategy}; use serde::Deserialize; use serde_json::Value; use sqlx::{Error, FromRow}; use std::borrow::{Borrow, BorrowMut}; +use std::collections::HashMap; use std::collections::HashSet; use std::sync::Arc; -use std::collections::HashMap; use tokio::{join, sync::Mutex, time::*}; -use log; #[derive(Debug, Clone)] pub struct TradeFee { @@ -184,7 +184,7 @@ async fn de_trade_fee_json( object_map = element.as_object().unwrap(); let mut object_map_iter = object_map.iter(); - + for element in object_map_iter { match element.0.as_str() { "symbol" => symbol = element.1.as_str().unwrap().to_string(), @@ -203,7 +203,7 @@ async fn de_trade_fee_json( } } } - tradefee_map_build.insert(symbol.clone(), tradefee_data.clone()); + tradefee_map_build.insert(symbol.clone(), tradefee_data.clone()); } *tradefee_map = tradefee_map_build; Ok(()) @@ -374,8 +374,7 @@ pub async fn request_exchange_infomation( .unwrap() .ends_with("USDT") { - symbol = - (element.get("symbol").unwrap().as_str().unwrap().to_string()); + symbol = (element.get("symbol").unwrap().as_str().unwrap().to_string()); exchange_info.base_asset_precision = (element.get("baseAssetPrecision").unwrap().as_u64().unwrap()) as u32; exchange_info.base_commission_precision = (element @@ -442,7 +441,7 @@ pub async fn request_delist_symbols( secret_key: &str, local_epoch: u128, difference_epoch: i64, - client: &Client + client: &Client, ) -> Result<(), Box> { let mut base_url = String::from("https://api.binance.com/sapi/v1/spot/delist-schedule?"); @@ -512,28 +511,26 @@ async fn de_deilst_symbol_json( for element in into_vec.unwrap() { object_map = element.as_object().unwrap(); let mut object_map_iter = object_map.iter(); - + for element in object_map_iter { match element.0.as_str() { - "delistTime" => { - }, + "delistTime" => {} "symbols" => { if let Some(array) = element.1.as_array() { for delist_symbol in array { if let Some(symbol) = delist_symbol.as_str() { if symbol.ends_with("USDT") && !delist_hashset.contains(symbol) { - delist_hashset.insert(symbol.to_string()); + delist_hashset.insert(symbol.to_string()); } } } } - }, + } _ => { log::error!("Elements in body msg are changed. Please update both your delist table and vectors."); } } } - } if delist_hashset.len() != 0 { @@ -541,7 +538,7 @@ async fn de_deilst_symbol_json( struct Symbols { symbol: String, } - + let table_name = String::from("banned_usdt_trades"); let column_name = String::from("symbol"); let condition = None; @@ -550,8 +547,8 @@ async fn de_deilst_symbol_json( }; let mut select_result = try_select_record(&table_name, &column_name, &condition, &symbols) - .await - .unwrap(); + .await + .unwrap(); let mut banned_usdt_trades_set: HashSet = HashSet::new(); for element in select_result { banned_usdt_trades_set.insert(element.symbol.clone()); @@ -562,11 +559,11 @@ async fn de_deilst_symbol_json( if !banned_usdt_trades_set.contains(element) { let insert_values = vec![element.clone()]; insert_one_record(&table_name, &insert_column, &insert_values) - .await - .unwrap(); - } + .await + .unwrap(); + } } } - + Ok(()) } diff --git a/src/initialization.rs b/src/initialization.rs index 1c3a405..a031053 100644 --- a/src/initialization.rs +++ b/src/initialization.rs @@ -292,8 +292,8 @@ async fn initialize_database() { vec![String::from("ANCUSDT")], ]; insert_records(&table_name, &columns, &value_wrapper) - .await - .unwrap(); + .await + .unwrap(); } println!("Ok"); } @@ -907,9 +907,10 @@ async fn initialize_database() { let mut symbols = Symbols { symbol: String::new(), }; - let symbols_vec = select_record(&fetch_table_name, &column_name, &condition, &symbols) - .await - .expect("Failed to fetch records!"); + let symbols_vec = + select_record(&fetch_table_name, &column_name, &condition, &symbols) + .await + .expect("Failed to fetch records!"); let insert_table_name = String::from("wallet"); let insert_columns = vec!["asset", "free", "locked"]; let mut insert_values: Vec> = Vec::new(); diff --git a/src/main.rs b/src/main.rs index dcad773..ed4c77d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,8 +8,11 @@ use crate::server_health_check_team::ServerHealth; use crate::strategy_team::AllData; use crate::time_checking_team::UserTime; use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; +use clap::{arg, Command}; +use log::Level; use reqwest::{Client, ClientBuilder}; use rust_decimal::Decimal; +use simple_logger::set_up_color_terminal; use sqlx::{mysql::*, Connection, Executor, FromRow, Row}; use std::collections::{HashMap, HashSet}; use std::{ @@ -18,9 +21,6 @@ use std::{ }; use tokio::{fs::*, join, sync::mpsc, sync::watch, sync::Mutex, task::*, time::*}; use tradingbot::{RunningMode::*, *}; -use clap::{arg, Command}; -use log::Level; -use simple_logger::set_up_color_terminal; #[tokio::main] async fn main() -> Result<(), Box> { @@ -301,9 +301,9 @@ async fn main() -> Result<(), Box> { loop { let instant = Instant::now(); let client = ClientBuilder::new() - .timeout(Duration::from_millis(1000)) - .build() - .unwrap(); + .timeout(Duration::from_millis(1000)) + .build() + .unwrap(); let mut usertime = UserTime::new(); let mut serverhealth = ServerHealth::new(); let mut epoch_difference_vec: Vec = Vec::new(); @@ -330,14 +330,18 @@ async fn main() -> Result<(), Box> { match result { Ok(T) => { - local_epoch_tx1.send(usertime.local_epoch) + local_epoch_tx1 + .send(usertime.local_epoch) .expect("local_epoch_tx1-local_epoch_rx1 channel has been closed."); - epoch_difference_tx1.send(usertime.epoch_difference) - .expect("epoch_difference_tx1-epoch_difference_rx1 channel has been closed."); - local_epoch_tx2.send(usertime.local_epoch) + epoch_difference_tx1.send(usertime.epoch_difference).expect( + "epoch_difference_tx1-epoch_difference_rx1 channel has been closed.", + ); + local_epoch_tx2 + .send(usertime.local_epoch) .expect("local_epoch_tx2-local_epoch_rx2 channel has been closed."); - epoch_difference_tx2.send(usertime.epoch_difference) - .expect("epoch_difference_tx2-epoch_difference_rx2 channel has been closed."); + epoch_difference_tx2.send(usertime.epoch_difference).expect( + "epoch_difference_tx2-epoch_difference_rx2 channel has been closed.", + ); tx_task1.send(1).expect("The mpsc channel has been closed."); } Err(E) => {} @@ -398,9 +402,9 @@ async fn main() -> Result<(), Box> { loop { let instant = Instant::now(); let client = ClientBuilder::new() - .timeout(tokio::time::Duration::from_millis(3000)) - .build() - .unwrap(); + .timeout(tokio::time::Duration::from_millis(3000)) + .build() + .unwrap(); let tx1_changed = local_epoch_rx1.changed().await; let tx2_changed = epoch_difference_rx1.changed().await; let local_epoch = *local_epoch_rx1.borrow(); @@ -429,7 +433,6 @@ async fn main() -> Result<(), Box> { tx_tradefee_map.send_modify(|vec| *vec = tradefee_vec_temp); tx_task2.send(2).expect("The mpsc channel has been closed."); - } Err(E) => { panic!("tx2-rx2 channel has been closed.") @@ -452,9 +455,9 @@ async fn main() -> Result<(), Box> { tokio::task::spawn(async move { loop { let client = ClientBuilder::new() - .timeout(tokio::time::Duration::from_millis(3000)) - .build() - .unwrap(); + .timeout(tokio::time::Duration::from_millis(3000)) + .build() + .unwrap(); let mut exchange_info_map_temp: HashMap = HashMap::new(); let mut result; loop { @@ -472,7 +475,6 @@ async fn main() -> Result<(), Box> { } } - tx_exchange_info_map.send_modify(|vec| *vec = exchange_info_map_temp); tx_task3.send(3).expect("The mpsc channel has been closed."); @@ -492,9 +494,9 @@ async fn main() -> Result<(), Box> { loop { let instant = Instant::now(); let client = ClientBuilder::new() - .timeout(tokio::time::Duration::from_millis(3000)) - .build() - .unwrap(); + .timeout(tokio::time::Duration::from_millis(3000)) + .build() + .unwrap(); let result = request_others::request_24hr_ticker_price_change_statistics(&client).await; match result { Ok(T) => { @@ -506,9 +508,8 @@ async fn main() -> Result<(), Box> { ) .await; - tx_valid_usdt_trade_set - .send_modify(|vec| *vec = valid_usdt_trade_set_temp); - tx_task4.send(4).expect("The mpsc channel has been closed."); + tx_valid_usdt_trade_set.send_modify(|vec| *vec = valid_usdt_trade_set_temp); + tx_task4.send(4).expect("The mpsc channel has been closed."); } Err(E) => { log::warn!(">>> Failed to monitor usdt_24h_change_profit_index."); @@ -560,9 +561,9 @@ async fn main() -> Result<(), Box> { loop { let instant = Instant::now(); let client = ClientBuilder::new() - .timeout(tokio::time::Duration::from_millis(1000)) - .build() - .unwrap(); + .timeout(tokio::time::Duration::from_millis(1000)) + .build() + .unwrap(); let mut price_vec_temp: HashMap = HashMap::new(); let mut price_vec_temp_c: HashMap = HashMap::new(); request_others::request_all_coin_price(&client, &mut price_vec_temp).await; @@ -576,8 +577,10 @@ async fn main() -> Result<(), Box> { let interval = String::from("1m"); let candle_1m_vec = rx_candle_1m_map.borrow().clone(); let dummy_data: HashMap> = HashMap::new(); - let mut rt_price_1m_map_write_temp: HashMap> = HashMap::new(); - let mut rt_price_1m_map_write_temp_c: HashMap> = HashMap::new(); + let mut rt_price_1m_map_write_temp: HashMap> = + HashMap::new(); + let mut rt_price_1m_map_write_temp_c: HashMap> = + HashMap::new(); let result = value_estimation_team::datapoints::price_data::update_realtime_price_data( &interval, &candle_1m_vec, @@ -597,8 +600,10 @@ async fn main() -> Result<(), Box> { // 30m let interval = String::from("30m"); let candle_30m_map = rx_candle_30m_map.borrow().clone(); - let mut rt_price_30m_map_write_temp: HashMap> = HashMap::new(); - let mut rt_price_30m_map_write_temp_c: HashMap> = HashMap::new(); + let mut rt_price_30m_map_write_temp: HashMap> = + HashMap::new(); + let mut rt_price_30m_map_write_temp_c: HashMap> = + HashMap::new(); if !rt_price_1m_map_write_temp_c.is_empty() { let result = value_estimation_team::datapoints::price_data::update_realtime_price_data( @@ -614,15 +619,19 @@ async fn main() -> Result<(), Box> { if tx_rt_price_30m_map.is_closed() { log::error!("tx_rt_price_30m_vec has been closed!"); } else { - tx_rt_price_30m_map - .send_modify(|map: &mut HashMap>| *map = rt_price_30m_map_write_temp); - } + tx_rt_price_30m_map.send_modify( + |map: &mut HashMap>| { + *map = rt_price_30m_map_write_temp + }, + ); + } } // 1d let interval = String::from("1d"); let candle_1d_vec = rx_candle_1d_map.borrow().clone(); - let mut rt_price_1d_map_write_temp: HashMap> = HashMap::new(); + let mut rt_price_1d_map_write_temp: HashMap> = + HashMap::new(); if !rt_price_30m_map_write_temp_c.is_empty() { let result = @@ -1055,8 +1064,7 @@ async fn main() -> Result<(), Box> { let instant = Instant::now(); let exchange_info_map = rx_exchange_info_map.borrow().clone(); let trade_fee_map = rx_tradefee_map.borrow().clone(); - let result = - coex::exchange_team::buy_coin(&exchange_info_map, &trade_fee_map).await; + let result = coex::exchange_team::buy_coin(&exchange_info_map, &trade_fee_map).await; // send Task#0 a message to notify running on match result { @@ -1087,15 +1095,12 @@ async fn main() -> Result<(), Box> { loop { let instant = Instant::now(); let client = ClientBuilder::new() - .timeout(tokio::time::Duration::from_millis(3000)) - .build() - .unwrap(); + .timeout(tokio::time::Duration::from_millis(3000)) + .build() + .unwrap(); let trade_fee_map = rx2_tradefee_map.borrow().clone(); - let result = coex::order_team::monitoring_open_buy_order( - &client, - &trade_fee_map, - ) - .await; + let result = + coex::order_team::monitoring_open_buy_order(&client, &trade_fee_map).await; // send Task#0 a message to notify running on match result { @@ -1162,9 +1167,9 @@ async fn main() -> Result<(), Box> { loop { let instant = Instant::now(); let client = ClientBuilder::new() - .timeout(tokio::time::Duration::from_millis(3000)) - .build() - .unwrap(); + .timeout(tokio::time::Duration::from_millis(3000)) + .build() + .unwrap(); let exchange_info_vec = rx4_exchange_info_map.borrow().clone(); let trade_fee_vec = rx4_tradefee_map.borrow().clone(); let result = coex::order_team::monitoring_open_sell_order( @@ -1200,9 +1205,9 @@ async fn main() -> Result<(), Box> { loop { let instant = Instant::now(); let client = ClientBuilder::new() - .timeout(tokio::time::Duration::from_millis(3000)) - .build() - .unwrap(); + .timeout(tokio::time::Duration::from_millis(3000)) + .build() + .unwrap(); let result = coex::order_team::monitoring_filled_sell_order(&client).await; // send Task#0 a message to notify running on @@ -1260,9 +1265,9 @@ async fn main() -> Result<(), Box> { loop { let instant = Instant::now(); let client = ClientBuilder::new() - .timeout(tokio::time::Duration::from_millis(3000)) - .build() - .unwrap(); + .timeout(tokio::time::Duration::from_millis(3000)) + .build() + .unwrap(); let result = coex::assets_managing_team::monitoring_asset_usdt(&mut previous_result, &client) .await; @@ -1320,25 +1325,26 @@ async fn main() -> Result<(), Box> { loop { let instant = Instant::now(); let client = ClientBuilder::new() - .timeout(tokio::time::Duration::from_millis(3000)) - .build() - .unwrap(); + .timeout(tokio::time::Duration::from_millis(3000)) + .build() + .unwrap(); let tx1_changed = local_epoch_rx2.changed().await; let tx2_changed = epoch_difference_rx2.changed().await; let local_epoch = *local_epoch_rx2.borrow(); let difference_epoch = *epoch_difference_rx2.borrow(); - - + let result = request_others::request_delist_symbols( API_KEY, SECRET_KEY, local_epoch, difference_epoch, - &client + &client, ) .await; - tx_task27.send(27).expect("The mpsc channel has been closed."); + tx_task27 + .send(27) + .expect("The mpsc channel has been closed."); // sleep as much as the loop recurs per 300 seconds if all operation finished within 300 seconds. elapsed_time = instant.elapsed().as_secs(); @@ -1348,18 +1354,20 @@ async fn main() -> Result<(), Box> { } }); - loop { - } + loop {} Ok(()) } fn program_setting() { let matches = Command::new("Tradingbot") - .arg(arg!(log_level: -l --log "Select log level: trace, debug, info, warn, error").default_value("error")) + .arg( + arg!(log_level: -l --log "Select log level: trace, debug, info, warn, error") + .default_value("error"), + ) .arg(arg!(mode: -m --mode "Select mode: real, simul, test").required(true)) .get_matches(); - + // set log level set_up_color_terminal(); if let Some(level) = matches.get_one::("log_level") { @@ -1374,7 +1382,6 @@ fn program_setting() { log::error!("wrong log level argument."); std::process::exit(0); } - } } @@ -1385,15 +1392,15 @@ fn program_setting() { "real" => { RUNNING_MODE = RunningMode::REAL; println!("*** REAL MODE ***"); - }, + } "simul" => { RUNNING_MODE = RunningMode::SIMUL; println!("*** SIMULATION MODE ***"); - }, + } "test" => { RUNNING_MODE = RunningMode::TEST; println!("*** TEST MODE ***"); - }, + } _ => { log::error!("wrong mode argument."); std::process::exit(0); diff --git a/src/server_health_check_team.rs b/src/server_health_check_team.rs index 93033e3..2b7d804 100644 --- a/src/server_health_check_team.rs +++ b/src/server_health_check_team.rs @@ -3,11 +3,11 @@ use crate::database_control::*; use crate::time_checking_team::UserTime; +use log; use rand::*; use reqwest::{Client, ClientBuilder, Response}; use tokio::join; use tokio::time::*; -use log; #[derive(Debug)] pub struct ServerHealth { @@ -55,7 +55,7 @@ pub async fn execute_server_health_check( if waiting_time > 2 { log::warn!(">>> retry connection after {} second(s).", waiting_time); } - + sleep(Duration::from_secs(waiting_time as u64)).await; } } diff --git a/src/strategy_team/mod.rs b/src/strategy_team/mod.rs index 50f9204..88f7703 100644 --- a/src/strategy_team/mod.rs +++ b/src/strategy_team/mod.rs @@ -9,13 +9,15 @@ pub mod strategy_008; // pub mod strategy_test; pub mod strategy_manager; -use crate::coex::order_team::{limit_order_sell, select_filled_buy_orders}; use crate::coex::exchange_team::get_server_epoch; +use crate::coex::order_team::{limit_order_sell, select_filled_buy_orders}; use crate::coin_health_check_team::request_others::{ExchangeInfo, TradeFee}; use crate::database_control::*; use crate::decimal_funcs::*; -use crate::value_estimation_team::datapoints::price_data::{RealtimePriceData, CandleType}; +use crate::value_estimation_team::datapoints::price_data::{CandleType, RealtimePriceData}; +use crate::value_estimation_team::indicators::adx::{adx, AdxData}; use crate::value_estimation_team::indicators::bollingerband::{bollingerband, BollingerBandData}; +use crate::value_estimation_team::indicators::dema::{dema, DemaData}; use crate::value_estimation_team::indicators::ema::{ema, ema_opclo, EmaData}; use crate::value_estimation_team::indicators::heatmap_volume::{ heatmap_volume, HeatMapLevel, HeatmapVolumeData, @@ -24,19 +26,19 @@ use crate::value_estimation_team::indicators::macd::{ema_macd, MacdData}; use crate::value_estimation_team::indicators::rsi::{rsi, RsiData}; use crate::value_estimation_team::indicators::sma::{sma, sma_opclo, SmaData}; use crate::value_estimation_team::indicators::stoch_rsi::{stoch_rsi, StochRsiData}; -use crate::value_estimation_team::indicators::supertrend::{supertrend, SupertrendData, SuperTrendArea, SuperTrendSignal}; -use crate::value_estimation_team::indicators::adx::{AdxData, adx}; -use crate::value_estimation_team::indicators::dema::{DemaData, dema}; -use crate::value_estimation_team::indicators::tema::{TemaData, tema}; +use crate::value_estimation_team::indicators::supertrend::{ + supertrend, SuperTrendArea, SuperTrendSignal, SupertrendData, +}; +use crate::value_estimation_team::indicators::tema::{tema, TemaData}; use futures::future::try_join_all; use reqwest::{Client, ClientBuilder}; use rust_decimal::{prelude::FromPrimitive, prelude::ToPrimitive, Decimal, RoundingStrategy}; use rust_decimal_macros::dec; use sqlx::FromRow; +use std::collections::{HashMap, HashSet}; use std::sync::Arc; use strategy_manager::insert_pre_suggested_coins; use tokio::sync::Mutex; -use std::collections::{HashMap, HashSet}; #[derive(Debug, Clone)] pub struct AllData { @@ -92,9 +94,10 @@ impl FilteredDataValue { } } -pub async fn duplicate_filter(registerer: i32, original_filtered_data: &HashMap) --> Result, Box> -{ +pub async fn duplicate_filter( + registerer: i32, + original_filtered_data: &HashMap, +) -> Result, Box> { let inspect_table_name_1 = String::from("buy_ordered_coin_list"); let inspect_table_name_2 = String::from("sell_ordered_coin_list"); let inspect_table_name_3 = String::from("pre_suggested_coin_list"); @@ -146,14 +149,17 @@ pub async fn duplicate_filter(registerer: i32, original_filtered_data: &HashMap< Ok(filtered_data_c) } -pub async fn remove_keys(filtered_data: &mut HashMap, keys_to_remove: HashSet) { +pub async fn remove_keys( + filtered_data: &mut HashMap, + keys_to_remove: HashSet, +) { let len_prev = filtered_data.len(); // remove key-value in filtered_data for symbol in keys_to_remove { filtered_data.remove(&symbol); } let len_now = filtered_data.len(); - + // shrink capacity of filtered_data if len_now != len_prev { filtered_data.shrink_to_fit(); diff --git a/src/strategy_team/strategy_001.rs b/src/strategy_team/strategy_001.rs index ac7e670..08c7650 100644 --- a/src/strategy_team/strategy_001.rs +++ b/src/strategy_team/strategy_001.rs @@ -1,11 +1,12 @@ use crate::value_estimation_team::indicators::bollingerband::bollingerband; use super::{ - dec, decimal_add, decimal_sub, decimal_div, ema, exists_record, insert_pre_suggested_coins, - limit_order_sell, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, AllData, - Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredDataValue, Mutex, - RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, get_server_epoch, MacdData, ema_macd, - BollingerBandData, ToPrimitive, duplicate_filter, HashMap, HashSet, remove_keys, SuperTrendArea, SuperTrendSignal + adx, dec, decimal_add, decimal_div, decimal_sub, duplicate_filter, ema, ema_macd, + exists_record, get_server_epoch, insert_pre_suggested_coins, limit_order_sell, remove_keys, + rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, update_record3, AdxData, + AllData, Arc, BollingerBandData, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, + FilteredDataValue, HashMap, HashSet, MacdData, Mutex, RealtimePriceData, RoundingStrategy, + RsiData, StochRsiData, SuperTrendArea, SuperTrendSignal, SupertrendData, ToPrimitive, TradeFee, }; // BB lowerband + SuperTrend + StochRSI @@ -23,7 +24,7 @@ pub async fn list_up_for_buy( for symbol in &alldata.valid_symbol_vec { filtered_data.insert(symbol.clone(), FilteredDataValue::new()); } - + // 4th filtering: 0.5% <= the average amplitude of the latest 10 30m candles <= 1.0% let mut keys_to_remove: HashSet = HashSet::new(); for (symbol, values) in &mut filtered_data { @@ -31,12 +32,14 @@ pub async fn list_up_for_buy( let rt_price_30m = alldata.rt_price_30m_vec.get(symbol).unwrap(); let vec_len = rt_price_30m.len(); - if let Some(candles) = rt_price_30m.get(vec_len-12..vec_len-1) { + if let Some(candles) = rt_price_30m.get(vec_len - 12..vec_len - 1) { let windows = candles.windows(2); let mut average_amplitude = 0.0; for window in windows { - average_amplitude += (window.last().unwrap().high_price - window.last().unwrap().low_price) / window.first().unwrap().close_price; + average_amplitude += (window.last().unwrap().high_price + - window.last().unwrap().low_price) + / window.first().unwrap().close_price; } average_amplitude /= 10.0; @@ -52,61 +55,48 @@ pub async fn list_up_for_buy( } } remove_keys(&mut filtered_data, keys_to_remove).await; - + // 2nd filtering: supertrend(ATR period 20, multiplier: 2, 30m close price) let mut keys_to_remove: HashSet = HashSet::new(); let server_epoch = get_server_epoch().await; - let supertrend_30m_map = supertrend(20, 2.0, true, &alldata.rt_price_30m_vec, &filtered_data).await?; + let supertrend_30m_map = + supertrend(20, 2.0, true, &alldata.rt_price_30m_vec, &filtered_data).await?; for (symbol, values) in &mut filtered_data { - if let (Some(supertrend_vec), Some(rt_price_vec)) = (supertrend_30m_map.get(symbol), alldata.rt_price_30m_vec.get(symbol)) { - if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && - rt_price_vec.last().unwrap().close_time > server_epoch { - // input closetime, current_price - values.closetime = rt_price_vec.last().unwrap().close_time; - values.current_price = rust_decimal::prelude::FromPrimitive::from_f64(rt_price_vec.last().unwrap().close_price).unwrap(); - - // input stoploss - let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap(); - if supertrend_vec.last().unwrap().area == SuperTrendArea::DOWN && - supertrend_vec.last().unwrap().band_value > values.current_price.to_f64().unwrap() - { - values.stoploss = decimal_sub(values.current_price, decimal_sub(band_value, values.current_price)); - - } else if supertrend_vec.last().unwrap().area == SuperTrendArea::UP && - supertrend_vec.last().unwrap().band_value < values.current_price.to_f64().unwrap() - { - values.stoploss = band_value; - } else { - keys_to_remove.insert(symbol.clone()); - } + if let (Some(supertrend_vec), Some(rt_price_vec)) = ( + supertrend_30m_map.get(symbol), + alldata.rt_price_30m_vec.get(symbol), + ) { + if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time + && rt_price_vec.last().unwrap().close_time > server_epoch + { + // input closetime, current_price + values.closetime = rt_price_vec.last().unwrap().close_time; + values.current_price = rust_decimal::prelude::FromPrimitive::from_f64( + rt_price_vec.last().unwrap().close_price, + ) + .unwrap(); + + // input stoploss + let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64( + supertrend_vec.last().unwrap().band_value, + ) + .unwrap(); + if supertrend_vec.last().unwrap().area == SuperTrendArea::DOWN + && supertrend_vec.last().unwrap().band_value + > values.current_price.to_f64().unwrap() + { + values.stoploss = decimal_sub( + values.current_price, + decimal_sub(band_value, values.current_price), + ); + } else if supertrend_vec.last().unwrap().area == SuperTrendArea::UP + && supertrend_vec.last().unwrap().band_value + < values.current_price.to_f64().unwrap() + { + values.stoploss = band_value; } else { keys_to_remove.insert(symbol.clone()); } - } else { - keys_to_remove.insert(symbol.clone()); - } - } - remove_keys(&mut filtered_data, keys_to_remove).await; - - // filtering: the latest 5 30m candle close prices > EMA 200 - let mut keys_to_remove: HashSet = HashSet::new(); - let emas = ema(200, &alldata.rt_price_30m_vec, &filtered_data).await?; - for (symbol, values) in &mut filtered_data { - if emas.contains_key(symbol) && alldata.rt_price_30m_vec.contains_key(symbol) { - let ema = emas.get(symbol).unwrap(); - let rt_price_30m = alldata.rt_price_30m_vec.get(symbol).unwrap(); - let rt_price_30m_len = rt_price_30m.len(); - let search_result = ema.binary_search_by_key( - &alldata.rt_price_30m_vec.get(symbol).unwrap().last().unwrap().close_time, - |EmaData { - ema_value, - close_time, - }| *close_time); - if search_result.is_ok_and(|x| ema[search_result.unwrap()].ema_value < rt_price_30m[rt_price_30m_len-1].close_price) && - search_result.is_ok_and(|x| ema[search_result.unwrap()-1].ema_value < rt_price_30m[rt_price_30m_len-2].close_price) && - search_result.is_ok_and(|x| ema[search_result.unwrap()-2].ema_value < rt_price_30m[rt_price_30m_len-3].close_price) && - search_result.is_ok_and(|x| ema[search_result.unwrap()-3].ema_value < rt_price_30m[rt_price_30m_len-4].close_price) && - search_result.is_ok_and(|x| ema[search_result.unwrap()-4].ema_value < rt_price_30m[rt_price_30m_len-5].close_price) { } else { keys_to_remove.insert(symbol.clone()); } @@ -115,15 +105,65 @@ pub async fn list_up_for_buy( } } remove_keys(&mut filtered_data, keys_to_remove).await; - + + // filtering: the latest 5 30m candle close prices > EMA 200 + let mut keys_to_remove: HashSet = HashSet::new(); + let emas = ema(200, &alldata.rt_price_30m_vec, &filtered_data).await?; + for (symbol, values) in &mut filtered_data { + if emas.contains_key(symbol) && alldata.rt_price_30m_vec.contains_key(symbol) { + let ema = emas.get(symbol).unwrap(); + let rt_price_30m = alldata.rt_price_30m_vec.get(symbol).unwrap(); + let rt_price_30m_len = rt_price_30m.len(); + let search_result = ema.binary_search_by_key( + &alldata + .rt_price_30m_vec + .get(symbol) + .unwrap() + .last() + .unwrap() + .close_time, + |EmaData { + ema_value, + close_time, + }| *close_time, + ); + if search_result.is_ok_and(|x| { + ema[search_result.unwrap()].ema_value + < rt_price_30m[rt_price_30m_len - 1].close_price + }) && search_result.is_ok_and(|x| { + ema[search_result.unwrap() - 1].ema_value + < rt_price_30m[rt_price_30m_len - 2].close_price + }) && search_result.is_ok_and(|x| { + ema[search_result.unwrap() - 2].ema_value + < rt_price_30m[rt_price_30m_len - 3].close_price + }) && search_result.is_ok_and(|x| { + ema[search_result.unwrap() - 3].ema_value + < rt_price_30m[rt_price_30m_len - 4].close_price + }) && search_result.is_ok_and(|x| { + ema[search_result.unwrap() - 4].ema_value + < rt_price_30m[rt_price_30m_len - 5].close_price + }) { + } else { + keys_to_remove.insert(symbol.clone()); + } + } else { + keys_to_remove.insert(symbol.clone()); + } + } + remove_keys(&mut filtered_data, keys_to_remove).await; + // filtering: 30m StochRSI (RSI_len: 10, StochRSI_len: 10, K: 3, D: 3) previous K < 5 && current K > previous K let mut keys_to_remove: HashSet = HashSet::new(); let stoch_rsis = stoch_rsi(10, 10, 3, 3, &alldata.rt_price_30m_vec, &filtered_data).await?; for (symbol, values) in &mut filtered_data { if alldata.rt_price_30m_vec.contains_key(symbol) && stoch_rsis.contains_key(symbol) { let stoch_rsi_vec = stoch_rsis.get(symbol).unwrap(); - let search_result = stoch_rsi_vec.iter().position(|x| x.close_time == values.closetime); - if search_result.is_some_and(|a| stoch_rsi_vec[a-1].k < 5.0 && stoch_rsi_vec[a].k > stoch_rsi_vec[a-1].k) { + let search_result = stoch_rsi_vec + .iter() + .position(|x| x.close_time == values.closetime); + if search_result.is_some_and(|a| { + stoch_rsi_vec[a - 1].k < 5.0 && stoch_rsi_vec[a].k > stoch_rsi_vec[a - 1].k + }) { } else { keys_to_remove.insert(symbol.clone()); } @@ -132,17 +172,26 @@ pub async fn list_up_for_buy( } } remove_keys(&mut filtered_data, keys_to_remove).await; - + // filtering: 1d MACD (3, 7, 30) current MACD-signal > prev MACD-signal let mut keys_to_remove: HashSet = HashSet::new(); let macd_1d_map = ema_macd(3, 7, 30, &alldata.rt_price_1d_vec, &filtered_data).await?; for (symbol, values) in &mut filtered_data { - if let (Some(macd_vec), Some(rt_price_vec)) = (macd_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol)) { - if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && - rt_price_vec.last().unwrap().close_time > server_epoch { - if macd_vec[macd_vec.len()-1].macd_value - macd_vec[macd_vec.len()-1].signal_value > macd_vec[macd_vec.len()-2].macd_value - macd_vec[macd_vec.len()-2].signal_value - { - values.current_price = rust_decimal::prelude::FromPrimitive::from_f64(rt_price_vec.last().unwrap().close_price).unwrap(); + if let (Some(macd_vec), Some(rt_price_vec)) = + (macd_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol)) + { + if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time + && rt_price_vec.last().unwrap().close_time > server_epoch + { + if macd_vec[macd_vec.len() - 1].macd_value + - macd_vec[macd_vec.len() - 1].signal_value + > macd_vec[macd_vec.len() - 2].macd_value + - macd_vec[macd_vec.len() - 2].signal_value + { + values.current_price = rust_decimal::prelude::FromPrimitive::from_f64( + rt_price_vec.last().unwrap().close_price, + ) + .unwrap(); values.closetime = rt_price_vec.last().unwrap().close_time; } else { keys_to_remove.insert(symbol.clone()); @@ -161,10 +210,13 @@ pub async fn list_up_for_buy( let adx_vec = adx(10, 10, &alldata.rt_price_30m_vec, &filtered_data).await?; for (symbol, values) in &mut filtered_data { if let Some(adx_vec) = adx_vec.get(symbol) { - if let Some(last_idx) = adx_vec.iter().position(|elem| elem.close_time == values.closetime) { - if - adx_vec[last_idx].adx < adx_vec[last_idx-1].adx && - adx_vec[last_idx-1].adx < adx_vec[last_idx-2].adx { + if let Some(last_idx) = adx_vec + .iter() + .position(|elem| elem.close_time == values.closetime) + { + if adx_vec[last_idx].adx < adx_vec[last_idx - 1].adx + && adx_vec[last_idx - 1].adx < adx_vec[last_idx - 2].adx + { } else { keys_to_remove.insert(symbol.clone()); } @@ -173,7 +225,7 @@ pub async fn list_up_for_buy( } } else { keys_to_remove.insert(symbol.clone()); - } + } } remove_keys(&mut filtered_data, keys_to_remove).await; @@ -202,37 +254,49 @@ pub async fn list_up_for_sell( for element in &filled_buy_orders { filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new()); } - let supertrend_30m = supertrend(20, 2.0, true, &all_data.rt_price_30m_vec, &filtered_symbols).await?; - let stoch_rsis = stoch_rsi(10, 10, 3, 3, &all_data.rt_price_30m_vec, &filtered_symbols).await?; + let supertrend_30m = + supertrend(20, 2.0, true, &all_data.rt_price_30m_vec, &filtered_symbols).await?; + let stoch_rsis = + stoch_rsi(10, 10, 3, 3, &all_data.rt_price_30m_vec, &filtered_symbols).await?; for element in filled_buy_orders { if element.used_usdt >= dec!(10.0) { - if let (Some(exchange_info), Some(tradefee), Some(stoch_rsi), Some(supertrend_vec)) = - (exchange_info_map.get(&element.symbol), trade_fee_map.get(&element.symbol), stoch_rsis.get(&element.symbol), supertrend_30m.get(&element.symbol)) { + if let ( + Some(exchange_info), + Some(tradefee), + Some(stoch_rsi), + Some(supertrend_vec), + ) = ( + exchange_info_map.get(&element.symbol), + trade_fee_map.get(&element.symbol), + stoch_rsis.get(&element.symbol), + supertrend_30m.get(&element.symbol), + ) { // update stoploss - let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap(); + let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64( + supertrend_vec.last().unwrap().band_value, + ) + .unwrap(); if supertrend_vec.last().unwrap().area == SuperTrendArea::UP - && band_value > element.stoploss { - let update_table_name = String::from("buy_ordered_coin_list"); - let update_value = vec![ - (String::from("stoploss"), band_value.to_string()), - ]; - let update_condition = vec![(String::from("id"), element.id.to_string())]; - update_record3(&update_table_name, &update_value, &update_condition) - .await - .unwrap(); - } + && band_value > element.stoploss + { + let update_table_name = String::from("buy_ordered_coin_list"); + let update_value = vec![(String::from("stoploss"), band_value.to_string())]; + let update_condition = vec![(String::from("id"), element.id.to_string())]; + update_record3(&update_table_name, &update_value, &update_condition) + .await + .unwrap(); + } let lot_step_size = exchange_info.stepsize; let quote_commission_precision = exchange_info.quote_commission_precision; - let base_qty_to_be_ordered = - element.base_qty_ordered.round_dp_with_strategy( - lot_step_size.normalize().scale(), - RoundingStrategy::ToZero, - ); + let base_qty_to_be_ordered = element.base_qty_ordered.round_dp_with_strategy( + lot_step_size.normalize().scale(), + RoundingStrategy::ToZero, + ); let stoch_rsi_k = stoch_rsi.last().unwrap().k; - let stoch_rsi_k_prev = stoch_rsi[stoch_rsi.len()-2].k; + let stoch_rsi_k_prev = stoch_rsi[stoch_rsi.len() - 2].k; let stoch_rsi_d = stoch_rsi.last().unwrap().d; - let stoch_rsi_d_prev = stoch_rsi[stoch_rsi.len()-2].d; + let stoch_rsi_d_prev = stoch_rsi[stoch_rsi.len() - 2].d; if (element.is_long == 0 || element.is_long == 1) && !element.current_price.is_zero() { @@ -266,9 +330,9 @@ pub async fn list_up_for_sell( &trade_fee_map, ) .await; - } else if stoch_rsi_k >= 90.0 && - (stoch_rsi_k < stoch_rsi_d && - stoch_rsi_k_prev > stoch_rsi_d_prev) { + } else if stoch_rsi_k >= 90.0 + && (stoch_rsi_k < stoch_rsi_d && stoch_rsi_k_prev > stoch_rsi_d_prev) + { limit_order_sell( &element, element.current_price, @@ -280,7 +344,7 @@ pub async fn list_up_for_sell( .await; } // TODO: sell_count가 1일 때 적용하기 - // else if (supertrend_vec + // else if (supertrend_vec // .last() // .unwrap() // .signal @@ -305,7 +369,6 @@ pub async fn list_up_for_sell( // } } } - } } } diff --git a/src/strategy_team/strategy_002.rs b/src/strategy_team/strategy_002.rs index 968b2ad..59f82a3 100644 --- a/src/strategy_team/strategy_002.rs +++ b/src/strategy_team/strategy_002.rs @@ -1,11 +1,12 @@ use crate::value_estimation_team::indicators::bollingerband::bollingerband; use super::{ - dec, decimal_add, decimal_sub, decimal_div, ema, exists_record, insert_pre_suggested_coins, - limit_order_sell, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, AllData, - Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredDataValue, Mutex, - RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, get_server_epoch, MacdData, ema_macd, - BollingerBandData, ToPrimitive, duplicate_filter, HashMap, HashSet, remove_keys, SuperTrendArea, SuperTrendSignal + adx, dec, decimal_add, decimal_div, decimal_sub, duplicate_filter, ema, ema_macd, + exists_record, get_server_epoch, insert_pre_suggested_coins, limit_order_sell, remove_keys, + rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, update_record3, AdxData, + AllData, Arc, BollingerBandData, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, + FilteredDataValue, HashMap, HashSet, MacdData, Mutex, RealtimePriceData, RoundingStrategy, + RsiData, StochRsiData, SuperTrendArea, SuperTrendSignal, SupertrendData, ToPrimitive, TradeFee, }; // BB 30m lowerband + BB 1m lowerband @@ -29,12 +30,14 @@ pub async fn list_up_for_buy( if let Some(rt_price_30m_vec) = alldata.rt_price_30m_vec.get(symbol) { let vec_len = rt_price_30m_vec.len(); if vec_len >= 11 { - let candles = rt_price_30m_vec.get(vec_len-12..vec_len-1).unwrap(); + let candles = rt_price_30m_vec.get(vec_len - 12..vec_len - 1).unwrap(); let windows = candles.windows(2); let mut average_amplitude = 0.0; for window in windows { - average_amplitude += (window.last().unwrap().high_price - window.last().unwrap().low_price) / window.first().unwrap().close_price; + average_amplitude += (window.last().unwrap().high_price + - window.last().unwrap().low_price) + / window.first().unwrap().close_price; } average_amplitude /= 10.0; @@ -50,30 +53,46 @@ pub async fn list_up_for_buy( } } remove_keys(&mut filtered_data, keys_to_remove).await; - + // 2nd filtering: BollingerBand (len:10, multiplier 2.5) previous_30m_price (close or low price) < lower_band let mut keys_to_remove: HashSet = HashSet::new(); - let bollingerbands_30m_map = bollingerband(10, 2.5, &alldata.rt_price_30m_vec, &filtered_data).await?; - let bollingerbands_1m_map = bollingerband(30, 3.0, &alldata.rt_price_1m_vec, &filtered_data).await?; + let bollingerbands_30m_map = + bollingerband(10, 2.5, &alldata.rt_price_30m_vec, &filtered_data).await?; + let bollingerbands_1m_map = + bollingerband(30, 3.0, &alldata.rt_price_1m_vec, &filtered_data).await?; let server_epoch = get_server_epoch().await; for (symbol, filtered_data) in &mut filtered_data { - if let (Some(bb_30m_vec), Some(bb_1m_vec), Some(rt_30m_vec)) = (bollingerbands_30m_map.get(symbol), bollingerbands_1m_map.get(symbol), alldata.rt_price_30m_vec.get(symbol)) { - if rt_30m_vec.len() >= 3 && bb_30m_vec.len() >= 3 && bb_1m_vec.len() >= 3 && rt_30m_vec.last().unwrap().close_time > server_epoch { + if let (Some(bb_30m_vec), Some(bb_1m_vec), Some(rt_30m_vec)) = ( + bollingerbands_30m_map.get(symbol), + bollingerbands_1m_map.get(symbol), + alldata.rt_price_30m_vec.get(symbol), + ) { + if rt_30m_vec.len() >= 3 + && bb_30m_vec.len() >= 3 + && bb_1m_vec.len() >= 3 + && rt_30m_vec.last().unwrap().close_time > server_epoch + { let bb_30m_search_result = bb_30m_vec.binary_search_by_key( &rt_30m_vec.last().unwrap().close_time, |BollingerBandData { - sma, - upperband, - lowerband, - close_time, - }| *close_time, + sma, + upperband, + lowerband, + close_time, + }| *close_time, ); if bb_30m_search_result.is_ok() { - if bb_30m_vec[bb_30m_search_result.unwrap()].lowerband > rt_30m_vec[rt_30m_vec.len()-1].close_price && - bb_1m_vec.last().unwrap().lowerband > rt_30m_vec[rt_30m_vec.len()-1].close_price + if bb_30m_vec[bb_30m_search_result.unwrap()].lowerband + > rt_30m_vec[rt_30m_vec.len() - 1].close_price + && bb_1m_vec.last().unwrap().lowerband + > rt_30m_vec[rt_30m_vec.len() - 1].close_price { filtered_data.closetime = rt_30m_vec.last().unwrap().close_time; - filtered_data.current_price = rust_decimal::prelude::FromPrimitive::from_f64(rt_30m_vec.last().unwrap().close_price).unwrap(); + filtered_data.current_price = + rust_decimal::prelude::FromPrimitive::from_f64( + rt_30m_vec.last().unwrap().close_price, + ) + .unwrap(); } else { keys_to_remove.insert(symbol.clone()); } @@ -91,26 +110,48 @@ pub async fn list_up_for_buy( // 3rd filtering: supertrend(ATR period 7, multiplier: 1.5, 30m close price), area should be DOWN let mut keys_to_remove: HashSet = HashSet::new(); - let supertrend_30m_map = supertrend( 7, 1.5, true, &alldata.rt_price_30m_vec, &filtered_data).await?; + let supertrend_30m_map = + supertrend(7, 1.5, true, &alldata.rt_price_30m_vec, &filtered_data).await?; let server_epoch = get_server_epoch().await; - for (symbol, filtered_data) in &mut filtered_data { - if let (Some(rt_30m_vec), Some(supertrend_vec)) = (alldata.rt_price_30m_vec.get(symbol), supertrend_30m_map.get(symbol)) { - if rt_30m_vec.len() >= 3 && supertrend_vec.len() >= 3 && rt_30m_vec.last().unwrap().close_time > server_epoch { + for (symbol, filtered_data) in &mut filtered_data { + if let (Some(rt_30m_vec), Some(supertrend_vec)) = ( + alldata.rt_price_30m_vec.get(symbol), + supertrend_30m_map.get(symbol), + ) { + if rt_30m_vec.len() >= 3 + && supertrend_vec.len() >= 3 + && rt_30m_vec.last().unwrap().close_time > server_epoch + { let supertrend_search_result = supertrend_vec.binary_search_by_key( &rt_30m_vec.last().unwrap().close_time, |SupertrendData { - band_value, - signal, - area, - close_time, - }| *close_time, + band_value, + signal, + area, + close_time, + }| *close_time, ); if supertrend_search_result.is_ok() { - if supertrend_vec[supertrend_search_result.unwrap()].area == SuperTrendArea::DOWN - && supertrend_vec[supertrend_search_result.unwrap()].band_value > filtered_data.current_price.to_f64().unwrap() + if supertrend_vec[supertrend_search_result.unwrap()].area + == SuperTrendArea::DOWN + && supertrend_vec[supertrend_search_result.unwrap()].band_value + > filtered_data.current_price.to_f64().unwrap() { - filtered_data.target_price = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec[supertrend_search_result.unwrap()].band_value).unwrap(); - let stop_loss = decimal_sub(filtered_data.current_price, decimal_div(decimal_sub(filtered_data.target_price, filtered_data.current_price), dec!(2))); + filtered_data.target_price = + rust_decimal::prelude::FromPrimitive::from_f64( + supertrend_vec[supertrend_search_result.unwrap()].band_value, + ) + .unwrap(); + let stop_loss = decimal_sub( + filtered_data.current_price, + decimal_div( + decimal_sub( + filtered_data.target_price, + filtered_data.current_price, + ), + dec!(2), + ), + ); filtered_data.stoploss = stop_loss; } else { keys_to_remove.insert(symbol.clone()); @@ -131,18 +172,31 @@ pub async fn list_up_for_buy( let mut keys_to_remove: HashSet = HashSet::new(); let ema_map = ema(200, &alldata.rt_price_30m_vec, &filtered_data).await?; for (symbol, filtered_data) in &mut filtered_data { - if let (Some(ema_vec), Some(rt_30m_vec)) = (ema_map.get(symbol), alldata.rt_price_30m_vec.get(symbol)) { + if let (Some(ema_vec), Some(rt_30m_vec)) = + (ema_map.get(symbol), alldata.rt_price_30m_vec.get(symbol)) + { let search_result = ema_vec.binary_search_by_key( &rt_30m_vec.last().unwrap().close_time, |EmaData { - ema_value, - close_time, - }| *close_time); - if search_result.is_ok_and(|x| ema_vec[x].ema_value < rt_30m_vec[rt_30m_vec.len()-1].close_price) && - search_result.is_ok_and(|x| ema_vec[x-1].ema_value < rt_30m_vec[rt_30m_vec.len()-2].close_price) && - search_result.is_ok_and(|x| ema_vec[x-2].ema_value < rt_30m_vec[rt_30m_vec.len()-3].close_price) && - search_result.is_ok_and(|x| ema_vec[x-3].ema_value < rt_30m_vec[rt_30m_vec.len()-4].close_price) && - search_result.is_ok_and(|x| ema_vec[x-4].ema_value < rt_30m_vec[rt_30m_vec.len()-5].close_price) { + ema_value, + close_time, + }| *close_time, + ); + if search_result + .is_ok_and(|x| ema_vec[x].ema_value < rt_30m_vec[rt_30m_vec.len() - 1].close_price) + && search_result.is_ok_and(|x| { + ema_vec[x - 1].ema_value < rt_30m_vec[rt_30m_vec.len() - 2].close_price + }) + && search_result.is_ok_and(|x| { + ema_vec[x - 2].ema_value < rt_30m_vec[rt_30m_vec.len() - 3].close_price + }) + && search_result.is_ok_and(|x| { + ema_vec[x - 3].ema_value < rt_30m_vec[rt_30m_vec.len() - 4].close_price + }) + && search_result.is_ok_and(|x| { + ema_vec[x - 4].ema_value < rt_30m_vec[rt_30m_vec.len() - 5].close_price + }) + { } else { keys_to_remove.insert(symbol.clone()); } @@ -156,12 +210,21 @@ pub async fn list_up_for_buy( let mut keys_to_remove: HashSet = HashSet::new(); let macd_1d_map = ema_macd(3, 7, 30, &alldata.rt_price_1d_vec, &filtered_data).await?; for (symbol, values) in &mut filtered_data { - if let (Some(macd_vec), Some(rt_price_vec)) = (macd_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol)) { - if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && - rt_price_vec.last().unwrap().close_time > server_epoch { - if macd_vec[macd_vec.len()-1].macd_value - macd_vec[macd_vec.len()-1].signal_value > macd_vec[macd_vec.len()-2].macd_value - macd_vec[macd_vec.len()-2].signal_value - { - values.current_price = rust_decimal::prelude::FromPrimitive::from_f64(rt_price_vec.last().unwrap().close_price).unwrap(); + if let (Some(macd_vec), Some(rt_price_vec)) = + (macd_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol)) + { + if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time + && rt_price_vec.last().unwrap().close_time > server_epoch + { + if macd_vec[macd_vec.len() - 1].macd_value + - macd_vec[macd_vec.len() - 1].signal_value + > macd_vec[macd_vec.len() - 2].macd_value + - macd_vec[macd_vec.len() - 2].signal_value + { + values.current_price = rust_decimal::prelude::FromPrimitive::from_f64( + rt_price_vec.last().unwrap().close_price, + ) + .unwrap(); values.closetime = rt_price_vec.last().unwrap().close_time; } else { keys_to_remove.insert(symbol.clone()); @@ -174,7 +237,7 @@ pub async fn list_up_for_buy( } } remove_keys(&mut filtered_data, keys_to_remove).await; - + let final_filtered_data = duplicate_filter(2, &filtered_data).await?; insert_pre_suggested_coins(2, false, &final_filtered_data, &alldata).await; @@ -199,17 +262,15 @@ pub async fn list_up_for_sell( if let Some(exchange_info) = exchange_info_map.get(&element.symbol) { let lot_step_size = exchange_info.stepsize; let quote_commission_precision = exchange_info.quote_commission_precision; - let base_qty_to_be_ordered = - element.base_qty_ordered.round_dp_with_strategy( - lot_step_size.normalize().scale(), - RoundingStrategy::ToZero, - ); + let base_qty_to_be_ordered = element.base_qty_ordered.round_dp_with_strategy( + lot_step_size.normalize().scale(), + RoundingStrategy::ToZero, + ); if (element.is_long == 0 || element.is_long == 1) && !element.current_price.is_zero() { - if element.current_price >= element.target_price - { + if element.current_price >= element.target_price { limit_order_sell( &element, element.current_price, @@ -241,7 +302,7 @@ pub async fn list_up_for_sell( .await; } // TODO: sell_count가 1일 때 적용하기 - // else if (supertrend_vec + // else if (supertrend_vec // .last() // .unwrap() // .signal diff --git a/src/strategy_team/strategy_003.rs b/src/strategy_team/strategy_003.rs index abd35dd..2729d99 100644 --- a/src/strategy_team/strategy_003.rs +++ b/src/strategy_team/strategy_003.rs @@ -1,11 +1,13 @@ use crate::value_estimation_team::indicators::bollingerband::bollingerband; use super::{ - dec, decimal_add, decimal_sub, decimal_div, ema, ema_opclo, sma, sma_opclo, exists_record, insert_pre_suggested_coins, - limit_order_sell, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, AllData, - Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredDataValue, Mutex, SmaData, - RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, get_server_epoch, MacdData, ema_macd, - BollingerBandData, ToPrimitive, duplicate_filter, HashMap, HashSet, remove_keys, SuperTrendArea, SuperTrendSignal + adx, dec, decimal_add, decimal_div, decimal_sub, duplicate_filter, ema, ema_macd, ema_opclo, + exists_record, get_server_epoch, insert_pre_suggested_coins, limit_order_sell, remove_keys, + rsi, select_filled_buy_orders, sma, sma_opclo, stoch_rsi, supertrend, try_join_all, + update_record3, AdxData, AllData, Arc, BollingerBandData, Client, ClientBuilder, Decimal, + EmaData, ExchangeInfo, FilteredDataValue, HashMap, HashSet, MacdData, Mutex, RealtimePriceData, + RoundingStrategy, RsiData, SmaData, StochRsiData, SuperTrendArea, SuperTrendSignal, + SupertrendData, ToPrimitive, TradeFee, }; // BUY: 30m SMA5 (opclo_price) < 30m EMA3 (opclo_price) @@ -25,23 +27,31 @@ pub async fn list_up_for_buy( // 3rd filtering: supertrend(ATR period 20, multiplier: 4.0, 30m close price), area should be UP let mut keys_to_remove: HashSet = HashSet::new(); - let supertrend_30m_map = supertrend(20, 4.0, true, &alldata.rt_price_30m_vec, &filtered_data).await?; + let supertrend_30m_map = + supertrend(20, 4.0, true, &alldata.rt_price_30m_vec, &filtered_data).await?; let server_epoch = get_server_epoch().await; - for (symbol, filtered_data) in &mut filtered_data { - if let (Some(rt_30m_vec), Some(supertrend_vec)) = (alldata.rt_price_30m_vec.get(symbol), supertrend_30m_map.get(symbol)) { - if rt_30m_vec.len() >= 3 && supertrend_vec.len() >= 3 && rt_30m_vec.last().unwrap().close_time > server_epoch { + for (symbol, filtered_data) in &mut filtered_data { + if let (Some(rt_30m_vec), Some(supertrend_vec)) = ( + alldata.rt_price_30m_vec.get(symbol), + supertrend_30m_map.get(symbol), + ) { + if rt_30m_vec.len() >= 3 + && supertrend_vec.len() >= 3 + && rt_30m_vec.last().unwrap().close_time > server_epoch + { let supertrend_search_result = supertrend_vec.binary_search_by_key( &rt_30m_vec.last().unwrap().close_time, |SupertrendData { - band_value, - signal, - area, - close_time, - }| *close_time, + band_value, + signal, + area, + close_time, + }| *close_time, ); if supertrend_search_result.is_ok() { if supertrend_vec[supertrend_search_result.unwrap()].area == SuperTrendArea::UP - && supertrend_vec[supertrend_search_result.unwrap()].band_value < filtered_data.current_price.to_f64().unwrap() + && supertrend_vec[supertrend_search_result.unwrap()].band_value + < filtered_data.current_price.to_f64().unwrap() { } else { keys_to_remove.insert(symbol.clone()); @@ -57,17 +67,24 @@ pub async fn list_up_for_buy( } } remove_keys(&mut filtered_data, keys_to_remove).await; - + // 5th filtering: 30m StochRSI (RSI_len: 30, StochRSI_len: 30, K: 3, D: 3) previous K, D / current K, D < 10 && current K > current D let mut keys_to_remove: HashSet = HashSet::new(); let stoch_rsis = stoch_rsi(30, 30, 3, 3, &alldata.rt_price_30m_vec, &filtered_data).await?; for (symbol, values) in &mut filtered_data { if alldata.rt_price_30m_vec.contains_key(symbol) && stoch_rsis.contains_key(symbol) { let stoch_rsi_vec = stoch_rsis.get(symbol).unwrap(); - let search_result = stoch_rsi_vec.iter().position(|x| x.close_time == values.closetime); - if search_result.is_some_and(|a| stoch_rsi_vec[a-1].k < 10.0 && stoch_rsi_vec[a].k < 10.0 - && stoch_rsi_vec[a-1].d < 10.0 && stoch_rsi_vec[a].d< 10.0 && - stoch_rsi_vec[a-1].k <= stoch_rsi_vec[a-1].d && stoch_rsi_vec[a].k > stoch_rsi_vec[a].d) { + let search_result = stoch_rsi_vec + .iter() + .position(|x| x.close_time == values.closetime); + if search_result.is_some_and(|a| { + stoch_rsi_vec[a - 1].k < 10.0 + && stoch_rsi_vec[a].k < 10.0 + && stoch_rsi_vec[a - 1].d < 10.0 + && stoch_rsi_vec[a].d < 10.0 + && stoch_rsi_vec[a - 1].k <= stoch_rsi_vec[a - 1].d + && stoch_rsi_vec[a].k > stoch_rsi_vec[a].d + }) { } else { keys_to_remove.insert(symbol.clone()); } @@ -80,19 +97,31 @@ pub async fn list_up_for_buy( // 3rd filtering: the latest 5 30m candle close prices > EMA 200 let mut keys_to_remove: HashSet = HashSet::new(); let emas = ema(200, &alldata.rt_price_30m_vec, &filtered_data).await?; - for (symbol, values) in &mut filtered_data { + for (symbol, values) in &mut filtered_data { if emas.contains_key(symbol) && alldata.rt_price_30m_vec.contains_key(symbol) { let ema = emas.get(symbol).unwrap(); let rt_price_30m = alldata.rt_price_30m_vec.get(symbol).unwrap(); let rt_price_30m_len = rt_price_30m.len(); let search_result = ema.binary_search_by_key( - &alldata.rt_price_30m_vec.get(symbol).unwrap().last().unwrap().close_time, + &alldata + .rt_price_30m_vec + .get(symbol) + .unwrap() + .last() + .unwrap() + .close_time, |EmaData { - ema_value, - close_time, - }| *close_time); - if search_result.is_ok_and(|x| ema[search_result.unwrap()].ema_value < rt_price_30m[rt_price_30m_len-1].close_price) && - search_result.is_ok_and(|x| ema[search_result.unwrap()-1].ema_value < rt_price_30m[rt_price_30m_len-2].close_price) { + ema_value, + close_time, + }| *close_time, + ); + if search_result.is_ok_and(|x| { + ema[search_result.unwrap()].ema_value + < rt_price_30m[rt_price_30m_len - 1].close_price + }) && search_result.is_ok_and(|x| { + ema[search_result.unwrap() - 1].ema_value + < rt_price_30m[rt_price_30m_len - 2].close_price + }) { } else { keys_to_remove.insert(symbol.clone()); } @@ -106,12 +135,21 @@ pub async fn list_up_for_buy( let mut keys_to_remove: HashSet = HashSet::new(); let macd_1d_map = ema_macd(3, 7, 30, &alldata.rt_price_1d_vec, &filtered_data).await?; for (symbol, values) in &mut filtered_data { - if let (Some(macd_vec), Some(rt_price_vec)) = (macd_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol)) { - if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && - rt_price_vec.last().unwrap().close_time > server_epoch { - if macd_vec[macd_vec.len()-1].macd_value - macd_vec[macd_vec.len()-1].signal_value > macd_vec[macd_vec.len()-2].macd_value - macd_vec[macd_vec.len()-2].signal_value - { - values.current_price = rust_decimal::prelude::FromPrimitive::from_f64(rt_price_vec.last().unwrap().close_price).unwrap(); + if let (Some(macd_vec), Some(rt_price_vec)) = + (macd_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol)) + { + if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time + && rt_price_vec.last().unwrap().close_time > server_epoch + { + if macd_vec[macd_vec.len() - 1].macd_value + - macd_vec[macd_vec.len() - 1].signal_value + > macd_vec[macd_vec.len() - 2].macd_value + - macd_vec[macd_vec.len() - 2].signal_value + { + values.current_price = rust_decimal::prelude::FromPrimitive::from_f64( + rt_price_vec.last().unwrap().close_price, + ) + .unwrap(); values.closetime = rt_price_vec.last().unwrap().close_time; } else { keys_to_remove.insert(symbol.clone()); @@ -124,7 +162,7 @@ pub async fn list_up_for_buy( } } remove_keys(&mut filtered_data, keys_to_remove).await; - + let final_filtered_data = duplicate_filter(3, &filtered_data).await?; insert_pre_suggested_coins(3, false, &final_filtered_data, &alldata).await; @@ -148,27 +186,29 @@ pub async fn list_up_for_sell( filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new()); } let server_epoch = get_server_epoch().await; - let stoch_rsis = stoch_rsi(30, 30, 3, 3, &all_data.rt_price_30m_vec, &filtered_symbols).await?; + let stoch_rsis = + stoch_rsi(30, 30, 3, 3, &all_data.rt_price_30m_vec, &filtered_symbols).await?; for element in filled_buy_orders { if element.used_usdt >= dec!(10.0) { - if let (Some(exchange_info), Some(stoch_rsi)) = (exchange_info_map.get(&element.symbol), stoch_rsis.get(&element.symbol)) { + if let (Some(exchange_info), Some(stoch_rsi)) = ( + exchange_info_map.get(&element.symbol), + stoch_rsis.get(&element.symbol), + ) { let lot_step_size = exchange_info.stepsize; let quote_commission_precision = exchange_info.quote_commission_precision; - let base_qty_to_be_ordered = - element.base_qty_ordered.round_dp_with_strategy( - lot_step_size.normalize().scale(), - RoundingStrategy::ToZero, - ); + let base_qty_to_be_ordered = element.base_qty_ordered.round_dp_with_strategy( + lot_step_size.normalize().scale(), + RoundingStrategy::ToZero, + ); let stoch_rsi_k = stoch_rsi.last().unwrap().k; - let stoch_rsi_k_prev = stoch_rsi[stoch_rsi.len()-2].k; + let stoch_rsi_k_prev = stoch_rsi[stoch_rsi.len() - 2].k; let stoch_rsi_d = stoch_rsi.last().unwrap().d; - let stoch_rsi_d_prev = stoch_rsi[stoch_rsi.len()-2].d; + let stoch_rsi_d_prev = stoch_rsi[stoch_rsi.len() - 2].d; if (element.is_long == 0 || element.is_long == 1) && !element.current_price.is_zero() { - if element.current_price >= element.target_price - { + if element.current_price >= element.target_price { limit_order_sell( &element, element.current_price, @@ -208,10 +248,12 @@ pub async fn list_up_for_sell( &trade_fee_map, ) .await; - } else if stoch_rsi_k >= 20.0 && stoch_rsi_k >= 20.0 && - stoch_rsi_k_prev >= 20.0 && stoch_rsi_k_prev >= 20.0 && - stoch_rsi_k < stoch_rsi_d && - stoch_rsi_k_prev >= stoch_rsi_d_prev + } else if stoch_rsi_k >= 20.0 + && stoch_rsi_k >= 20.0 + && stoch_rsi_k_prev >= 20.0 + && stoch_rsi_k_prev >= 20.0 + && stoch_rsi_k < stoch_rsi_d + && stoch_rsi_k_prev >= stoch_rsi_d_prev { limit_order_sell( &element, @@ -222,9 +264,9 @@ pub async fn list_up_for_sell( &trade_fee_map, ) .await; - } + } // TODO: sell_count가 1일 때 적용하기 - // else if (supertrend_vec + // else if (supertrend_vec // .last() // .unwrap() // .signal diff --git a/src/strategy_team/strategy_004.rs b/src/strategy_team/strategy_004.rs index c63ae0e..f244b56 100644 --- a/src/strategy_team/strategy_004.rs +++ b/src/strategy_team/strategy_004.rs @@ -1,11 +1,13 @@ use crate::value_estimation_team::indicators::bollingerband::bollingerband; use super::{ - dec, decimal_add, decimal_sub, decimal_div, ema, exists_record, insert_pre_suggested_coins, - limit_order_sell, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, AllData, - Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredDataValue, Mutex, - RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, get_server_epoch, MacdData, ema_macd, - BollingerBandData, ToPrimitive, duplicate_filter, HashMap, HashSet, remove_keys, SuperTrendArea, SuperTrendSignal, CandleType + adx, dec, decimal_add, decimal_div, decimal_sub, duplicate_filter, ema, ema_macd, + exists_record, get_server_epoch, insert_pre_suggested_coins, limit_order_sell, remove_keys, + rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, update_record3, AdxData, + AllData, Arc, BollingerBandData, CandleType, Client, ClientBuilder, Decimal, EmaData, + ExchangeInfo, FilteredDataValue, HashMap, HashSet, MacdData, Mutex, RealtimePriceData, + RoundingStrategy, RsiData, StochRsiData, SuperTrendArea, SuperTrendSignal, SupertrendData, + ToPrimitive, TradeFee, }; // BB lowerband + SuperTrend + StochRSI @@ -30,12 +32,14 @@ pub async fn list_up_for_buy( if let Some(rt_price_30m_vec) = alldata.rt_price_30m_vec.get(symbol) { let vec_len = rt_price_30m_vec.len(); if vec_len >= 11 { - let candles = rt_price_30m_vec.get(vec_len-12..vec_len-1).unwrap(); + let candles = rt_price_30m_vec.get(vec_len - 12..vec_len - 1).unwrap(); let windows = candles.windows(2); let mut average_amplitude = 0.0; for window in windows { - average_amplitude += (window.last().unwrap().high_price - window.last().unwrap().low_price) / window.first().unwrap().close_price; + average_amplitude += (window.last().unwrap().high_price + - window.last().unwrap().low_price) + / window.first().unwrap().close_price; } average_amplitude /= 10.0; @@ -51,31 +55,45 @@ pub async fn list_up_for_buy( } } remove_keys(&mut filtered_data, keys_to_remove).await; - + // 2nd filtering: BollingerBand (len:30, multiplier 3) previous_30m_price (close or low price) < lower_band let mut keys_to_remove: HashSet = HashSet::new(); - let bollingerband_map = bollingerband(30, 3.0, &alldata.rt_price_30m_vec, &filtered_data).await?; + let bollingerband_map = + bollingerband(30, 3.0, &alldata.rt_price_30m_vec, &filtered_data).await?; let server_epoch = get_server_epoch().await; - for (symbol, filtered_data) in &mut filtered_data { - if let (Some(bb_vec), Some(rt_30m_vec)) = (bollingerband_map.get(symbol), alldata.rt_price_30m_vec.get(symbol)) { - if rt_30m_vec.len() >= 3 && bb_vec.len() >= 3 && rt_30m_vec.last().unwrap().close_time > server_epoch { + for (symbol, filtered_data) in &mut filtered_data { + if let (Some(bb_vec), Some(rt_30m_vec)) = ( + bollingerband_map.get(symbol), + alldata.rt_price_30m_vec.get(symbol), + ) { + if rt_30m_vec.len() >= 3 + && bb_vec.len() >= 3 + && rt_30m_vec.last().unwrap().close_time > server_epoch + { let bb_search_result = bb_vec.binary_search_by_key( &rt_30m_vec.last().unwrap().close_time, |BollingerBandData { - sma, - upperband, - lowerband, - close_time, - }| *close_time, + sma, + upperband, + lowerband, + close_time, + }| *close_time, ); if bb_search_result.is_ok() { - if bb_vec[bb_search_result.unwrap()-1].lowerband > rt_30m_vec[rt_30m_vec.len()-2].low_price && - rt_30m_vec[rt_30m_vec.len()-2].opclo_price > rt_30m_vec.last().unwrap().close_price && - rt_30m_vec[rt_30m_vec.len()-2].candle_type == CandleType::DOWN && - rt_30m_vec[rt_30m_vec.len()-2].high_price < bb_vec[bb_search_result.unwrap()-1].sma + if bb_vec[bb_search_result.unwrap() - 1].lowerband + > rt_30m_vec[rt_30m_vec.len() - 2].low_price + && rt_30m_vec[rt_30m_vec.len() - 2].opclo_price + > rt_30m_vec.last().unwrap().close_price + && rt_30m_vec[rt_30m_vec.len() - 2].candle_type == CandleType::DOWN + && rt_30m_vec[rt_30m_vec.len() - 2].high_price + < bb_vec[bb_search_result.unwrap() - 1].sma { filtered_data.closetime = rt_30m_vec.last().unwrap().close_time; - filtered_data.current_price = rust_decimal::prelude::FromPrimitive::from_f64(rt_30m_vec.last().unwrap().close_price).unwrap(); + filtered_data.current_price = + rust_decimal::prelude::FromPrimitive::from_f64( + rt_30m_vec.last().unwrap().close_price, + ) + .unwrap(); } else { keys_to_remove.insert(symbol.clone()); } @@ -94,24 +112,43 @@ pub async fn list_up_for_buy( // 3rd filtering: supertrend(ATR period 10, multiplier: 2, 30m close price), area should be DOWN let mut keys_to_remove: HashSet = HashSet::new(); let server_epoch = get_server_epoch().await; - let supertrend_30m_map = supertrend( 10, 2.0, true, &alldata.rt_price_30m_vec, &filtered_data).await?; + let supertrend_30m_map = + supertrend(10, 2.0, true, &alldata.rt_price_30m_vec, &filtered_data).await?; for (symbol, filtered_data) in &mut filtered_data { - if let (Some(supertrend_vec), Some(rt_30m_vec)) = (supertrend_30m_map.get(symbol), alldata.rt_price_30m_vec.get(symbol)) { - if rt_30m_vec.len() >= 3 && supertrend_vec.len() >= 3 && rt_30m_vec.last().unwrap().close_time > server_epoch { + if let (Some(supertrend_vec), Some(rt_30m_vec)) = ( + supertrend_30m_map.get(symbol), + alldata.rt_price_30m_vec.get(symbol), + ) { + if rt_30m_vec.len() >= 3 + && supertrend_vec.len() >= 3 + && rt_30m_vec.last().unwrap().close_time > server_epoch + { let supertrend_search_result = supertrend_vec.binary_search_by_key( &rt_30m_vec.last().unwrap().close_time, |SupertrendData { - band_value, - signal, - area, - close_time, - }| *close_time, + band_value, + signal, + area, + close_time, + }| *close_time, ); - if supertrend_search_result.is_ok_and(|x| - supertrend_vec[x].area == SuperTrendArea::DOWN && supertrend_vec[x].band_value > filtered_data.current_price.to_f64().unwrap()) { - filtered_data.target_price = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec[supertrend_search_result.unwrap()].band_value).unwrap(); - let stop_loss = decimal_sub(filtered_data.current_price, decimal_div(decimal_sub(filtered_data.target_price, filtered_data.current_price), dec!(2))); - filtered_data.stoploss = stop_loss; + if supertrend_search_result.is_ok_and(|x| { + supertrend_vec[x].area == SuperTrendArea::DOWN + && supertrend_vec[x].band_value + > filtered_data.current_price.to_f64().unwrap() + }) { + filtered_data.target_price = rust_decimal::prelude::FromPrimitive::from_f64( + supertrend_vec[supertrend_search_result.unwrap()].band_value, + ) + .unwrap(); + let stop_loss = decimal_sub( + filtered_data.current_price, + decimal_div( + decimal_sub(filtered_data.target_price, filtered_data.current_price), + dec!(2), + ), + ); + filtered_data.stoploss = stop_loss; } else { keys_to_remove.insert(symbol.clone()); } @@ -128,18 +165,32 @@ pub async fn list_up_for_buy( let mut keys_to_remove: HashSet = HashSet::new(); let ema_map = ema(200, &alldata.rt_price_30m_vec, &filtered_data).await?; for (symbol, filtered_data) in &mut filtered_data { - if let (Some(ema_vec), Some(rt_30m_vec)) = (ema_map.get(symbol), alldata.rt_price_30m_vec.get(symbol)) { + if let (Some(ema_vec), Some(rt_30m_vec)) = + (ema_map.get(symbol), alldata.rt_price_30m_vec.get(symbol)) + { let search_result = ema_vec.binary_search_by_key( &rt_30m_vec.last().unwrap().close_time, |EmaData { - ema_value, - close_time, - }| *close_time); - if search_result.is_ok_and(|x| ema_vec[search_result.unwrap()].ema_value < rt_30m_vec[rt_30m_vec.len()-1].close_price) && - search_result.is_ok_and(|x| ema_vec[search_result.unwrap()-1].ema_value < rt_30m_vec[rt_30m_vec.len()-2].close_price) && - search_result.is_ok_and(|x| ema_vec[search_result.unwrap()-2].ema_value < rt_30m_vec[rt_30m_vec.len()-3].close_price) && - search_result.is_ok_and(|x| ema_vec[search_result.unwrap()-3].ema_value < rt_30m_vec[rt_30m_vec.len()-4].close_price) && - search_result.is_ok_and(|x| ema_vec[search_result.unwrap()-4].ema_value < rt_30m_vec[rt_30m_vec.len()-5].close_price) { + ema_value, + close_time, + }| *close_time, + ); + if search_result.is_ok_and(|x| { + ema_vec[search_result.unwrap()].ema_value + < rt_30m_vec[rt_30m_vec.len() - 1].close_price + }) && search_result.is_ok_and(|x| { + ema_vec[search_result.unwrap() - 1].ema_value + < rt_30m_vec[rt_30m_vec.len() - 2].close_price + }) && search_result.is_ok_and(|x| { + ema_vec[search_result.unwrap() - 2].ema_value + < rt_30m_vec[rt_30m_vec.len() - 3].close_price + }) && search_result.is_ok_and(|x| { + ema_vec[search_result.unwrap() - 3].ema_value + < rt_30m_vec[rt_30m_vec.len() - 4].close_price + }) && search_result.is_ok_and(|x| { + ema_vec[search_result.unwrap() - 4].ema_value + < rt_30m_vec[rt_30m_vec.len() - 5].close_price + }) { } else { keys_to_remove.insert(symbol.clone()); } @@ -152,10 +203,13 @@ pub async fn list_up_for_buy( // 6th filtering: 30m StochRSI (RSI_len: 10, StochRSI_len: 10, K: 3, D: 3) current K, D < 20 let mut keys_to_remove: HashSet = HashSet::new(); let stoch_rsi_map = stoch_rsi(10, 10, 3, 3, &alldata.rt_price_30m_vec, &filtered_data).await?; - for (symbol, filtered_data) in &mut filtered_data { + for (symbol, filtered_data) in &mut filtered_data { if let Some(stoch_rsi_vec) = stoch_rsi_map.get(symbol) { - let search_result = stoch_rsi_vec.iter().position(|x| x.close_time == filtered_data.closetime); - if search_result.is_some_and(|a| stoch_rsi_vec[a].k < 15.0 && stoch_rsi_vec[a].d < 15.0) { + let search_result = stoch_rsi_vec + .iter() + .position(|x| x.close_time == filtered_data.closetime); + if search_result.is_some_and(|a| stoch_rsi_vec[a].k < 15.0 && stoch_rsi_vec[a].d < 15.0) + { } else { keys_to_remove.insert(symbol.clone()); } @@ -169,12 +223,21 @@ pub async fn list_up_for_buy( let mut keys_to_remove: HashSet = HashSet::new(); let macd_1d_map = ema_macd(3, 7, 30, &alldata.rt_price_1d_vec, &filtered_data).await?; for (symbol, values) in &mut filtered_data { - if let (Some(macd_vec), Some(rt_price_vec)) = (macd_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol)) { - if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && - rt_price_vec.last().unwrap().close_time > server_epoch { - if macd_vec[macd_vec.len()-1].macd_value - macd_vec[macd_vec.len()-1].signal_value > macd_vec[macd_vec.len()-2].macd_value - macd_vec[macd_vec.len()-2].signal_value - { - values.current_price = rust_decimal::prelude::FromPrimitive::from_f64(rt_price_vec.last().unwrap().close_price).unwrap(); + if let (Some(macd_vec), Some(rt_price_vec)) = + (macd_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol)) + { + if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time + && rt_price_vec.last().unwrap().close_time > server_epoch + { + if macd_vec[macd_vec.len() - 1].macd_value + - macd_vec[macd_vec.len() - 1].signal_value + > macd_vec[macd_vec.len() - 2].macd_value + - macd_vec[macd_vec.len() - 2].signal_value + { + values.current_price = rust_decimal::prelude::FromPrimitive::from_f64( + rt_price_vec.last().unwrap().close_price, + ) + .unwrap(); values.closetime = rt_price_vec.last().unwrap().close_time; } else { keys_to_remove.insert(symbol.clone()); @@ -213,17 +276,15 @@ pub async fn list_up_for_sell( if let Some(exchange_info) = exchange_info_map.get(&element.symbol) { let lot_step_size = exchange_info.stepsize; let quote_commission_precision = exchange_info.quote_commission_precision; - let base_qty_to_be_ordered = - element.base_qty_ordered.round_dp_with_strategy( - lot_step_size.normalize().scale(), - RoundingStrategy::ToZero, - ); + let base_qty_to_be_ordered = element.base_qty_ordered.round_dp_with_strategy( + lot_step_size.normalize().scale(), + RoundingStrategy::ToZero, + ); if (element.is_long == 0 || element.is_long == 1) && !element.current_price.is_zero() { - if element.current_price >= element.target_price - { + if element.current_price >= element.target_price { limit_order_sell( &element, element.current_price, @@ -255,7 +316,7 @@ pub async fn list_up_for_sell( .await; } // TODO: sell_count가 1일 때 적용하기 - // else if (supertrend_vec + // else if (supertrend_vec // .last() // .unwrap() // .signal diff --git a/src/strategy_team/strategy_005.rs b/src/strategy_team/strategy_005.rs index 86414bf..70f9adb 100644 --- a/src/strategy_team/strategy_005.rs +++ b/src/strategy_team/strategy_005.rs @@ -1,15 +1,17 @@ use super::{ - dec, decimal_add, decimal_sub, decimal_div, decimal_mul, ema, exists_record, insert_pre_suggested_coins, - limit_order_sell, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, AllData, - Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredDataValue, Mutex, - RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, get_server_epoch, MacdData, ema_macd, - BollingerBandData, ToPrimitive, duplicate_filter, HashMap, HashSet, remove_keys, SuperTrendArea, SuperTrendSignal, get_current_price + adx, dec, decimal_add, decimal_div, decimal_mul, decimal_sub, duplicate_filter, ema, ema_macd, + exists_record, get_current_price, get_server_epoch, insert_pre_suggested_coins, + limit_order_sell, remove_keys, rsi, select_filled_buy_orders, stoch_rsi, supertrend, + try_join_all, update_record3, AdxData, AllData, Arc, BollingerBandData, Client, ClientBuilder, + Decimal, EmaData, ExchangeInfo, FilteredDataValue, HashMap, HashSet, MacdData, Mutex, + RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SuperTrendArea, SuperTrendSignal, + SupertrendData, ToPrimitive, TradeFee, }; // BUY conditions // (1) 1d MACD (3, 7, 30) cross // (2) supertrend (30, 3): UP area -// stoploss: (update) supertrend(10, 1.5) lowerband +// stoploss: (update) supertrend(10, 1.5) lowerband // target price: (fixed) stoploss inverse x 3 times profit pub async fn list_up_for_buy( alldata: &AllData, @@ -27,12 +29,17 @@ pub async fn list_up_for_buy( // 2nd filtering: supertrend(ATR period 14, multiplier: 1.2, 1d close price) let mut keys_to_remove: HashSet = HashSet::new(); let server_epoch = get_server_epoch().await; - let supertrend_1d_map = supertrend(14, 1.2, true, &alldata.rt_price_1d_vec, &filtered_data).await?; + let supertrend_1d_map = + supertrend(14, 1.2, true, &alldata.rt_price_1d_vec, &filtered_data).await?; for (symbol, values) in &mut filtered_data { - if let (Some(supertrend_vec), Some(rt_price_vec)) = (supertrend_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol)) { - if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && - rt_price_vec.last().unwrap().close_time > server_epoch { - if supertrend_vec.last().unwrap().area == SuperTrendArea::UP{ + if let (Some(supertrend_vec), Some(rt_price_vec)) = ( + supertrend_1d_map.get(symbol), + alldata.rt_price_1d_vec.get(symbol), + ) { + if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time + && rt_price_vec.last().unwrap().close_time > server_epoch + { + if supertrend_vec.last().unwrap().area == SuperTrendArea::UP { } else { keys_to_remove.insert(symbol.clone()); } @@ -49,12 +56,21 @@ pub async fn list_up_for_buy( let mut keys_to_remove: HashSet = HashSet::new(); let macd_1d_map = ema_macd(3, 5, 30, &alldata.rt_price_1d_vec, &filtered_data).await?; for (symbol, values) in &mut filtered_data { - if let (Some(macd_vec), Some(rt_price_vec)) = (macd_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol)) { - if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && - rt_price_vec.last().unwrap().close_time > server_epoch { - if macd_vec[macd_vec.len()-1].macd_value > macd_vec[macd_vec.len()-1].signal_value && - macd_vec[macd_vec.len()-2].macd_value < macd_vec[macd_vec.len()-2].signal_value { - values.current_price = rust_decimal::prelude::FromPrimitive::from_f64(rt_price_vec.last().unwrap().close_price).unwrap(); + if let (Some(macd_vec), Some(rt_price_vec)) = + (macd_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol)) + { + if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time + && rt_price_vec.last().unwrap().close_time > server_epoch + { + if macd_vec[macd_vec.len() - 1].macd_value + > macd_vec[macd_vec.len() - 1].signal_value + && macd_vec[macd_vec.len() - 2].macd_value + < macd_vec[macd_vec.len() - 2].signal_value + { + values.current_price = rust_decimal::prelude::FromPrimitive::from_f64( + rt_price_vec.last().unwrap().close_price, + ) + .unwrap(); values.closetime = rt_price_vec.last().unwrap().close_time; } else { keys_to_remove.insert(symbol.clone()); @@ -71,24 +87,48 @@ pub async fn list_up_for_buy( // 2nd filtering: supertrend(ATR period 14, multiplier: 1.2, 1d close price) let mut keys_to_remove: HashSet = HashSet::new(); let server_epoch = get_server_epoch().await; - let supertrend_1d_map = supertrend(14, 1.2, true, &alldata.rt_price_1d_vec, &filtered_data).await?; + let supertrend_1d_map = + supertrend(14, 1.2, true, &alldata.rt_price_1d_vec, &filtered_data).await?; for (symbol, values) in &mut filtered_data { - if let (Some(supertrend_vec), Some(rt_price_vec)) = (supertrend_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol)) { - if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && - rt_price_vec.last().unwrap().close_time > server_epoch { + if let (Some(supertrend_vec), Some(rt_price_vec)) = ( + supertrend_1d_map.get(symbol), + alldata.rt_price_1d_vec.get(symbol), + ) { + if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time + && rt_price_vec.last().unwrap().close_time > server_epoch + { // input stoploss, target_price - let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap(); - if supertrend_vec.last().unwrap().area == SuperTrendArea::DOWN && - supertrend_vec.last().unwrap().band_value > values.current_price.to_f64().unwrap() + let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64( + supertrend_vec.last().unwrap().band_value, + ) + .unwrap(); + if supertrend_vec.last().unwrap().area == SuperTrendArea::DOWN + && supertrend_vec.last().unwrap().band_value + > values.current_price.to_f64().unwrap() { - values.stoploss = decimal_sub(values.current_price, decimal_sub(band_value, values.current_price)); - values.target_price = decimal_add(decimal_mul(decimal_sub(values.current_price, values.stoploss), dec!(2.0)), values.current_price); - - } else if supertrend_vec.last().unwrap().area == SuperTrendArea::UP && - supertrend_vec.last().unwrap().band_value < values.current_price.to_f64().unwrap() + values.stoploss = decimal_sub( + values.current_price, + decimal_sub(band_value, values.current_price), + ); + values.target_price = decimal_add( + decimal_mul( + decimal_sub(values.current_price, values.stoploss), + dec!(2.0), + ), + values.current_price, + ); + } else if supertrend_vec.last().unwrap().area == SuperTrendArea::UP + && supertrend_vec.last().unwrap().band_value + < values.current_price.to_f64().unwrap() { values.stoploss = band_value; - values.target_price = decimal_add(decimal_mul(decimal_sub(values.current_price, values.stoploss), dec!(2.0)), values.current_price); + values.target_price = decimal_add( + decimal_mul( + decimal_sub(values.current_price, values.stoploss), + dec!(2.0), + ), + values.current_price, + ); } else { keys_to_remove.insert(symbol.clone()); } @@ -99,16 +139,18 @@ pub async fn list_up_for_buy( keys_to_remove.insert(symbol.clone()); } } - remove_keys(&mut filtered_data, keys_to_remove).await; + remove_keys(&mut filtered_data, keys_to_remove).await; // filtering: the 1 previous ADX(3, 5)s increase let mut keys_to_remove: HashSet = HashSet::new(); let adx_vec = adx(3, 5, &alldata.rt_price_1d_vec, &filtered_data).await?; for (symbol, values) in &mut filtered_data { if let Some(adx_vec) = adx_vec.get(symbol) { - if let Some(last_idx) = adx_vec.iter().position(|elem| elem.close_time == values.closetime) { - if - adx_vec[last_idx].adx > adx_vec[last_idx-1].adx { + if let Some(last_idx) = adx_vec + .iter() + .position(|elem| elem.close_time == values.closetime) + { + if adx_vec[last_idx].adx > adx_vec[last_idx - 1].adx { } else { keys_to_remove.insert(symbol.clone()); } @@ -117,7 +159,7 @@ pub async fn list_up_for_buy( } } else { keys_to_remove.insert(symbol.clone()); - } + } } remove_keys(&mut filtered_data, keys_to_remove).await; @@ -126,14 +168,19 @@ pub async fn list_up_for_buy( let server_epoch = get_server_epoch().await; for (symbol, values) in &mut filtered_data { if let Some(rt_price_vec) = alldata.rt_price_1d_vec.get(symbol) { - if rt_price_vec.last().unwrap().close_time > server_epoch && rt_price_vec.len() >= 6 { + if rt_price_vec.last().unwrap().close_time > server_epoch && rt_price_vec.len() >= 6 { let mut opclo_vec: Vec = Vec::new(); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-2].opclo_price); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-3].opclo_price); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-4].opclo_price); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-5].opclo_price); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-6].opclo_price); - let max_idx = opclo_vec.iter().position(|&x| x == *opclo_vec.iter().max_by(|&a, &b| a.partial_cmp(b).unwrap()).unwrap()); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 2].opclo_price); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 3].opclo_price); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 4].opclo_price); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 5].opclo_price); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 6].opclo_price); + let max_idx = opclo_vec.iter().position(|&x| { + x == *opclo_vec + .iter() + .max_by(|&a, &b| a.partial_cmp(b).unwrap()) + .unwrap() + }); opclo_vec.remove(max_idx.unwrap()); let mut mean = 0.0; @@ -155,7 +202,7 @@ pub async fn list_up_for_buy( keys_to_remove.insert(symbol.clone()); } } - remove_keys(&mut filtered_data, keys_to_remove).await; + remove_keys(&mut filtered_data, keys_to_remove).await; let final_filtered_data = duplicate_filter(5, &filtered_data).await?; insert_pre_suggested_coins(5, false, &final_filtered_data, &alldata).await; @@ -181,33 +228,38 @@ pub async fn list_up_for_sell( for element in &filled_buy_orders { filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new()); } - let supertrend_1d = supertrend(14, 1.2, true, &all_data.rt_price_1d_vec, &filtered_symbols).await?; + let supertrend_1d = + supertrend(14, 1.2, true, &all_data.rt_price_1d_vec, &filtered_symbols).await?; for element in filled_buy_orders { if element.used_usdt >= dec!(10.0) { - if let (Some(exchange_info), Some(tradefee), Some(supertrend_vec)) = - (exchange_info_map.get(&element.symbol), trade_fee_map.get(&element.symbol), supertrend_1d.get(&element.symbol)) { + if let (Some(exchange_info), Some(tradefee), Some(supertrend_vec)) = ( + exchange_info_map.get(&element.symbol), + trade_fee_map.get(&element.symbol), + supertrend_1d.get(&element.symbol), + ) { // update stoploss - let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap(); + let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64( + supertrend_vec.last().unwrap().band_value, + ) + .unwrap(); if supertrend_vec.last().unwrap().area == SuperTrendArea::UP - && band_value > element.stoploss { - let update_table_name = String::from("buy_ordered_coin_list"); - let update_value = vec![ - (String::from("stoploss"), band_value.to_string()), - ]; - let update_condition = vec![(String::from("id"), element.id.to_string())]; - update_record3(&update_table_name, &update_value, &update_condition) - .await - .unwrap(); - } + && band_value > element.stoploss + { + let update_table_name = String::from("buy_ordered_coin_list"); + let update_value = vec![(String::from("stoploss"), band_value.to_string())]; + let update_condition = vec![(String::from("id"), element.id.to_string())]; + update_record3(&update_table_name, &update_value, &update_condition) + .await + .unwrap(); + } let lot_step_size = exchange_info.stepsize; let quote_commission_precision = exchange_info.quote_commission_precision; - let base_qty_to_be_ordered = - element.base_qty_ordered.round_dp_with_strategy( - lot_step_size.normalize().scale(), - RoundingStrategy::ToZero, - ); - + let base_qty_to_be_ordered = element.base_qty_ordered.round_dp_with_strategy( + lot_step_size.normalize().scale(), + RoundingStrategy::ToZero, + ); + if (element.is_long == 0 || element.is_long == 1) && !element.current_price.is_zero() { @@ -231,7 +283,8 @@ pub async fn list_up_for_sell( &trade_fee_map, ) .await; - } else if server_epoch - element.transact_time > (86_400_000) * 7 { // 7 days timeout selling + } else if server_epoch - element.transact_time > (86_400_000) * 7 { + // 7 days timeout selling limit_order_sell( &element, element.current_price, @@ -243,7 +296,7 @@ pub async fn list_up_for_sell( .await; } // TODO: sell_count가 1일 때 적용하기 - // else if (supertrend_vec + // else if (supertrend_vec // .last() // .unwrap() // .signal diff --git a/src/strategy_team/strategy_006.rs b/src/strategy_team/strategy_006.rs index 8294a42..9015454 100644 --- a/src/strategy_team/strategy_006.rs +++ b/src/strategy_team/strategy_006.rs @@ -1,15 +1,17 @@ use super::{ - dec, decimal_add, decimal_sub, decimal_div, decimal_mul, ema, exists_record, insert_pre_suggested_coins, - limit_order_sell, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, AllData, - Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredDataValue, Mutex, - RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, get_server_epoch, MacdData, ema_macd, - BollingerBandData, ToPrimitive, duplicate_filter, HashMap, HashSet, remove_keys, SuperTrendArea, SuperTrendSignal, get_current_price, dema, DemaData, tema, TemaData + adx, dec, decimal_add, decimal_div, decimal_mul, decimal_sub, dema, duplicate_filter, ema, + ema_macd, exists_record, get_current_price, get_server_epoch, insert_pre_suggested_coins, + limit_order_sell, remove_keys, rsi, select_filled_buy_orders, stoch_rsi, supertrend, tema, + try_join_all, update_record3, AdxData, AllData, Arc, BollingerBandData, Client, ClientBuilder, + Decimal, DemaData, EmaData, ExchangeInfo, FilteredDataValue, HashMap, HashSet, MacdData, Mutex, + RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SuperTrendArea, SuperTrendSignal, + SupertrendData, TemaData, ToPrimitive, TradeFee, }; // BUY conditions // (1) 1d MACD (3, 7, 30): MACD_current - Signal_current < 0, MACD_prev - Signal_prev < MACD_current - Signal_current -// (2) stoch RSI(30, 30, 2, 2): K_prev < 10, K_prev < K_current -// stoploss: (update) supertrend(10, 1.5) lowerband +// (2) stoch RSI(30, 30, 2, 2): K_prev < 10, K_prev < K_current +// stoploss: (update) supertrend(10, 1.5) lowerband // target price: (fixed) stoploss inverse x 3 times profit pub async fn list_up_for_buy( alldata: &AllData, @@ -29,15 +31,28 @@ pub async fn list_up_for_buy( let macd_1d_map = ema_macd(3, 7, 30, &alldata.rt_price_1d_vec, &filtered_data).await?; let server_epoch = get_server_epoch().await; for (symbol, values) in &mut filtered_data { - if let (Some(macd_vec), Some(rt_price_vec)) = (macd_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol)) { - if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && - rt_price_vec.last().unwrap().close_time > server_epoch { - if macd_vec.len() >= 30 && - (macd_vec[macd_vec.len()-1].macd_value - macd_vec[macd_vec.len()-1].signal_value).is_sign_negative() && - (macd_vec[macd_vec.len()-2].macd_value - macd_vec[macd_vec.len()-2].signal_value).is_sign_negative() && - (macd_vec[macd_vec.len()-1].macd_value - macd_vec[macd_vec.len()-1].signal_value > - macd_vec[macd_vec.len()-2].macd_value - macd_vec[macd_vec.len()-2].signal_value) { - values.current_price = rust_decimal::prelude::FromPrimitive::from_f64(rt_price_vec.last().unwrap().close_price).unwrap(); + if let (Some(macd_vec), Some(rt_price_vec)) = + (macd_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol)) + { + if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time + && rt_price_vec.last().unwrap().close_time > server_epoch + { + if macd_vec.len() >= 30 + && (macd_vec[macd_vec.len() - 1].macd_value + - macd_vec[macd_vec.len() - 1].signal_value) + .is_sign_negative() + && (macd_vec[macd_vec.len() - 2].macd_value + - macd_vec[macd_vec.len() - 2].signal_value) + .is_sign_negative() + && (macd_vec[macd_vec.len() - 1].macd_value + - macd_vec[macd_vec.len() - 1].signal_value + > macd_vec[macd_vec.len() - 2].macd_value + - macd_vec[macd_vec.len() - 2].signal_value) + { + values.current_price = rust_decimal::prelude::FromPrimitive::from_f64( + rt_price_vec.last().unwrap().close_price, + ) + .unwrap(); values.closetime = rt_price_vec.last().unwrap().close_time; } else { keys_to_remove.insert(symbol.clone()); @@ -51,20 +66,26 @@ pub async fn list_up_for_buy( } remove_keys(&mut filtered_data, keys_to_remove).await; - // 1d StochRSI (RSI_len: 30, StochRSI_len: 30, K: 3, D: 3) K_prev < 10, K_prev < K_current + // 1d StochRSI (RSI_len: 30, StochRSI_len: 30, K: 3, D: 3) K_prev < 10, K_prev < K_current let mut keys_to_remove: HashSet = HashSet::new(); let stoch_rsis = stoch_rsi(30, 30, 3, 3, &alldata.rt_price_1d_vec, &filtered_data).await?; for (symbol, values) in &mut filtered_data { if stoch_rsis.contains_key(symbol) { let stoch_rsi_vec = stoch_rsis.get(symbol).unwrap(); - let search_result = stoch_rsi_vec.iter().position(|x| x.close_time == values.closetime); - if stoch_rsi_vec.len() > 10 && search_result.is_some_and(|a| stoch_rsi_vec[a-3].k < 20.0 && - stoch_rsi_vec[a-2].k < 15.0 && - stoch_rsi_vec[a-1].k < 10.0 && - stoch_rsi_vec[a-1].k < stoch_rsi_vec[a].k && - stoch_rsi_vec[a].k < stoch_rsi_vec[a].d && - !stoch_rsi_vec[a].d.is_subnormal() && - stoch_rsi_vec[a].d > 0.00000001) { + let search_result = stoch_rsi_vec + .iter() + .position(|x| x.close_time == values.closetime); + if stoch_rsi_vec.len() > 10 + && search_result.is_some_and(|a| { + stoch_rsi_vec[a - 3].k < 20.0 + && stoch_rsi_vec[a - 2].k < 15.0 + && stoch_rsi_vec[a - 1].k < 10.0 + && stoch_rsi_vec[a - 1].k < stoch_rsi_vec[a].k + && stoch_rsi_vec[a].k < stoch_rsi_vec[a].d + && !stoch_rsi_vec[a].d.is_subnormal() + && stoch_rsi_vec[a].d > 0.00000001 + }) + { } else { keys_to_remove.insert(symbol.clone()); } @@ -77,11 +98,16 @@ pub async fn list_up_for_buy( // 2nd filtering: supertrend(ATR period 30, multiplier: 2.0, 1d close price) UP area let mut keys_to_remove: HashSet = HashSet::new(); let server_epoch = get_server_epoch().await; - let supertrend_1d_map = supertrend(30, 2.0, true, &alldata.rt_price_1d_vec, &filtered_data).await?; + let supertrend_1d_map = + supertrend(30, 2.0, true, &alldata.rt_price_1d_vec, &filtered_data).await?; for (symbol, values) in &mut filtered_data { - if let (Some(supertrend_vec), Some(rt_price_vec)) = (supertrend_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol)) { - if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && - rt_price_vec.last().unwrap().close_time > server_epoch { + if let (Some(supertrend_vec), Some(rt_price_vec)) = ( + supertrend_1d_map.get(symbol), + alldata.rt_price_1d_vec.get(symbol), + ) { + if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time + && rt_price_vec.last().unwrap().close_time > server_epoch + { if supertrend_vec.last().unwrap().area == SuperTrendArea::UP { } else { keys_to_remove.insert(symbol.clone()); @@ -93,29 +119,53 @@ pub async fn list_up_for_buy( keys_to_remove.insert(symbol.clone()); } } - remove_keys(&mut filtered_data, keys_to_remove).await; + remove_keys(&mut filtered_data, keys_to_remove).await; // 2nd filtering: supertrend(ATR period 14, multiplier: 1.2, 1d close price) let mut keys_to_remove: HashSet = HashSet::new(); let server_epoch = get_server_epoch().await; - let supertrend_1d_map = supertrend(14, 1.2, true, &alldata.rt_price_1d_vec, &filtered_data).await?; + let supertrend_1d_map = + supertrend(14, 1.2, true, &alldata.rt_price_1d_vec, &filtered_data).await?; for (symbol, values) in &mut filtered_data { - if let (Some(supertrend_vec), Some(rt_price_vec)) = (supertrend_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol)) { - if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && - rt_price_vec.last().unwrap().close_time > server_epoch { + if let (Some(supertrend_vec), Some(rt_price_vec)) = ( + supertrend_1d_map.get(symbol), + alldata.rt_price_1d_vec.get(symbol), + ) { + if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time + && rt_price_vec.last().unwrap().close_time > server_epoch + { // input stoploss, target_price - let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap(); - if supertrend_vec.last().unwrap().area == SuperTrendArea::DOWN && - supertrend_vec.last().unwrap().band_value > values.current_price.to_f64().unwrap() + let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64( + supertrend_vec.last().unwrap().band_value, + ) + .unwrap(); + if supertrend_vec.last().unwrap().area == SuperTrendArea::DOWN + && supertrend_vec.last().unwrap().band_value + > values.current_price.to_f64().unwrap() { - values.stoploss = decimal_sub(values.current_price, decimal_sub(band_value, values.current_price)); - values.target_price = decimal_add(decimal_mul(decimal_sub(values.current_price, values.stoploss), dec!(2.0)), values.current_price); - - } else if supertrend_vec.last().unwrap().area == SuperTrendArea::UP && - supertrend_vec.last().unwrap().band_value < values.current_price.to_f64().unwrap() + values.stoploss = decimal_sub( + values.current_price, + decimal_sub(band_value, values.current_price), + ); + values.target_price = decimal_add( + decimal_mul( + decimal_sub(values.current_price, values.stoploss), + dec!(2.0), + ), + values.current_price, + ); + } else if supertrend_vec.last().unwrap().area == SuperTrendArea::UP + && supertrend_vec.last().unwrap().band_value + < values.current_price.to_f64().unwrap() { values.stoploss = band_value; - values.target_price = decimal_add(decimal_mul(decimal_sub(values.current_price, values.stoploss), dec!(2.0)), values.current_price); + values.target_price = decimal_add( + decimal_mul( + decimal_sub(values.current_price, values.stoploss), + dec!(2.0), + ), + values.current_price, + ); } else { keys_to_remove.insert(symbol.clone()); } @@ -126,21 +176,26 @@ pub async fn list_up_for_buy( keys_to_remove.insert(symbol.clone()); } } - remove_keys(&mut filtered_data, keys_to_remove).await; + remove_keys(&mut filtered_data, keys_to_remove).await; // limit buy price: 3 * abs(이전 3 개 중 최대값 제거 한 opclo 값 평균 - 현재 open 값) + 현재 open 값 > current_price let mut keys_to_remove: HashSet = HashSet::new(); let server_epoch = get_server_epoch().await; for (symbol, values) in &mut filtered_data { if let Some(rt_price_vec) = alldata.rt_price_1d_vec.get(symbol) { - if rt_price_vec.last().unwrap().close_time > server_epoch && rt_price_vec.len() >= 6 { + if rt_price_vec.last().unwrap().close_time > server_epoch && rt_price_vec.len() >= 6 { let mut opclo_vec: Vec = Vec::new(); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-2].opclo_price); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-3].opclo_price); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-4].opclo_price); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-5].opclo_price); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-6].opclo_price); - let max_idx = opclo_vec.iter().position(|&x| x == *opclo_vec.iter().max_by(|&a, &b| a.partial_cmp(b).unwrap()).unwrap()); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 2].opclo_price); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 3].opclo_price); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 4].opclo_price); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 5].opclo_price); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 6].opclo_price); + let max_idx = opclo_vec.iter().position(|&x| { + x == *opclo_vec + .iter() + .max_by(|&a, &b| a.partial_cmp(b).unwrap()) + .unwrap() + }); opclo_vec.remove(max_idx.unwrap()); let mut mean = 0.0; @@ -162,7 +217,7 @@ pub async fn list_up_for_buy( keys_to_remove.insert(symbol.clone()); } } - remove_keys(&mut filtered_data, keys_to_remove).await; + remove_keys(&mut filtered_data, keys_to_remove).await; let final_filtered_data = duplicate_filter(6, &filtered_data).await?; insert_pre_suggested_coins(6, false, &final_filtered_data, &alldata).await; @@ -188,63 +243,76 @@ pub async fn list_up_for_sell( for element in &filled_buy_orders { filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new()); } - let supertrend_1d = supertrend(14, 1.2, true, &all_data.rt_price_1d_vec, &filtered_symbols).await?; + let supertrend_1d = + supertrend(14, 1.2, true, &all_data.rt_price_1d_vec, &filtered_symbols).await?; for element in filled_buy_orders { let mut is_sell = false; let opclo_sample_length: usize = 15; // 15 candle samsples let mut target_profit_percent = 0.0; - if let Some(price_1d_vec) = all_data - .rt_price_1d_vec - .get(&element.symbol) - { + if let Some(price_1d_vec) = all_data.rt_price_1d_vec.get(&element.symbol) { let vec_len = price_1d_vec.len(); - if let Some(candles) = price_1d_vec.get(vec_len-opclo_sample_length-2..vec_len-1) { + if let Some(candles) = + price_1d_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; - + for window in windows { - sum_amplitude_candles += ((window.last().unwrap().high_price - window.last().unwrap().low_price) * 100.0) / window.first().unwrap().close_price; + 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 += ((((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); + target_profit_percent = + average_amplitude + (standard_deviation_amplitude * 0.5); } } if element.used_usdt >= dec!(10.0) { - if let (Some(exchange_info), Some(tradefee), Some(supertrend_vec)) = - (exchange_info_map.get(&element.symbol), trade_fee_map.get(&element.symbol), supertrend_1d.get(&element.symbol)) { + if let (Some(exchange_info), Some(tradefee), Some(supertrend_vec)) = ( + exchange_info_map.get(&element.symbol), + trade_fee_map.get(&element.symbol), + supertrend_1d.get(&element.symbol), + ) { // update stoploss - let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap(); + let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64( + supertrend_vec.last().unwrap().band_value, + ) + .unwrap(); if supertrend_vec.last().unwrap().area == SuperTrendArea::UP - && band_value > element.stoploss { - let update_table_name = String::from("buy_ordered_coin_list"); - let update_value = vec![ - (String::from("stoploss"), band_value.to_string()), - ]; - let update_condition = vec![(String::from("id"), element.id.to_string())]; - update_record3(&update_table_name, &update_value, &update_condition) - .await - .unwrap(); - } + && band_value > element.stoploss + { + let update_table_name = String::from("buy_ordered_coin_list"); + let update_value = vec![(String::from("stoploss"), band_value.to_string())]; + let update_condition = vec![(String::from("id"), element.id.to_string())]; + update_record3(&update_table_name, &update_value, &update_condition) + .await + .unwrap(); + } let lot_step_size = exchange_info.stepsize; let quote_commission_precision = exchange_info.quote_commission_precision; - let base_qty_to_be_ordered = - element.base_qty_ordered.round_dp_with_strategy( - lot_step_size.normalize().scale(), - RoundingStrategy::ToZero, - ); - + let base_qty_to_be_ordered = element.base_qty_ordered.round_dp_with_strategy( + lot_step_size.normalize().scale(), + RoundingStrategy::ToZero, + ); + if (element.is_long == 0 || element.is_long == 1) && !element.current_price.is_zero() { @@ -252,19 +320,32 @@ pub async fn list_up_for_sell( is_sell = true; } else if element.current_price >= element.target_price { is_sell = true; - } else if (element.pure_profit_percent >= target_profit_percent * 2.5) && (target_profit_percent != 0.0 && target_profit_percent.is_sign_positive()) { // absolute sell profit percent + } else if (element.pure_profit_percent >= target_profit_percent * 2.5) + && (target_profit_percent != 0.0 + && target_profit_percent.is_sign_positive()) + { + // absolute sell profit percent is_sell = true; - } else if server_epoch - element.transact_time > (86_400_000) * 5 && - (target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && target_profit_percent * 2.0 <= element.pure_profit_percent){ // scaled selling with time up selling (5 days) + } else if server_epoch - element.transact_time > (86_400_000) * 5 + && (target_profit_percent != 0.0 + && target_profit_percent.is_sign_positive() + && target_profit_percent * 2.0 <= element.pure_profit_percent) + { + // scaled selling with time up selling (5 days) is_sell = true; - } else if server_epoch - element.transact_time > (86_400_000) * 6 && - (target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && target_profit_percent * 1.5 <= element.pure_profit_percent){ // scaled selling with time up selling (6 days) + } else if server_epoch - element.transact_time > (86_400_000) * 6 + && (target_profit_percent != 0.0 + && target_profit_percent.is_sign_positive() + && target_profit_percent * 1.5 <= element.pure_profit_percent) + { + // scaled selling with time up selling (6 days) is_sell = true; - } else if server_epoch - element.transact_time > (86_400_000) * 7 { // time up selling + } else if server_epoch - element.transact_time > (86_400_000) * 7 { + // time up selling is_sell = true; } // TODO: sell_count가 1일 때 적용하기 - // else if (supertrend_vec + // else if (supertrend_vec // .last() // .unwrap() // .signal diff --git a/src/strategy_team/strategy_007.rs b/src/strategy_team/strategy_007.rs index edf5db2..f68994e 100644 --- a/src/strategy_team/strategy_007.rs +++ b/src/strategy_team/strategy_007.rs @@ -1,9 +1,11 @@ use super::{ - dec, decimal_add, decimal_sub, decimal_div, decimal_mul, ema, exists_record, insert_pre_suggested_coins, - limit_order_sell, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, AllData, - Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredDataValue, Mutex, - RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, get_server_epoch, MacdData, ema_macd, - BollingerBandData, ToPrimitive, duplicate_filter, HashMap, HashSet, remove_keys, SuperTrendArea, SuperTrendSignal, get_current_price + adx, dec, decimal_add, decimal_div, decimal_mul, decimal_sub, duplicate_filter, ema, ema_macd, + exists_record, get_current_price, get_server_epoch, insert_pre_suggested_coins, + limit_order_sell, remove_keys, rsi, select_filled_buy_orders, stoch_rsi, supertrend, + try_join_all, update_record3, AdxData, AllData, Arc, BollingerBandData, Client, ClientBuilder, + Decimal, EmaData, ExchangeInfo, FilteredDataValue, HashMap, HashSet, MacdData, Mutex, + RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SuperTrendArea, SuperTrendSignal, + SupertrendData, ToPrimitive, TradeFee, }; // BUY conditions @@ -31,80 +33,15 @@ pub async fn list_up_for_buy( let adx_vec = adx(10, 10, &alldata.rt_price_1d_vec, &filtered_data).await?; for (symbol, values) in &mut filtered_data { if let Some(adx_vec) = adx_vec.get(symbol) { - if let Some(last_idx) = adx_vec.iter().position(|elem| elem.close_time == values.closetime) { - if - adx_vec[last_idx].adx > adx_vec[last_idx-1].adx && - adx_vec[last_idx-1].adx > adx_vec[last_idx-2].adx && - adx_vec[last_idx].adx < 25.0 { - } else { - keys_to_remove.insert(symbol.clone()); - } - } else { - keys_to_remove.insert(symbol.clone()); - } - } else { - keys_to_remove.insert(symbol.clone()); - } - } - remove_keys(&mut filtered_data, keys_to_remove).await; - - // 2nd filtering: the 2 previous ADX(5, 5)s increase, ADX < 40 - let mut keys_to_remove: HashSet = HashSet::new(); - let adx_vec = adx(10, 10, &alldata.rt_price_1d_vec, &filtered_data).await?; - for (symbol, values) in &mut filtered_data { - if let Some(adx_vec) = adx_vec.get(symbol) { - if let Some(last_idx) = adx_vec.iter().position(|elem| elem.close_time == values.closetime) { - if - adx_vec[last_idx].adx > adx_vec[last_idx-1].adx && - adx_vec[last_idx-1].adx > adx_vec[last_idx-2].adx && - adx_vec[last_idx].adx < 40.0 { - } else { - keys_to_remove.insert(symbol.clone()); - } - } else { - keys_to_remove.insert(symbol.clone()); - } - } else { - keys_to_remove.insert(symbol.clone()); - } - } - remove_keys(&mut filtered_data, keys_to_remove).await; - - // 3rd filtering: RSI 5 < 75.0 - let mut keys_to_remove: HashSet = HashSet::new(); - let rsi_map = rsi(5, &alldata.rt_price_1d_vec, &filtered_data).await?; - for (symbol, values) in &mut filtered_data { - if let Some(rsi_vec) = rsi_map.get(symbol) { - if let Some(last_idx) = rsi_vec.iter().position(|elem| elem.close_time == values.closetime) { - if rsi_vec[last_idx].rsi_value > 75.0 { - keys_to_remove.insert(symbol.clone()); - } - } else { - keys_to_remove.insert(symbol.clone()); - } - } else { - keys_to_remove.insert(symbol.clone()); - } - } - remove_keys(&mut filtered_data, keys_to_remove).await; - - // 4th filtering: supertrend(ATR period 14, multiplier: 1.2, 1d close price) - let mut keys_to_remove: HashSet = HashSet::new(); - let server_epoch = get_server_epoch().await; - let supertrend_1d_map = supertrend(14, 1.2, true, &alldata.rt_price_1d_vec, &filtered_data).await?; - for (symbol, values) in &mut filtered_data { - if let (Some(supertrend_vec), Some(rt_price_vec)) = (supertrend_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol)) { - if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && - rt_price_vec.last().unwrap().close_time > server_epoch { - // input stoploss, target_price - let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap(); - if supertrend_vec.last().unwrap().area == SuperTrendArea::UP && - supertrend_vec.last().unwrap().band_value < values.current_price.to_f64().unwrap() + if let Some(last_idx) = adx_vec + .iter() + .position(|elem| elem.close_time == values.closetime) + { + if adx_vec.len() > 10 + && adx_vec[last_idx].adx > adx_vec[last_idx - 1].adx + && adx_vec[last_idx - 1].adx > adx_vec[last_idx - 2].adx + && adx_vec[last_idx].adx < 25.0 { - values.current_price = rust_decimal::prelude::FromPrimitive::from_f64(rt_price_vec.last().unwrap().close_price).unwrap(); - values.closetime = rt_price_vec.last().unwrap().close_time; - values.stoploss = band_value; - values.target_price = decimal_add(decimal_mul(decimal_sub(values.current_price, values.stoploss), dec!(3.0)), values.current_price); } else { keys_to_remove.insert(symbol.clone()); } @@ -115,21 +52,120 @@ pub async fn list_up_for_buy( keys_to_remove.insert(symbol.clone()); } } - remove_keys(&mut filtered_data, keys_to_remove).await; + remove_keys(&mut filtered_data, keys_to_remove).await; + println!("{}", filtered_data.keys().len()); + // 2nd filtering: the 2 previous ADX(5, 5)s increase, ADX < 40 + let mut keys_to_remove: HashSet = HashSet::new(); + let adx_vec = adx(10, 10, &alldata.rt_price_1d_vec, &filtered_data).await?; + for (symbol, values) in &mut filtered_data { + if let Some(adx_vec) = adx_vec.get(symbol) { + if let Some(last_idx) = adx_vec + .iter() + .position(|elem| elem.close_time == values.closetime) + { + if adx_vec.len() > 10 + && adx_vec[last_idx].adx > adx_vec[last_idx - 1].adx + && adx_vec[last_idx - 1].adx > adx_vec[last_idx - 2].adx + && adx_vec[last_idx].adx < 40.0 + { + } else { + keys_to_remove.insert(symbol.clone()); + } + } else { + keys_to_remove.insert(symbol.clone()); + } + } else { + keys_to_remove.insert(symbol.clone()); + } + } + remove_keys(&mut filtered_data, keys_to_remove).await; + + // 3rd filtering: RSI 5 < 75.0 + let mut keys_to_remove: HashSet = HashSet::new(); + let rsi_map = rsi(5, &alldata.rt_price_1d_vec, &filtered_data).await?; + for (symbol, values) in &mut filtered_data { + if let Some(rsi_vec) = rsi_map.get(symbol) { + if let Some(last_idx) = rsi_vec + .iter() + .position(|elem| elem.close_time == values.closetime) + { + if rsi_vec[last_idx].rsi_value > 75.0 { + keys_to_remove.insert(symbol.clone()); + } + } else { + keys_to_remove.insert(symbol.clone()); + } + } else { + keys_to_remove.insert(symbol.clone()); + } + } + remove_keys(&mut filtered_data, keys_to_remove).await; + + // 4th filtering: supertrend(ATR period 14, multiplier: 1.2, 1d close price) + let mut keys_to_remove: HashSet = HashSet::new(); + let server_epoch = get_server_epoch().await; + let supertrend_1d_map = + supertrend(14, 1.2, true, &alldata.rt_price_1d_vec, &filtered_data).await?; + for (symbol, values) in &mut filtered_data { + if let (Some(supertrend_vec), Some(rt_price_vec)) = ( + supertrend_1d_map.get(symbol), + alldata.rt_price_1d_vec.get(symbol), + ) { + if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time + && rt_price_vec.last().unwrap().close_time > server_epoch + { + // input stoploss, target_price + let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64( + supertrend_vec.last().unwrap().band_value, + ) + .unwrap(); + if supertrend_vec.last().unwrap().area == SuperTrendArea::UP + && supertrend_vec.last().unwrap().band_value + < values.current_price.to_f64().unwrap() + { + values.current_price = rust_decimal::prelude::FromPrimitive::from_f64( + rt_price_vec.last().unwrap().close_price, + ) + .unwrap(); + values.closetime = rt_price_vec.last().unwrap().close_time; + values.stoploss = band_value; + values.target_price = decimal_add( + decimal_mul( + decimal_sub(values.current_price, values.stoploss), + dec!(3.0), + ), + values.current_price, + ); + } else { + keys_to_remove.insert(symbol.clone()); + } + } else { + keys_to_remove.insert(symbol.clone()); + } + } else { + keys_to_remove.insert(symbol.clone()); + } + } + remove_keys(&mut filtered_data, keys_to_remove).await; // limit buy price: 3 * abs(이전 3 개 중 최대값 제거 한 opclo 값 평균 - 현재 open 값) + 현재 open 값 > current_price let mut keys_to_remove: HashSet = HashSet::new(); let server_epoch = get_server_epoch().await; for (symbol, values) in &mut filtered_data { if let Some(rt_price_vec) = alldata.rt_price_1d_vec.get(symbol) { - if rt_price_vec.last().unwrap().close_time > server_epoch && rt_price_vec.len() >= 6 { + if rt_price_vec.last().unwrap().close_time > server_epoch && rt_price_vec.len() >= 6 { let mut opclo_vec: Vec = Vec::new(); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-2].opclo_price); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-3].opclo_price); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-4].opclo_price); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-5].opclo_price); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-6].opclo_price); - let max_idx = opclo_vec.iter().position(|&x| x == *opclo_vec.iter().max_by(|&a, &b| a.partial_cmp(b).unwrap()).unwrap()); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 2].opclo_price); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 3].opclo_price); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 4].opclo_price); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 5].opclo_price); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 6].opclo_price); + let max_idx = opclo_vec.iter().position(|&x| { + x == *opclo_vec + .iter() + .max_by(|&a, &b| a.partial_cmp(b).unwrap()) + .unwrap() + }); opclo_vec.remove(max_idx.unwrap()); let mut mean = 0.0; @@ -151,7 +187,7 @@ pub async fn list_up_for_buy( keys_to_remove.insert(symbol.clone()); } } - remove_keys(&mut filtered_data, keys_to_remove).await; + remove_keys(&mut filtered_data, keys_to_remove).await; let final_filtered_data = duplicate_filter(7, &filtered_data).await?; insert_pre_suggested_coins(7, false, &final_filtered_data, &alldata).await; @@ -180,36 +216,45 @@ pub async fn list_up_for_sell( for element in &filled_buy_orders { filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new()); } - let supertrend_1d = supertrend(14, 1.2, true, &all_data.rt_price_1d_vec, &filtered_symbols).await?; + let supertrend_1d = + supertrend(14, 1.2, true, &all_data.rt_price_1d_vec, &filtered_symbols).await?; for element in filled_buy_orders { let mut is_sell = false; - if element.used_usdt >= dec!(10.0) { - if let (Some(exchange_info), Some(tradefee), Some(supertrend_vec)) = - (exchange_info_map.get(&element.symbol), trade_fee_map.get(&element.symbol), supertrend_1d.get(&element.symbol)) { + if let (Some(exchange_info), Some(tradefee), Some(supertrend_vec)) = ( + exchange_info_map.get(&element.symbol), + trade_fee_map.get(&element.symbol), + supertrend_1d.get(&element.symbol), + ) { // update stoploss - let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap(); + let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64( + supertrend_vec.last().unwrap().band_value, + ) + .unwrap(); if supertrend_vec.last().unwrap().area == SuperTrendArea::UP - && band_value > element.stoploss { - let update_table_name = String::from("buy_ordered_coin_list"); - let update_value = vec![ - (String::from("stoploss"), band_value.to_string()), - ]; - let update_condition = vec![(String::from("id"), element.id.to_string())]; - update_record3(&update_table_name, &update_value, &update_condition) - .await - .unwrap(); - } + && band_value > element.stoploss + { + let update_table_name = String::from("buy_ordered_coin_list"); + let update_value = vec![(String::from("stoploss"), band_value.to_string())]; + let update_condition = vec![(String::from("id"), element.id.to_string())]; + update_record3(&update_table_name, &update_value, &update_condition) + .await + .unwrap(); + } let lot_step_size = exchange_info.stepsize; let quote_commission_precision = exchange_info.quote_commission_precision; - let base_qty_to_be_ordered = - element.base_qty_ordered.round_dp_with_strategy( - lot_step_size.normalize().scale(), - RoundingStrategy::ToZero, - ); - let target_profit_percent = decimal_div(decimal_sub(element.stoploss, element.buy_price), element.buy_price).to_f64().unwrap(); + let base_qty_to_be_ordered = element.base_qty_ordered.round_dp_with_strategy( + lot_step_size.normalize().scale(), + RoundingStrategy::ToZero, + ); + let target_profit_percent = decimal_div( + decimal_sub(element.target_price, element.buy_price), + element.buy_price, + ) + .to_f64() + .unwrap(); if (element.is_long == 0 || element.is_long == 1) && !element.current_price.is_zero() { @@ -217,32 +262,62 @@ pub async fn list_up_for_sell( is_sell = true; } else if element.current_price >= element.target_price { is_sell = true; - } else if server_epoch - element.transact_time > (86_400_000) * 8 && - (target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && target_profit_percent * (13.0/14.0) <= element.pure_profit_percent) { + } else if server_epoch - element.transact_time > (86_400_000) * 8 + && (target_profit_percent != 0.0 + && target_profit_percent.is_sign_positive() + && target_profit_percent * (13.0 / 14.0) + <= element.pure_profit_percent) + { is_sell = true; - } else if server_epoch - element.transact_time > (86_400_000) * 9 && - (target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && target_profit_percent * (12.0/14.0) <= element.pure_profit_percent) { + } else if server_epoch - element.transact_time > (86_400_000) * 9 + && (target_profit_percent != 0.0 + && target_profit_percent.is_sign_positive() + && target_profit_percent * (12.0 / 14.0) + <= element.pure_profit_percent) + { is_sell = true; - } else if server_epoch - element.transact_time > (86_400_000) * 10 && - (target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && target_profit_percent * (11.0/14.0) <= element.pure_profit_percent) { + } else if server_epoch - element.transact_time > (86_400_000) * 10 + && (target_profit_percent != 0.0 + && target_profit_percent.is_sign_positive() + && target_profit_percent * (11.0 / 14.0) + <= element.pure_profit_percent) + { is_sell = true; - } else if server_epoch - element.transact_time > (86_400_000) * 11 && - (target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && target_profit_percent * (10.0/14.0) <= element.pure_profit_percent) { + } else if server_epoch - element.transact_time > (86_400_000) * 11 + && (target_profit_percent != 0.0 + && target_profit_percent.is_sign_positive() + && target_profit_percent * (10.0 / 14.0) + <= element.pure_profit_percent) + { is_sell = true; - } else if server_epoch - element.transact_time > (86_400_000) * 12 && - (target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && target_profit_percent * (9.0/14.0) <= element.pure_profit_percent) { + } else if server_epoch - element.transact_time > (86_400_000) * 12 + && (target_profit_percent != 0.0 + && target_profit_percent.is_sign_positive() + && target_profit_percent * (9.0 / 14.0) + <= element.pure_profit_percent) + { is_sell = true; - } else if server_epoch - element.transact_time > (86_400_000) * 13 && - (target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && target_profit_percent * (8.0/14.0) <= element.pure_profit_percent) { + } else if server_epoch - element.transact_time > (86_400_000) * 13 + && (target_profit_percent != 0.0 + && target_profit_percent.is_sign_positive() + && target_profit_percent * (8.0 / 14.0) + <= element.pure_profit_percent) + { is_sell = true; - } else if server_epoch - element.transact_time > (86_400_000) * 14 && - (target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && target_profit_percent * (1.0/2.0) <= element.pure_profit_percent) { // scaled selling with time up selling (6 days){ + } else if server_epoch - element.transact_time > (86_400_000) * 14 + && (target_profit_percent != 0.0 + && target_profit_percent.is_sign_positive() + && target_profit_percent * (1.0 / 2.0) + <= element.pure_profit_percent) + { + // scaled selling with time up selling (6 days){ is_sell = true; - } else if server_epoch - element.transact_time > (86_400_000) * 15 { // time up selling + } else if server_epoch - element.transact_time > (86_400_000) * 15 { + // time up selling is_sell = true; } // TODO: sell_count가 1일 때 적용하기 - // else if (supertrend_vec + // else if (supertrend_vec // .last() // .unwrap() // .signal diff --git a/src/strategy_team/strategy_008.rs b/src/strategy_team/strategy_008.rs index b30b3c3..1021f64 100644 --- a/src/strategy_team/strategy_008.rs +++ b/src/strategy_team/strategy_008.rs @@ -1,10 +1,12 @@ use super::{ - dec, decimal_add, decimal_sub, decimal_div, decimal_mul, ema, exists_record, insert_pre_suggested_coins, - limit_order_sell, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, AllData, - Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredDataValue, Mutex, - RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, get_server_epoch, MacdData, ema_macd, - BollingerBandData, ToPrimitive, duplicate_filter, HashMap, HashSet, remove_keys, SuperTrendArea, SuperTrendSignal, get_current_price, dema, DemaData, tema, TemaData, - heatmap_volume, HeatMapLevel, HeatmapVolumeData + adx, dec, decimal_add, decimal_div, decimal_mul, decimal_sub, dema, duplicate_filter, ema, + ema_macd, exists_record, get_current_price, get_server_epoch, heatmap_volume, + insert_pre_suggested_coins, limit_order_sell, remove_keys, rsi, select_filled_buy_orders, + stoch_rsi, supertrend, tema, try_join_all, update_record3, AdxData, AllData, Arc, + BollingerBandData, Client, ClientBuilder, Decimal, DemaData, EmaData, ExchangeInfo, + FilteredDataValue, HashMap, HashSet, HeatMapLevel, HeatmapVolumeData, MacdData, Mutex, + RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SuperTrendArea, SuperTrendSignal, + SupertrendData, TemaData, ToPrimitive, TradeFee, }; // BUY conditions @@ -28,10 +30,12 @@ pub async fn list_up_for_buy( let server_epoch = get_server_epoch().await; for (symbol, values) in &mut filtered_data { if let (Some(tema5_vec), Some(tema10_vec)) = (tema_3.get(symbol), tema_10.get(symbol)) { - if tema5_vec.len() > 2 && tema10_vec.len() > 2 && - tema5_vec.last().unwrap().close_time == tema10_vec.last().unwrap().close_time && - tema5_vec.last().unwrap().close_time > server_epoch && - tema10_vec.last().unwrap().close_time > server_epoch { + if tema5_vec.len() > 2 + && tema10_vec.len() > 2 + && tema5_vec.last().unwrap().close_time == tema10_vec.last().unwrap().close_time + && tema5_vec.last().unwrap().close_time > server_epoch + && tema10_vec.last().unwrap().close_time > server_epoch + { if tema5_vec.last().unwrap().tema_value > tema10_vec.last().unwrap().tema_value { } else { keys_to_remove.insert(symbol.clone()); @@ -53,17 +57,30 @@ pub async fn list_up_for_buy( let tema_200 = tema(200, &alldata.rt_price_30m_vec, &filtered_data).await?; let server_epoch = get_server_epoch().await; for (symbol, values) in &mut filtered_data { - if let (Some(tema30_vec), Some(tema5_vec), Some(tema_200_vec), Some(rt_price_vec)) = - (tema_30.get(symbol), tema_5.get(symbol), tema_200.get(symbol), alldata.rt_price_30m_vec.get(symbol)) { - if (tema30_vec.len() > 10 && tema5_vec.len() > 10 && tema_200_vec.len() > 10 && rt_price_vec.len() > 10) && - tema30_vec.last().unwrap().close_time == tema5_vec.last().unwrap().close_time && - tema_200_vec.last().unwrap().close_time == tema5_vec.last().unwrap().close_time && - tema5_vec.last().unwrap().close_time > server_epoch { - if tema30_vec.last().unwrap().tema_value < tema5_vec.last().unwrap().tema_value && - tema30_vec[tema30_vec.len()-2].tema_value > tema5_vec[tema5_vec.len()-2].tema_value && - tema30_vec[tema30_vec.len()-3].tema_value > tema5_vec[tema5_vec.len()-3].tema_value && - tema_200_vec.last().unwrap().tema_value < rt_price_vec.last().unwrap().opclo_price && - tema_200_vec[tema_200_vec.len()-2].tema_value < rt_price_vec[rt_price_vec.len()-2].opclo_price{ + if let (Some(tema30_vec), Some(tema5_vec), Some(tema_200_vec), Some(rt_price_vec)) = ( + tema_30.get(symbol), + tema_5.get(symbol), + tema_200.get(symbol), + alldata.rt_price_30m_vec.get(symbol), + ) { + if (tema30_vec.len() > 10 + && tema5_vec.len() > 10 + && tema_200_vec.len() > 10 + && rt_price_vec.len() > 10) + && tema30_vec.last().unwrap().close_time == tema5_vec.last().unwrap().close_time + && tema_200_vec.last().unwrap().close_time == tema5_vec.last().unwrap().close_time + && tema5_vec.last().unwrap().close_time > server_epoch + { + if tema30_vec.last().unwrap().tema_value < tema5_vec.last().unwrap().tema_value + && tema30_vec[tema30_vec.len() - 2].tema_value + > tema5_vec[tema5_vec.len() - 2].tema_value + && tema30_vec[tema30_vec.len() - 3].tema_value + > tema5_vec[tema5_vec.len() - 3].tema_value + && tema_200_vec.last().unwrap().tema_value + < rt_price_vec.last().unwrap().opclo_price + && tema_200_vec[tema_200_vec.len() - 2].tema_value + < rt_price_vec[rt_price_vec.len() - 2].opclo_price + { } else { keys_to_remove.insert(symbol.clone()); } @@ -79,32 +96,51 @@ pub async fn list_up_for_buy( // supertrend(ATR period 10, multiplier: 2.0, 30m close price) let mut keys_to_remove: HashSet = HashSet::new(); let server_epoch = get_server_epoch().await; - let supertrend_30m_map = supertrend(10, 2.0, true, &alldata.rt_price_30m_vec, &filtered_data).await?; + let supertrend_30m_map = + supertrend(10, 2.0, true, &alldata.rt_price_30m_vec, &filtered_data).await?; for (symbol, values) in &mut filtered_data { - if let (Some(supertrend_vec), Some(rt_price_vec)) = (supertrend_30m_map.get(symbol), alldata.rt_price_30m_vec.get(symbol)) { - if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && - rt_price_vec.last().unwrap().close_time > server_epoch { + if let (Some(supertrend_vec), Some(rt_price_vec)) = ( + supertrend_30m_map.get(symbol), + alldata.rt_price_30m_vec.get(symbol), + ) { + if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time + && rt_price_vec.last().unwrap().close_time > server_epoch + { // input stoploss, target_price - let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap(); - let open_price: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(rt_price_vec.last().unwrap().open_price).unwrap(); - let current_price: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(rt_price_vec.last().unwrap().close_price).unwrap(); - if supertrend_vec.last().unwrap().area == SuperTrendArea::UP && - band_value < current_price && - band_value < open_price + let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64( + supertrend_vec.last().unwrap().band_value, + ) + .unwrap(); + let open_price: Decimal = rust_decimal::prelude::FromPrimitive::from_f64( + rt_price_vec.last().unwrap().open_price, + ) + .unwrap(); + let current_price: Decimal = rust_decimal::prelude::FromPrimitive::from_f64( + rt_price_vec.last().unwrap().close_price, + ) + .unwrap(); + if supertrend_vec.last().unwrap().area == SuperTrendArea::UP + && band_value < current_price + && band_value < open_price { values.current_price = current_price; values.closetime = rt_price_vec.last().unwrap().close_time; values.stoploss = band_value; - values.target_price = decimal_add(decimal_mul(decimal_sub(current_price, values.stoploss), dec!(3.0)), current_price); - } else if supertrend_vec.last().unwrap().area == SuperTrendArea::DOWN && - band_value > current_price && - band_value > open_price + values.target_price = decimal_add( + decimal_mul(decimal_sub(current_price, values.stoploss), dec!(3.0)), + current_price, + ); + } else if supertrend_vec.last().unwrap().area == SuperTrendArea::DOWN + && band_value > current_price + && band_value > open_price { values.current_price = current_price; values.closetime = rt_price_vec.last().unwrap().close_time; values.stoploss = decimal_sub(open_price, decimal_sub(band_value, open_price)); - values.target_price = decimal_add(decimal_mul(decimal_sub(open_price, values.stoploss), dec!(3.0)), current_price); - + values.target_price = decimal_add( + decimal_mul(decimal_sub(open_price, values.stoploss), dec!(3.0)), + current_price, + ); } else { keys_to_remove.insert(symbol.clone()); } @@ -115,7 +151,7 @@ pub async fn list_up_for_buy( keys_to_remove.insert(symbol.clone()); } } - remove_keys(&mut filtered_data, keys_to_remove).await; + remove_keys(&mut filtered_data, keys_to_remove).await; // current ADX(15, 15) < 25 // let mut keys_to_remove: HashSet = HashSet::new(); @@ -133,21 +169,27 @@ pub async fn list_up_for_buy( // } // } else { // keys_to_remove.insert(symbol.clone()); - // } + // } // } // remove_keys(&mut filtered_data, keys_to_remove).await; - // StochRSI (RSI_len: 10, StochRSI_len: 10, K: 3, D: 3) K_current < 70, K_current > d_current + // StochRSI (RSI_len: 10, StochRSI_len: 10, K: 3, D: 3) K_current < 70, K_current > d_current let mut keys_to_remove: HashSet = HashSet::new(); let stoch_rsis = stoch_rsi(10, 10, 3, 3, &alldata.rt_price_30m_vec, &filtered_data).await?; for (symbol, values) in &mut filtered_data { if stoch_rsis.contains_key(symbol) { let stoch_rsi_vec = stoch_rsis.get(symbol).unwrap(); - let search_result = stoch_rsi_vec.iter().position(|x| x.close_time == values.closetime); - if stoch_rsi_vec.len() > 10 && search_result.is_some_and(|a| stoch_rsi_vec[a].k > stoch_rsi_vec[a].d && - stoch_rsi_vec[a].k < 70.0 && - stoch_rsi_vec[a-1].k < 60.0 && - stoch_rsi_vec[a-2].k < 50.0) { + let search_result = stoch_rsi_vec + .iter() + .position(|x| x.close_time == values.closetime); + if stoch_rsi_vec.len() > 10 + && search_result.is_some_and(|a| { + stoch_rsi_vec[a].k > stoch_rsi_vec[a].d + && stoch_rsi_vec[a].k < 70.0 + && stoch_rsi_vec[a - 1].k < 60.0 + && stoch_rsi_vec[a - 2].k < 50.0 + }) + { } else { keys_to_remove.insert(symbol.clone()); } @@ -159,25 +201,52 @@ pub async fn list_up_for_buy( // Heatmap volume: filtering close price with Extra High is over the previous candle from 30 previous candles let mut keys_to_remove: HashSet = HashSet::new(); - let heatmap_volumes = heatmap_volume(30, 30, 4.0, 2.5, 1.0, -0.5, &filtered_data, &alldata.rt_price_30m_vec).await?; + let heatmap_volumes = heatmap_volume( + 30, + 30, + 4.0, + 2.5, + 1.0, + -0.5, + &filtered_data, + &alldata.rt_price_30m_vec, + ) + .await?; for (symbol, values) in &mut filtered_data { if stoch_rsis.contains_key(symbol) { let heatmap_volume_vec = heatmap_volumes.get(symbol).unwrap(); if heatmap_volume_vec.len() > 50 { - let heatmap_volume_trunc = heatmap_volume_vec.get(heatmap_volume_vec.len()-31..heatmap_volume_vec.len()-1).unwrap(); + let heatmap_volume_trunc = heatmap_volume_vec + .get(heatmap_volume_vec.len() - 31..heatmap_volume_vec.len() - 1) + .unwrap(); let windows = heatmap_volume_trunc.windows(2); for slice in windows { if slice[1].heatmap_level == HeatMapLevel::ExtraHigh { if let (prev_candle_idx, current_candle_idx) = ( - (&alldata.rt_price_30m_vec.get(symbol).unwrap().iter().position(|x| x.close_time == slice[0].close_time)).unwrap(), - (&alldata.rt_price_30m_vec.get(symbol).unwrap().iter().position(|x| x.close_time == slice[1].close_time)).unwrap() + (&alldata + .rt_price_30m_vec + .get(symbol) + .unwrap() + .iter() + .position(|x| x.close_time == slice[0].close_time)) + .unwrap(), + (&alldata + .rt_price_30m_vec + .get(symbol) + .unwrap() + .iter() + .position(|x| x.close_time == slice[1].close_time)) + .unwrap(), ) { - let prev_candle = &alldata.rt_price_30m_vec.get(symbol).unwrap()[prev_candle_idx]; - let current_candle = &alldata.rt_price_30m_vec.get(symbol).unwrap()[current_candle_idx]; - if current_candle.close_price > prev_candle.close_price || - current_candle.close_price > prev_candle.open_price || - current_candle.close_price > prev_candle.high_price || - current_candle.close_price > prev_candle.low_price { + let prev_candle = + &alldata.rt_price_30m_vec.get(symbol).unwrap()[prev_candle_idx]; + let current_candle = + &alldata.rt_price_30m_vec.get(symbol).unwrap()[current_candle_idx]; + if current_candle.close_price > prev_candle.close_price + || current_candle.close_price > prev_candle.open_price + || current_candle.close_price > prev_candle.high_price + || current_candle.close_price > prev_candle.low_price + { keys_to_remove.insert(symbol.clone()); } } @@ -193,14 +262,19 @@ pub async fn list_up_for_buy( let server_epoch = get_server_epoch().await; for (symbol, values) in &mut filtered_data { if let Some(rt_price_vec) = alldata.rt_price_30m_vec.get(symbol) { - if rt_price_vec.last().unwrap().close_time > server_epoch && rt_price_vec.len() >= 6 { + if rt_price_vec.last().unwrap().close_time > server_epoch && rt_price_vec.len() >= 6 { let mut opclo_vec: Vec = Vec::new(); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-2].opclo_price); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-3].opclo_price); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-4].opclo_price); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-5].opclo_price); - opclo_vec.push(rt_price_vec[rt_price_vec.len()-6].opclo_price); - let max_idx = opclo_vec.iter().position(|&x| x == *opclo_vec.iter().max_by(|&a, &b| a.partial_cmp(b).unwrap()).unwrap()); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 2].opclo_price); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 3].opclo_price); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 4].opclo_price); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 5].opclo_price); + opclo_vec.push(rt_price_vec[rt_price_vec.len() - 6].opclo_price); + let max_idx = opclo_vec.iter().position(|&x| { + x == *opclo_vec + .iter() + .max_by(|&a, &b| a.partial_cmp(b).unwrap()) + .unwrap() + }); opclo_vec.remove(max_idx.unwrap()); let mut mean = 0.0; @@ -222,7 +296,7 @@ pub async fn list_up_for_buy( keys_to_remove.insert(symbol.clone()); } } - remove_keys(&mut filtered_data, keys_to_remove).await; + remove_keys(&mut filtered_data, keys_to_remove).await; let final_filtered_data = duplicate_filter(8, &filtered_data).await?; insert_pre_suggested_coins(8, false, &final_filtered_data, &alldata).await; @@ -248,43 +322,56 @@ pub async fn list_up_for_sell( for element in &filled_buy_orders { filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new()); } - let supertrend_30m = supertrend(10, 2.0, true, &all_data.rt_price_30m_vec, &filtered_symbols).await?; + let supertrend_30m = + supertrend(10, 2.0, true, &all_data.rt_price_30m_vec, &filtered_symbols).await?; let tema_30 = tema(30, &all_data.rt_price_30m_vec, &filtered_symbols).await?; let tema_5 = tema(5, &all_data.rt_price_30m_vec, &filtered_symbols).await?; for element in filled_buy_orders { let mut is_sell = false; let mut is_overturned = false; - if element.used_usdt >= dec!(10.0) { - if let (Some(tema30_vec), Some(tema5_vec)) = (tema_30.get(&element.symbol), tema_5.get(&element.symbol)) { - if tema30_vec.len() > 2 && tema5_vec.len() > 2 && - tema30_vec.last().unwrap().close_time == tema5_vec.last().unwrap().close_time && - tema30_vec.last().unwrap().close_time > server_epoch && - tema5_vec.last().unwrap().close_time > server_epoch && - tema30_vec.last().unwrap().tema_value > tema5_vec.last().unwrap().tema_value && - tema30_vec[tema30_vec.len()-2].tema_value < tema5_vec[tema5_vec.len()-2].tema_value { + if element.used_usdt >= dec!(10.0) { + if let (Some(tema30_vec), Some(tema5_vec)) = + (tema_30.get(&element.symbol), tema_5.get(&element.symbol)) + { + if tema30_vec.len() > 2 + && tema5_vec.len() > 2 + && tema30_vec.last().unwrap().close_time + == tema5_vec.last().unwrap().close_time + && tema30_vec.last().unwrap().close_time > server_epoch + && tema5_vec.last().unwrap().close_time > server_epoch + && tema30_vec.last().unwrap().tema_value + > tema5_vec.last().unwrap().tema_value + && tema30_vec[tema30_vec.len() - 2].tema_value + < tema5_vec[tema5_vec.len() - 2].tema_value + { is_overturned = true; - } - } - - if let (Some(exchange_info), Some(tradefee), Some(supertrend_vec)) = - (exchange_info_map.get(&element.symbol), trade_fee_map.get(&element.symbol), supertrend_30m.get(&element.symbol)) { + } + } + + if let (Some(exchange_info), Some(tradefee), Some(supertrend_vec)) = ( + exchange_info_map.get(&element.symbol), + trade_fee_map.get(&element.symbol), + supertrend_30m.get(&element.symbol), + ) { // update stoploss - let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap(); + let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64( + supertrend_vec.last().unwrap().band_value, + ) + .unwrap(); if supertrend_vec.last().unwrap().area == SuperTrendArea::UP - && band_value > element.stoploss { - let update_table_name = String::from("buy_ordered_coin_list"); - let update_value = vec![ - (String::from("stoploss"), band_value.to_string()), - ]; - let update_condition = vec![(String::from("id"), element.id.to_string())]; - update_record3(&update_table_name, &update_value, &update_condition) - .await - .unwrap(); - } + && band_value > element.stoploss + { + let update_table_name = String::from("buy_ordered_coin_list"); + let update_value = vec![(String::from("stoploss"), band_value.to_string())]; + let update_condition = vec![(String::from("id"), element.id.to_string())]; + update_record3(&update_table_name, &update_value, &update_condition) + .await + .unwrap(); + } let lot_step_size = exchange_info.stepsize; let quote_commission_precision = exchange_info.quote_commission_precision; - + // TODO: BNB 코인이 있으면 // let base_qty_to_be_ordered = // element.base_qty_ordered.round_dp_with_strategy( @@ -293,40 +380,53 @@ pub async fn list_up_for_sell( // ); // TODO: BNB 코인이 없으면 let base_qty_to_be_ordered = - element.base_qty_fee_adjusted.round_dp_with_strategy( - lot_step_size.normalize().scale(), - RoundingStrategy::ToZero, - ); - let target_profit_percent = decimal_div(decimal_sub(element.target_price, element.buy_price), element.buy_price).to_f64().unwrap(); - if !element.current_price.is_zero() - { + element.base_qty_fee_adjusted.round_dp_with_strategy( + lot_step_size.normalize().scale(), + RoundingStrategy::ToZero, + ); + let target_profit_percent = decimal_div( + decimal_sub(element.target_price, element.buy_price), + element.buy_price, + ) + .to_f64() + .unwrap(); + if !element.current_price.is_zero() { if element.current_price <= element.stoploss { is_sell = true; } else if element.current_price >= element.target_price { is_sell = true; - } else if server_epoch - element.transact_time > (1_800_000) * 1 && is_overturned == true { + } else if server_epoch - element.transact_time > (1_800_000) * 1 + && is_overturned == true + { is_sell = true; - } - + } + let minimum_candles = 5; let maximum_candles = 30; for count_candles in minimum_candles..=maximum_candles { - if count_candles < maximum_candles && - server_epoch - element.transact_time > (1_800_000) * count_candles && - (target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && - target_profit_percent * ((maximum_candles - count_candles) as f64 / (maximum_candles - minimum_candles + 1) as f64) <= element.pure_profit_percent) { + if count_candles < maximum_candles + && server_epoch - element.transact_time + > (1_800_000) * count_candles + && (target_profit_percent != 0.0 + && target_profit_percent.is_sign_positive() + && target_profit_percent + * ((maximum_candles - count_candles) as f64 + / (maximum_candles - minimum_candles + 1) as f64) + <= element.pure_profit_percent) + { is_sell = true; break; - } else if count_candles == maximum_candles { // time up selling + } else if count_candles == maximum_candles { + // time up selling is_sell = true; break; } else { break; } } - + // TODO: sell_count가 1일 때 적용하기 - // else if (supertrend_vec + // else if (supertrend_vec // .last() // .unwrap() // .signal diff --git a/src/strategy_team/strategy_manager.rs b/src/strategy_team/strategy_manager.rs index 5714d62..ba2450e 100644 --- a/src/strategy_team/strategy_manager.rs +++ b/src/strategy_team/strategy_manager.rs @@ -8,7 +8,7 @@ use serde::Deserialize; // use super::strategy_test; use super::{ exists_record, insert_one_record, try_join_all, try_select_record, AllData, ExchangeInfo, - FilteredDataValue, FromRow, RealtimePriceData, TradeFee, HashMap + FilteredDataValue, FromRow, HashMap, RealtimePriceData, TradeFee, }; use crate::signal_association::signal_decision::*; use tokio::time::{sleep, Duration, Instant}; @@ -39,7 +39,7 @@ pub async fn execute_list_up_for_buy( crate::strategy_team::strategy_006::list_up_for_buy(all_data).await; crate::strategy_team::strategy_007::list_up_for_buy(all_data).await; crate::strategy_team::strategy_008::list_up_for_buy(all_data).await; - + Ok(()) } @@ -47,16 +47,31 @@ pub async fn execute_list_up_for_sell( all_data: &AllData, exchange_info_map: &HashMap, trade_fee_map: &HashMap, -) -> Result<(), Box> { +) -> Result<(), Box> { // crate::strategy_team::strategy_001::list_up_for_sell(&all_data, &exchange_info_map, &trade_fee_map).await; // crate::strategy_team::strategy_002::list_up_for_sell(&all_data, &exchange_info_map, &trade_fee_map).await; // crate::strategy_team::strategy_003::list_up_for_sell(&all_data, &exchange_info_map, &trade_fee_map).await; // crate::strategy_team::strategy_004::list_up_for_sell(&all_data, &exchange_info_map, &trade_fee_map).await; // crate::strategy_team::strategy_005::list_up_for_sell(&all_data, &exchange_info_map, &trade_fee_map).await; - crate::strategy_team::strategy_006::list_up_for_sell(&all_data, &exchange_info_map, &trade_fee_map).await; - crate::strategy_team::strategy_007::list_up_for_sell(&all_data, &exchange_info_map, &trade_fee_map).await; - crate::strategy_team::strategy_008::list_up_for_sell(&all_data, &exchange_info_map, &trade_fee_map).await; - + crate::strategy_team::strategy_006::list_up_for_sell( + &all_data, + &exchange_info_map, + &trade_fee_map, + ) + .await; + crate::strategy_team::strategy_007::list_up_for_sell( + &all_data, + &exchange_info_map, + &trade_fee_map, + ) + .await; + crate::strategy_team::strategy_008::list_up_for_sell( + &all_data, + &exchange_info_map, + &trade_fee_map, + ) + .await; + Ok(()) } @@ -172,13 +187,13 @@ pub async fn insert_pre_suggested_coins( if is_dupe == false { let mut insert_values = vec![ - symbol.clone(), // symbol + symbol.clone(), // symbol filtered_data.closetime.to_string(), // close_time filtered_data.current_price.to_string(), // suggested_price filtered_data.current_price.to_string(), // current_price filtered_data.stoploss.to_string(), // stoploss filtered_data.target_price.to_string(), // target_price - get_server_epoch().await.to_string(), // registered_server_epoch + get_server_epoch().await.to_string(), // registered_server_epoch 0.0.to_string(), // profit_percent 0.0.to_string(), // minimum_profit_percent 0.0.to_string(), // maximum_profit_percent @@ -258,13 +273,13 @@ pub async fn insert_pre_suggested_coins( if is_dupe == false { let mut insert_values = vec![ - symbol.clone(), // symbol + symbol.clone(), // symbol filtered_data.closetime.to_string(), // close_time filtered_data.current_price.to_string(), // suggested_price filtered_data.current_price.to_string(), // current_price filtered_data.stoploss.to_string(), // stoploss filtered_data.target_price.to_string(), // target_price - get_server_epoch().await.to_string(), // registered_server_epoch + get_server_epoch().await.to_string(), // registered_server_epoch 0.0.to_string(), // profit_percent 0.0.to_string(), // minimum_profit_percent 0.0.to_string(), // maximum_profit_percent @@ -334,13 +349,13 @@ pub async fn insert_pre_suggested_coins( if is_dupe == false { let mut insert_values = vec![ - symbol.clone(), // symbol + symbol.clone(), // symbol filtered_data.closetime.to_string(), // close_time filtered_data.current_price.to_string(), // suggested_price filtered_data.current_price.to_string(), // current_price filtered_data.stoploss.to_string(), // stoploss filtered_data.target_price.to_string(), // target_price - get_server_epoch().await.to_string(), // registered_server_epoch + get_server_epoch().await.to_string(), // registered_server_epoch 0.0.to_string(), // profit_percent 0.0.to_string(), // minimum_profit_percent 0.0.to_string(), // maximum_profit_percent @@ -394,13 +409,13 @@ pub async fn insert_pre_suggested_coins( if is_dupe == false { let mut insert_values = vec![ - symbol.clone(), // symbol + symbol.clone(), // symbol filtered_data.closetime.to_string(), // close_time filtered_data.current_price.to_string(), // suggested_price filtered_data.current_price.to_string(), // current_price filtered_data.stoploss.to_string(), // stoploss filtered_data.target_price.to_string(), // target_price - get_server_epoch().await.to_string(), // registered_server_epoch + get_server_epoch().await.to_string(), // registered_server_epoch 0.0.to_string(), // profit_percent 0.0.to_string(), // minimum_profit_percent 0.0.to_string(), // maximum_profit_percent @@ -480,13 +495,13 @@ pub async fn insert_pre_suggested_coins( if is_dupe == false { let mut insert_values = vec![ - symbol.clone(), // symbol + symbol.clone(), // symbol filtered_data.closetime.to_string(), // close_time filtered_data.current_price.to_string(), // suggested_price filtered_data.current_price.to_string(), // current_price filtered_data.stoploss.to_string(), // stoploss filtered_data.target_price.to_string(), // target_price - get_server_epoch().await.to_string(), // registered_server_epoch + get_server_epoch().await.to_string(), // registered_server_epoch 0.0.to_string(), // profit_percent 0.0.to_string(), // minimum_profit_percent 0.0.to_string(), // maximum_profit_percent @@ -550,13 +565,13 @@ pub async fn insert_pre_suggested_coins( if is_dupe == false { let mut insert_values = vec![ - symbol.clone(), // symbol + symbol.clone(), // symbol filtered_data.closetime.to_string(), // close_time filtered_data.current_price.to_string(), // suggested_price filtered_data.current_price.to_string(), // current_price filtered_data.stoploss.to_string(), // stoploss filtered_data.target_price.to_string(), // target_price - get_server_epoch().await.to_string(), // registered_server_epoch + get_server_epoch().await.to_string(), // registered_server_epoch 0.0.to_string(), // profit_percent 0.0.to_string(), // minimum_profit_percent 0.0.to_string(), // maximum_profit_percent @@ -610,13 +625,13 @@ pub async fn insert_pre_suggested_coins( if is_dupe == false { let mut insert_values = vec![ - symbol.clone(), // symbol + symbol.clone(), // symbol filtered_data.closetime.to_string(), // close_time filtered_data.current_price.to_string(), // suggested_price filtered_data.current_price.to_string(), // current_price filtered_data.stoploss.to_string(), // stoploss filtered_data.target_price.to_string(), // target_price - get_server_epoch().await.to_string(), // registered_server_epoch + get_server_epoch().await.to_string(), // registered_server_epoch 0.0.to_string(), // profit_percent 0.0.to_string(), // minimum_profit_percent 0.0.to_string(), // maximum_profit_percent @@ -635,13 +650,13 @@ pub async fn insert_pre_suggested_coins( } else { for (symbol, filtered_data) in filtered_coins { let mut insert_values = vec![ - symbol.clone(), // symbol + symbol.clone(), // symbol filtered_data.closetime.to_string(), // close_time filtered_data.current_price.to_string(), // suggested_price filtered_data.current_price.to_string(), // current_price filtered_data.stoploss.to_string(), // stoploss filtered_data.target_price.to_string(), // target_price - get_server_epoch().await.to_string(), // registered_server_epoch + get_server_epoch().await.to_string(), // registered_server_epoch 0.0.to_string(), // profit_percent 0.0.to_string(), // minimum_profit_percent 0.0.to_string(), // maximum_profit_percent diff --git a/src/strategy_team/strategy_test.rs b/src/strategy_team/strategy_test.rs index 6ae58c2..be94875 100644 --- a/src/strategy_team/strategy_test.rs +++ b/src/strategy_team/strategy_test.rs @@ -1,7 +1,7 @@ use super::{ dec, decimal_add, decimal_sub, ema, exists_record, insert_pre_suggested_coins, limit_order_sell, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, AllData, - Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredData, Mutex, + Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredDataValue, Mutex, RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, MacdData, ema_macd }; @@ -14,13 +14,13 @@ pub async fn strategist_test( // println!("BTCUSDT: {:?}", alldata.rt_price_30m_vec[a.unwrap()].1.last().unwrap()); // 1st filtering: lookup tables if the tradepair is already there - let mut symbol_1 = FilteredData::new(); + let mut symbol_1 = FilteredDataValue::new(); // let mut symbol_2 = FilteredData::new(); // let mut symbol_3 = FilteredData::new(); symbol_1.symbol = String::from("BTCUSDT"); // symbol_2.symbol = String::from("XRPUSDT"); // symbol_3.symbol = String::from("ETHUSDT"); - let mut test_symbols: Vec = Vec::new(); + let mut test_symbols: Vec = Vec::new(); test_symbols.push(symbol_1); // test_symbols.push(symbol_2); // test_symbols.push(symbol_3); diff --git a/src/value_estimation_team/datapoints/price_data.rs b/src/value_estimation_team/datapoints/price_data.rs index b550074..095d52d 100644 --- a/src/value_estimation_team/datapoints/price_data.rs +++ b/src/value_estimation_team/datapoints/price_data.rs @@ -7,12 +7,15 @@ use csv::{DeserializeRecordsIter, StringRecord}; use rust_decimal::{prelude::ToPrimitive, Decimal}; use serde::Deserialize; use sqlx::FromRow; +use std::collections::{HashMap, HashSet}; use std::sync::Arc; use tokio::{fs::*, sync::Mutex, time::*}; -use std::collections::{HashMap, HashSet}; #[derive(Debug, Clone, PartialEq)] -pub enum CandleType { UP, DOWN } +pub enum CandleType { + UP, + DOWN, +} #[derive(Debug, Clone, PartialEq)] pub struct RealtimePriceData { @@ -93,15 +96,11 @@ pub async fn update_realtime_price_data( rt_price_vec.last_mut().unwrap().candle_type = CandleType::DOWN; } // update high_price - if rt_price_vec.last_mut().unwrap().high_price - < *current_price - { + if rt_price_vec.last_mut().unwrap().high_price < *current_price { rt_price_vec.last_mut().unwrap().high_price = *current_price; } // update low_price - if rt_price_vec.last_mut().unwrap().low_price - > *current_price - { + if rt_price_vec.last_mut().unwrap().low_price > *current_price { rt_price_vec.last_mut().unwrap().low_price = *current_price; } } @@ -112,7 +111,7 @@ pub async fn update_realtime_price_data( // for 1mon, uses 1w candle if let Some(rt_vec) = read_candle_for_rt.get(element) { if rt_vec.len() >= 2 && rt_price_vec.len() >= 2 { - let previous_close_time = rt_price_vec[rt_price_vec.len()-2].close_time; + let previous_close_time = rt_price_vec[rt_price_vec.len() - 2].close_time; // update realtime information for the latest candle let mut update_closeprice = 0.0; let mut update_highprice = 0.0; @@ -134,25 +133,22 @@ pub async fn update_realtime_price_data( }| *close_time, ); if prev_closetime_result.is_ok() { - let result = - rt_vec.get(prev_closetime_result.unwrap() + 1..); + let result = rt_vec.get(prev_closetime_result.unwrap() + 1..); if result.is_some() { let update_highprice_result = result.unwrap().iter().max_by(|x, y| { x.high_price.partial_cmp(&y.high_price).unwrap() }); if update_highprice_result.is_some() { - update_highprice = - update_highprice_result.unwrap().high_price; + update_highprice = update_highprice_result.unwrap().high_price; } - let update_lowprice_result = - result.unwrap().iter().min_by(|x, y| { - x.low_price.partial_cmp(&y.low_price).unwrap() - }); + let update_lowprice_result = result + .unwrap() + .iter() + .min_by(|x, y| x.low_price.partial_cmp(&y.low_price).unwrap()); if update_lowprice_result.is_some() { - update_lowprice = - update_lowprice_result.unwrap().low_price; + update_lowprice = update_lowprice_result.unwrap().low_price; } for element in result.unwrap() { @@ -189,12 +185,10 @@ pub async fn update_realtime_price_data( && !update_closeprice.is_nan() && update_closeprice.is_finite() { - rt_price_vec.last_mut().unwrap().close_price = - update_closeprice; - rt_price_vec.last_mut().unwrap().opclo_price = - (update_closeprice - + rt_price_vec.last_mut().unwrap().open_price) - / 2.0; + rt_price_vec.last_mut().unwrap().close_price = update_closeprice; + rt_price_vec.last_mut().unwrap().opclo_price = (update_closeprice + + rt_price_vec.last_mut().unwrap().open_price) + / 2.0; } } } diff --git a/src/value_estimation_team/indicators/adx.rs b/src/value_estimation_team/indicators/adx.rs index 0309ca6..0f5653e 100644 --- a/src/value_estimation_team/indicators/adx.rs +++ b/src/value_estimation_team/indicators/adx.rs @@ -1,9 +1,9 @@ -use super::{FilteredDataValue, RealtimePriceData, try_join_all, Arc, Mutex, HashMap}; +use super::{try_join_all, Arc, FilteredDataValue, HashMap, Mutex, RealtimePriceData}; #[derive(Clone, Debug)] pub struct AdxData { pub adx: f64, - pub close_time: i64 + pub close_time: i64, } struct BasicData { @@ -19,12 +19,16 @@ struct DiData { close_time: i64, } -pub async fn adx(adx_len: usize, di_len: usize, input_rt_data: &HashMap>, -filtered_symbols: &HashMap,) -> Result>, Box> { +pub async fn adx( + adx_len: usize, + di_len: usize, + input_rt_data: &HashMap>, + filtered_symbols: &HashMap, +) -> Result>, Box> { if filtered_symbols.is_empty() { Err(("Err"))?; } - + let mut adx_vec: HashMap> = HashMap::new(); let mut adx_vec_arc = Arc::new(Mutex::new(adx_vec)); let mut task_vec = Vec::new(); @@ -32,9 +36,9 @@ filtered_symbols: &HashMap,) -> Result adx_len && rt_price_data.len() > di_len { + if rt_price_data.len() - 1 > adx_len && rt_price_data.len() > di_len { // step 1: calculate +DM, -DM, TR let windows = rt_price_data.windows(2); let mut basic_data_vec: Vec = Vec::new(); @@ -46,17 +50,21 @@ filtered_symbols: &HashMap,) -> Result down && up > 0.0 { up } else { 0.0 }, dm_minus: if down > up && down > 0.0 { down } else { 0.0 }, - true_range: f64::max(f64::max(current_rt_data.high_price - current_rt_data.low_price, - current_rt_data.high_price - prev_rt_data.close_price), - current_rt_data.low_price - prev_rt_data.close_price), + true_range: f64::max( + f64::max( + current_rt_data.high_price - current_rt_data.low_price, + current_rt_data.high_price - prev_rt_data.close_price, + ), + current_rt_data.low_price - prev_rt_data.close_price, + ), close_time: current_rt_data.close_time, }; basic_data_vec.push(basic_data); } // step 2: smoothing +DM, -DM, TR - let alpha: f64 = 1.0/(di_len as f64); + let alpha: f64 = 1.0 / (di_len as f64); let mut smoothed_basic_data_vec: Vec = Vec::new(); - + let partial_vec1 = basic_data_vec.get(..di_len).unwrap(); // for calculation of initial value let partial_vec2 = basic_data_vec.get(di_len..).unwrap(); // for calculation of the rest @@ -71,22 +79,38 @@ filtered_symbols: &HashMap,) -> Result = Vec::new(); for basic_data in smoothed_basic_data_vec { - let di_data = DiData { di_plus: (100.0 * basic_data.dm_plus) / basic_data.true_range, di_minus: (100.0 * basic_data.dm_minus) / basic_data.true_range, close_time: basic_data.close_time}; + let di_data = DiData { + di_plus: (100.0 * basic_data.dm_plus) / basic_data.true_range, + di_minus: (100.0 * basic_data.dm_minus) / basic_data.true_range, + close_time: basic_data.close_time, + }; di_data_vec.push(di_data); } @@ -96,11 +120,17 @@ filtered_symbols: &HashMap,) -> Result = Vec::new(); @@ -109,14 +139,20 @@ filtered_symbols: &HashMap,) -> Result,) -> Result> = sma(period, input_rt_data, filtered_symbols).await?; + let mut sma_data_map: HashMap> = + sma(period, input_rt_data, filtered_symbols).await?; let mut bb_data_wrapper: HashMap> = HashMap::new(); let mut bb_data_wrapper_arc = Arc::new(Mutex::new(bb_data_wrapper)); let mut task_vec = Vec::new(); @@ -56,33 +57,31 @@ pub async fn bollingerband( task_vec.push(tokio::spawn(async move { let mut bb_data = BollingerBandData::new(); let mut bb_data_vec: Vec = Vec::new(); - + // if the data has shorter than buffer if sma_data_vec_c.len() > period { let result = rt_data_vec_c.binary_search_by_key( &sma_data_vec_c.first().unwrap().close_time, |RealtimePriceData { - opclo_price, - open_price, - close_price, - high_price, - low_price, - close_time, - quote_asset_volume, - candle_type, - }| *close_time, + opclo_price, + open_price, + close_price, + high_price, + low_price, + close_time, + quote_asset_volume, + candle_type, + }| *close_time, ); - + match result { Ok(T) => { if T <= period - 1 { - let mut read_data_iter = - sma_data_vec_c.iter(); + let mut read_data_iter = sma_data_vec_c.iter(); for _ in T..period - 1 { read_data_iter.next(); } - let window_iter = - rt_data_vec_c.windows(period); + let window_iter = rt_data_vec_c.windows(period); for buffer in window_iter { let mut sd_mean = 0.0; let mut standard_deviation = 0.0; @@ -94,16 +93,14 @@ pub async fn bollingerband( standard_deviation += (element.close_price - sd_mean).powi(2); } - standard_deviation = sd_factor - * ((standard_deviation / period as f64).sqrt()); - + standard_deviation = + sd_factor * ((standard_deviation / period as f64).sqrt()); + match read_data_iter.next() { Some(T) => { bb_data.sma = T.sma_value; - bb_data.upperband = - T.sma_value + standard_deviation; - bb_data.lowerband = - T.sma_value - standard_deviation; + bb_data.upperband = T.sma_value + standard_deviation; + bb_data.lowerband = T.sma_value - standard_deviation; bb_data.close_time = T.close_time; bb_data_vec.push(bb_data.clone()); } @@ -115,8 +112,7 @@ pub async fn bollingerband( Err(E) => {} } let mut bb_data_wrapper_lock = bb_data_wrapper_arc_c.lock().await; - bb_data_wrapper_lock - .insert(symbol_c, bb_data_vec.clone()); + bb_data_wrapper_lock.insert(symbol_c, bb_data_vec.clone()); } })); } diff --git a/src/value_estimation_team/indicators/dema.rs b/src/value_estimation_team/indicators/dema.rs index 82c89c9..42badb1 100644 --- a/src/value_estimation_team/indicators/dema.rs +++ b/src/value_estimation_team/indicators/dema.rs @@ -1,15 +1,15 @@ #![allow(unused)] #![allow(warnings)] +use super::ema::{ema, EmaData}; +use super::FilteredDataValue; use crate::database_control::*; use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; use futures::future::try_join_all; use serde::Deserialize; use sqlx::FromRow; +use std::collections::HashMap; use std::{iter::zip, sync::Arc}; use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*}; -use super::FilteredDataValue; -use std::collections::HashMap; -use super::ema::{ema, EmaData}; #[derive(Clone, Debug)] pub struct DemaData { @@ -81,7 +81,6 @@ pub async fn dema( ema_data_wrapper_lock.insert(symbol_c, ema_data_vec.clone()); } })); - } try_join_all(task_vec).await?; let e2 = ema_data_wrapper_arc.lock().await.to_owned(); @@ -91,14 +90,16 @@ pub async fn dema( let mut task_vec = Vec::new(); for (symbol, e2_vec) in e2 { if let Some(e1_vec) = e1.get(&symbol) { - let dema_data_wrapper_arc_c: Arc>>> = Arc::clone(&dema_data_wrapper_arc); + let dema_data_wrapper_arc_c: Arc>>> = + Arc::clone(&dema_data_wrapper_arc); let symbol_c = symbol.clone(); let e1_vec_c = e1_vec.clone(); task_vec.push(tokio::spawn(async move { - if e2_vec.last().unwrap().close_time == e1_vec_c.last().unwrap().close_time && - e2_vec.len() < e1_vec_c.len() { + if e2_vec.last().unwrap().close_time == e1_vec_c.last().unwrap().close_time + && e2_vec.len() < e1_vec_c.len() + { let mut dema_data_vec: Vec = Vec::new(); - let e1_vec_part = e1_vec_c.get(e1_vec_c.len()-e2_vec.len()..).unwrap(); + let e1_vec_part = e1_vec_c.get(e1_vec_c.len() - e2_vec.len()..).unwrap(); let zipped = e1_vec_part.iter().zip(e2_vec.iter()); for element in zipped { let mut dema_data = DemaData::new(); diff --git a/src/value_estimation_team/indicators/ema.rs b/src/value_estimation_team/indicators/ema.rs index 613f7cd..7b5f3de 100644 --- a/src/value_estimation_team/indicators/ema.rs +++ b/src/value_estimation_team/indicators/ema.rs @@ -1,14 +1,14 @@ #![allow(unused)] #![allow(warnings)] +use super::FilteredDataValue; use crate::database_control::*; use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; use futures::future::try_join_all; use serde::Deserialize; use sqlx::FromRow; +use std::collections::HashMap; use std::sync::Arc; use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*}; -use super::FilteredDataValue; -use std::collections::HashMap; #[derive(Clone, Debug)] pub struct EmaData { diff --git a/src/value_estimation_team/indicators/heatmap_volume.rs b/src/value_estimation_team/indicators/heatmap_volume.rs index f2de9fd..dd1886c 100644 --- a/src/value_estimation_team/indicators/heatmap_volume.rs +++ b/src/value_estimation_team/indicators/heatmap_volume.rs @@ -1,9 +1,9 @@ -use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; use super::FilteredDataValue; +use super::HashMap; +use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; +use futures::future::try_join_all; use std::sync::Arc; use tokio::sync::Mutex; -use futures::future::try_join_all; -use super::HashMap; #[derive(Debug, Clone, PartialEq)] pub enum HeatMapLevel { @@ -49,7 +49,7 @@ pub async fn heatmap_volume( mean_value: f64, close_time: i64, } - + let mut mean_vec: Vec = Vec::new(); let mut mean_data = MeanData { mean_value: 0.0, @@ -65,10 +65,10 @@ pub async fn heatmap_volume( } mean_data.mean_value /= ma_len as f64; mean_data.close_time = buffer.last().unwrap().close_time; - + mean_vec.push(mean_data.clone()); } - + // calc pstdev #[derive(Debug, Clone)] struct PstdevData { @@ -94,18 +94,18 @@ pub async fn heatmap_volume( mean += element.quote_asset_volume; } mean /= std_len as f64; - + for element in buffer { sample_minus_mean = element.quote_asset_volume - mean; summation += sample_minus_mean.powi(2); } - + pstdev_data.pstdev_value = (summation / std_len as f64).sqrt(); pstdev_data.close_time = buffer.last().unwrap().close_time; - + pstdev_vec.push(pstdev_data.clone()); } - + // calc stdbar and heatmap volume let mut heatmap_vol_vec: Vec = Vec::new(); let mut heatmap_vol_data = HeatmapVolumeData { @@ -113,11 +113,11 @@ pub async fn heatmap_volume( heatmap_level: HeatMapLevel::Normal, close_time: 0, }; - + if ma_len == std_len { let mut rt_data_trunc_vec = - rt_price_vec_c.get(ma_len - 1..).unwrap().iter(); - + rt_price_vec_c.get(ma_len - 1..).unwrap().iter(); + let zipped = mean_vec.iter().zip(pstdev_vec.iter()); for element in zipped { heatmap_vol_data.heatmap_value = @@ -140,7 +140,7 @@ pub async fn heatmap_volume( } } else if ma_len > std_len { let mut rt_data_trunc_vec = - rt_price_vec_c.get(std_len - 1..).unwrap().iter(); + rt_price_vec_c.get(std_len - 1..).unwrap().iter(); let mut mean_trunc_vec = mean_vec.get(mean_vec.len() - std_len..).unwrap().iter(); let zipped = mean_trunc_vec.zip(pstdev_vec.iter()); @@ -154,7 +154,7 @@ pub async fn heatmap_volume( } } else { let mut rt_data_trunc_vec = - rt_price_vec_c.get(ma_len - 1..).unwrap().iter(); + rt_price_vec_c.get(ma_len - 1..).unwrap().iter(); let mut pstdev_trunc_vec = pstdev_vec.get(pstdev_vec.len() - ma_len..).unwrap().iter(); let zipped = mean_vec.iter().zip(pstdev_trunc_vec); @@ -172,7 +172,6 @@ pub async fn heatmap_volume( } })); } - } try_join_all(task_vec).await?; let a = heatmap_data_wrapper_arc.lock().await.to_owned(); diff --git a/src/value_estimation_team/indicators/macd.rs b/src/value_estimation_team/indicators/macd.rs index e7ef55d..d787a24 100644 --- a/src/value_estimation_team/indicators/macd.rs +++ b/src/value_estimation_team/indicators/macd.rs @@ -1,9 +1,9 @@ -use crate::value_estimation_team::indicators::ema::{EmaData, ema}; -use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; use super::{FilteredDataValue, HashMap}; +use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; +use crate::value_estimation_team::indicators::ema::{ema, EmaData}; +use futures::future::try_join_all; use std::sync::Arc; use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*}; -use futures::future::try_join_all; #[derive(Debug, Clone)] pub struct MacdData { @@ -44,7 +44,7 @@ pub async fn ema_macd( slow_len: usize, signal_smoothing: usize, input_rt_data: &HashMap>, - filtered_symbols: &HashMap + filtered_symbols: &HashMap, ) -> Result>, Box> { let mut macd_oscil_vec: HashMap> = HashMap::new(); @@ -56,19 +56,23 @@ pub async fn ema_macd( for (symbol, filtered_data) in filtered_symbols { let fast_emas_c = fast_emas.clone(); let slow_emas_c = slow_emas.clone(); - if let (Some(fast_ema_vec), Some(slow_ema_vec)) = (fast_emas.get(symbol), slow_emas_c.get(symbol)) { + if let (Some(fast_ema_vec), Some(slow_ema_vec)) = + (fast_emas.get(symbol), slow_emas_c.get(symbol)) + { let symbol_c = symbol.clone(); let fast_ema_vec_c = fast_ema_vec.clone(); let slow_ema_vec_c = slow_ema_vec.clone(); let macd_data_wrapper_arc_c = Arc::clone(&macd_data_wrapper_arc); task_vec.push(tokio::spawn(async move { - if fast_ema_vec_c.len() >= signal_smoothing && slow_ema_vec_c.len() >= signal_smoothing { + if fast_ema_vec_c.len() >= signal_smoothing + && slow_ema_vec_c.len() >= signal_smoothing + { let result = fast_ema_vec_c.binary_search_by_key( &slow_ema_vec_c.first().unwrap().close_time, |&EmaData { - ema_value, - close_time, - }| close_time, + ema_value, + close_time, + }| close_time, ); if result.is_ok() { // making MACD @@ -81,7 +85,7 @@ pub async fn ema_macd( macd.close_time = element.0.close_time; macd_vec.push(macd.clone()); } - + // making signal (smoothed MACD) // TODO: this should be calculated in EMA (currently, SMA) // let macd_vec_window = macd_vec.windows(signal_smoothing); @@ -121,20 +125,17 @@ pub async fn ema_macd( ema_prev = ema_t; } - + let result = macd_vec.binary_search_by_key( &macd_signal_vec.first().unwrap().close_time, - |&TempData { - value, - close_time, - }| close_time, + |&TempData { value, close_time }| close_time, ); if result.is_ok() { let result = macd_vec.get(result.unwrap()..); if result.is_some() { let zipped = result.unwrap().iter().zip(macd_signal_vec); - + let mut macd_vec: Vec = Vec::new(); for element in zipped { let mut macd = MacdData::new(); @@ -143,14 +144,15 @@ pub async fn ema_macd( macd.close_time = element.0.close_time; macd_vec.push(macd); } - let mut macd_data_wrapper_lock = macd_data_wrapper_arc_c.lock().await; + let mut macd_data_wrapper_lock = + macd_data_wrapper_arc_c.lock().await; macd_data_wrapper_lock.insert(symbol_c, macd_vec.clone()); } } } } })); - } + } } try_join_all(task_vec).await?; let a = macd_data_wrapper_arc.lock().await.to_owned(); diff --git a/src/value_estimation_team/indicators/mod.rs b/src/value_estimation_team/indicators/mod.rs index 82f3aea..82aac14 100644 --- a/src/value_estimation_team/indicators/mod.rs +++ b/src/value_estimation_team/indicators/mod.rs @@ -1,5 +1,6 @@ pub mod adx; pub mod bollingerband; +pub mod dema; pub mod ema; pub mod heatmap_volume; pub mod macd; @@ -8,11 +9,10 @@ pub mod sma; pub mod stoch_rsi; pub mod supertrend; pub mod tema; -pub mod dema; use crate::strategy_team::FilteredDataValue; use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; use futures::future::try_join_all; +use std::collections::HashMap; use std::sync::Arc; use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*}; -use std::collections::HashMap; diff --git a/src/value_estimation_team/indicators/rsi.rs b/src/value_estimation_team/indicators/rsi.rs index a81276c..0ac3be5 100644 --- a/src/value_estimation_team/indicators/rsi.rs +++ b/src/value_estimation_team/indicators/rsi.rs @@ -1,16 +1,16 @@ #![allow(unused)] #![allow(warnings)] +use super::HashMap; use crate::database_control::*; -use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; use crate::strategy_team::FilteredDataValue; +use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; use futures::future::try_join_all; use serde::Deserialize; use sqlx::FromRow; use std::f64::NAN; use std::sync::Arc; use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*}; -use super::HashMap; #[derive(Clone, Debug)] pub struct RsiData { @@ -132,8 +132,8 @@ pub async fn rsi( prev_avg_ups = current_avg_ups; prev_avg_downs = current_avg_downs; - let rs = current_avg_ups.unwrap() - / (current_avg_downs.unwrap() + 0.00000001); // 0.00000001 is used to avoid division by 0 + let rs = + current_avg_ups.unwrap() / (current_avg_downs.unwrap() + 0.00000001); // 0.00000001 is used to avoid division by 0 let rsi = 100.0 - (100.0 / (1.0 + rs)); diff --git a/src/value_estimation_team/indicators/sma.rs b/src/value_estimation_team/indicators/sma.rs index 62cc3b3..1b5aaac 100644 --- a/src/value_estimation_team/indicators/sma.rs +++ b/src/value_estimation_team/indicators/sma.rs @@ -1,15 +1,15 @@ #![allow(unused)] #![allow(warnings)] +use super::HashMap; use crate::database_control::*; -use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; use crate::strategy_team::FilteredDataValue; +use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; use futures::future::try_join_all; use serde::Deserialize; use sqlx::FromRow; use std::sync::Arc; use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*}; -use super::HashMap; #[derive(Clone, Debug)] pub struct SmaData { @@ -48,7 +48,7 @@ pub async fn sma( task_vec.push(tokio::spawn(async move { let mut sma_data = SmaData::new(); let mut sma_data_vec: Vec = Vec::new(); - + if rt_price_data.len() >= moving_number { let mut iter = rt_price_data.windows(moving_number); for buffer in iter { @@ -95,7 +95,7 @@ pub async fn sma_opclo( task_vec.push(tokio::spawn(async move { let mut sma_data = SmaData::new(); let mut sma_data_vec: Vec = Vec::new(); - + if rt_price_data.len() >= moving_number { let mut iter = rt_price_data.windows(moving_number); for buffer in iter { diff --git a/src/value_estimation_team/indicators/stoch_rsi.rs b/src/value_estimation_team/indicators/stoch_rsi.rs index 071b729..fa2a659 100644 --- a/src/value_estimation_team/indicators/stoch_rsi.rs +++ b/src/value_estimation_team/indicators/stoch_rsi.rs @@ -2,16 +2,16 @@ #![allow(warnings)] use crate::database_control::*; -use crate::value_estimation_team::indicators::rsi::{RsiData, rsi}; -use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; use crate::strategy_team::FilteredDataValue; +use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; +use crate::value_estimation_team::indicators::rsi::{rsi, RsiData}; use futures::{future::try_join_all, lock::Mutex}; use serde::Deserialize; use sqlx::FromRow; +use std::collections::{HashMap, HashSet}; use std::f64::NAN; use std::sync::Arc; use tokio::{fs::*, io::AsyncWriteExt, time::*}; -use std::collections::{HashMap, HashSet}; #[derive(Clone, Debug)] pub struct StochRsiData { diff --git a/src/value_estimation_team/indicators/supertrend.rs b/src/value_estimation_team/indicators/supertrend.rs index 7911c78..e8b30b4 100644 --- a/src/value_estimation_team/indicators/supertrend.rs +++ b/src/value_estimation_team/indicators/supertrend.rs @@ -1,21 +1,27 @@ +use super::{FilteredDataValue, HashMap}; use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; -use super::{HashMap, FilteredDataValue}; -use std::sync::Arc; use futures::future::try_join_all; +use std::sync::Arc; use tokio::sync::Mutex; #[derive(PartialEq, Clone, Debug)] -pub enum SuperTrendArea { UP, DOWN } +pub enum SuperTrendArea { + UP, + DOWN, +} #[derive(PartialEq, Clone, Debug)] -pub enum SuperTrendSignal { BUY, SELL } +pub enum SuperTrendSignal { + BUY, + SELL, +} #[derive(Clone, Debug)] pub struct SupertrendData { pub band_value: f64, pub signal: Option, // BUY or SELL - pub area: SuperTrendArea, // UP or DOWN - pub close_time: i64 + pub area: SuperTrendArea, // UP or DOWN + pub close_time: i64, } impl SupertrendData { @@ -24,7 +30,7 @@ impl SupertrendData { band_value: 0.0, signal: None, area: SuperTrendArea::DOWN, - close_time: 0 + close_time: 0, }; a @@ -68,7 +74,7 @@ impl ATRData { // TODO: should return type be Option? // Implementation from TradingView Script (SuperTrend by KivancOzbilgic, source price: closeprice) // return key: close_time -pub async fn supertrend( +pub async fn supertrend( atr_period: usize, multiplier: f64, is_original_method: bool, @@ -81,7 +87,7 @@ pub async fn supertrend( let mut task_vec = Vec::new(); for (symbol, filtered_data) in filtered_symbols { if let Some(rt_price_vec) = input_rt_data.get(symbol) { - if atr_period < rt_price_vec.len()-1 { + if atr_period < rt_price_vec.len() - 1 { let symbol_c = symbol.clone(); let supertrend_data_wrapper_arc_c = Arc::clone(&supertrend_data_wrapper_arc); let rt_price_vec_c = rt_price_vec.clone(); @@ -92,34 +98,34 @@ pub async fn supertrend( 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 = rt_price_vec_c.windows(2); - + tr_data.tr_value = rt_price_vec_c.first().unwrap().high_price - rt_price_vec_c.first().unwrap().low_price; tr_data.close_time = rt_price_vec_c.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; } @@ -127,35 +133,36 @@ pub async fn supertrend( 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)) + 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 { @@ -163,7 +170,7 @@ pub async fn supertrend( 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(); @@ -172,13 +179,13 @@ pub async fn supertrend( basic_lowerband: 0.0, close_time: 0, }; - + let closetime_search_result = rt_price_vec_c.iter().position(|x| { x.close_time == average_true_range_vec.first().unwrap().close_time }); let mut rt_data = &rt_price_vec_c[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) @@ -189,7 +196,7 @@ pub async fn supertrend( 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; @@ -206,14 +213,14 @@ pub async fn supertrend( } 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 = SuperTrendArea::UP; @@ -222,13 +229,13 @@ pub async fn supertrend( supertrend_data.area = SuperTrendArea::DOWN; trend = -1; } - + if supertrend_data.area == SuperTrendArea::UP { supertrend_data.band_value = final_upperband; } else { supertrend_data.band_value = final_lowerband; } - + if trend == 1 && prev_trend == -1 { supertrend_data.signal = Some(SuperTrendSignal::BUY); } else if trend == -1 && prev_trend == 1 { @@ -236,16 +243,17 @@ pub async fn supertrend( } 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; } - let mut supertrend_data_wrapper_lock = supertrend_data_wrapper_arc_c.lock().await; + let mut supertrend_data_wrapper_lock = + supertrend_data_wrapper_arc_c.lock().await; supertrend_data_wrapper_lock.insert(symbol_c, supertrend_vec.clone()); })); } diff --git a/src/value_estimation_team/indicators/tema.rs b/src/value_estimation_team/indicators/tema.rs index b49bc23..1ac9738 100644 --- a/src/value_estimation_team/indicators/tema.rs +++ b/src/value_estimation_team/indicators/tema.rs @@ -1,15 +1,15 @@ #![allow(unused)] #![allow(warnings)] +use super::ema::{ema, EmaData}; +use super::FilteredDataValue; use crate::database_control::*; use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; use futures::future::try_join_all; use serde::Deserialize; use sqlx::FromRow; +use std::collections::HashMap; use std::{iter::zip, sync::Arc}; use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*}; -use super::FilteredDataValue; -use std::collections::HashMap; -use super::ema::{ema, EmaData}; #[derive(Clone, Debug)] pub struct TemaData { @@ -81,7 +81,6 @@ pub async fn tema( ema_data_wrapper_lock.insert(symbol_c, ema_data_vec.clone()); } })); - } try_join_all(task_vec).await?; let e2 = ema_data_wrapper_arc.lock().await.to_owned(); @@ -126,7 +125,6 @@ pub async fn tema( ema_data_wrapper_lock.insert(symbol_c, ema_data_vec.clone()); } })); - } try_join_all(task_vec).await?; let e3 = ema_data_wrapper_arc.lock().await.to_owned(); @@ -136,25 +134,29 @@ pub async fn tema( let mut task_vec = Vec::new(); for (symbol, e3_vec) in e3 { if let (Some(e1_vec), Some(e2_vec)) = (e1.get(&symbol), e2.get(&symbol)) { - let tema_data_wrapper_arc_c: Arc>>> = Arc::clone(&tema_data_wrapper_arc); + let tema_data_wrapper_arc_c: Arc>>> = + Arc::clone(&tema_data_wrapper_arc); let symbol_c = symbol.clone(); let e1_vec_c = e1_vec.clone(); let e2_vec_c = e2_vec.clone(); task_vec.push(tokio::spawn(async move { - if e3_vec.last().unwrap().close_time == e1_vec_c.last().unwrap().close_time && - e3_vec.last().unwrap().close_time == e2_vec_c.last().unwrap().close_time && - e3_vec.len() < e1_vec_c.len() && - e3_vec.len() < e2_vec_c.len() && - e2_vec_c.len() < e1_vec_c.len() { + if e3_vec.last().unwrap().close_time == e1_vec_c.last().unwrap().close_time + && e3_vec.last().unwrap().close_time == e2_vec_c.last().unwrap().close_time + && e3_vec.len() < e1_vec_c.len() + && e3_vec.len() < e2_vec_c.len() + && e2_vec_c.len() < e1_vec_c.len() + { let mut tema_data_vec: Vec = Vec::new(); - let e1_vec_part = e1_vec_c.get(e1_vec_c.len()-e3_vec.len()..).unwrap(); - let e2_vec_part = e2_vec_c.get(e2_vec_c.len()-e3_vec.len()..).unwrap(); + let e1_vec_part = e1_vec_c.get(e1_vec_c.len() - e3_vec.len()..).unwrap(); + let e2_vec_part = e2_vec_c.get(e2_vec_c.len() - e3_vec.len()..).unwrap(); let zipped_e1_e2 = e1_vec_part.iter().zip(e2_vec_part.iter()); let zipped_e1_e2_e3 = zipped_e1_e2.zip(e3_vec.iter()); for element in zipped_e1_e2_e3 { let mut tema_data = TemaData::new(); - tema_data.close_time = element.0.0.close_time; - tema_data.tema_value = (3.0 * (element.0.0.ema_value - element.0.1.ema_value)) + element.1.ema_value; + tema_data.close_time = element.0 .0.close_time; + tema_data.tema_value = (3.0 + * (element.0 .0.ema_value - element.0 .1.ema_value)) + + element.1.ema_value; tema_data_vec.push(tema_data); } let mut dema_data_wrapper_lock = tema_data_wrapper_arc_c.lock().await;