Replace Vec with HashMap (#3)

All vec operation was replaced with HashMap.

Increase outstanding performance.

Co-authored-by: Sik Yoon <younxxxx@gmail.com>
Reviewed-on: http://192.168.1.100:3000/Sik/tradingbot/pulls/3
This commit is contained in:
Sik 2024-01-29 16:03:04 +00:00
parent 6adf6cab88
commit 223c6760c1
25 changed files with 2249 additions and 2682 deletions

View File

@ -1,14 +1,16 @@
use crate::coex::assets_managing_team::*;
use crate::coex::order_team::*;
use crate::coin_health_check_team::request_others::{CoinPriceData, ExchangeInfo, TradeFee};
use crate::coin_health_check_team::request_others::{ExchangeInfo, TradeFee};
use crate::database_control::*;
use crate::decimal_funcs::{decimal, decimal_add, decimal_div, decimal_mul, decimal_sub};
use crate::signal_association::signal_decision::*;
use crate::strategy_team::AllData;
use hmac_sha256::Hash;
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;
#[derive(Debug, FromRow)]
struct IsTradableInfo {
@ -219,13 +221,13 @@ impl DBlist for MarketCapIndex {
// buy coin
pub async fn buy_coin(
exchange_info_vec: &Vec<ExchangeInfo>,
trade_fee_vec: &Vec<TradeFee>,
exchange_info_map: &HashMap<String, ExchangeInfo>,
trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut suggested_coin = get_suggested_coin_list().await;
if !suggested_coin.is_empty() && exchange_info_vec.len() != 0 && trade_fee_vec.len() != 0 {
let server_epoch = server_epoch().await;
if !suggested_coin.is_empty() && exchange_info_map.len() != 0 && trade_fee_map.len() != 0 {
let server_epoch = get_server_epoch().await;
let mut filtered_suggested_coin_vec: Vec<&SuggestedCoin> = Vec::new();
let mut is_exist_delete_symbol: bool = false;
let mut delete_condition = String::from("WHERE ");
@ -255,22 +257,12 @@ pub async fn buy_coin(
.unwrap();
for element in &filtered_suggested_coin_vec {
if is_tradable == true {
let exchange_info_result = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == element.symbol);
let trade_fee_result = trade_fee_vec
.iter()
.position(|trade_fee| trade_fee.symbol == element.symbol);
if exchange_info_result.is_some() && trade_fee_result.is_some() {
let lot_step_size =
exchange_info_vec[exchange_info_result.unwrap()].stepsize;
let tick_size = exchange_info_vec[exchange_info_result.unwrap()].ticksize;
let base_commission_precision = exchange_info_vec
[exchange_info_result.unwrap()]
.base_commission_precision;
let trade_fee = trade_fee_vec[trade_fee_result.unwrap()].takercommission;
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;
let base_commission_precision = exchange_info.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]
let mut base_qty_ordered = Decimal::new(0, 8);
@ -312,7 +304,7 @@ pub async fn buy_coin(
// order the symbol based on base_qty_ordered and current_price
limit_order_buy(
element,
exchange_info_vec,
exchange_info_map,
trade_fee,
TimeInForce::Gtc,
element.suggested_price,
@ -348,7 +340,6 @@ pub async fn buy_coin(
delete_record(&suggested_coin_list_table_name, &delete_condition)
.await
.unwrap();
println!("Delete suggested coin list");
}
}
@ -358,16 +349,15 @@ pub async fn buy_coin(
// buy coin for test
pub async fn buy_coin_for_test(
client: &Client,
coin_price_vec: &Vec<CoinPriceData>,
exchange_info_vec: &Vec<ExchangeInfo>,
trade_fee_vec: &Vec<TradeFee>,
exchange_info_map: &HashMap<String, ExchangeInfo>,
trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut suggested_coin = get_suggested_coin_list().await;
let mut delete_condition = String::from("WHERE ");
let unit_trade_usdt = get_unit_trade_usdt().await;
let server_epoch = server_epoch().await;
let server_epoch = get_server_epoch().await;
let mut filtered_suggested_coin_vec: Vec<&SuggestedCoin> = Vec::new();
for element in &suggested_coin {
@ -385,74 +375,60 @@ pub async fn buy_coin_for_test(
let insert_table_name = String::from("buy_ordered_coin_list");
for element in &filtered_suggested_coin_vec {
let lot_step_size = exchange_info_vec
.iter()
.find(|exchange_info| exchange_info.symbol == element.symbol)
.unwrap()
.stepsize;
let tick_size = exchange_info_vec
.iter()
.find(|exchange_info| exchange_info.symbol == element.symbol)
.unwrap()
.ticksize;
let base_commission_precision = exchange_info_vec
.iter()
.find(|exchange_info| exchange_info.symbol == element.symbol)
.unwrap()
.base_commission_precision;
let trade_fee = trade_fee_vec
.iter()
.find(|trade_fee| trade_fee.symbol == element.symbol)
.unwrap()
.takercommission;
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 trade_fee = trade_fee_map.get(&element.symbol).unwrap().takercommission;
// buy the suggested coin and transfer it into [buy_ordered_coin_list]
// let price_index = coin_price_vec.iter().position(|x| *x.symbol == element.symbol);
// let current_price: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(coin_price_vec[price_index.unwrap()].current_price).unwrap();
// let order_price = current_price;
let order_price = element.suggested_price;
// buy the suggested coin and transfer it into [buy_ordered_coin_list]
// let price_index = coin_price_vec.iter().position(|x| *x.symbol == element.symbol);
// let current_price: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(coin_price_vec[price_index.unwrap()].current_price).unwrap();
// 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_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)
.round_dp_with_strategy(tick_size.normalize().scale(), RoundingStrategy::ToZero);
expected_pure_profit_percent = decimal_sub(
decimal_div(
decimal_mul(base_qty_fee_adjusted, order_price).round_dp_with_strategy(
tick_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)
.round_dp_with_strategy(tick_size.normalize().scale(), RoundingStrategy::ToZero);
expected_pure_profit_percent = decimal_sub(
decimal_div(
decimal_mul(base_qty_fee_adjusted, order_price).round_dp_with_strategy(
tick_size.normalize().scale(),
RoundingStrategy::ToZero,
),
used_usdt,
),
used_usdt,
),
dec!(1),
)
.to_f64()
.unwrap()
* 100.0;
dec!(1),
)
.to_f64()
.unwrap()
* 100.0;
// order the symbol based on base_qty_ordered and current_price
limit_order_buy(
element,
exchange_info_vec,
trade_fee,
TimeInForce::Gtc,
order_price,
base_qty_ordered,
used_usdt,
&base_qty_fee_adjusted.to_string(),
&client,
)
.await;
println!(" buy coin 완료");
// order the symbol based on base_qty_ordered and current_price
limit_order_buy(
element,
exchange_info_map,
trade_fee,
TimeInForce::Gtc,
order_price,
base_qty_ordered,
used_usdt,
&base_qty_fee_adjusted.to_string(),
&client,
)
.await;
println!(" buy coin 완료");
}
}
Ok(())
}
// monitoring the price change of pre-suggested coins before suggesting them and move them when conditions are agree
pub async fn monitoring_pre_suggested_coins(
coin_price_vec: &Vec<CoinPriceData>,
coin_price_map: &HashMap<String, f64>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let select_table_name = String::from("pre_suggested_coin_list");
let pre_suggested_coin_list = select_pre_suggested_coin_list().await;
@ -468,14 +444,8 @@ pub async fn monitoring_pre_suggested_coins(
let mut update_condition: Vec<(String, String)> = Vec::new();
for element in pre_suggested_coin_list {
let price_index = coin_price_vec
.iter()
.position(|x| *x.symbol == element.symbol);
if price_index.is_some() {
price = rust_decimal::prelude::FromPrimitive::from_f64(
coin_price_vec[price_index.unwrap()].current_price,
)
.unwrap();
if let Some(current_price) = coin_price_map.get(&element.symbol) {
price = rust_decimal::prelude::FromPrimitive::from_f64(*current_price).unwrap();
profit_percent = ((price - element.suggested_price) / element.suggested_price)
.to_f64()
.unwrap()
@ -561,7 +531,7 @@ pub async fn monitoring_pre_suggested_coins(
insert_values.push(element.stoploss.to_string()); // stoploss
insert_values.push(element.target_price.to_string()); // target_price
insert_values.push(element.close_time.to_string()); // close_time
insert_values.push(server_epoch().await.to_string()); // registered_server_epoch
insert_values.push(get_server_epoch().await.to_string()); // registered_server_epoch
insert_values.push(element.registerer.to_string()); // registerer
insert_values.push(element.is_long.to_string()); // is_long
insert_values.push(0.to_string()); // already_buy
@ -626,7 +596,7 @@ pub async fn monitoring_scoreboard(
let scoreboard_list = select_scoreboard().await;
let mut count_short_coins = 0;
let mut count_long_coins = 0;
let server_epoch = server_epoch().await;
let server_epoch = get_server_epoch().await;
let mut timer_check = false;
for element in &filled_buy_orders {
@ -756,12 +726,9 @@ pub async fn monitoring_scoreboard(
let mut base_profit_percent = 0.0;
let mut target_profit_percent = 0.0;
let opclo_1m_option = all_data
.rt_price_1m_vec
.iter()
.position(|x| *x.0 == String::from("BTCUSDT"));
if opclo_1m_option.is_some() {
let mut opclo_1m_vec = all_data.rt_price_1m_vec[opclo_1m_option.unwrap()].1.clone();
let symbol = String::from("BTCUSDT");
if let Some(rt_price_1m_vec) = all_data.rt_price_1m_vec.get(&symbol) {
let mut opclo_1m_vec = rt_price_1m_vec.clone();
let opclo_sample_length: usize = 5; // 10 candle samsples
opclo_1m_vec.pop();
opclo_1m_vec.reverse();
@ -949,7 +916,7 @@ async fn get_suggested_coin_list() -> Vec<SuggestedCoin> {
select_result
}
pub async fn server_epoch() -> i64 {
pub async fn get_server_epoch() -> i64 {
let table_name = String::from("time");
let columns = String::from("*");
let condition = None;

View File

@ -11,7 +11,7 @@ use crate::URL_TEST;
// use crates
use crate::coex::assets_managing_team::*;
use crate::coex::exchange_team::*;
use crate::coin_health_check_team::request_others::{CoinPriceData, ExchangeInfo, TradeFee};
use crate::coin_health_check_team::request_others::{ExchangeInfo, TradeFee};
use crate::database_control::*;
use crate::decimal_funcs::*;
use crate::signal_association::signal_decision::*;
@ -30,6 +30,7 @@ use rust_decimal_macros::dec;
use serde_json::Value;
use sqlx::FromRow;
use tokio::time::*;
use std::collections::HashMap;
pub enum OrderSide {
Buy,
@ -298,7 +299,7 @@ pub async fn limit_order_buy_test(
pub async fn limit_order_buy(
element: &SuggestedCoin,
exchange_info_vec: &Vec<ExchangeInfo>,
exchange_info_map: &HashMap<String, ExchangeInfo>,
trade_fee: Decimal,
tif: TimeInForce,
order_price: Decimal,
@ -331,7 +332,7 @@ pub async fn limit_order_buy(
];
let mut insert_values: Vec<Vec<String>> = Vec::new();
let mut insert_value_container: Vec<String> = Vec::new();
let server_epoch = server_epoch().await;
let server_epoch = get_server_epoch().await;
if RUNNING_MODE == SIMUL {
insert_value_container.push(element.symbol.clone()); // symbol
insert_value_container.push(0.to_string()); // order_id
@ -350,7 +351,6 @@ pub async fn limit_order_buy(
insert_value_container.push(0.0.to_string()); // pure_profit_percent
insert_value_container.push(0.0.to_string()); // minimum_profit_percent
insert_value_container.push(0.0.to_string()); // maximum_profit_percent
insert_value_container.push(0.to_string()); // registerer
insert_value_container.push(element.registerer.to_string()); // registerer
insert_value_container.push(0.to_string()); // is_long
@ -498,13 +498,12 @@ pub async fn limit_order_buy(
pub async fn monitoring_open_buy_order(
client: &Client,
exchange_info_vec: &Vec<ExchangeInfo>,
trade_fee_vec: &Vec<TradeFee>,
trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let open_buy_orders = select_open_buy_orders().await;
if !open_buy_orders.is_empty() {
let server_epoch = server_epoch().await;
let server_epoch = get_server_epoch().await;
let orders_outdated = open_buy_orders
.iter()
.filter(|&element| server_epoch - element.transact_time >= 600_000)
@ -517,14 +516,14 @@ pub async fn monitoring_open_buy_order(
// cancel orders outdated over 3mins and delete the records in [buy_ordered_coin_list]
if !orders_outdated.is_empty() {
for element in orders_outdated {
cancel_buy_order(element, &client, exchange_info_vec, trade_fee_vec).await;
cancel_buy_order(element, &client, trade_fee_map).await;
sleep(Duration::from_millis(500)).await; // Use max 30 LIMIT/min
}
}
if !orders_to_be_queried.is_empty() {
for element in orders_to_be_queried {
query_buy_order(element, &client, exchange_info_vec, trade_fee_vec).await;
query_buy_order(element, &client, trade_fee_map).await;
sleep(Duration::from_millis(500)).await; // Use max 30 LIMIT/min
}
}
@ -534,9 +533,9 @@ pub async fn monitoring_open_buy_order(
}
pub async fn update_price_of_filled_buy_order(
coin_price_vec: &Vec<CoinPriceData>,
exchange_info_vec: &Vec<ExchangeInfo>,
trade_fee_vec: &Vec<TradeFee>,
coin_price_map: &HashMap<String, f64>,
exchange_info_map: &HashMap<String, ExchangeInfo>,
trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let filled_buy_orders = select_filled_buy_orders(0).await?;
@ -548,9 +547,9 @@ pub async fn update_price_of_filled_buy_order(
for chunk in chunks {
let chunk_vec = chunk.to_vec();
let coin_price_vec_c = coin_price_vec.clone();
let exchange_info_vec_c = exchange_info_vec.clone();
let trade_fee_vec_c = trade_fee_vec.clone();
let coin_price_vec_c = coin_price_map.clone();
let exchange_info_vec_c = exchange_info_map.clone();
let trade_fee_vec_c = trade_fee_map.clone();
task_vec.push(tokio::spawn(async move {
update_repeat_task(
chunk_vec,
@ -567,16 +566,16 @@ pub async fn update_price_of_filled_buy_order(
async fn update_repeat_task(
buy_ordered_coin_vec: Vec<BuyOrderedCoinList>,
coin_price_vec: &Vec<CoinPriceData>,
exchange_info_vec: &Vec<ExchangeInfo>,
trade_fee_vec: &Vec<TradeFee>,
coin_price_map: &HashMap<String, f64>,
exchange_info_map: &HashMap<String, ExchangeInfo>,
trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let update_table_name = String::from("buy_ordered_coin_list");
let mut update_values: Vec<(String, String)> = Vec::new();
let mut update_condition: Vec<(String, String)> = Vec::new();
let mut price = Decimal::new(0, 8);
let mut profit_percent = 0.0;
let server_epoch = server_epoch().await;
let server_epoch = get_server_epoch().await;
let update_colums = vec![
"current_price",
@ -594,96 +593,71 @@ async fn update_repeat_task(
for element in buy_ordered_coin_vec {
// build update values
update_record_build.clear();
let price_index_option = coin_price_vec
.iter()
.position(|x| *x.symbol == element.symbol);
let lot_step_size_result = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == element.symbol);
let quote_commission_precision_result = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == element.symbol);
let trade_fee_result = trade_fee_vec
.iter()
.position(|trade_fee| trade_fee.symbol == element.symbol);
if price_index_option.is_some()
&& lot_step_size_result.is_some()
&& quote_commission_precision_result.is_some()
&& trade_fee_result.is_some()
{
let price_result: Option<Decimal> = rust_decimal::prelude::FromPrimitive::from_f64(
coin_price_vec[price_index_option.unwrap()].current_price,
);
if price_result.is_some() {
price = price_result.unwrap();
if !price.is_zero() {
// to get quote_commission_precision
let trade_fee = trade_fee_vec[trade_fee_result.unwrap()].takercommission;
let lot_step_size = exchange_info_vec[lot_step_size_result.unwrap()].stepsize;
let quote_commission_precision = exchange_info_vec
[quote_commission_precision_result.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,
RoundingStrategy::ToZero,
),
decimal_sub(dec!(1), trade_fee),
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,
);
// TODO: sell_count >=1 이면 expected_get_usdt 는 한번만 tradefee만 적용하여 업데이트 할 것. 현재는 수수료를 2번 (매수,매도)를 계산함. 아래 변수에 든 값으로 업데이트 하면 됨
// let expected_get_usdt =
// decimal_mul(base_qty_to_be_ordered, price).round_dp_with_strategy(
// quote_commission_precision,
// RoundingStrategy::ToZero,
// );
let pure_profit_percent = ((expected_get_usdt.to_f64().unwrap()
/ element.used_usdt.to_f64().unwrap())
- 1.0)
* 100.0;
let expected_get_usdt = decimal_mul(
decimal_mul(base_qty_to_be_ordered, price).round_dp_with_strategy(
quote_commission_precision,
RoundingStrategy::ToZero,
),
decimal_sub(dec!(1), trade_fee),
);
// TODO: sell_count >=1 이면 expected_get_usdt 는 한번만 tradefee만 적용하여 업데이트 할 것. 현재는 수수료를 2번 (매수,매도)를 계산함. 아래 변수에 든 값으로 업데이트 하면 됨
// let expected_get_usdt =
// decimal_mul(base_qty_to_be_ordered, price).round_dp_with_strategy(
// quote_commission_precision,
// RoundingStrategy::ToZero,
// );
let pure_profit_percent = ((expected_get_usdt.to_f64().unwrap()
/ element.used_usdt.to_f64().unwrap())
- 1.0)
* 100.0;
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
update_record_build
.push(decimal_sub(expected_get_usdt, element.used_usdt).to_string()); // expected_usdt_profit
update_record_build.push(pure_profit_percent.to_string()); // pure_profit_percent
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
update_record_build
.push(decimal_sub(expected_get_usdt, element.used_usdt).to_string()); // expected_usdt_profit
update_record_build.push(pure_profit_percent.to_string()); // pure_profit_percent
if element.minimum_profit_percent > pure_profit_percent {
update_record_build.push(pure_profit_percent.to_string());
if element.minimum_profit_percent > pure_profit_percent {
update_record_build.push(pure_profit_percent.to_string());
// minimum_profit_percent
} else if pure_profit_percent >= 0.0 {
update_record_build.push(0.0.to_string()); // minimum_profit_percent
} else {
update_record_build.push(element.minimum_profit_percent.to_string());
// minimum_profit_percent
} else if pure_profit_percent >= 0.0 {
update_record_build.push(0.0.to_string()); // minimum_profit_percent
} else {
update_record_build.push(element.minimum_profit_percent.to_string());
// minimum_profit_percent
}
if element.maximum_profit_percent < pure_profit_percent {
update_record_build.push(pure_profit_percent.to_string());
// maximum_profit_percent
} else if pure_profit_percent <= 0.0 {
update_record_build.push(0.0.to_string()); // maximum_profit_percent
} else {
update_record_build.push(element.maximum_profit_percent.to_string());
// maximum_profit_percent
}
if server_epoch - element.transact_time >= 86_400_000 {
// turn is_long from 0 to 1 for orders whose transact time is over than a day (86,400,000 millis = a day)
update_record_build.push(1.to_string());
} else {
update_record_build.push(0.to_string());
}
update_record.push(update_record_build.clone());
}
if element.maximum_profit_percent < pure_profit_percent {
update_record_build.push(pure_profit_percent.to_string());
// maximum_profit_percent
} else if pure_profit_percent <= 0.0 {
update_record_build.push(0.0.to_string()); // maximum_profit_percent
} else {
update_record_build.push(element.maximum_profit_percent.to_string());
// maximum_profit_percent
}
if server_epoch - element.transact_time >= 86_400_000 {
// turn is_long from 0 to 1 for orders whose transact time is over than a day (86,400,000 millis = a day)
update_record_build.push(1.to_string());
} else {
update_record_build.push(0.to_string());
}
update_record.push(update_record_build.clone());
}
}
}
@ -698,8 +672,8 @@ pub async fn limit_order_sell(
sell_base_price: Decimal,
sell_base_quantity: Decimal,
client: &Client,
exchange_info_vec: &Vec<ExchangeInfo>,
trade_fee_vec: &Vec<TradeFee>,
exchange_info_map: &HashMap<String, ExchangeInfo>,
trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let insert_table_name = String::from("sell_ordered_coin_list");
let insert_columns = vec![
@ -724,18 +698,11 @@ pub async fn limit_order_sell(
];
let mut insert_values: Vec<Vec<String>> = Vec::new();
let mut insert_value_container: Vec<String> = Vec::new();
let server_epoch = server_epoch().await;
let server_epoch = get_server_epoch().await;
if RUNNING_MODE == SIMUL && buy_ordered_coin.status == "SIMUL" {
let quote_asset_precision_option = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == buy_ordered_coin.symbol);
let trade_fee_option = trade_fee_vec
.iter()
.position(|trade_fee| trade_fee.symbol == buy_ordered_coin.symbol);
if quote_asset_precision_option.is_some() && trade_fee_option.is_some() {
let quote_asset_precision =
exchange_info_vec[quote_asset_precision_option.unwrap()].quote_asset_precision;
let trade_fee = trade_fee_vec[trade_fee_option.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);
@ -839,17 +806,9 @@ 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
let quote_asset_precision_option = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == buy_ordered_coin.symbol);
let trade_fee_option = trade_fee_vec
.iter()
.position(|trade_fee| trade_fee.symbol == buy_ordered_coin.symbol);
if quote_asset_precision_option.is_some() && trade_fee_option.is_some() {
let quote_asset_precision = exchange_info_vec
[quote_asset_precision_option.unwrap()]
.quote_asset_precision;
let trade_fee = trade_fee_vec[trade_fee_option.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(),
)
@ -932,13 +891,13 @@ pub async fn limit_order_sell(
pub async fn monitoring_open_sell_order(
client: &Client,
exchange_info_vec: &Vec<ExchangeInfo>,
trade_fee_vec: &Vec<TradeFee>,
exchange_info_map: &HashMap<String, ExchangeInfo>,
trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let open_sell_orders = select_open_sell_orders().await;
if !open_sell_orders.is_empty() {
let server_epoch = server_epoch().await;
let server_epoch = get_server_epoch().await;
let orders_outdated = open_sell_orders
.iter()
.filter(|&element| server_epoch - element.transact_time >= 5_000)
@ -951,14 +910,14 @@ pub async fn monitoring_open_sell_order(
// cancel orders outdated over 30secs, delete its records in [sell_ordered_coin_list], and move them into [buy_ordered_coin_list]
if !orders_outdated.is_empty() {
for element in orders_outdated {
cancel_sell_order(element, &client, exchange_info_vec, trade_fee_vec).await;
cancel_sell_order(element, &client, exchange_info_map, trade_fee_map).await;
sleep(Duration::from_millis(200)).await; // Use max 30 LIMIT/min
}
}
if !orders_to_be_queried.is_empty() {
for element in orders_to_be_queried {
query_sell_order(element, &client, exchange_info_vec, trade_fee_vec).await;
query_sell_order(element, &client, exchange_info_map, trade_fee_map).await;
sleep(Duration::from_millis(300)).await; // Use max 30 LIMIT/min
}
}
@ -991,7 +950,7 @@ pub async fn monitoring_filled_sell_order(
let mut insert_value_build: Vec<String> = Vec::new();
let update_table_name = String::from("achievement_evaluation");
let server_epoch = server_epoch().await;
let server_epoch = get_server_epoch().await;
let mut total_get_usdt = Decimal::new(0, 8);
for element in filled_sell_orders {
@ -1119,8 +1078,7 @@ pub async fn market_order(
pub async fn cancel_buy_order(
order: &BuyOrderedCoinList,
client: &Client,
exchange_info_vec: &Vec<ExchangeInfo>,
trade_fee_vec: &Vec<TradeFee>,
trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// building URL and API-keys
let mut url = String::new();
@ -1195,18 +1153,8 @@ pub async fn cancel_buy_order(
status_value_build.push('\'');
// calculate values to be updated
let trade_fee_option = trade_fee_vec
.iter()
.position(|trade_fee| trade_fee.symbol == order.symbol);
let base_asset_precision_option = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == order.symbol);
if trade_fee_option.is_some() && base_asset_precision_option.is_some() {
let trade_fee =
trade_fee_vec[trade_fee_option.unwrap()].takercommission;
if trade_fee_map.contains_key(&order.symbol) {
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(),
)
@ -1214,10 +1162,6 @@ pub async fn cancel_buy_order(
let base_qty_fee_adjusted =
decimal_mul(base_qty_ordered, decimal_sub(dec!(1), trade_fee));
let base_asset_precision = exchange_info_vec
[base_asset_precision_option.unwrap()]
.base_asset_precision;
let buy_price = decimal_div(cummulative_quote_qty, base_qty_ordered)
.round_dp_with_strategy(8, RoundingStrategy::ToZero);
@ -1261,11 +1205,11 @@ pub async fn cancel_buy_order(
} else if T.get("code").is_some() {
// case that the order isn't canceled because the order completes while canceling
// update record in ordered_coin_list
query_buy_order(order, &client, exchange_info_vec, trade_fee_vec).await;
query_buy_order(order, &client, trade_fee_map).await;
}
}
Err(e) => {
query_buy_order(order, &client, exchange_info_vec, trade_fee_vec).await;
query_buy_order(order, &client, trade_fee_map).await;
// println!("cancel order buy failed!: {}", body);
}
}
@ -1277,8 +1221,8 @@ pub async fn cancel_buy_order(
pub async fn cancel_sell_order(
order: &SellOrderedCoinList,
client: &Client,
exchange_info_vec: &Vec<ExchangeInfo>,
trade_fee_vec: &Vec<TradeFee>,
exchange_info_map: &HashMap<String, ExchangeInfo>,
trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// building URL and API-keys
let mut url = String::new();
@ -1364,7 +1308,7 @@ pub async fn cancel_sell_order(
// insert record in [buy_ordered_coin_list]
let mut insert_values: Vec<Vec<String>> = Vec::new();
let mut insert_value_container: Vec<String> = Vec::new();
let server_epoch = server_epoch().await;
let server_epoch = get_server_epoch().await;
insert_value_container.push(order.symbol.clone()); // symbol
insert_value_container.push(order.buy_order_id.to_string()); // order_id
insert_value_container.push(order.transact_time.to_string()); // transact_time
@ -1388,19 +1332,9 @@ pub async fn cancel_sell_order(
insert_values.push(insert_value_container.clone());
insert_records(&insert_table_name, &insert_columns, &insert_values).await;
} else {
let quote_asset_precision_option = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == order.symbol);
let trade_fee_option = trade_fee_vec
.iter()
.position(|trade_fee| trade_fee.symbol == order.symbol);
if quote_asset_precision_option.is_some() && trade_fee_option.is_some() {
let quote_asset_precision = exchange_info_vec
[quote_asset_precision_option.unwrap()]
.quote_asset_precision;
let trade_fee =
trade_fee_vec[trade_fee_option.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
@ -1514,7 +1448,7 @@ pub async fn cancel_sell_order(
decimal_mul(rest_base_qty, decimal_sub(dec!(1), trade_fee));
let mut insert_values: Vec<Vec<String>> = Vec::new();
let mut insert_value_container: Vec<String> = Vec::new();
let server_epoch = server_epoch().await;
let server_epoch = get_server_epoch().await;
insert_value_container.push(order.symbol.clone()); // symbol
insert_value_container.push(order.buy_order_id.to_string()); // order_id
insert_value_container.push(server_epoch.to_string()); // transact_time
@ -1542,12 +1476,12 @@ pub async fn cancel_sell_order(
}
}
} else {
query_sell_order(&order, &client, exchange_info_vec, trade_fee_vec).await;
query_sell_order(&order, &client, exchange_info_map, trade_fee_map).await;
}
} else if T.get("code").is_some() {
// case that the order isn't canceled because the order completes while canceling
// update record in ordered_coin_list
query_sell_order(&order, &client, exchange_info_vec, trade_fee_vec).await;
query_sell_order(&order, &client, exchange_info_map, trade_fee_map).await;
}
}
Err(e) => {
@ -1626,8 +1560,7 @@ pub async fn all_orders(
pub async fn query_buy_order(
order: &BuyOrderedCoinList,
client: &Client,
exchange_info_vec: &Vec<ExchangeInfo>,
trade_fee_vec: &Vec<TradeFee>,
trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// building URL and API-keys
let mut url = String::new();
@ -1687,18 +1620,8 @@ pub async fn query_buy_order(
value_build.push('\'');
// calculate values to be updated
let trade_fee_option = trade_fee_vec
.iter()
.position(|trade_fee| trade_fee.symbol == order.symbol);
let base_asset_precision_option = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == order.symbol);
if trade_fee_option.is_some() && base_asset_precision_option.is_some() {
let trade_fee = trade_fee_vec[trade_fee_option.unwrap()].takercommission;
let base_asset_precision = exchange_info_vec
[base_asset_precision_option.unwrap()]
.base_asset_precision;
if trade_fee_map.contains_key(&order.symbol) {
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(),
)
@ -1773,8 +1696,8 @@ pub async fn query_buy_order(
pub async fn query_sell_order(
order: &SellOrderedCoinList,
client: &Client,
exchange_info_vec: &Vec<ExchangeInfo>,
trade_fee_vec: &Vec<TradeFee>,
exchange_info_map: &HashMap<String, ExchangeInfo>,
trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// building URL and API-keys
let mut url = String::new();
@ -1817,18 +1740,9 @@ pub async fn query_sell_order(
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")
{
let quote_asset_precision_option = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == order.symbol);
let trade_fee_option = trade_fee_vec
.iter()
.position(|trade_fee| trade_fee.symbol == order.symbol);
if quote_asset_precision_option.is_some() && trade_fee_option.is_some() {
let quote_asset_precision = exchange_info_vec
[quote_asset_precision_option.unwrap()]
.quote_asset_precision;
let trade_fee = trade_fee_vec[trade_fee_option.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;
let get_usdt = rust_decimal::prelude::FromStr::from_str(
T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(),
)

View File

@ -13,6 +13,7 @@ use sqlx::{Error, FromRow};
use std::borrow::{Borrow, BorrowMut};
use std::sync::Arc;
use tokio::{join, sync::Mutex, time::*};
use std::collections::{HashMap, HashSet};
#[derive(Debug, FromRow)]
struct AllCoinProfitChangeAvgList {
@ -85,8 +86,8 @@ pub async fn initialize_valid_usde_trade() -> Result<(), Box<dyn std::error::Err
// filter valid USDT trades from all24hstatistics table in database
pub async fn collect_valid_usde_trade(
valid_usdt_trade_vec: &mut Vec<String>,
exchange_info_vec: &Vec<ExchangeInfo>,
valid_usdt_trade_vec: &mut HashSet<String>,
exchange_info_vec: &HashMap<String, ExchangeInfo>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
#[derive(Debug, FromRow)]
struct UsdtTrades {
@ -138,27 +139,23 @@ pub async fn collect_valid_usde_trade(
select_record(&fetch_table_name, &column_name, &condition, &usdt_trades).await?;
// filtering usdt trades
let mut filtered_usdt_trades: Vec<String> = Vec::new();
let mut excluded_usdt_trades: Vec<String> = Vec::new();
let mut filtered_usdt_trades: HashSet<String> = HashSet::new();
let mut excluded_usdt_trades: HashSet<String> = HashSet::new();
for usdt_trade in usdt_trades {
// build update values
let step_size_result = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == usdt_trade.symbol);
if step_size_result.is_some() {
if let Some(value) = exchange_info_vec.get(&usdt_trade.symbol) {
let avg_price: Decimal =
rust_decimal::prelude::FromPrimitive::from_f64(usdt_trade.weightedavgprice)
.unwrap();
let step_size = exchange_info_vec[step_size_result.unwrap()].stepsize;
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;
// 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)) {
excluded_usdt_trades.push(usdt_trade.symbol.clone());
excluded_usdt_trades.insert(usdt_trade.symbol.clone());
} else {
filtered_usdt_trades.push(usdt_trade.symbol.clone());
filtered_usdt_trades.insert(usdt_trade.symbol.clone());
}
}
}
@ -188,74 +185,6 @@ pub async fn collect_valid_usde_trade(
delete_all_rows(&table_name).await?;
insert_records(&table_name, &columns, &value_wrapper).await?;
// major trades
// if RUNNING_MODE == TEST || RUNNING_MODE == REAL || RUNNING_MODE == SIMUL {
// let table_name = String::from("valid_usdt_trades");
// let columns = vec!["symbol"];
// let mut symbol_vec: Vec<String> = Vec::new();
// let mut value_wrapper: Vec<Vec<String>> = Vec::new();
// value_wrapper.push(vec![String::from("BTCUSDT")]);
// value_wrapper.push(vec![String::from("ETHUSDT")]);
// value_wrapper.push(vec![String::from("XRPUSDT")]);
// value_wrapper.push(vec![String::from("DOGEUSDT")]);
// value_wrapper.push(vec![String::from("LTCUSDT")]);
// value_wrapper.push(vec![String::from("TRXUSDT")]);
// value_wrapper.push(vec![String::from("DOTUSDT")]);
// value_wrapper.push(vec![String::from("LINKUSDT")]);
// value_wrapper.push(vec![String::from("AVAXUSDT")]);
// value_wrapper.push(vec![String::from("BCHUSDT")]);
// value_wrapper.push(vec![String::from("SHIBUSDT")]);
// value_wrapper.push(vec![String::from("APTUSDT")]);
// value_wrapper.push(vec![String::from("ARBUSDT")]);
// value_wrapper.push(vec![String::from("ETCUSDT")]);
// value_wrapper.push(vec![String::from("XMRUSDT")]);
// value_wrapper.push(vec![String::from("HBARUSDT")]);
// value_wrapper.push(vec![String::from("UNIUSDT")]);
// value_wrapper.push(vec![String::from("XLMUSDT")]);
// value_wrapper.push(vec![String::from("VETUSDT")]);
// value_wrapper.push(vec![String::from("QNTUSDT")]);
// value_wrapper.push(vec![String::from("GRTUSDT")]);
// value_wrapper.push(vec![String::from("AAVEUSDT")]);
// value_wrapper.push(vec![String::from("EGLDUSDT")]);
// value_wrapper.push(vec![String::from("LDOUSDT")]);
// value_wrapper.push(vec![String::from("STXUSDT")]);
// value_wrapper.push(vec![String::from("MKRUSDT")]);
// value_wrapper.push(vec![String::from("OPUSDT")]);
// symbol_vec.push(String::from("BTCUSDT"));
// symbol_vec.push(String::from("ETHUSDT"));
// symbol_vec.push(String::from("XRPUSDT"));
// symbol_vec.push(String::from("DOGEUSDT"));
// symbol_vec.push(String::from("LTCUSDT"));
// symbol_vec.push(String::from("TRXUSDT"));
// symbol_vec.push(String::from("DOTUSDT"));
// symbol_vec.push(String::from("LINKUSDT"));
// symbol_vec.push(String::from("AVAXUSDT"));
// symbol_vec.push(String::from("BCHUSDT"));
// symbol_vec.push(String::from("SHIBUSDT"));
// symbol_vec.push(String::from("APTUSDT"));
// symbol_vec.push(String::from("ARBUSDT"));
// symbol_vec.push(String::from("ETCUSDT"));
// symbol_vec.push(String::from("XMRUSDT"));
// symbol_vec.push(String::from("HBARUSDT"));
// symbol_vec.push(String::from("UNIUSDT"));
// symbol_vec.push(String::from("XLMUSDT"));
// symbol_vec.push(String::from("VETUSDT"));
// symbol_vec.push(String::from("QNTUSDT"));
// symbol_vec.push(String::from("GRTUSDT"));
// symbol_vec.push(String::from("AAVEUSDT"));
// symbol_vec.push(String::from("EGLDUSDT"));
// symbol_vec.push(String::from("LDOUSDT"));
// symbol_vec.push(String::from("STXUSDT"));
// symbol_vec.push(String::from("MKRUSDT"));
// symbol_vec.push(String::from("OPUSDT"));
// *valid_usdt_trade_vec = symbol_vec;
// delete_all_rows(&table_name).await?;
// insert_records(&table_name, &columns, &value_wrapper).await?;
// }
Ok(())
}

View File

@ -8,6 +8,7 @@ use sqlx::{Error, FromRow};
use std::borrow::{Borrow, BorrowMut};
use std::sync::Arc;
use tokio::{join, sync::Mutex, time::*};
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct CandleData {
@ -25,6 +26,26 @@ pub struct CandleData {
pub ignore_this: f64,
}
impl CandleData {
fn new() -> CandleData {
let candle_data = CandleData {
open_time: 0,
open_price: 0.0,
high_price: 0.0,
low_price: 0.0,
close_price: 0.0,
volume: 0.0,
close_time: 0,
quote_asset_volume: 0.0,
number_of_trades: 0,
taker_buy_base_asset_volume: 0.0,
taker_buy_quote_asset_volume: 0.0,
ignore_this: 0.0,
};
candle_data
}
}
// fetch the list of valid usdt trades
#[derive(Debug, FromRow, Clone)]
struct Symbols {
@ -37,7 +58,7 @@ async fn request_candlestick_data(
symbol: String,
interval: &String,
client: &Client,
candle_set: &mut Vec<(String, Vec<CandleData>)>,
candle_set: &mut HashMap<String, Vec<CandleData>>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut query = String::from("https://api.binance.com/api/v3/klines?");
query.push_str("&symbol=");
@ -67,7 +88,7 @@ async fn de_candle_json(
symbol: String,
interval: &String,
body: &String,
candle_set: &mut Vec<(String, Vec<CandleData>)>,
candle_map: &mut HashMap<String, Vec<CandleData>>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let v: Value = serde_json::from_str(body.as_str())?;
let mut into_vec = v.as_array();
@ -75,21 +96,8 @@ async fn de_candle_json(
return Err("Err")?;
}
let mut candle_data = CandleData {
open_time: 0,
open_price: 0.0,
high_price: 0.0,
low_price: 0.0,
close_price: 0.0,
volume: 0.0,
close_time: 0,
quote_asset_volume: 0.0,
number_of_trades: 0,
taker_buy_base_asset_volume: 0.0,
taker_buy_quote_asset_volume: 0.0,
ignore_this: 0.0,
};
let mut candle_vec: Vec<CandleData> = Vec::new();
let mut candle_data = CandleData::new();
for element in into_vec.unwrap() {
let inner_into_vec = element.as_array().unwrap();
candle_data.open_time = element[0].as_i64().unwrap();
@ -110,7 +118,7 @@ async fn de_candle_json(
candle_vec.push(candle_data.clone());
}
candle_set.push((symbol, candle_vec));
candle_map.insert(symbol, candle_vec);
// let search_result = candle_set.iter().position(|x| x.0 == symbol);
// match search_result {
// Some(T) => {
@ -127,7 +135,7 @@ async fn de_candle_json2(
symbol: String,
interval: &String,
body: &String,
candle_set: &mut Vec<(String, Vec<CandleData>)>,
candle_map: &mut HashMap<String, Vec<CandleData>>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let v: Value = serde_json::from_str(body.as_str())?;
let mut into_vec = v.as_array();
@ -135,21 +143,8 @@ async fn de_candle_json2(
return Err("Err")?;
}
let mut candle_data = CandleData {
open_time: 0,
open_price: 0.0,
high_price: 0.0,
low_price: 0.0,
close_price: 0.0,
volume: 0.0,
close_time: 0,
quote_asset_volume: 0.0,
number_of_trades: 0,
taker_buy_base_asset_volume: 0.0,
taker_buy_quote_asset_volume: 0.0,
ignore_this: 0.0,
};
let mut candle_vec: Vec<CandleData> = Vec::new();
let mut candle_data = CandleData::new();
for element in into_vec.unwrap() {
let inner_into_vec = element.as_array().unwrap();
candle_data.open_time = element[0].as_i64().unwrap();
@ -170,14 +165,10 @@ async fn de_candle_json2(
candle_vec.push(candle_data.clone());
}
let search_result = candle_set.iter().position(|x| x.0 == symbol);
match search_result {
Some(T) => {
candle_set[T].1 = candle_vec;
}
None => {
candle_set.push((symbol, candle_vec));
}
if let Some(value) = candle_map.get_mut(&symbol) {
*value = candle_vec;
} else {
candle_map.insert(symbol, candle_vec);
}
Ok(())
}
@ -262,7 +253,7 @@ pub async fn request_candlestick_initial(
symbol: String,
interval: &String,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut candle_set: Vec<(String, Vec<CandleData>)> = Vec::new();
let mut candle_set: HashMap<String, Vec<CandleData>> = HashMap::new();
let client = ClientBuilder::new()
.timeout(tokio::time::Duration::from_millis(20000))
.build()
@ -449,7 +440,7 @@ pub async fn create_candle_table(
// for fetching 1m and 30m candle
pub async fn fetch_candle_parallel(
interval: &String,
candle_vec: &mut Vec<(String, Vec<CandleData>)>,
candle_map: &mut HashMap<String, Vec<CandleData>>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let instant = Instant::now();
let fetch_table_name = String::from("valid_usdt_trades");
@ -466,10 +457,10 @@ pub async fn fetch_candle_parallel(
let chunks = select_result.chunks(20);
let nbr_chunks = chunks.len();
let mut candle_vec_arc_wrapper: Vec<Arc<Mutex<Vec<(String, Vec<CandleData>)>>>> = Vec::new();
let mut candle_vec_arc_wrapper: Vec<Arc<Mutex<HashMap<String, Vec<CandleData>>>>> = Vec::new();
for _ in 0..nbr_chunks {
let mut candle_vec_temp: Vec<(String, Vec<CandleData>)> = Vec::new();
let mut candle_vec_arc = Arc::new(Mutex::new(candle_vec_temp));
let mut candle_map_temp: HashMap<String, Vec<CandleData>> = HashMap::new();
let mut candle_vec_arc = Arc::new(Mutex::new(candle_map_temp));
candle_vec_arc_wrapper.push(candle_vec_arc);
}
@ -490,15 +481,15 @@ pub async fn fetch_candle_parallel(
match result {
Ok(T) => {
let mut candle_buffer: Vec<(String, Vec<CandleData>)> = Vec::new();
let mut candle_buffer: HashMap<String, Vec<CandleData>> = HashMap::new();
for element in candle_vec_arc_wrapper {
let a = element.lock().await.clone();
for element in a {
candle_buffer.push(element);
for (symbol, candle_data) in a {
candle_buffer.insert(symbol, candle_data);
}
}
*candle_vec = candle_buffer;
*candle_map = candle_buffer;
// println!(" candle {} 완료 elapsed:{:.2}s", interval.as_str(), instant.elapsed().as_secs_f32());
}
Err(E) => {
@ -512,7 +503,7 @@ pub async fn fetch_candle_parallel(
async fn repeat_task(
interval: String,
symbol_vec: Vec<Symbols>,
my_count: Arc<Mutex<Vec<(String, Vec<CandleData>)>>>,
my_count: Arc<Mutex<HashMap<String, Vec<CandleData>>>>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let client = ClientBuilder::new()
.timeout(tokio::time::Duration::from_millis(5000))
@ -531,7 +522,7 @@ async fn repeat_task(
// for fetching 1d, 1w, and 1mon candle
pub async fn fetch_candle_delay(
interval: &String,
candle_vec: &mut Vec<(String, Vec<CandleData>)>,
candle_vec: &mut HashMap<String, Vec<CandleData>>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let instant_func = Instant::now();
let server_epoch = server_epoch().await;

View File

@ -8,11 +8,11 @@ use serde_json::Value;
use sqlx::{Error, FromRow};
use std::borrow::{Borrow, BorrowMut};
use std::sync::Arc;
use std::collections::HashMap;
use tokio::{join, sync::Mutex, time::*};
#[derive(Debug, Clone)]
pub struct TradeFee {
pub symbol: String,
pub makercommission: Decimal,
pub takercommission: Decimal,
}
@ -20,7 +20,6 @@ pub struct TradeFee {
impl TradeFee {
fn new() -> TradeFee {
let a = TradeFee {
symbol: String::new(),
makercommission: Decimal::new(0, 8),
takercommission: Decimal::new(0, 8),
};
@ -30,7 +29,6 @@ impl TradeFee {
#[derive(Debug, FromRow, Clone)]
pub struct ExchangeInfo {
pub symbol: String,
pub stepsize: Decimal,
pub ticksize: Decimal,
pub base_asset_precision: u32,
@ -42,7 +40,6 @@ pub struct ExchangeInfo {
impl ExchangeInfo {
fn new() -> ExchangeInfo {
let a = ExchangeInfo {
symbol: String::new(),
stepsize: Decimal::new(0, 8),
ticksize: Decimal::new(0, 8),
base_asset_precision: 0,
@ -54,27 +51,12 @@ impl ExchangeInfo {
}
}
#[derive(Clone, Debug)]
pub struct CoinPriceData {
pub symbol: String,
pub current_price: f64,
}
impl CoinPriceData {
fn new() -> CoinPriceData {
let a = CoinPriceData {
symbol: String::new(),
current_price: 0.0,
};
a
}
}
// request all coin price (/api, Weight(IP) 2)
// request_all_coin_price -> de_all_coin_price_json -> store_coin_price_db
// pub async fn request_all_coin_price(client: &Client, price_vec: &Arc<Mutex<Vec<(String, f64)>>>) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
pub async fn request_all_coin_price(
client: &Client,
price_vec: &mut Vec<CoinPriceData>,
price_vec: &mut HashMap<String, f64>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let url = "https://api.binance.com/api/v3/ticker/price";
let mut response = client.get(url).send().await?;
@ -88,7 +70,7 @@ pub async fn request_all_coin_price(
async fn de_all_coin_price_json(
body: &String,
price_vec: &mut Vec<CoinPriceData>,
price_vec: &mut HashMap<String, f64>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let v: Value = serde_json::from_str(body.as_str())?;
let into_vec = v.as_array();
@ -97,27 +79,23 @@ async fn de_all_coin_price_json(
}
let mut object_map = &serde_json::map::Map::new();
let mut de_price_vec: Vec<CoinPriceData> = Vec::new();
let mut data_temp = CoinPriceData::new();
let mut symbol = String::new();
let mut price = 0.0;
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() {
"symbol" => data_temp.symbol = element.1.as_str().unwrap().to_string(),
"price" => {
data_temp.current_price = element.1.as_str().unwrap().parse::<f64>().unwrap()
}
"symbol" => symbol = element.1.as_str().unwrap().to_string(),
"price" => price = element.1.as_str().unwrap().parse::<f64>().unwrap(),
_ => {
println!("Elements in body msg are changed. Please update both your coinprices table and vectors.");
}
}
price_vec.insert(symbol.clone(), price);
}
de_price_vec.push(data_temp.clone());
}
*price_vec = de_price_vec;
Ok(())
}
@ -129,7 +107,7 @@ pub async fn request_trade_fee(
local_epoch: u128,
difference_epoch: i64,
client: &Client,
tradefee_vec: &mut Vec<TradeFee>,
tradefee_vec: &mut HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut base_url = String::from("https://api.binance.com/sapi/v1/asset/tradeFee?");
@ -188,7 +166,7 @@ pub async fn request_trade_fee(
async fn de_trade_fee_json(
body: &String,
tradefee_vec: &mut Vec<TradeFee>,
tradefee_map: &mut HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let v: Value = serde_json::from_str(body.as_str())?;
@ -197,17 +175,17 @@ async fn de_trade_fee_json(
return Err("Err")?;
}
let mut object_map = &serde_json::map::Map::new();
let mut de_tradefee_vec: Vec<TradeFee> = Vec::new();
let mut tradefee_map_build: HashMap<String, TradeFee> = HashMap::new();
let mut symbol = String::new();
let mut tradefee_data = TradeFee::new();
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() {
"symbol" => tradefee_data.symbol = element.1.as_str().unwrap().to_string(),
"symbol" => symbol = element.1.as_str().unwrap().to_string(),
"makerCommission" => {
tradefee_data.makercommission =
rust_decimal::prelude::FromStr::from_str(element.1.as_str().unwrap())
@ -223,10 +201,9 @@ async fn de_trade_fee_json(
}
}
}
de_tradefee_vec.push(tradefee_data.clone());
tradefee_map_build.insert(symbol.clone(), tradefee_data.clone());
}
tradefee_vec.clear();
*tradefee_vec = de_tradefee_vec;
*tradefee_map = tradefee_map_build;
Ok(())
}
@ -354,7 +331,7 @@ async fn store_24h_change_db(
// request exchange information. (/api, Weight(IP) 10)
pub async fn request_exchange_infomation(
client: &Client,
exchange_info_data: &mut Vec<ExchangeInfo>,
exchange_info_map: &mut HashMap<String, ExchangeInfo>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// building URL and API-keys
let mut url = String::new();
@ -380,8 +357,9 @@ pub async fn request_exchange_infomation(
return Err("Err")?;
}
let mut data_temp = ExchangeInfo::new();
let mut data_temp_vec: Vec<ExchangeInfo> = Vec::new();
let mut symbol = String::new();
let mut exchange_info = ExchangeInfo::new();
let mut data_map_temp: HashMap<String, ExchangeInfo> = HashMap::new();
for element in into_vec.unwrap() {
if element.0.contains("symbols") {
@ -394,23 +372,23 @@ pub async fn request_exchange_infomation(
.unwrap()
.ends_with("USDT")
{
data_temp.symbol =
symbol =
(element.get("symbol").unwrap().as_str().unwrap().to_string());
data_temp.base_asset_precision =
exchange_info.base_asset_precision =
(element.get("baseAssetPrecision").unwrap().as_u64().unwrap()) as u32;
data_temp.base_commission_precision = (element
exchange_info.base_commission_precision = (element
.get("baseCommissionPrecision")
.unwrap()
.as_u64()
.unwrap())
as u32;
data_temp.quote_asset_precision = (element
exchange_info.quote_asset_precision = (element
.get("quoteAssetPrecision")
.unwrap()
.as_u64()
.unwrap())
as u32;
data_temp.quote_commission_precision = (element
exchange_info.quote_commission_precision = (element
.get("quoteCommissionPrecision")
.unwrap()
.as_u64()
@ -426,7 +404,7 @@ pub async fn request_exchange_infomation(
.unwrap()
.starts_with("LOT_SIZE")
{
data_temp.stepsize = rust_decimal::prelude::FromStr::from_str(
exchange_info.stepsize = rust_decimal::prelude::FromStr::from_str(
element.get("stepSize").unwrap().as_str().unwrap(),
)
.unwrap();
@ -439,20 +417,20 @@ pub async fn request_exchange_infomation(
.unwrap()
.starts_with("PRICE_FILTER")
{
data_temp.ticksize = rust_decimal::prelude::FromStr::from_str(
exchange_info.ticksize = rust_decimal::prelude::FromStr::from_str(
element.get("tickSize").unwrap().as_str().unwrap(),
)
.unwrap();
}
}
data_temp_vec.push(data_temp.clone());
data_map_temp.insert(symbol.clone(), exchange_info.clone());
}
}
}
}
}
*exchange_info_data = data_temp_vec;
*exchange_info_map = data_map_temp;
Ok(())
}

View File

@ -22,7 +22,7 @@ pub enum RunningMode {
TEST,
REAL,
}
pub const RUNNING_MODE: RunningMode = RunningMode::REAL;
pub const RUNNING_MODE: RunningMode = RunningMode::SIMUL;
pub mod coex;
pub mod coin_health_check_team;

View File

@ -3,13 +3,15 @@
use crate::coin_health_check_team::*;
use crate::request_candles::CandleData;
use crate::request_others::{CoinPriceData, ExchangeInfo, TradeFee};
use crate::request_others::{ExchangeInfo, TradeFee};
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 reqwest::{Client, ClientBuilder};
use rust_decimal::Decimal;
use sqlx::{mysql::*, Connection, Executor, FromRow, Row};
use std::collections::{HashMap, HashSet};
use std::{
io::{self, Write},
sync::Arc,
@ -80,88 +82,88 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let (tx2, mut rx2) = watch::channel(0); // epoch_difference
// trade fee data
let mut tradefee_vec: Vec<TradeFee> = Vec::new(); // (symbol, makerCommission, takerCommission)
let (tx_tradefee_vec, mut rx_tradefee_vec) = watch::channel(tradefee_vec);
let mut rx2_tradefee_vec = rx_tradefee_vec.clone();
let mut rx3_tradefee_vec = rx_tradefee_vec.clone();
let mut rx4_tradefee_vec = rx_tradefee_vec.clone();
let mut rx5_tradefee_vec = rx_tradefee_vec.clone();
let mut tradefee_vec_capacity = rx_tradefee_vec.clone();
let mut tradefee_map: HashMap<String, TradeFee> = HashMap::new(); // <symbol, TradeFee>
let (tx_tradefee_map, mut rx_tradefee_map) = watch::channel(tradefee_map);
let mut rx2_tradefee_map = rx_tradefee_map.clone();
let mut rx3_tradefee_map = rx_tradefee_map.clone();
let mut rx4_tradefee_map = rx_tradefee_map.clone();
let mut rx5_tradefee_map = rx_tradefee_map.clone();
let mut tradefee_map_capacity = rx_tradefee_map.clone();
// valid usdt trade data
let mut valid_usdt_trade_vec: Vec<String> = Vec::new(); // symbol
let (tx_valid_usdt_trade_vec, mut rx_valid_usdt_trade_vec) =
watch::channel(valid_usdt_trade_vec);
let mut rx2_valid_usdt_trade_vec = rx_valid_usdt_trade_vec.clone();
let mut rx3_valid_usdt_trade_vec = rx_valid_usdt_trade_vec.clone();
let mut rx4_valid_usdt_trade_vec = rx_valid_usdt_trade_vec.clone();
let mut valid_usdt_trade_vec_capacity = rx_valid_usdt_trade_vec.clone();
let mut valid_usdt_trade_set: HashSet<String> = HashSet::new(); // symbol
let (tx_valid_usdt_trade_set, mut rx_valid_usdt_trade_set) =
watch::channel(valid_usdt_trade_set);
let mut rx2_valid_usdt_trade_set = rx_valid_usdt_trade_set.clone();
let mut rx3_valid_usdt_trade_set = rx_valid_usdt_trade_set.clone();
let mut rx4_valid_usdt_trade_set = rx_valid_usdt_trade_set.clone();
let mut valid_usdt_trade_set_capacity = rx_valid_usdt_trade_set.clone();
// price per second data and channels
let mut price_vec: Vec<CoinPriceData> = Vec::new(); // (symbol, price)
let (tx_price_vec, mut rx_price_vec) = watch::channel(price_vec);
let mut rx3_price_vec = rx_price_vec.clone();
let mut rx5_price_vec = rx_price_vec.clone();
let mut price_vec_capacity = rx_price_vec.clone();
let mut price_map: HashMap<String, f64> = HashMap::new(); // <symbol, price>
let (tx_price_map, mut rx_price_map) = watch::channel(price_map);
let mut rx3_price_map = rx_price_map.clone();
let mut rx5_price_map = rx_price_map.clone();
let mut price_map_capacity = rx_price_map.clone();
// candle data from endpoint and channels
let mut candle_1m_vec: Vec<(String, Vec<CandleData>)> = Vec::new(); // (symbol, Vec<CandleData)>
let (tx_candle_1m_vec, mut rx_candle_1m_vec) = watch::channel(candle_1m_vec);
let mut candle_1m_vec_capacity = rx_candle_1m_vec.clone();
let mut candle_1m_map: HashMap<String, Vec<CandleData>> = HashMap::new(); // <symbol, Vec<CandleData>>
let (tx_candle_1m_map, mut rx_candle_1m_map) = watch::channel(candle_1m_map);
let mut candle_1m_map_capacity = rx_candle_1m_map.clone();
let mut candle_30m_vec: Vec<(String, Vec<CandleData>)> = Vec::new();
let (tx_candle_30m_vec, mut rx_candle_30m_vec) = watch::channel(candle_30m_vec);
let mut candle_30m_vec_capacity = rx_candle_30m_vec.clone();
let mut candle_30m_map: HashMap<String, Vec<CandleData>> = HashMap::new();
let (tx_candle_30m_map, mut rx_candle_30m_map) = watch::channel(candle_30m_map);
let mut candle_30m_map_capacity = rx_candle_30m_map.clone();
let mut candle_1d_vec: Vec<(String, Vec<CandleData>)> = Vec::new();
let (tx_candle_1d_vec, mut rx_candle_1d_vec) = watch::channel(candle_1d_vec);
let mut candle_1d_vec_capacity = rx_candle_1d_vec.clone();
let mut candle_1d_map: HashMap<String, Vec<CandleData>> = HashMap::new();
let (tx_candle_1d_map, mut rx_candle_1d_map) = watch::channel(candle_1d_map);
let mut candle_1d_map_capacity = rx_candle_1d_map.clone();
let mut candle_1w_vec: Vec<(String, Vec<CandleData>)> = Vec::new();
let (tx_candle_1w_vec, mut rx_candle_1w_vec) = watch::channel(candle_1w_vec);
let mut candle_1w_vec_capacity = rx_candle_1w_vec.clone();
let mut candle_1w_map: HashMap<String, Vec<CandleData>> = HashMap::new();
let (tx_candle_1w_map, mut rx_candle_1w_map) = watch::channel(candle_1w_map);
let mut candle_1w_map_capacity = rx_candle_1w_map.clone();
let mut candle_1mon_vec: Vec<(String, Vec<CandleData>)> = Vec::new();
let (tx_candle_1mon_vec, mut rx_candle_1mon_vec) = watch::channel(candle_1mon_vec);
let mut candle_1mon_vec_capacity = rx_candle_1mon_vec.clone();
let mut candle_1mon_map: HashMap<String, Vec<CandleData>> = HashMap::new();
let (tx_candle_1mon_map, mut rx_candle_1mon_map) = watch::channel(candle_1mon_map);
let mut candle_1mon_map_capacity = rx_candle_1mon_map.clone();
// real-time reflected price data and channels
let mut rt_price_1m_vec: Vec<(String, Vec<RealtimePriceData>)> = Vec::new(); // (symbol, Vec<RealtimePriceData)>
let (tx_rt_price_1m_vec, mut rx_rt_price_1m_vec) = watch::channel(rt_price_1m_vec);
let mut rx2_rt_price_1m_vec = rx_rt_price_1m_vec.clone();
let mut rx3_rt_price_1m_vec = rx_rt_price_1m_vec.clone();
let mut rx4_rt_price_1m_vec = rx_rt_price_1m_vec.clone();
let mut rx5_rt_price_1m_vec = rx_rt_price_1m_vec.clone();
let mut rt_price_1m_vec_capacity = rx_rt_price_1m_vec.clone();
let mut rt_price_1m_map: HashMap<String, Vec<RealtimePriceData>> = HashMap::new(); // <symbol, Vec<RealtimePriceData>>
let (tx_rt_price_1m_map, mut rx_rt_price_1m_map) = watch::channel(rt_price_1m_map);
let mut rx2_rt_price_1m_map = rx_rt_price_1m_map.clone();
let mut rx3_rt_price_1m_map = rx_rt_price_1m_map.clone();
let mut rx4_rt_price_1m_map = rx_rt_price_1m_map.clone();
let mut rx5_rt_price_1m_map = rx_rt_price_1m_map.clone();
let mut rt_price_1m_map_capacity = rx_rt_price_1m_map.clone();
let mut rt_price_30m_vec: Vec<(String, Vec<RealtimePriceData>)> = Vec::new();
let (tx_rt_price_30m_vec, mut rx_rt_price_30m_vec) = watch::channel(rt_price_30m_vec);
let mut rx2_rt_price_30m_vec = rx_rt_price_30m_vec.clone();
let mut rx3_rt_price_30m_vec = rx_rt_price_30m_vec.clone();
let mut rx4_rt_price_30m_vec = rx_rt_price_30m_vec.clone();
let mut rx5_rt_price_30m_vec = rx_rt_price_30m_vec.clone();
let mut rt_price_30m_vec_capacity = rx_rt_price_30m_vec.clone();
let mut rt_price_30m_map: HashMap<String, Vec<RealtimePriceData>> = HashMap::new();
let (tx_rt_price_30m_map, mut rx_rt_price_30m_map) = watch::channel(rt_price_30m_map);
let mut rx2_rt_price_30m_map = rx_rt_price_30m_map.clone();
let mut rx3_rt_price_30m_map = rx_rt_price_30m_map.clone();
let mut rx4_rt_price_30m_map = rx_rt_price_30m_map.clone();
let mut rx5_rt_price_30m_map = rx_rt_price_30m_map.clone();
let mut rt_price_30m_map_capacity = rx_rt_price_30m_map.clone();
let mut rt_price_1d_vec: Vec<(String, Vec<RealtimePriceData>)> = Vec::new();
let (tx_rt_price_1d_vec, mut rx_rt_price_1d_vec) = watch::channel(rt_price_1d_vec);
let mut rx2_rt_price_1d_vec = rx_rt_price_1d_vec.clone();
let mut rx3_rt_price_1d_vec = rx_rt_price_1d_vec.clone();
let mut rx4_rt_price_1d_vec = rx_rt_price_1d_vec.clone();
let mut rt_price_1d_vec_capacity = rx_rt_price_1d_vec.clone();
let mut rt_price_1d_map: HashMap<String, Vec<RealtimePriceData>> = HashMap::new();
let (tx_rt_price_1d_map, mut rx_rt_price_1d_map) = watch::channel(rt_price_1d_map);
let mut rx2_rt_price_1d_map = rx_rt_price_1d_map.clone();
let mut rx3_rt_price_1d_map = rx_rt_price_1d_map.clone();
let mut rx4_rt_price_1d_map = rx_rt_price_1d_map.clone();
let mut rt_price_1d_map_capacity = rx_rt_price_1d_map.clone();
let mut rt_price_1w_vec: Vec<(String, Vec<RealtimePriceData>)> = Vec::new();
let (tx_rt_price_1w_vec, mut rx_rt_price_1w_vec) = watch::channel(rt_price_1w_vec);
let mut rx2_rt_price_1w_vec = rx_rt_price_1w_vec.clone();
let mut rx3_rt_price_1w_vec = rx_rt_price_1w_vec.clone();
let mut rx4_rt_price_1w_vec = rx_rt_price_1w_vec.clone();
let mut rt_price_1w_vec_capacity = rx_rt_price_1w_vec.clone();
let mut rt_price_1w_map: HashMap<String, Vec<RealtimePriceData>> = HashMap::new();
let (tx_rt_price_1w_map, mut rx_rt_price_1w_map) = watch::channel(rt_price_1w_map);
let mut rx2_rt_price_1w_map = rx_rt_price_1w_map.clone();
let mut rx3_rt_price_1w_map = rx_rt_price_1w_map.clone();
let mut rx4_rt_price_1w_map = rx_rt_price_1w_map.clone();
let mut rt_price_1w_map_capacity = rx_rt_price_1w_map.clone();
let mut rt_price_1mon_vec: Vec<(String, Vec<RealtimePriceData>)> = Vec::new();
let (tx_rt_price_1mon_vec, mut rx_rt_price_1mon_vec) = watch::channel(rt_price_1mon_vec);
let mut rx2_rt_price_1mon_vec = rx_rt_price_1mon_vec.clone();
let mut rx3_rt_price_1mon_vec = rx_rt_price_1mon_vec.clone();
let mut rx4_rt_price_1mon_vec = rx_rt_price_1mon_vec.clone();
let mut rt_price_1mon_vec_capacity = rx_rt_price_1mon_vec.clone();
let mut rt_price_1mon_map: HashMap<String, Vec<RealtimePriceData>> = HashMap::new();
let (tx_rt_price_1mon_map, mut rx_rt_price_1mon_map) = watch::channel(rt_price_1mon_map);
let mut rx2_rt_price_1mon_map = rx_rt_price_1mon_map.clone();
let mut rx3_rt_price_1mon_map = rx_rt_price_1mon_map.clone();
let mut rx4_rt_price_1mon_map = rx_rt_price_1mon_map.clone();
let mut rt_price_1mon_map_capacity = rx_rt_price_1mon_map.clone();
// TEMA data
// let mut tema3_1m_data: Vec<(String, Vec<(f64, i64)>)> = Vec::new(); // Vec<(symbol, Vec<(price, closetime)>)>
@ -198,14 +200,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// let (tx_tema30_1mon_data, mut rx_tema30_1mon_data) = watch::channel(tema30_1mon_data);
// Exchange Information data
let mut exchange_info_data: Vec<ExchangeInfo> = Vec::new();
let (tx_exchange_info_data, mut rx_exchange_info_data) = watch::channel(exchange_info_data);
let mut rx2_exchange_info_data = rx_exchange_info_data.clone();
let mut rx3_exchange_info_data = rx_exchange_info_data.clone();
let mut rx4_exchange_info_data = rx_exchange_info_data.clone();
let mut rx5_exchange_info_data = rx_exchange_info_data.clone();
let mut rx6_exchange_info_data = rx_exchange_info_data.clone();
let mut exchange_info_data_capacity = rx_exchange_info_data.clone();
let mut exchange_info_map: HashMap<String, ExchangeInfo> = HashMap::new();
let (tx_exchange_info_map, mut rx_exchange_info_map) = watch::channel(exchange_info_map);
let mut rx2_exchange_info_map = rx_exchange_info_map.clone();
let mut rx3_exchange_info_map = rx_exchange_info_map.clone();
let mut rx4_exchange_info_map = rx_exchange_info_map.clone();
let mut rx5_exchange_info_map = rx_exchange_info_map.clone();
let mut rx6_exchange_info_map = rx_exchange_info_map.clone();
let mut exchange_info_map_capacity = rx_exchange_info_map.clone();
{
if RUNNING_MODE == RunningMode::REAL {
@ -225,20 +227,20 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let interval_1w = String::from("1w");
let interval_1mon = String::from("1mon");
let mut candle_30m_vec_temp: Vec<(String, Vec<CandleData>)> = Vec::new();
let mut candle_1d_vec_temp: Vec<(String, Vec<CandleData>)> = Vec::new();
let mut candle_1w_vec_temp: Vec<(String, Vec<CandleData>)> = Vec::new();
let mut candle_1mon_vec_temp: Vec<(String, Vec<CandleData>)> = Vec::new();
let mut candle_30m_map_temp: HashMap<String, Vec<CandleData>> = HashMap::new();
let mut candle_1d_map_temp: HashMap<String, Vec<CandleData>> = HashMap::new();
let mut candle_1w_map_temp: HashMap<String, Vec<CandleData>> = HashMap::new();
let mut candle_1mon_map_temp: HashMap<String, Vec<CandleData>> = HashMap::new();
request_candles::fetch_candle_parallel(&interval_30m, &mut candle_30m_vec_temp).await;
request_candles::fetch_candle_parallel(&interval_1d, &mut candle_1d_vec_temp).await;
request_candles::fetch_candle_parallel(&interval_1w, &mut candle_1w_vec_temp).await;
request_candles::fetch_candle_parallel(&interval_1mon, &mut candle_1mon_vec_temp).await;
request_candles::fetch_candle_parallel(&interval_30m, &mut candle_30m_map_temp).await;
request_candles::fetch_candle_parallel(&interval_1d, &mut candle_1d_map_temp).await;
request_candles::fetch_candle_parallel(&interval_1w, &mut candle_1w_map_temp).await;
request_candles::fetch_candle_parallel(&interval_1mon, &mut candle_1mon_map_temp).await;
tx_candle_30m_vec.send_modify(|vec| *vec = candle_30m_vec_temp);
tx_candle_1d_vec.send_modify(|vec| *vec = candle_1d_vec_temp);
tx_candle_1w_vec.send_modify(|vec| *vec = candle_1w_vec_temp);
tx_candle_1mon_vec.send_modify(|vec| *vec = candle_1mon_vec_temp);
tx_candle_30m_map.send_modify(|map| *map = candle_30m_map_temp);
tx_candle_1d_map.send_modify(|map| *map = candle_1d_map_temp);
tx_candle_1w_map.send_modify(|map| *map = candle_1w_map_temp);
tx_candle_1mon_map.send_modify(|map| *map = candle_1mon_map_temp);
// sleep as much as the loop recurs per 60 seconds, expecting child threads will have finished within 60 seconds.
println!("Ok..");
@ -515,10 +517,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
match tx1_changed {
Ok(T) => match tx2_changed {
Ok(T) => {
let mut tradefee_vec_temp: Vec<TradeFee> = Vec::new();
let mut result;
let mut tradefee_vec_temp: HashMap<String, TradeFee> = HashMap::new();
loop {
result = request_others::request_trade_fee(
let result = request_others::request_trade_fee(
API_KEY,
SECRET_KEY,
local_epoch,
@ -535,13 +536,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
}
}
match result {
Ok(T) => {
tx_tradefee_vec.send_modify(|vec| *vec = tradefee_vec_temp);
tx_task2.send(2).expect("The mpsc channel has been closed.");
}
Err(E) => {}
}
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.")
@ -567,30 +564,27 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.timeout(tokio::time::Duration::from_millis(3000))
.build()
.unwrap();
let mut exchange_info_data_temp: Vec<ExchangeInfo> = Vec::new();
let mut exchange_info_map_temp: HashMap<String, ExchangeInfo> = HashMap::new();
let mut result;
loop {
result = coin_health_check_team::request_others::request_exchange_infomation(
&client,
&mut exchange_info_data_temp,
&mut exchange_info_map_temp,
)
.await;
// retry
if exchange_info_data_temp.len() == 0 {
if exchange_info_map_temp.len() == 0 {
sleep(Duration::from_secs(3)).await;
} else {
break;
}
}
match result {
Ok(T) => {
tx_exchange_info_data.send_modify(|vec| *vec = exchange_info_data_temp);
tx_task3.send(3).expect("The mpsc channel has been closed.");
}
Err(E) => {}
}
tx_exchange_info_map.send_modify(|vec| *vec = exchange_info_map_temp);
tx_task3.send(3).expect("The mpsc channel has been closed.");
sleep(Duration::from_secs(300)).await; // sleep for 5 mins
}
});
@ -613,22 +607,17 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let result = request_others::request_24hr_ticker_price_change_statistics(&client).await;
match result {
Ok(T) => {
let exchange_info_vec = rx5_exchange_info_data.borrow().clone();
let mut valid_usdt_trade_vec_temp: Vec<String> = Vec::new();
let result = monitors::collect_valid_usde_trade(
&mut valid_usdt_trade_vec_temp,
&exchange_info_vec,
let exchange_info_map = rx5_exchange_info_map.borrow().clone();
let mut valid_usdt_trade_set_temp: HashSet<String> = HashSet::new();
monitors::collect_valid_usde_trade(
&mut valid_usdt_trade_set_temp,
&exchange_info_map,
)
.await;
match result {
Ok(T) => {
tx_valid_usdt_trade_vec
.send_modify(|vec| *vec = valid_usdt_trade_vec_temp);
tx_task4.send(4).expect("The mpsc channel has been closed.");
}
Err(E) => {}
}
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) => {
println!(">>> Failed to monitor usdt_24h_change_profit_index.");
@ -676,7 +665,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Task#5: price per second
tokio::task::spawn(async move {
sleep(Duration::from_secs(20)).await;
let mut server_epoch = 0;
let mut elapsed_time = 0;
loop {
let instant = Instant::now();
@ -684,101 +672,85 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.timeout(tokio::time::Duration::from_millis(1000))
.build()
.unwrap();
let mut price_vec_temp: Vec<CoinPriceData> = Vec::new();
let result = request_others::request_all_coin_price(&client, &mut price_vec_temp).await;
let mut price_vec_temp_c: Vec<CoinPriceData> = Vec::new();
match result {
Ok(T) => {
price_vec_temp_c = price_vec_temp.clone();
tx_price_vec.send_modify(|vec| *vec = price_vec_temp);
tx_task5.send(5).expect("The mpsc channel has been closed.");
}
Err(E) => {}
}
let mut price_vec_temp: HashMap<String, f64> = HashMap::new();
let mut price_vec_temp_c: HashMap<String, f64> = HashMap::new();
request_others::request_all_coin_price(&client, &mut price_vec_temp).await;
price_vec_temp_c = price_vec_temp.clone();
tx_price_map.send_modify(|vec| *vec = price_vec_temp);
tx_task5.send(5).expect("The mpsc channel has been closed.");
// Task#10: make realtime price data of 1m, 30m, 1d, 1w and 1mon
let valid_usdt_trade_vec = rx_valid_usdt_trade_vec.borrow().clone();
let valid_usdt_trade_set = rx_valid_usdt_trade_set.borrow().clone();
// 1m
let interval = String::from("1m");
let candle_1m_vec = rx_candle_1m_vec.borrow().clone();
let rt_price_1m_vec_read_temp: Vec<(String, Vec<RealtimePriceData>)> = Vec::new();
let mut rt_price_1m_vec_write_temp: Vec<(String, Vec<RealtimePriceData>)> = Vec::new();
let candle_1m_vec = rx_candle_1m_map.borrow().clone();
let dummy_data: HashMap<String, Vec<RealtimePriceData>> = HashMap::new();
let mut rt_price_1m_map_write_temp: HashMap<String, Vec<RealtimePriceData>> = HashMap::new();
let mut rt_price_1m_map_write_temp_c: HashMap<String, Vec<RealtimePriceData>> = HashMap::new();
let result = value_estimation_team::datapoints::price_data::update_realtime_price_data(
&interval,
&candle_1m_vec,
&rt_price_1m_vec_read_temp,
&mut rt_price_1m_vec_write_temp,
&dummy_data,
&mut rt_price_1m_map_write_temp,
&price_vec_temp_c,
&valid_usdt_trade_vec,
&valid_usdt_trade_set,
)
.await;
match result {
Ok(T) => {
if tx_rt_price_1m_vec.is_closed() {
eprintln!("tx_rt_price_1m_vec has been closed!");
} else {
tx_rt_price_1m_vec.send_modify(|vec| *vec = rt_price_1m_vec_write_temp);
}
}
Err(E) => {}
rt_price_1m_map_write_temp_c = rt_price_1m_map_write_temp.clone();
if tx_rt_price_1m_map.is_closed() {
eprintln!("tx_rt_price_1m_vec has been closed!");
} else {
tx_rt_price_1m_map.send_modify(|vec| *vec = rt_price_1m_map_write_temp);
}
// 30m
let interval = String::from("30m");
let candle_30m_vec = rx_candle_30m_vec.borrow().clone();
let rt_price_1m_vec = rx_rt_price_1m_vec.borrow().clone();
let mut rt_price_30m_vec_write_temp: Vec<(String, Vec<RealtimePriceData>)> = Vec::new();
if !rt_price_1m_vec.is_empty() {
let candle_30m_map = rx_candle_30m_map.borrow().clone();
let mut rt_price_30m_map_write_temp: HashMap<String, Vec<RealtimePriceData>> = HashMap::new();
let mut rt_price_30m_map_write_temp_c: HashMap<String, Vec<RealtimePriceData>> = HashMap::new();
if !rt_price_1m_map_write_temp_c.is_empty() {
let result =
value_estimation_team::datapoints::price_data::update_realtime_price_data(
&interval,
&candle_30m_vec,
&rt_price_1m_vec,
&mut rt_price_30m_vec_write_temp,
&candle_30m_map,
&rt_price_1m_map_write_temp_c,
&mut rt_price_30m_map_write_temp,
&price_vec_temp_c,
&valid_usdt_trade_vec,
&valid_usdt_trade_set,
)
.await;
match result {
Ok(T) => {
if tx_rt_price_30m_vec.is_closed() {
eprintln!("tx_rt_price_30m_vec has been closed!");
} else {
tx_rt_price_30m_vec
.send_modify(|vec| *vec = rt_price_30m_vec_write_temp);
}
}
Err(E) => {}
}
rt_price_30m_map_write_temp_c = rt_price_30m_map_write_temp.clone();
if tx_rt_price_30m_map.is_closed() {
eprintln!("tx_rt_price_30m_vec has been closed!");
} else {
tx_rt_price_30m_map
.send_modify(|map: &mut HashMap<String, Vec<RealtimePriceData>>| *map = rt_price_30m_map_write_temp);
}
}
// 1d
let interval = String::from("1d");
let candle_1d_vec = rx_candle_1d_vec.borrow().clone();
let rt_price_30m_vec = rx_rt_price_30m_vec.borrow().clone();
let mut rt_price_1d_vec_write_temp: Vec<(String, Vec<RealtimePriceData>)> = Vec::new();
let candle_1d_vec = rx_candle_1d_map.borrow().clone();
let mut rt_price_1d_map_write_temp: HashMap<String, Vec<RealtimePriceData>> = HashMap::new();
if !rt_price_30m_vec.is_empty() {
if !rt_price_30m_map_write_temp_c.is_empty() {
let result =
value_estimation_team::datapoints::price_data::update_realtime_price_data(
&interval,
&candle_1d_vec,
&rt_price_30m_vec,
&mut rt_price_1d_vec_write_temp,
&rt_price_30m_map_write_temp_c,
&mut rt_price_1d_map_write_temp,
&price_vec_temp_c,
&valid_usdt_trade_vec,
&valid_usdt_trade_set,
)
.await;
match result {
Ok(T) => {
if tx_rt_price_1d_vec.is_closed() {
if tx_rt_price_1d_map.is_closed() {
eprintln!("tx_rt_price_1d_vec has been closed!");
} else {
tx_rt_price_1d_vec.send_modify(|vec| *vec = rt_price_1d_vec_write_temp);
tx_rt_price_1d_map.send_modify(|map| *map = rt_price_1d_map_write_temp);
}
}
Err(E) => {}
@ -831,13 +803,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let interval = String::from("1m");
loop {
let instant = Instant::now();
let mut candle_1m_vec_temp: Vec<(String, Vec<CandleData>)> = Vec::new();
let mut candle_1m_map_temp: HashMap<String, Vec<CandleData>> = HashMap::new();
let result =
request_candles::fetch_candle_parallel(&interval, &mut candle_1m_vec_temp).await;
request_candles::fetch_candle_parallel(&interval, &mut candle_1m_map_temp).await;
match result {
Ok(T) => {
tx_candle_1m_vec.send_modify(|vec| *vec = candle_1m_vec_temp);
tx_candle_1m_map.send_modify(|vec| *vec = candle_1m_map_temp);
tx_task6.send(6).expect("The mpsc channel has been closed.");
}
Err(E) => {}
@ -858,14 +830,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let interval = String::from("30m");
loop {
let instant = Instant::now();
let mut candle_30m_vec_temp: Vec<(String, Vec<CandleData>)> = Vec::new();
let mut candle_30m_map_temp: HashMap<String, Vec<CandleData>> = HashMap::new();
let result =
request_candles::fetch_candle_delay(&interval, &mut candle_30m_vec_temp).await;
request_candles::fetch_candle_delay(&interval, &mut candle_30m_map_temp).await;
// request_candles::fetch_candle_parallel(&interval, &mut candle_30m_vec_temp).await;
match result {
Ok(T) => {
tx_candle_30m_vec.send_modify(|vec| *vec = candle_30m_vec_temp);
tx_candle_30m_map.send_modify(|vec| *vec = candle_30m_map_temp);
tx_task7.send(7).expect("The mpsc channel has been closed.");
}
Err(E) => {}
@ -886,13 +858,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
sleep(Duration::from_secs(600)).await;
loop {
let instant = Instant::now();
let mut candle_1d_vec_temp: Vec<(String, Vec<CandleData>)> = Vec::new();
let mut candle_1d_map_temp: HashMap<String, Vec<CandleData>> = HashMap::new();
let result =
request_candles::fetch_candle_delay(&interval, &mut candle_1d_vec_temp).await;
request_candles::fetch_candle_delay(&interval, &mut candle_1d_map_temp).await;
match result {
Ok(T) => {
tx_candle_1d_vec.send_modify(|vec| *vec = candle_1d_vec_temp);
tx_candle_1d_map.send_modify(|vec| *vec = candle_1d_map_temp);
tx_task8.send(8).expect("The mpsc channel has been closed.");
}
Err(E) => {}
@ -1039,14 +1011,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
loop {
let instant = Instant::now();
let mut all_data = AllData::new();
all_data.valid_symbol_vec = rx3_valid_usdt_trade_vec.borrow().clone();
all_data.valid_symbol_vec = rx3_valid_usdt_trade_set.borrow().clone();
// realtime price data
all_data.rt_price_1m_vec = rx3_rt_price_1m_vec.borrow().clone();
all_data.rt_price_30m_vec = rx3_rt_price_30m_vec.borrow().clone();
all_data.rt_price_1d_vec = rx3_rt_price_1d_vec.borrow().clone();
all_data.rt_price_1w_vec = rx3_rt_price_1w_vec.borrow().clone();
all_data.rt_price_1mon_vec = rx3_rt_price_1mon_vec.borrow().clone();
all_data.rt_price_1m_vec = rx3_rt_price_1m_map.borrow().clone();
all_data.rt_price_30m_vec = rx3_rt_price_30m_map.borrow().clone();
all_data.rt_price_1d_vec = rx3_rt_price_1d_map.borrow().clone();
all_data.rt_price_1w_vec = rx3_rt_price_1w_map.borrow().clone();
all_data.rt_price_1mon_vec = rx3_rt_price_1mon_map.borrow().clone();
let result =
strategy_team::strategy_manager::execute_list_up_for_buy(&all_data).await;
@ -1076,13 +1048,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut all_data = AllData::new();
loop {
let instant = Instant::now();
all_data.valid_symbol_vec = rx3_valid_usdt_trade_vec.borrow().clone();
all_data.valid_symbol_vec = rx3_valid_usdt_trade_set.borrow().clone();
// realtime price data
all_data.rt_price_1m_vec = rx3_rt_price_1m_vec.borrow().clone();
all_data.rt_price_30m_vec = rx3_rt_price_30m_vec.borrow().clone();
all_data.rt_price_1d_vec = rx3_rt_price_1d_vec.borrow().clone();
all_data.rt_price_1w_vec = rx3_rt_price_1w_vec.borrow().clone();
all_data.rt_price_1mon_vec = rx3_rt_price_1mon_vec.borrow().clone();
all_data.rt_price_1m_vec = rx3_rt_price_1m_map.borrow().clone();
all_data.rt_price_30m_vec = rx3_rt_price_30m_map.borrow().clone();
all_data.rt_price_1d_vec = rx3_rt_price_1d_map.borrow().clone();
all_data.rt_price_1w_vec = rx3_rt_price_1w_map.borrow().clone();
all_data.rt_price_1mon_vec = rx3_rt_price_1mon_map.borrow().clone();
// let result = coex::strategy_team::execute_strategist_for_test(&all_data).await;
@ -1112,23 +1084,23 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
loop {
let instant = Instant::now();
let mut all_data = AllData::new();
let mut exchange_info_vec: Vec<ExchangeInfo> = Vec::new();
let mut trade_fee_vec: Vec<TradeFee> = Vec::new();
let mut exchange_info_map: HashMap<String, ExchangeInfo> = HashMap::new();
let mut trade_fee_map: HashMap<String, TradeFee> = HashMap::new();
all_data.valid_symbol_vec = rx4_valid_usdt_trade_vec.borrow().clone();
exchange_info_vec = rx6_exchange_info_data.borrow().clone();
trade_fee_vec = rx5_tradefee_vec.borrow().clone();
all_data.valid_symbol_vec = rx4_valid_usdt_trade_set.borrow().clone();
exchange_info_map = rx6_exchange_info_map.borrow().clone();
trade_fee_map = rx5_tradefee_map.borrow().clone();
// realtime price data
all_data.rt_price_1m_vec = rx5_rt_price_1m_vec.borrow().clone();
all_data.rt_price_30m_vec = rx5_rt_price_30m_vec.borrow().clone();
all_data.rt_price_1d_vec = rx4_rt_price_1d_vec.borrow().clone();
all_data.rt_price_1w_vec = rx4_rt_price_1w_vec.borrow().clone();
all_data.rt_price_1mon_vec = rx4_rt_price_1mon_vec.borrow().clone();
all_data.rt_price_1m_vec = rx5_rt_price_1m_map.borrow().clone();
all_data.rt_price_30m_vec = rx5_rt_price_30m_map.borrow().clone();
all_data.rt_price_1d_vec = rx4_rt_price_1d_map.borrow().clone();
all_data.rt_price_1w_vec = rx4_rt_price_1w_map.borrow().clone();
all_data.rt_price_1mon_vec = rx4_rt_price_1mon_map.borrow().clone();
let result = strategy_team::strategy_manager::execute_list_up_for_sell(
&all_data,
&exchange_info_vec,
&trade_fee_vec,
&exchange_info_map,
&trade_fee_map,
)
.await;
@ -1158,7 +1130,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut elapsed_time = 0;
loop {
let instant = Instant::now();
let coin_price_vec = rx3_price_vec.borrow().clone();
let coin_price_vec = rx3_price_map.borrow().clone();
let result = coex::exchange_team::monitoring_pre_suggested_coins(&coin_price_vec).await;
// send Task#0 a message to notify running on
@ -1187,10 +1159,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut elapsed_time = 0;
loop {
let instant = Instant::now();
let exchange_info_vec = rx_exchange_info_data.borrow().clone();
let trade_fee_vec = rx_tradefee_vec.borrow().clone();
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_vec, &trade_fee_vec).await;
coex::exchange_team::buy_coin(&exchange_info_map, &trade_fee_map).await;
// send Task#0 a message to notify running on
match result {
@ -1217,8 +1189,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut elapsed_time = 0;
let instant = Instant::now();
let exchange_info_vec = rx_exchange_info_data.borrow().clone();
let trade_fee_vec = rx_tradefee_vec.borrow().clone();
let exchange_info_vec = rx_exchange_info_map.borrow().clone();
let trade_fee_vec = rx_tradefee_map.borrow().clone();
// let result = coex::exchange_team::buy_coin_for_test(&client, &coin_price_vec, &exchange_info_vec, &trade_fee_vec).await;
let result = coex::exchange_team::buy_coin(&exchange_info_vec, &trade_fee_vec).await;
@ -1255,12 +1227,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.timeout(tokio::time::Duration::from_millis(3000))
.build()
.unwrap();
let exchange_info_vec = rx3_exchange_info_data.borrow().clone();
let trade_fee_vec = rx2_tradefee_vec.borrow().clone();
let trade_fee_map = rx2_tradefee_map.borrow().clone();
let result = coex::order_team::monitoring_open_buy_order(
&client,
&exchange_info_vec,
&trade_fee_vec,
&trade_fee_map,
)
.await;
@ -1293,9 +1263,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut elapsed_time = 0;
loop {
let instant = Instant::now();
let coin_price_vec = rx5_price_vec.borrow().clone();
let exchange_info_vec = rx2_exchange_info_data.borrow().clone();
let trade_fee_vec = rx3_tradefee_vec.borrow().clone();
let coin_price_vec = rx5_price_map.borrow().clone();
let exchange_info_vec = rx2_exchange_info_map.borrow().clone();
let trade_fee_vec = rx3_tradefee_map.borrow().clone();
let result = coex::order_team::update_price_of_filled_buy_order(
&coin_price_vec,
@ -1338,8 +1308,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.timeout(tokio::time::Duration::from_millis(3000))
.build()
.unwrap();
let exchange_info_vec = rx4_exchange_info_data.borrow().clone();
let trade_fee_vec = rx4_tradefee_vec.borrow().clone();
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(
&client,
&exchange_info_vec,
@ -1403,7 +1373,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut all_data = AllData::new();
// realtime price data
all_data.rt_price_1m_vec = rx4_rt_price_1m_vec.borrow().clone();
all_data.rt_price_1m_vec = rx4_rt_price_1m_map.borrow().clone();
let result = coex::exchange_team::monitoring_scoreboard(&all_data).await;

View File

@ -3,16 +3,16 @@ pub mod strategy_002;
pub mod strategy_003;
pub mod strategy_004;
pub mod strategy_005;
pub mod strategy_006;
pub mod strategy_test;
// pub mod strategy_006;
// 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::server_epoch;
use crate::coex::exchange_team::get_server_epoch;
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;
use crate::value_estimation_team::datapoints::price_data::{RealtimePriceData, CandleType};
use crate::value_estimation_team::indicators::bollingerband::{bollingerband, BollingerBandData};
use crate::value_estimation_team::indicators::ema::{ema, ema_opclo, EmaData};
use crate::value_estimation_team::indicators::heatmap_volume::{
@ -22,7 +22,7 @@ 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};
use crate::value_estimation_team::indicators::supertrend::{supertrend, SupertrendData, SuperTrendArea, SuperTrendSignal};
use crate::value_estimation_team::indicators::adx::{AdxData, adx};
use futures::future::try_join_all;
use reqwest::{Client, ClientBuilder};
@ -32,25 +32,26 @@ use sqlx::FromRow;
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 {
pub valid_symbol_vec: Vec<String>,
pub rt_price_1m_vec: Vec<(String, Vec<RealtimePriceData>)>,
pub rt_price_30m_vec: Vec<(String, Vec<RealtimePriceData>)>,
pub rt_price_1d_vec: Vec<(String, Vec<RealtimePriceData>)>,
pub rt_price_1w_vec: Vec<(String, Vec<RealtimePriceData>)>,
pub rt_price_1mon_vec: Vec<(String, Vec<RealtimePriceData>)>,
pub valid_symbol_vec: HashSet<String>,
pub rt_price_1m_vec: HashMap<String, Vec<RealtimePriceData>>,
pub rt_price_30m_vec: HashMap<String, Vec<RealtimePriceData>>,
pub rt_price_1d_vec: HashMap<String, Vec<RealtimePriceData>>,
pub rt_price_1w_vec: HashMap<String, Vec<RealtimePriceData>>,
pub rt_price_1mon_vec: HashMap<String, Vec<RealtimePriceData>>,
}
impl AllData {
pub fn new() -> AllData {
let a = AllData {
valid_symbol_vec: Vec::new(),
rt_price_1m_vec: Vec::new(),
rt_price_30m_vec: Vec::new(),
rt_price_1d_vec: Vec::new(),
rt_price_1w_vec: Vec::new(),
rt_price_1mon_vec: Vec::new(),
valid_symbol_vec: HashSet::new(),
rt_price_1m_vec: HashMap::new(),
rt_price_30m_vec: HashMap::new(),
rt_price_1d_vec: HashMap::new(),
rt_price_1w_vec: HashMap::new(),
rt_price_1mon_vec: HashMap::new(),
};
a
}
@ -68,18 +69,16 @@ pub struct TimeData {
}
#[derive(Clone)]
pub struct FilteredData {
pub symbol: String,
pub struct FilteredDataValue {
pub closetime: i64,
pub stoploss: Decimal,
pub target_price: Decimal,
pub current_price: Decimal,
}
impl FilteredData {
fn new() -> FilteredData {
let a = FilteredData {
symbol: String::new(),
impl FilteredDataValue {
fn new() -> FilteredDataValue {
let a = FilteredDataValue {
closetime: 0,
stoploss: Decimal::new(0, 8),
target_price: Decimal::new(0, 8),
@ -89,22 +88,22 @@ impl FilteredData {
}
}
pub async fn duplicate_filter(registerer: i32, original_filtered_data: &Vec<FilteredData>)
-> Result<Vec<FilteredData>, Box<dyn std::error::Error + Send + Sync>>
pub async fn duplicate_filter(registerer: i32, original_filtered_data: &HashMap<String, FilteredDataValue>)
-> Result<HashMap<String, FilteredDataValue>, Box<dyn std::error::Error + Send + Sync>>
{
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");
let inspect_table_name_4 = String::from("suggested_coin_list");
let mut filtered_data: Vec<FilteredData> = Vec::new();
let mut filtered_data_arc: Arc<Mutex<Vec<FilteredData>>> =
let mut filtered_data: HashMap<String, FilteredDataValue> = HashMap::new();
let mut filtered_data_arc: Arc<Mutex<HashMap<String, FilteredDataValue>>> =
Arc::new(Mutex::new(filtered_data));
let mut task_vec = Vec::new();
for element in original_filtered_data {
for (symbol, filtered_data) in original_filtered_data {
let mut exists_condition_build = String::from("symbol=\'");
exists_condition_build.push_str(element.symbol.as_str());
exists_condition_build.push_str(symbol.as_str());
exists_condition_build.push_str("\' AND registerer=");
exists_condition_build.push_str(registerer.to_string().as_str());
// exists_condition_build.push_str("\' AND close_time=");
@ -115,7 +114,8 @@ pub async fn duplicate_filter(registerer: i32, original_filtered_data: &Vec<Filt
let inspect_table_name_2_c = inspect_table_name_2.clone();
let inspect_table_name_3_c = inspect_table_name_3.clone();
let inspect_table_name_4_c = inspect_table_name_4.clone();
let element_c = element.clone();
let symbol_c = symbol.clone();
let filtered_data_c = filtered_data.clone();
let filtered_data_arc_c = Arc::clone(&filtered_data_arc);
task_vec.push(tokio::spawn(async move {
let inspect_result_1 =
@ -133,7 +133,7 @@ pub async fn duplicate_filter(registerer: i32, original_filtered_data: &Vec<Filt
&& inspect_result_4 == false
{
let mut filtered_data_lock = filtered_data_arc_c.lock().await;
filtered_data_lock.push(element_c);
filtered_data_lock.insert(symbol_c, filtered_data_c);
}
}));
}
@ -141,3 +141,30 @@ pub async fn duplicate_filter(registerer: i32, original_filtered_data: &Vec<Filt
let filtered_data_c = filtered_data_arc.lock().await.clone();
Ok(filtered_data_c)
}
pub async fn remove_keys(filtered_data: &mut HashMap<String, FilteredDataValue>, keys_to_remove: HashSet<String>) {
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();
}
}
// useful functions for strategists
pub async fn get_current_price(
symbol: &String,
rt_price_map: &HashMap<String, Vec<RealtimePriceData>>,
) -> Option<f64> {
if let Some(rt_vec) = rt_price_map.get(symbol) {
if rt_vec.last().is_some() {
return Some(rt_vec.last().unwrap().close_price);
}
}
None
}

View File

@ -3,9 +3,9 @@ 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, FilteredData, Mutex,
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, server_epoch, MacdData, ema_macd,
BollingerBandData, ToPrimitive, duplicate_filter
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
};
// BB lowerband + SuperTrend + StochRSI
@ -19,115 +19,19 @@ pub async fn list_up_for_buy(
// println!("BTCUSDT: {:?}", alldata.rt_price_30m_vec[a.unwrap()].1.last().unwrap());
// 1st filtering: filtering valid trade pair
let mut filtered_data_1st: Vec<FilteredData> = Vec::new();
let mut filtered_data: HashMap<String, FilteredDataValue> = HashMap::new();
for symbol in &alldata.valid_symbol_vec {
let mut filtered_data = FilteredData::new();
filtered_data.symbol = symbol.clone();
filtered_data_1st.push(filtered_data);
}
// 2nd filtering: supertrend(ATR period 20, multiplier: 2, 30m close price)
let mut filtered_data_2nd: Vec<FilteredData> = Vec::new();
let mut filtered_data_2nd_arc: Arc<Mutex<Vec<FilteredData>>> =
Arc::new(Mutex::new(filtered_data_2nd));
let mut task_vec = Vec::new();
for element in filtered_data_1st {
let mut rt_30m_vec: Vec<RealtimePriceData> = Vec::new();
let mut supertrend_vec: Vec<SupertrendData> = Vec::new();
let rt_price_30m_vec_c = alldata.rt_price_30m_vec.clone();
let filtered_data_2nd_arc_c = Arc::clone(&filtered_data_2nd_arc);
task_vec.push(tokio::spawn(async move {
let rt_30m_option = rt_price_30m_vec_c
.iter()
.position(|x| *x.0 == element.symbol);
let supertrend_option_30m =
supertrend(&element.symbol, &rt_price_30m_vec_c, 20, 2.0, true).await;
if rt_30m_option.is_some() && supertrend_option_30m.is_some() {
rt_30m_vec = rt_price_30m_vec_c[rt_30m_option.unwrap()].1.clone();
supertrend_vec = supertrend_option_30m.unwrap();
let server_epoch = server_epoch().await;
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,
);
if supertrend_search_result.is_ok() {
let current_price: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(rt_30m_vec.last().unwrap().close_price).unwrap();
let mut filtered_data_2nd_lock = filtered_data_2nd_arc_c.lock().await;
let mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
filtered_data.closetime = rt_30m_vec.last().unwrap().close_time;
filtered_data.current_price = current_price;
if supertrend_vec[supertrend_search_result.unwrap()].area.contains("DOWN")
&& supertrend_vec[supertrend_search_result.unwrap()].band_value > element.current_price.to_f64().unwrap()
{
let band_value: Decimal = 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(band_value, filtered_data.current_price), dec!(2)));
filtered_data.stoploss = stop_loss;
filtered_data_2nd_lock.push(filtered_data);
} else if supertrend_vec[supertrend_search_result.unwrap()].area.contains("UP")
&& supertrend_vec[supertrend_search_result.unwrap()].band_value < element.current_price.to_f64().unwrap()
{
let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec[supertrend_search_result.unwrap()].band_value).unwrap();
filtered_data.stoploss = band_value;
filtered_data_2nd_lock.push(filtered_data);
}
}
}
}
}));
}
try_join_all(task_vec).await?;
// 3rd filtering: the latest 5 30m candle close prices > EMA 200
let filtered_data_2nd = filtered_data_2nd_arc.lock().await.clone();
let mut filtered_data_3rd: Vec<FilteredData> = Vec::new();
let ema_vec = ema(200, &alldata.rt_price_30m_vec, &filtered_data_2nd).await?;
for element in filtered_data_2nd {
let ema_search_result = ema_vec.iter().position(|x| x.0 == element.symbol);
let candle_search_result = alldata.rt_price_30m_vec.iter().position(|x| x.0 == element.symbol);
if ema_search_result.is_some() && candle_search_result.is_some() {
let search_result = ema_vec[ema_search_result.unwrap()].1.binary_search_by_key(
&alldata.rt_price_30m_vec[candle_search_result.unwrap()].1.last().unwrap().close_time,
|EmaData {
ema_value,
close_time,
}| *close_time);
if search_result.is_ok_and(|x| ema_vec[ema_search_result.unwrap()].1[search_result.unwrap()].ema_value < alldata.rt_price_30m_vec[candle_search_result.unwrap()].1[alldata.rt_price_30m_vec[candle_search_result.unwrap()].1.len()-1].close_price) &&
search_result.is_ok_and(|x| ema_vec[ema_search_result.unwrap()].1[search_result.unwrap()-1].ema_value < alldata.rt_price_30m_vec[candle_search_result.unwrap()].1[alldata.rt_price_30m_vec[candle_search_result.unwrap()].1.len()-2].close_price) &&
search_result.is_ok_and(|x| ema_vec[ema_search_result.unwrap()].1[search_result.unwrap()-2].ema_value < alldata.rt_price_30m_vec[candle_search_result.unwrap()].1[alldata.rt_price_30m_vec[candle_search_result.unwrap()].1.len()-3].close_price) &&
search_result.is_ok_and(|x| ema_vec[ema_search_result.unwrap()].1[search_result.unwrap()-3].ema_value < alldata.rt_price_30m_vec[candle_search_result.unwrap()].1[alldata.rt_price_30m_vec[candle_search_result.unwrap()].1.len()-4].close_price) &&
search_result.is_ok_and(|x| ema_vec[ema_search_result.unwrap()].1[search_result.unwrap()-4].ema_value < alldata.rt_price_30m_vec[candle_search_result.unwrap()].1[alldata.rt_price_30m_vec[candle_search_result.unwrap()].1.len()-5].close_price) {
let mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
filtered_data.closetime = element.closetime;
filtered_data.current_price = element.current_price;
filtered_data.stoploss = element.stoploss;
filtered_data.target_price = element.target_price;
filtered_data_3rd.push(filtered_data);
}
}
filtered_data.insert(symbol.clone(), FilteredDataValue::new());
}
// 4th filtering: 0.5% <= the average amplitude of the latest 10 30m candles <= 1.0%
let mut filtered_data_4th: Vec<FilteredData> = Vec::new();
for element in filtered_data_3rd {
let position_idx = alldata.rt_price_30m_vec.iter().position(|elem| elem.0 == element.symbol);
if position_idx.is_some() {
let vec_len = alldata.rt_price_30m_vec[position_idx.unwrap()].1.len();
let mut keys_to_remove: HashSet<String> = HashSet::new();
for (symbol, values) in &mut filtered_data {
if alldata.rt_price_30m_vec.contains_key(symbol) {
let rt_price_30m = alldata.rt_price_30m_vec.get(symbol).unwrap();
let vec_len = rt_price_30m.len();
if vec_len >= 11 {
let candles = alldata.rt_price_30m_vec[position_idx.unwrap()].1.get(vec_len-12..vec_len-1).unwrap();
let candles = rt_price_30m.get(vec_len-12..vec_len-1).unwrap();
let windows = candles.windows(2);
let mut average_amplitude = 0.0;
@ -137,41 +41,143 @@ pub async fn list_up_for_buy(
average_amplitude /= 10.0;
if 0.005 <= average_amplitude && average_amplitude <= 0.01 {
let mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
filtered_data.closetime = element.closetime;
filtered_data.current_price = element.current_price;
filtered_data.stoploss = element.stoploss;
filtered_data.target_price = element.target_price;
filtered_data_4th.push(filtered_data);
} else {
keys_to_remove.insert(symbol.clone());
}
} else {
keys_to_remove.insert(symbol.clone());
}
} else {
keys_to_remove.insert(symbol.clone());
}
}
// 5th filtering: 30m StochRSI (RSI_len: 10, StochRSI_len: 10, K: 3, D: 3) previous K < 5 && current K > previous K
let mut filtered_data_5th: Vec<FilteredData> = Vec::new();
let stoch_rsis = stoch_rsi(10, 10, 3, 3, &alldata.rt_price_30m_vec, &filtered_data_4th).await?;
for element in filtered_data_4th {
let position_idx = stoch_rsis.iter().position(|elem| elem.0 == element.symbol);
if position_idx.is_some() {
let stoch_rsi_vec = stoch_rsis[position_idx.unwrap()].1.clone();
let search_result = stoch_rsi_vec.iter().position(|x| x.close_time == element.closetime);
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<String> = 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?;
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());
}
} 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<String> = 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<String> = 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 mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
filtered_data.closetime = element.closetime;
filtered_data.current_price = element.current_price;
filtered_data.stoploss = element.stoploss;
filtered_data.target_price = element.target_price;
filtered_data_5th.push(filtered_data);
} else {
keys_to_remove.insert(symbol.clone());
}
} else {
keys_to_remove.insert(symbol.clone());
}
}
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<String> = 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();
values.closetime = rt_price_vec.last().unwrap().close_time;
} 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;
let final_filtered_data = duplicate_filter(1, &filtered_data_5th).await?;
// filtering: the 2 previous ADX(10, 10)s decrease
let mut keys_to_remove: HashSet<String> = HashSet::new();
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 {
} 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;
let final_filtered_data = duplicate_filter(1, &filtered_data).await?;
insert_pre_suggested_coins(1, false, &final_filtered_data, &alldata).await;
Ok(())
@ -179,8 +185,8 @@ pub async fn list_up_for_buy(
pub async fn list_up_for_sell(
all_data: &AllData,
exchange_info_vec: &Vec<ExchangeInfo>,
trade_fee_vec: &Vec<TradeFee>,
exchange_info_map: &HashMap<String, ExchangeInfo>,
trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let filled_buy_orders = select_filled_buy_orders(1).await?;
@ -190,47 +196,21 @@ pub async fn list_up_for_sell(
.build()
.unwrap();
let mut supertrend_vec: Vec<SupertrendData> = Vec::new();
let server_epoch = server_epoch().await;
let server_epoch = get_server_epoch().await;
let mut filtered_symbol_vec: Vec<FilteredData> = Vec::new();
let mut filtered_symbols: HashMap<String, FilteredDataValue> = HashMap::new();
for element in &filled_buy_orders {
let filltered_data = FilteredData{
symbol: element.symbol.clone(),
closetime: 0,
stoploss: dec!(0),
target_price: dec!(0),
current_price: dec!(0),
};
filtered_symbol_vec.push(filltered_data);
filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new());
}
let stoch_rsis = stoch_rsi(10, 10, 3, 3, &all_data.rt_price_30m_vec, &filtered_symbol_vec).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) {
let lot_step_size_option = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == element.symbol);
let quote_commission_precision_option = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == element.symbol);
let search_result = stoch_rsis.iter().position(|x| x.0 == element.symbol);
let opclo_30m_option = all_data
.rt_price_30m_vec
.iter()
.position(|x| *x.0 == element.symbol);
let supertrend_option_30m =
supertrend(&element.symbol, &all_data.rt_price_30m_vec, 20, 2.0, true).await;
if lot_step_size_option.is_some()
&& quote_commission_precision_option.is_some()
&& search_result.is_some()
&& opclo_30m_option.is_some()
&& supertrend_option_30m.is_some()
{
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
supertrend_vec = supertrend_option_30m.unwrap();
let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap();
if supertrend_vec.last().unwrap().area.contains("UP")
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![
@ -242,19 +222,17 @@ pub async fn list_up_for_sell(
.unwrap();
}
let lot_step_size = exchange_info_vec[lot_step_size_option.unwrap()].stepsize;
let quote_commission_precision = exchange_info_vec
[quote_commission_precision_option.unwrap()]
.quote_commission_precision;
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 stoch_rsi_k = stoch_rsis[search_result.unwrap()].1.last().unwrap().k;
let stoch_rsi_k_prev = stoch_rsis[search_result.unwrap()].1[stoch_rsis[search_result.unwrap()].1.len()-2].k;
let stoch_rsi_d = stoch_rsis[search_result.unwrap()].1.last().unwrap().d;
let stoch_rsi_d_prev = stoch_rsis[search_result.unwrap()].1[stoch_rsis[search_result.unwrap()].1.len()-2].d;
let stoch_rsi_k = stoch_rsi.last().unwrap().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;
if (element.is_long == 0 || element.is_long == 1)
&& !element.current_price.is_zero()
{
@ -264,49 +242,40 @@ pub async fn list_up_for_sell(
element.current_price,
base_qty_to_be_ordered,
&client,
&exchange_info_vec,
&trade_fee_vec,
&exchange_info_map,
&trade_fee_map,
)
.await;
} else if server_epoch - element.transact_time > (1_800_000) * 20 {
} else if server_epoch - element.transact_time > (1_800_000) * 15 {
limit_order_sell(
&element,
element.current_price,
base_qty_to_be_ordered,
&client,
&exchange_info_vec,
&trade_fee_vec,
&exchange_info_map,
&trade_fee_map,
)
.await;
} else if element.pure_profit_percent > 5.0 {
} else if element.pure_profit_percent > 3.0 {
limit_order_sell(
&element,
element.current_price,
base_qty_to_be_ordered,
&client,
&exchange_info_vec,
&trade_fee_vec,
&exchange_info_map,
&trade_fee_map,
)
.await;
} else if stoch_rsi_k >= 95.0 {
} 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,
base_qty_to_be_ordered,
&client,
&exchange_info_vec,
&trade_fee_vec,
)
.await;
} else if stoch_rsi_k < stoch_rsi_d &&
stoch_rsi_k_prev >= stoch_rsi_d_prev {
limit_order_sell(
&element,
element.current_price,
base_qty_to_be_ordered,
&client,
&exchange_info_vec,
&trade_fee_vec,
&exchange_info_map,
&trade_fee_map,
)
.await;
}
@ -336,6 +305,7 @@ pub async fn list_up_for_sell(
// }
}
}
}
}
}

View File

@ -3,9 +3,9 @@ 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, FilteredData, Mutex,
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, server_epoch, MacdData, ema_macd,
BollingerBandData, ToPrimitive, duplicate_filter
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
};
// BB 30m lowerband + BB 1m lowerband
@ -18,82 +18,83 @@ pub async fn list_up_for_buy(
// println!("BTCUSDT: {:?}", alldata.rt_price_30m_vec[a.unwrap()].1.last().unwrap());
// 1st filtering: filtering valid trade pair
let mut filtered_data_1st: Vec<FilteredData> = Vec::new();
let mut filtered_data: HashMap<String, FilteredDataValue> = HashMap::new();
for symbol in &alldata.valid_symbol_vec {
let mut filtered_data = FilteredData::new();
filtered_data.symbol = symbol.clone();
filtered_data_1st.push(filtered_data);
filtered_data.insert(symbol.clone(), FilteredDataValue::new());
}
// 2nd filtering: BollingerBand (len:10, multiplier 2.5) previous_30m_price (close or low price) < lower_band
let mut filtered_data_2nd: Vec<FilteredData> = Vec::new();
let mut filtered_data_2nd_arc: Arc<Mutex<Vec<FilteredData>>> =
Arc::new(Mutex::new(filtered_data_2nd));
let mut task_vec = Vec::new();
let bollingerbands_30m = bollingerband(10, 2.5, &alldata.rt_price_30m_vec, &filtered_data_1st).await?;
let bollingerbands_1m = bollingerband(30, 3.0, &alldata.rt_price_1m_vec, &filtered_data_1st).await?;
for element in filtered_data_1st {
let mut rt_30m_vec: Vec<RealtimePriceData> = Vec::new();
let mut bb_30m_vec: Vec<BollingerBandData> = Vec::new();
let mut bb_1m_vec: Vec<BollingerBandData> = Vec::new();
let rt_price_30m_vec_c: Vec<(String, Vec<RealtimePriceData>)> = alldata.rt_price_30m_vec.clone();
let bollingerbands_30m_c = bollingerbands_30m.clone();
let bollingerbands_1m_c = bollingerbands_1m.clone();
let filtered_data_2nd_arc_c = Arc::clone(&filtered_data_2nd_arc);
task_vec.push(tokio::spawn(async move {
let rt_30m_option = rt_price_30m_vec_c
.iter()
.position(|x| *x.0 == element.symbol);
let bb_option_30m = bollingerbands_30m_c.iter().position(|x| x.0 == element.symbol);
let bb_option_1m = bollingerbands_1m_c.iter().position(|x| x.0 == element.symbol);
if rt_30m_option.is_some() && bb_option_30m.is_some() && bb_option_1m.is_some() {
rt_30m_vec = rt_price_30m_vec_c[rt_30m_option.unwrap()].1.clone();
bb_30m_vec = bollingerbands_30m_c[bb_option_30m.unwrap()].1.clone();
bb_1m_vec = bollingerbands_1m_c[bb_option_1m.unwrap()].1.clone();
let server_epoch = server_epoch().await;
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,
);
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
{
let mut filtered_data_2nd_lock = filtered_data_2nd_arc_c.lock().await;
let mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
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_2nd_lock.push(filtered_data);
}
}
// 5th filtering: 0.5% <= the average amplitude of the latest 10 30m candles <= 1.0%
let mut keys_to_remove: HashSet<String> = HashSet::new();
for (symbol, filtered_data) in &mut filtered_data {
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 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 /= 10.0;
if 0.005 <= average_amplitude && average_amplitude <= 0.01 {
} else {
keys_to_remove.insert(symbol.clone());
}
} else {
keys_to_remove.insert(symbol.clone());
}
}));
} else {
keys_to_remove.insert(symbol.clone());
}
}
try_join_all(task_vec).await?;
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<String> = 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 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 {
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,
);
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
{
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();
} else {
keys_to_remove.insert(symbol.clone());
}
} 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: supertrend(ATR period 7, multiplier: 1.5, 30m close price), area should be DOWN
let filtered_data_2nd = filtered_data_2nd_arc.lock().await.clone();
let mut filtered_data_3rd: Vec<FilteredData> = Vec::new();
for element in filtered_data_2nd {
let rt_30m_option = alldata.rt_price_30m_vec
.iter()
.position(|x| *x.0 == element.symbol);
let supertrend_option_30m =
supertrend(&element.symbol, &alldata.rt_price_30m_vec, 7, 1.5, true).await;
if rt_30m_option.is_some() && supertrend_option_30m.is_some() {
let rt_30m_vec = alldata.rt_price_30m_vec[rt_30m_option.unwrap()].1.clone();
let supertrend_vec = supertrend_option_30m.unwrap();
let server_epoch = server_epoch().await;
let mut keys_to_remove: HashSet<String> = HashSet::new();
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 {
let supertrend_search_result = supertrend_vec.binary_search_by_key(
&rt_30m_vec.last().unwrap().close_time,
@ -105,88 +106,76 @@ pub async fn list_up_for_buy(
}| *close_time,
);
if supertrend_search_result.is_ok() {
if supertrend_vec[supertrend_search_result.unwrap()].area.contains("DOWN")
&& supertrend_vec[supertrend_search_result.unwrap()].band_value > element.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()
{
let mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
filtered_data.closetime = element.closetime;
filtered_data.current_price = element.current_price;
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;
filtered_data_3rd.push(filtered_data);
} else {
keys_to_remove.insert(symbol.clone());
}
} 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;
// 4th filtering: the latest 5 30m candle close prices > EMA 200
let mut filtered_data_4th: Vec<FilteredData> = Vec::new();
let ema_vec = ema(200, &alldata.rt_price_30m_vec, &filtered_data_3rd).await?;
for element in filtered_data_3rd {
let mut opclo_30m_vec = alldata.rt_price_30m_vec.clone();
let ema_search_result = ema_vec.iter().position(|x| x.0 == element.symbol);
let candle_search_result = alldata.rt_price_30m_vec.iter().position(|x| x.0 == element.symbol);
if ema_search_result.is_some() && candle_search_result.is_some() {
let search_result = ema_vec[ema_search_result.unwrap()].1.binary_search_by_key(
&alldata.rt_price_30m_vec[candle_search_result.unwrap()].1.last().unwrap().close_time,
let mut keys_to_remove: HashSet<String> = 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)) {
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[ema_search_result.unwrap()].1[search_result.unwrap()].ema_value < alldata.rt_price_30m_vec[candle_search_result.unwrap()].1[alldata.rt_price_30m_vec[candle_search_result.unwrap()].1.len()-1].close_price) &&
search_result.is_ok_and(|x| ema_vec[ema_search_result.unwrap()].1[search_result.unwrap()-1].ema_value < alldata.rt_price_30m_vec[candle_search_result.unwrap()].1[alldata.rt_price_30m_vec[candle_search_result.unwrap()].1.len()-2].close_price) &&
search_result.is_ok_and(|x| ema_vec[ema_search_result.unwrap()].1[search_result.unwrap()-2].ema_value < alldata.rt_price_30m_vec[candle_search_result.unwrap()].1[alldata.rt_price_30m_vec[candle_search_result.unwrap()].1.len()-3].close_price) &&
search_result.is_ok_and(|x| ema_vec[ema_search_result.unwrap()].1[search_result.unwrap()-3].ema_value < alldata.rt_price_30m_vec[candle_search_result.unwrap()].1[alldata.rt_price_30m_vec[candle_search_result.unwrap()].1.len()-4].close_price) &&
search_result.is_ok_and(|x| ema_vec[ema_search_result.unwrap()].1[search_result.unwrap()-4].ema_value < alldata.rt_price_30m_vec[candle_search_result.unwrap()].1[alldata.rt_price_30m_vec[candle_search_result.unwrap()].1.len()-5].close_price) {
let mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
filtered_data.closetime = element.closetime;
filtered_data.current_price = element.current_price;
filtered_data.stoploss = element.stoploss;
filtered_data.target_price = element.target_price;
filtered_data_4th.push(filtered_data);
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());
}
} else {
keys_to_remove.insert(symbol.clone());
}
}
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<String> = 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();
values.closetime = rt_price_vec.last().unwrap().close_time;
} 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;
// 5th filtering: 0.5% <= the average amplitude of the latest 10 30m candles <= 1.0%
let mut filtered_data_5th: Vec<FilteredData> = Vec::new();
for element in filtered_data_4th {
let position_idx = alldata.rt_price_30m_vec.iter().position(|elem| elem.0 == element.symbol);
if position_idx.is_some() {
let vec_len = alldata.rt_price_30m_vec[position_idx.unwrap()].1.len();
if vec_len >= 11 {
let candles = alldata.rt_price_30m_vec[position_idx.unwrap()].1.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 /= 10.0;
if 0.005 <= average_amplitude && average_amplitude <= 0.01 {
let mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
filtered_data.closetime = element.closetime;
filtered_data.current_price = element.current_price;
filtered_data.stoploss = element.stoploss;
filtered_data.target_price = element.target_price;
filtered_data_5th.push(filtered_data);
}
}
}
}
let final_filtered_data = duplicate_filter(2, &filtered_data_5th).await?;
let final_filtered_data = duplicate_filter(2, &filtered_data).await?;
insert_pre_suggested_coins(2, false, &final_filtered_data, &alldata).await;
Ok(())
@ -194,8 +183,8 @@ pub async fn list_up_for_buy(
pub async fn list_up_for_sell(
all_data: &AllData,
exchange_info_vec: &Vec<ExchangeInfo>,
trade_fee_vec: &Vec<TradeFee>,
exchange_info_map: &HashMap<String, ExchangeInfo>,
trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let filled_buy_orders = select_filled_buy_orders(2).await?;
@ -204,24 +193,12 @@ pub async fn list_up_for_sell(
.timeout(tokio::time::Duration::from_millis(5000))
.build()
.unwrap();
let mut supertrend_vec: Vec<SupertrendData> = Vec::new();
let server_epoch = server_epoch().await;
let server_epoch = get_server_epoch().await;
for element in filled_buy_orders {
if element.used_usdt >= dec!(10.0) {
let lot_step_size_option = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == element.symbol);
let quote_commission_precision_option = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == element.symbol);
if lot_step_size_option.is_some()
&& quote_commission_precision_option.is_some()
{
let lot_step_size = exchange_info_vec[lot_step_size_option.unwrap()].stepsize;
let quote_commission_precision = exchange_info_vec
[quote_commission_precision_option.unwrap()]
.quote_commission_precision;
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(),
@ -238,8 +215,8 @@ pub async fn list_up_for_sell(
element.current_price,
base_qty_to_be_ordered,
&client,
&exchange_info_vec,
&trade_fee_vec,
&exchange_info_map,
&trade_fee_map,
)
.await;
} else if element.current_price <= element.stoploss {
@ -248,8 +225,8 @@ pub async fn list_up_for_sell(
element.current_price,
base_qty_to_be_ordered,
&client,
&exchange_info_vec,
&trade_fee_vec,
&exchange_info_map,
&trade_fee_map,
)
.await;
} else if server_epoch - element.transact_time > (1_800_000) * 20 {
@ -258,8 +235,8 @@ pub async fn list_up_for_sell(
element.current_price,
base_qty_to_be_ordered,
&client,
&exchange_info_vec,
&trade_fee_vec,
&exchange_info_map,
&trade_fee_map,
)
.await;
}

View File

@ -3,188 +3,129 @@ 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, FilteredData, Mutex, SmaData,
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, server_epoch, MacdData, ema_macd,
BollingerBandData, ToPrimitive, duplicate_filter
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
};
// BUY: 30m SMA5 (opclo_price) < 30m EMA3 (opclo_price)
// SELL: 30m SMA5 (opclo_price) > 30m EMA3 (opclo_price)
pub async fn list_up_for_buy(
alldata: AllData,
alldata: &AllData,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// print rt_price for debugging
// let a = alldata.rt_price_30m_vec.iter().position(|a| a.0 == "BTCUSDT");
// println!("BTCUSDT: {:?}", alldata.rt_price_30m_vec[a.unwrap()].1.last().unwrap());
// 1st filtering: filtering valid trade pair
let mut filtered_data_1st: Vec<FilteredData> = Vec::new();
let mut filtered_data: HashMap<String, FilteredDataValue> = HashMap::new();
for symbol in &alldata.valid_symbol_vec {
let mut filtered_data = FilteredData::new();
filtered_data.symbol = symbol.clone();
filtered_data_1st.push(filtered_data);
filtered_data.insert(symbol.clone(), FilteredDataValue::new());
}
// 2nd filtering: SMA5 (opclo_price) < EMA3 (opclo_price) && 5 previous MA > current MA
let mut filtered_data_2nd: Vec<FilteredData> = Vec::new();
let mut filtered_data_2nd_arc: Arc<Mutex<Vec<FilteredData>>> =
Arc::new(Mutex::new(filtered_data_2nd));
let mut task_vec = Vec::new();
// 3rd filtering: supertrend(ATR period 20, multiplier: 4.0, 30m close price), area should be UP
let mut keys_to_remove: HashSet<String> = HashSet::new();
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 {
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,
);
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()
{
} else {
keys_to_remove.insert(symbol.clone());
}
} 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;
let sma_close = sma(5, &alldata.rt_price_30m_vec, &filtered_data_1st).await?;
let ema_close = ema(3, &alldata.rt_price_30m_vec, &filtered_data_1st).await?;
for element in filtered_data_1st {
let mut rt_30m_vec: Vec<RealtimePriceData> = Vec::new();
let mut sma_vec: Vec<SmaData> = Vec::new();
let mut ema_vec: Vec<EmaData> = Vec::new();
let rt_price_30m_vec_c: Vec<(String, Vec<RealtimePriceData>)> = alldata.rt_price_30m_vec.clone();
let sma_opclos_c = sma_close.clone();
let ema_opclos_c = ema_close.clone();
let filtered_data_2nd_arc_c = Arc::clone(&filtered_data_2nd_arc);
task_vec.push(tokio::spawn(async move {
let rt_30m_option = rt_price_30m_vec_c
.iter()
.position(|x: &(String, Vec<RealtimePriceData>)| *x.0 == element.symbol);
let sma_option_30m = sma_opclos_c.iter().position(|x| x.0 == element.symbol);
let ema_option_30m = ema_opclos_c.iter().position(|x| x.0 == element.symbol);
if rt_30m_option.is_some() && sma_option_30m.is_some() && ema_option_30m.is_some() {
rt_30m_vec = rt_price_30m_vec_c[rt_30m_option.unwrap()].1.clone();
sma_vec = sma_opclos_c[sma_option_30m.unwrap()].1.clone();
ema_vec = ema_opclos_c[ema_option_30m.unwrap()].1.clone();
let server_epoch = server_epoch().await;
if rt_30m_vec.len() >= 3 && sma_vec.len() >= 3 && ema_vec.len() >= 3 && rt_30m_vec.last().unwrap().close_time > server_epoch {
let sma_search_result = sma_vec.binary_search_by_key(
&rt_30m_vec.last().unwrap().close_time,
|SmaData {
sma_value,
close_time,
}| *close_time,
);
let ema_search_result = ema_vec.binary_search_by_key(
&rt_30m_vec.last().unwrap().close_time,
|EmaData {
ema_value,
close_time,
}| *close_time,
);
if sma_search_result.is_ok() && ema_search_result.is_ok() {
if sma_vec[sma_search_result.unwrap()].sma_value < ema_vec[ema_search_result.unwrap()].ema_value &&
sma_vec[sma_search_result.unwrap()-1].sma_value > ema_vec[ema_search_result.unwrap()-1].ema_value &&
sma_vec[sma_search_result.unwrap()-4].sma_value > sma_vec[sma_search_result.unwrap()].sma_value {
let mut filtered_data_2nd_lock = filtered_data_2nd_arc_c.lock().await;
let mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
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_2nd_lock.push(filtered_data);
}
}
}
// 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<String> = 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) {
} else {
keys_to_remove.insert(symbol.clone());
}
}));
} else {
keys_to_remove.insert(symbol.clone());
}
}
try_join_all(task_vec).await?;
remove_keys(&mut filtered_data, keys_to_remove).await;
// 3rd filtering: the latest 5 30m candle close prices > EMA 200
let mut keys_to_remove: HashSet<String> = 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) {
} else {
keys_to_remove.insert(symbol.clone());
}
} else {
keys_to_remove.insert(symbol.clone());
}
}
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<String> = 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();
values.closetime = rt_price_vec.last().unwrap().close_time;
} 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: 0.5% <= the average amplitude of the latest 10 30m candles <= 1.0%
let filtered_data_2nd_c = filtered_data_2nd_arc.lock().await.clone();
let mut filtered_data_3rd: Vec<FilteredData> = Vec::new();
let mut filtered_data_3rd_arc: Arc<Mutex<Vec<FilteredData>>> =
Arc::new(Mutex::new(filtered_data_3rd));
let mut task_vec = Vec::new();
for element in filtered_data_2nd_c {
let mut supertrend_vec: Vec<SupertrendData> = Vec::new();
let rt_price_30m_vec_c = alldata.rt_price_30m_vec.clone();
let filtered_data_3rd_arc_c = Arc::clone(&filtered_data_3rd_arc);
task_vec.push(tokio::spawn(async move {
let position_idx = rt_price_30m_vec_c.iter().position(|elem| elem.0 == element.symbol);
if position_idx.is_some() {
let vec_len = rt_price_30m_vec_c[position_idx.unwrap()].1.len();
if vec_len >= 11 {
let candles = rt_price_30m_vec_c[position_idx.unwrap()].1.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 /= 10.0;
if 0.005 <= average_amplitude && average_amplitude <= 0.01 {
let mut filtered_data_3rd_lock = filtered_data_3rd_arc_c.lock().await;
let mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
filtered_data.closetime = element.closetime;
filtered_data.current_price = element.current_price;
filtered_data.stoploss = element.stoploss;
filtered_data.target_price = element.target_price;
filtered_data_3rd_lock.push(filtered_data);
}
}
}
}));
}
try_join_all(task_vec).await?;
// 4th filtering: BollingerBand (len:20, multiplier 2) 5 previous_30m_price (close or low price) < lower_band
let filtered_data_3rd = filtered_data_3rd_arc.lock().await.clone();
let mut filtered_data_4th: Vec<FilteredData> = Vec::new();
let mut filtered_data_4th_arc: Arc<Mutex<Vec<FilteredData>>> =
Arc::new(Mutex::new(filtered_data_4th));
let mut task_vec = Vec::new();
let bollingerbands = bollingerband(20, 2.0, &alldata.rt_price_30m_vec, &filtered_data_3rd).await?;
for element in filtered_data_3rd {
let mut rt_30m_vec: Vec<RealtimePriceData> = Vec::new();
let mut bb_vec: Vec<BollingerBandData> = Vec::new();
let rt_price_30m_vec_c: Vec<(String, Vec<RealtimePriceData>)> = alldata.rt_price_30m_vec.clone();
let bollingerbands_c = bollingerbands.clone();
let filtered_data_4th_arc_c = Arc::clone(&filtered_data_4th_arc);
task_vec.push(tokio::spawn(async move {
let rt_30m_option = rt_price_30m_vec_c
.iter()
.position(|x| *x.0 == element.symbol);
let bb_option_30m = bollingerbands_c.iter().position(|x| x.0 == element.symbol);
if rt_30m_option.is_some() && bb_option_30m.is_some() {
rt_30m_vec = rt_price_30m_vec_c[rt_30m_option.unwrap()].1.clone();
bb_vec = bollingerbands_c[bb_option_30m.unwrap()].1.clone();
let server_epoch = server_epoch().await;
if rt_30m_vec.len() >= 6 && bb_vec.len() >= 6 && 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,
);
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 ||
bb_vec[bb_search_result.unwrap()-2].lowerband > rt_30m_vec[rt_30m_vec.len()-3].low_price ||
bb_vec[bb_search_result.unwrap()-3].lowerband > rt_30m_vec[rt_30m_vec.len()-4].low_price ||
bb_vec[bb_search_result.unwrap()-4].lowerband > rt_30m_vec[rt_30m_vec.len()-5].low_price ||
bb_vec[bb_search_result.unwrap()-5].lowerband > rt_30m_vec[rt_30m_vec.len()-6].low_price
{
let mut filtered_data_4th_lock = filtered_data_4th_arc_c.lock().await;
let mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
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_4th_lock.push(filtered_data);
}
}
}
}
}));
}
try_join_all(task_vec).await?;
let final_filtered_data = duplicate_filter(3, &filtered_data_4th_arc.lock().await.clone()).await?;
let final_filtered_data = duplicate_filter(3, &filtered_data).await?;
insert_pre_suggested_coins(3, false, &final_filtered_data, &alldata).await;
Ok(())
@ -192,8 +133,8 @@ pub async fn list_up_for_buy(
pub async fn list_up_for_sell(
all_data: &AllData,
exchange_info_vec: &Vec<ExchangeInfo>,
trade_fee_vec: &Vec<TradeFee>,
exchange_info_map: &HashMap<String, ExchangeInfo>,
trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let filled_buy_orders = select_filled_buy_orders(3).await?;
@ -202,62 +143,110 @@ pub async fn list_up_for_sell(
.timeout(tokio::time::Duration::from_millis(5000))
.build()
.unwrap();
let mut supertrend_vec: Vec<SupertrendData> = Vec::new();
let server_epoch = server_epoch().await;
let mut filtered_symbol_vec: Vec<FilteredData> = Vec::new();
let mut filtered_symbols: HashMap<String, FilteredDataValue> = HashMap::new();
for element in &filled_buy_orders {
let filltered_data = FilteredData{
symbol: element.symbol.clone(),
closetime: 0,
stoploss: dec!(0),
target_price: dec!(0),
current_price: dec!(0),
};
filtered_symbol_vec.push(filltered_data);
filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new());
}
let sma_close = sma(5, &all_data.rt_price_30m_vec, &filtered_symbol_vec).await?;
let ema_close = ema(3, &all_data.rt_price_30m_vec, &filtered_symbol_vec).await?;
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?;
for element in filled_buy_orders {
let sma_result = sma_close.iter().position(|x| x.0 == element.symbol);
let ema_result = ema_close.iter().position(|x| x.0 == element.symbol);
if sma_result.is_some() && ema_result.is_some() && element.used_usdt >= dec!(10.0) {
let lot_step_size_option = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == element.symbol);
let quote_commission_precision_option = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == element.symbol);
if lot_step_size_option.is_some()
&& quote_commission_precision_option.is_some()
{
let lot_step_size = exchange_info_vec[lot_step_size_option.unwrap()].stepsize;
let quote_commission_precision = exchange_info_vec
[quote_commission_precision_option.unwrap()]
.quote_commission_precision;
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)) {
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 stoch_rsi_k = stoch_rsi.last().unwrap().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;
if (element.is_long == 0 || element.is_long == 1)
&& !element.current_price.is_zero()
{
if sma_close[sma_result.unwrap()].1.last().unwrap().sma_value > ema_close[ema_result.unwrap()].1.last().unwrap().ema_value &&
sma_close[sma_result.unwrap()].1[sma_close.len()-2].sma_value < ema_close[ema_result.unwrap()].1[ema_close.len()-2].ema_value
if element.current_price >= element.target_price
{
limit_order_sell(
&element,
element.current_price,
base_qty_to_be_ordered,
&client,
&exchange_info_vec,
&trade_fee_vec,
&exchange_info_map,
&trade_fee_map,
)
.await;
} else if element.current_price <= element.stoploss {
limit_order_sell(
&element,
element.current_price,
base_qty_to_be_ordered,
&client,
&exchange_info_map,
&trade_fee_map,
)
.await;
} else if server_epoch - element.transact_time > (1_800_000) * 35 {
limit_order_sell(
&element,
element.current_price,
base_qty_to_be_ordered,
&client,
&exchange_info_map,
&trade_fee_map,
)
.await;
} else if stoch_rsi_k >= 90.0 {
limit_order_sell(
&element,
element.current_price,
base_qty_to_be_ordered,
&client,
&exchange_info_map,
&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
{
limit_order_sell(
&element,
element.current_price,
base_qty_to_be_ordered,
&client,
&exchange_info_map,
&trade_fee_map,
)
.await;
}
// TODO: sell_count가 1일 때 적용하기
// else if (supertrend_vec
// .last()
// .unwrap()
// .signal
// .as_ref()
// .is_some_and(|x| x.contains("SELL"))
// || supertrend_vec.last().unwrap().area.contains("DOWN"))
// && (supertrend_vec.last().unwrap().close_time > element.close_time)
// {
// println!(
// "SELL signal selling {} {:.2}",
// element.symbol, element.pure_profit_percent
// );
// limit_order_sell(
// &element,
// element.current_price,
// base_qty_to_be_ordered,
// &client,
// &exchange_info_vec,
// &trade_fee_vec,
// )
// .await;
// }
}
}
}

View File

@ -3,9 +3,9 @@ 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, FilteredData, Mutex,
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, server_epoch, MacdData, ema_macd,
BollingerBandData, ToPrimitive, duplicate_filter
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
};
// BB lowerband + SuperTrend + StochRSI
@ -19,81 +19,84 @@ pub async fn list_up_for_buy(
// println!("BTCUSDT: {:?}", alldata.rt_price_30m_vec[a.unwrap()].1.last().unwrap());
// 1st filtering: filtering valid trade pair
let mut filtered_data_1st: Vec<FilteredData> = Vec::new();
let mut filtered_data: HashMap<String, FilteredDataValue> = HashMap::new();
for symbol in &alldata.valid_symbol_vec {
let mut filtered_data = FilteredData::new();
filtered_data.symbol = symbol.clone();
filtered_data_1st.push(filtered_data);
filtered_data.insert(symbol.clone(), FilteredDataValue::new());
}
// 2nd filtering: BollingerBand (len:30, multiplier 3) previous_30m_price (close or low price) < lower_band
let mut filtered_data_2nd: Vec<FilteredData> = Vec::new();
let mut filtered_data_2nd_arc: Arc<Mutex<Vec<FilteredData>>> =
Arc::new(Mutex::new(filtered_data_2nd));
let mut task_vec = Vec::new();
let bollingerbands = bollingerband(30, 3.0, &alldata.rt_price_30m_vec, &filtered_data_1st).await?;
for element in filtered_data_1st {
let mut rt_30m_vec: Vec<RealtimePriceData> = Vec::new();
let mut bb_vec: Vec<BollingerBandData> = Vec::new();
let rt_price_30m_vec_c: Vec<(String, Vec<RealtimePriceData>)> = alldata.rt_price_30m_vec.clone();
let bollingerbands_c = bollingerbands.clone();
let filtered_data_2nd_arc_c = Arc::clone(&filtered_data_2nd_arc);
task_vec.push(tokio::spawn(async move {
let rt_30m_option = rt_price_30m_vec_c
.iter()
.position(|x| *x.0 == element.symbol);
let bb_option_30m = bollingerbands_c.iter().position(|x| x.0 == element.symbol);
// 5th filtering: 0.5% <= the average amplitude of the latest 10 30m candles <= 1.0%
let mut keys_to_remove: HashSet<String> = HashSet::new();
for (symbol, filtered_data) in &mut filtered_data {
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 windows = candles.windows(2);
let mut average_amplitude = 0.0;
if rt_30m_option.is_some() && bb_option_30m.is_some() {
rt_30m_vec = rt_price_30m_vec_c[rt_30m_option.unwrap()].1.clone();
bb_vec = bollingerbands_c[bb_option_30m.unwrap()].1.clone();
let server_epoch = server_epoch().await;
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,
);
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.contains("DOWN") &&
rt_30m_vec[rt_30m_vec.len()-2].high_price < bb_vec[bb_search_result.unwrap()-1].sma
{
let mut filtered_data_2nd_lock = filtered_data_2nd_arc_c.lock().await;
let mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
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_2nd_lock.push(filtered_data);
}
}
for window in windows {
average_amplitude += (window.last().unwrap().high_price - window.last().unwrap().low_price) / window.first().unwrap().close_price;
}
average_amplitude /= 10.0;
if 0.005 <= average_amplitude && average_amplitude <= 0.01 {
} else {
keys_to_remove.insert(symbol.clone());
}
} else {
keys_to_remove.insert(symbol.clone());
}
}));
} else {
keys_to_remove.insert(symbol.clone());
}
}
try_join_all(task_vec).await?;
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<String> = HashSet::new();
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 {
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,
);
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
{
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();
} else {
keys_to_remove.insert(symbol.clone());
}
} 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: supertrend(ATR period 10, multiplier: 2, 30m close price), area should be DOWN
let filtered_data_2nd = filtered_data_2nd_arc.lock().await.clone();
let mut filtered_data_3rd: Vec<FilteredData> = Vec::new();
for element in filtered_data_2nd {
let mut rt_30m_vec: Vec<RealtimePriceData> = Vec::new();
let mut supertrend_vec: Vec<SupertrendData> = Vec::new();
let rt_30m_option = alldata.rt_price_30m_vec
.iter()
.position(|x| *x.0 == element.symbol);
let supertrend_option_30m =
supertrend(&element.symbol, &alldata.rt_price_30m_vec, 10, 2.0, true).await;
if rt_30m_option.is_some() && supertrend_option_30m.is_some() {
rt_30m_vec = alldata.rt_price_30m_vec[rt_30m_option.unwrap()].1.clone();
supertrend_vec = supertrend_option_30m.unwrap();
let server_epoch = server_epoch().await;
let mut keys_to_remove: HashSet<String> = 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?;
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 {
let supertrend_search_result = supertrend_vec.binary_search_by_key(
&rt_30m_vec.last().unwrap().close_time,
@ -105,106 +108,87 @@ pub async fn list_up_for_buy(
}| *close_time,
);
if supertrend_search_result.is_ok_and(|x|
supertrend_vec[x].area.contains("DOWN") && supertrend_vec[x].band_value > element.current_price.to_f64().unwrap()) {
let mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
filtered_data.closetime = element.closetime;
filtered_data.current_price = element.current_price;
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;
filtered_data_3rd.push(filtered_data);
} 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;
// 4th filtering: the latest 5 30m candle close prices > EMA 200
let mut filtered_data_4th: Vec<FilteredData> = Vec::new();
let ema_vec = ema(200, &alldata.rt_price_30m_vec, &filtered_data_3rd).await?;
for element in filtered_data_3rd {
let ema_search_result = ema_vec.iter().position(|x| x.0 == element.symbol);
let candle_search_result = alldata.rt_price_30m_vec.iter().position(|x| x.0 == element.symbol);
if ema_search_result.is_some() && candle_search_result.is_some() {
let search_result = ema_vec[ema_search_result.unwrap()].1.binary_search_by_key(
&alldata.rt_price_30m_vec[candle_search_result.unwrap()].1.last().unwrap().close_time,
let mut keys_to_remove: HashSet<String> = 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)) {
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[ema_search_result.unwrap()].1[search_result.unwrap()].ema_value < alldata.rt_price_30m_vec[candle_search_result.unwrap()].1[alldata.rt_price_30m_vec[candle_search_result.unwrap()].1.len()-1].close_price) &&
search_result.is_ok_and(|x| ema_vec[ema_search_result.unwrap()].1[search_result.unwrap()-1].ema_value < alldata.rt_price_30m_vec[candle_search_result.unwrap()].1[alldata.rt_price_30m_vec[candle_search_result.unwrap()].1.len()-2].close_price) &&
search_result.is_ok_and(|x| ema_vec[ema_search_result.unwrap()].1[search_result.unwrap()-2].ema_value < alldata.rt_price_30m_vec[candle_search_result.unwrap()].1[alldata.rt_price_30m_vec[candle_search_result.unwrap()].1.len()-3].close_price) &&
search_result.is_ok_and(|x| ema_vec[ema_search_result.unwrap()].1[search_result.unwrap()-3].ema_value < alldata.rt_price_30m_vec[candle_search_result.unwrap()].1[alldata.rt_price_30m_vec[candle_search_result.unwrap()].1.len()-4].close_price) &&
search_result.is_ok_and(|x| ema_vec[ema_search_result.unwrap()].1[search_result.unwrap()-4].ema_value < alldata.rt_price_30m_vec[candle_search_result.unwrap()].1[alldata.rt_price_30m_vec[candle_search_result.unwrap()].1.len()-5].close_price) {
let mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
filtered_data.closetime = element.closetime;
filtered_data.current_price = element.current_price;
filtered_data.stoploss = element.stoploss;
filtered_data.target_price = element.target_price;
filtered_data_4th.push(filtered_data);
}
}
}
// 5th filtering: 0.5% <= the average amplitude of the latest 10 30m candles <= 1.0%
let mut filtered_data_5th: Vec<FilteredData> = Vec::new();
for element in filtered_data_4th {
let position_idx = alldata.rt_price_30m_vec.iter().position(|elem| elem.0 == element.symbol);
if position_idx.is_some() {
let vec_len = alldata.rt_price_30m_vec[position_idx.unwrap()].1.len();
if vec_len >= 11 {
let candles = alldata.rt_price_30m_vec[position_idx.unwrap()].1.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 /= 10.0;
if 0.005 <= average_amplitude && average_amplitude <= 0.01 {
let mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
filtered_data.closetime = element.closetime;
filtered_data.current_price = element.current_price;
filtered_data.stoploss = element.stoploss;
filtered_data.target_price = element.target_price;
filtered_data_5th.push(filtered_data);
}
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());
}
} else {
keys_to_remove.insert(symbol.clone());
}
}
remove_keys(&mut filtered_data, keys_to_remove).await;
// 6th filtering: 30m StochRSI (RSI_len: 10, StochRSI_len: 10, K: 3, D: 3) current K, D < 20
let mut filtered_data_6th: Vec<FilteredData> = Vec::new();
let stoch_rsis = stoch_rsi(10, 10, 3, 3, &alldata.rt_price_30m_vec, &filtered_data_5th).await?;
for element in filtered_data_5th {
let position_idx = stoch_rsis.iter().position(|elem| elem.0 == element.symbol);
if position_idx.is_some() {
let stoch_rsi_vec = stoch_rsis[position_idx.unwrap()].1.clone();
let search_result = stoch_rsi_vec.iter().position(|x| x.close_time == element.closetime);
let mut keys_to_remove: HashSet<String> = 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 {
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 mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
filtered_data.closetime = element.closetime;
filtered_data.current_price = element.current_price;
filtered_data.stoploss = element.stoploss;
filtered_data.target_price = element.target_price;
filtered_data_6th.push(filtered_data);
} else {
keys_to_remove.insert(symbol.clone());
}
} else {
keys_to_remove.insert(symbol.clone());
}
}
remove_keys(&mut filtered_data, keys_to_remove).await;
let final_filtered_data = duplicate_filter(4, &filtered_data_6th).await?;
// filtering: 1d MACD (3, 7, 30) current MACD-signal > prev MACD-signal
let mut keys_to_remove: HashSet<String> = 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();
values.closetime = rt_price_vec.last().unwrap().close_time;
} 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;
let final_filtered_data = duplicate_filter(4, &filtered_data).await?;
insert_pre_suggested_coins(4, false, &final_filtered_data, &alldata).await;
Ok(())
@ -212,8 +196,8 @@ pub async fn list_up_for_buy(
pub async fn list_up_for_sell(
all_data: &AllData,
exchange_info_vec: &Vec<ExchangeInfo>,
trade_fee_vec: &Vec<TradeFee>,
exchange_info_map: &HashMap<String, ExchangeInfo>,
trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let filled_buy_orders = select_filled_buy_orders(4).await?;
@ -223,23 +207,12 @@ pub async fn list_up_for_sell(
.build()
.unwrap();
let mut supertrend_vec: Vec<SupertrendData> = Vec::new();
let server_epoch = server_epoch().await;
let server_epoch = get_server_epoch().await;
for element in filled_buy_orders {
if element.used_usdt >= dec!(10.0) {
let lot_step_size_option = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == element.symbol);
let quote_commission_precision_option = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == element.symbol);
if lot_step_size_option.is_some()
&& quote_commission_precision_option.is_some()
{
let lot_step_size = exchange_info_vec[lot_step_size_option.unwrap()].stepsize;
let quote_commission_precision = exchange_info_vec
[quote_commission_precision_option.unwrap()]
.quote_commission_precision;
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(),
@ -256,8 +229,8 @@ pub async fn list_up_for_sell(
element.current_price,
base_qty_to_be_ordered,
&client,
&exchange_info_vec,
&trade_fee_vec,
&exchange_info_map,
&trade_fee_map,
)
.await;
} else if element.current_price <= element.stoploss {
@ -266,8 +239,8 @@ pub async fn list_up_for_sell(
element.current_price,
base_qty_to_be_ordered,
&client,
&exchange_info_vec,
&trade_fee_vec,
&exchange_info_map,
&trade_fee_map,
)
.await;
} else if server_epoch - element.transact_time > (1_800_000) * 20 {
@ -276,8 +249,8 @@ pub async fn list_up_for_sell(
element.current_price,
base_qty_to_be_ordered,
&client,
&exchange_info_vec,
&trade_fee_vec,
&exchange_info_map,
&trade_fee_map,
)
.await;
}

View File

@ -1,217 +1,163 @@
use super::{
dec, decimal_add, decimal_sub, ema, exists_record, insert_pre_suggested_coins,
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, FilteredData, Mutex,
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, server_epoch, MacdData, ema_macd,
duplicate_filter
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
};
// Triple SuperTrend strategy
// SuperTrend length: 20, multiplier: 1.5, BUY signal
// ADX(10, 10) < 25.0
// the latest 5 candle close prices > EMA 200
// BUY conditions
// (1) 1d MACD (3, 7, 30) cross
// (2) supertrend (30, 3): UP area
// 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,
alldata: &AllData,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// print rt_price for debugging
// let a = alldata.rt_price_30m_vec.iter().position(|a| a.0 == "BTCUSDT");
// println!("BTCUSDT: {:?}", alldata.rt_price_30m_vec[a.unwrap()].1.last().unwrap());
// 1st filtering: filtering valid trade pair
let mut filtered_data_1st: Vec<FilteredData> = Vec::new();
let mut filtered_data: HashMap<String, FilteredDataValue> = HashMap::new();
for symbol in &alldata.valid_symbol_vec {
let mut filtered_data = FilteredData::new();
filtered_data.symbol = symbol.clone();
filtered_data_1st.push(filtered_data);
filtered_data.insert(symbol.clone(), FilteredDataValue::new());
}
// 2nd filtering: supertrend(ATR period 20, multiplier: 1.5, 30m close price), signal should be BUY
let mut filtered_data_2nd: Vec<FilteredData> = Vec::new();
let mut filtered_data_2nd_arc: Arc<Mutex<Vec<FilteredData>>> =
Arc::new(Mutex::new(filtered_data_2nd));
let mut task_vec = Vec::new();
for element in filtered_data_1st {
let mut rt_30m_vec: Vec<RealtimePriceData> = Vec::new();
let mut supertrend_vec: Vec<SupertrendData> = Vec::new();
let rt_price_30m_vec_c = alldata.rt_price_30m_vec.clone();
let filtered_data_2nd_arc_c = Arc::clone(&filtered_data_2nd_arc);
task_vec.push(tokio::spawn(async move {
let rt_30m_option = rt_price_30m_vec_c
.iter()
.position(|x| *x.0 == element.symbol);
let supertrend_option_30m =
supertrend(&element.symbol, &rt_price_30m_vec_c, 20, 1.5, true).await;
if rt_30m_option.is_some() && supertrend_option_30m.is_some() {
rt_30m_vec = rt_price_30m_vec_c[rt_30m_option.unwrap()].1.clone();
supertrend_vec = supertrend_option_30m.unwrap();
let server_epoch = server_epoch().await;
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,
);
if supertrend_search_result.is_ok() {
let current_price: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(rt_30m_vec.last().unwrap().close_price).unwrap();
if supertrend_vec[supertrend_search_result.unwrap()].signal.as_ref().is_some_and(|x| x.contains("BUY"))
&& current_price
< rust_decimal::prelude::FromPrimitive::from_f64(
supertrend_vec[supertrend_search_result.unwrap()-1].band_value * 1.002,
)
.unwrap()
&& supertrend_vec[supertrend_search_result.unwrap()-1].band_value > supertrend_vec[supertrend_search_result.unwrap()].band_value
{
let mut filtered_data_2nd_lock = filtered_data_2nd_arc_c.lock().await;
let mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
filtered_data.closetime = supertrend_vec[supertrend_search_result.unwrap()].close_time;
filtered_data.current_price = current_price;
filtered_data.stoploss = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec[supertrend_search_result.unwrap()].band_value).unwrap();
let target_price = decimal_add(
filtered_data.current_price,
decimal_sub(filtered_data.current_price, filtered_data.stoploss),
);
filtered_data.target_price = target_price;
filtered_data_2nd_lock.push(filtered_data);
}
}
// 2nd filtering: supertrend(ATR period 14, multiplier: 1.2, 1d close price)
let mut keys_to_remove: HashSet<String> = 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 {
if supertrend_vec.last().unwrap().area == SuperTrendArea::UP{
} else {
keys_to_remove.insert(symbol.clone());
}
} else {
keys_to_remove.insert(symbol.clone());
}
}));
}
try_join_all(task_vec).await?;
// 3rd filtering: ADX(10, 10) < 25.0
let filtered_data_2nd = filtered_data_2nd_arc.lock().await.clone();
let mut filtered_data_3rd: Vec<FilteredData> = Vec::new();
let mut filtered_data_3rd_arc: Arc<Mutex<Vec<FilteredData>>> =
Arc::new(Mutex::new(filtered_data_3rd));
let mut task_vec = Vec::new();
let valid_symbol_vec_c = alldata.valid_symbol_vec.clone();
let adx_vec = adx(10, 10, &alldata.rt_price_30m_vec, &filtered_data_2nd).await?;
for element in filtered_data_2nd {
let mut adx_vec_c = adx_vec.clone();
let symbol = element.symbol.clone();
let close_time = element.closetime;
let idx_result = adx_vec.iter().position(|elem| elem.0 == symbol);
let filtered_data_3rd_arc_c = Arc::clone(&filtered_data_3rd_arc);
if idx_result.is_some(){
task_vec.push(tokio::spawn(async move {
let closetime_idx_result = adx_vec_c[idx_result.unwrap()].1.iter().position(|elem| elem.close_time==close_time);
if closetime_idx_result.is_some() {
if adx_vec_c[idx_result.unwrap()].1[closetime_idx_result.unwrap()].adx < 25.0 {
let mut filtered_3rd_symbols_lock =
filtered_data_3rd_arc_c.lock().await;
let mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
filtered_data.closetime = element.closetime;
filtered_data.current_price = element.current_price;
filtered_data.stoploss = element.stoploss;
filtered_data.target_price = element.target_price;
filtered_3rd_symbols_lock.push(filtered_data);
}
}
}));
} else {
keys_to_remove.insert(symbol.clone());
}
}
try_join_all(task_vec).await?;
remove_keys(&mut filtered_data, keys_to_remove).await;
// 4th filtering: the latest 5 30m candle close prices > EMA 200
let filtered_data_3rd = filtered_data_3rd_arc.lock().await.clone();
let mut filtered_data_4th: Vec<FilteredData> = Vec::new();
let mut filtered_data_4th_arc: Arc<Mutex<Vec<FilteredData>>> =
Arc::new(Mutex::new(filtered_data_4th));
let mut task_vec = Vec::new();
let ema_vec = ema(200, &alldata.rt_price_30m_vec, &filtered_data_3rd).await?;
for element in filtered_data_3rd {
let mut opclo_30m_vec = alldata.rt_price_30m_vec.clone();
let mut supertrend_vec: Vec<SupertrendData> = Vec::new();
let ema_vec_c = ema_vec.clone();
let filtered_data_4th_arc_c = Arc::clone(&filtered_data_4th_arc);
task_vec.push(tokio::spawn(async move {
let ema_search_result = ema_vec_c.iter().position(|x| x.0 == element.symbol);
let candle_search_result = opclo_30m_vec.iter().position(|x| x.0 == element.symbol);
if ema_search_result.is_some() && candle_search_result.is_some() {
let search_result = ema_vec_c[ema_search_result.unwrap()].1.binary_search_by_key(
&opclo_30m_vec[candle_search_result.unwrap()].1.last().unwrap().close_time,
|EmaData {
ema_value,
close_time,
}| *close_time);
if search_result.is_ok_and(|x| ema_vec_c[ema_search_result.unwrap()].1[search_result.unwrap()].ema_value < opclo_30m_vec[candle_search_result.unwrap()].1[opclo_30m_vec[candle_search_result.unwrap()].1.len()-1].close_price) &&
search_result.is_ok_and(|x| ema_vec_c[ema_search_result.unwrap()].1[search_result.unwrap()-1].ema_value < opclo_30m_vec[candle_search_result.unwrap()].1[opclo_30m_vec[candle_search_result.unwrap()].1.len()-2].close_price) &&
search_result.is_ok_and(|x| ema_vec_c[ema_search_result.unwrap()].1[search_result.unwrap()-2].ema_value < opclo_30m_vec[candle_search_result.unwrap()].1[opclo_30m_vec[candle_search_result.unwrap()].1.len()-3].close_price) &&
search_result.is_ok_and(|x| ema_vec_c[ema_search_result.unwrap()].1[search_result.unwrap()-3].ema_value < opclo_30m_vec[candle_search_result.unwrap()].1[opclo_30m_vec[candle_search_result.unwrap()].1.len()-4].close_price) &&
search_result.is_ok_and(|x| ema_vec_c[ema_search_result.unwrap()].1[search_result.unwrap()-4].ema_value < opclo_30m_vec[candle_search_result.unwrap()].1[opclo_30m_vec[candle_search_result.unwrap()].1.len()-5].close_price) {
let mut filtered_4th_symbols_lock =
filtered_data_4th_arc_c.lock().await;
let mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
filtered_data.closetime = element.closetime;
filtered_data.current_price = element.current_price;
filtered_data.stoploss = element.stoploss;
filtered_data.target_price = element.target_price;
filtered_4th_symbols_lock.push(filtered_data);
// 2nd filtering: 1d MACD (3, 5, 30) cross
let mut keys_to_remove: HashSet<String> = 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();
values.closetime = rt_price_vec.last().unwrap().close_time;
} else {
keys_to_remove.insert(symbol.clone());
}
} else {
keys_to_remove.insert(symbol.clone());
}
}));
} else {
keys_to_remove.insert(symbol.clone());
}
}
try_join_all(task_vec).await?;
// 5th filtering: 0.5% <= the average amplitude of the latest 10 30m candles <= 1.0%
let filtered_data_4th_c = filtered_data_4th_arc.lock().await.clone();
let mut filtered_data_5th: Vec<FilteredData> = Vec::new();
let mut filtered_data_5th_arc: Arc<Mutex<Vec<FilteredData>>> =
Arc::new(Mutex::new(filtered_data_5th));
let mut task_vec = Vec::new();
for element in filtered_data_4th_c {
let mut supertrend_vec: Vec<SupertrendData> = Vec::new();
let rt_price_30m_vec_c = alldata.rt_price_30m_vec.clone();
let filtered_data_5th_arc_c = Arc::clone(&filtered_data_5th_arc);
remove_keys(&mut filtered_data, keys_to_remove).await;
task_vec.push(tokio::spawn(async move {
let position_idx = rt_price_30m_vec_c.iter().position(|elem| elem.0 == element.symbol);
if position_idx.is_some() {
let vec_len = rt_price_30m_vec_c[position_idx.unwrap()].1.len();
if vec_len >= 11 {
let candles = rt_price_30m_vec_c[position_idx.unwrap()].1.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 /= 10.0;
if 0.005 <= average_amplitude && average_amplitude <= 0.01 {
let mut filtered_data_5th_lock = filtered_data_5th_arc_c.lock().await;
let mut filtered_data = FilteredData::new();
filtered_data.symbol = element.symbol.clone();
filtered_data.closetime = element.closetime;
filtered_data.current_price = element.current_price;
filtered_data.stoploss = element.stoploss;
filtered_data.target_price = element.target_price;
filtered_data_5th_lock.push(filtered_data);
}
// 2nd filtering: supertrend(ATR period 14, multiplier: 1.2, 1d close price)
let mut keys_to_remove: HashSet<String> = 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::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 = band_value;
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());
}
} else {
keys_to_remove.insert(symbol.clone());
}
}));
} else {
keys_to_remove.insert(symbol.clone());
}
}
try_join_all(task_vec).await?;
remove_keys(&mut filtered_data, keys_to_remove).await;
let final_filtered_data = duplicate_filter(5, &filtered_data_5th_arc.lock().await.clone()).await?;
// filtering: the 1 previous ADX(3, 5)s increase
let mut keys_to_remove: HashSet<String> = 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 {
} 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<String> = 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 {
let mut opclo_vec: Vec<f64> = 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.remove(max_idx.unwrap());
let mut mean = 0.0;
for element in &opclo_vec {
mean += element;
}
mean /= opclo_vec.len() as f64;
let current_price = rt_price_vec.last().unwrap().close_price;
let difference = (mean - rt_price_vec.last().unwrap().open_price).abs();
if current_price < rt_price_vec.last().unwrap().open_price + (3.0 * difference) {
} 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;
let final_filtered_data = duplicate_filter(5, &filtered_data).await?;
insert_pre_suggested_coins(5, false, &final_filtered_data, &alldata).await;
Ok(())
@ -219,8 +165,8 @@ pub async fn list_up_for_buy(
pub async fn list_up_for_sell(
all_data: &AllData,
exchange_info_vec: &Vec<ExchangeInfo>,
trade_fee_vec: &Vec<TradeFee>,
exchange_info_map: &HashMap<String, ExchangeInfo>,
trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let filled_buy_orders = select_filled_buy_orders(5).await?;
@ -230,32 +176,19 @@ pub async fn list_up_for_sell(
.build()
.unwrap();
let mut supertrend_vec: Vec<SupertrendData> = Vec::new();
let server_epoch = server_epoch().await;
let server_epoch = get_server_epoch().await;
let mut filtered_symbols: HashMap<String, FilteredDataValue> = HashMap::new();
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?;
for element in filled_buy_orders {
if element.used_usdt >= dec!(10.0) {
let lot_step_size_option = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == element.symbol);
let quote_commission_precision_option = exchange_info_vec
.iter()
.position(|exchange_info| exchange_info.symbol == element.symbol);
let opclo_30m_option = all_data
.rt_price_30m_vec
.iter()
.position(|x| *x.0 == element.symbol);
let supertrend_option_30m =
supertrend(&element.symbol, &all_data.rt_price_30m_vec, 20, 1.5, true).await;
if lot_step_size_option.is_some()
&& quote_commission_precision_option.is_some()
&& opclo_30m_option.is_some()
&& supertrend_option_30m.is_some()
{
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
supertrend_vec = supertrend_option_30m.unwrap();
let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap();
if supertrend_vec.last().unwrap().area.contains("UP")
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![
@ -267,52 +200,38 @@ pub async fn list_up_for_sell(
.unwrap();
}
let lot_step_size = exchange_info_vec[lot_step_size_option.unwrap()].stepsize;
let quote_commission_precision = exchange_info_vec
[quote_commission_precision_option.unwrap()]
.quote_commission_precision;
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,
);
if (element.is_long == 0 || element.is_long == 1)
&& !element.current_price.is_zero()
{
if element.current_price >= element.target_price
&& element.pure_profit_percent >= 0.1
{
if element.current_price <= element.stoploss {
limit_order_sell(
&element,
element.current_price,
base_qty_to_be_ordered,
&client,
&exchange_info_vec,
&trade_fee_vec,
&exchange_info_map,
&trade_fee_map,
)
.await;
} else if element.current_price <= element.stoploss {
} else if element.current_price >= element.target_price {
limit_order_sell(
&element,
element.current_price,
base_qty_to_be_ordered,
&client,
&exchange_info_vec,
&trade_fee_vec,
&exchange_info_map,
&trade_fee_map,
)
.await;
} else if server_epoch - element.transact_time > (1_800_000) * 10 && element.pure_profit_percent.is_sign_positive() {
limit_order_sell(
&element,
element.current_price,
base_qty_to_be_ordered,
&client,
&exchange_info_vec,
&trade_fee_vec,
)
.await;
}
}
// TODO: sell_count가 1일 때 적용하기
// else if (supertrend_vec
// .last()

View File

@ -1,15 +1,14 @@
use crate::coex::exchange_team::*;
use crate::coex::order_team::*;
use crate::coin_health_check_team::request_others::CoinPriceData;
use csv::{DeserializeRecordsIter, StringRecord};
use rust_decimal::prelude::ToPrimitive;
use rust_decimal::Decimal;
use serde::Deserialize;
use super::strategy_test;
// use super::strategy_test;
use super::{
exists_record, insert_one_record, try_join_all, try_select_record, AllData, ExchangeInfo,
FilteredData, FromRow, RealtimePriceData, TradeFee,
FilteredDataValue, FromRow, RealtimePriceData, TradeFee, HashMap
};
use crate::signal_association::signal_decision::*;
use tokio::time::{sleep, Duration, Instant};
@ -34,46 +33,31 @@ pub async fn execute_list_up_for_buy(
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
crate::strategy_team::strategy_001::list_up_for_buy(all_data).await;
crate::strategy_team::strategy_002::list_up_for_buy(all_data).await;
crate::strategy_team::strategy_003::list_up_for_buy(all_data).await;
crate::strategy_team::strategy_004::list_up_for_buy(all_data).await;
crate::strategy_team::strategy_005::list_up_for_buy(all_data).await;
Ok(())
}
pub async fn execute_list_up_for_sell(
all_data: &AllData,
exchange_info_vec: &Vec<ExchangeInfo>,
trade_fee_vec: &Vec<TradeFee>,
exchange_info_map: &HashMap<String, ExchangeInfo>,
trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
crate::strategy_team::strategy_001::list_up_for_sell(&all_data, &exchange_info_vec, &trade_fee_vec).await;
crate::strategy_team::strategy_002::list_up_for_sell(&all_data, &exchange_info_vec, &trade_fee_vec).await;
crate::strategy_team::strategy_004::list_up_for_sell(&all_data, &exchange_info_vec, &trade_fee_vec).await;
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;
Ok(())
}
// useful functions for strategists
pub async fn get_current_price(
symbol: &String,
rt_price_vec: &Vec<(String, Vec<RealtimePriceData>)>,
) -> Option<f64> {
let index_result = rt_price_vec.iter().position(|x| *x.0 == *symbol);
match index_result {
Some(T) => {
if rt_price_vec[T].1.last().is_some() {
Some(rt_price_vec[T].1.last().unwrap().close_price)
} else {
None
}
}
None => None,
}
}
pub async fn insert_pre_suggested_coins(
registerer: i32,
is_long: bool,
filtered_coins: &Vec<FilteredData>,
filtered_coins: &HashMap<String, FilteredDataValue>,
alldata: &AllData,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// Check the existance of record that is registered by this strategist
@ -150,12 +134,12 @@ pub async fn insert_pre_suggested_coins(
.await?;
// insert record without duplicate(registerer, closetime, symbol) into [pre_suggested_coin_list]
for filtered_coin in filtered_coins {
for (symbol, filtered_data) in filtered_coins {
let mut is_dupe = false; // initialize
for list_element in &suggested_coin_list {
if (filtered_coin.symbol == list_element.symbol)
&& (filtered_coin.closetime == list_element.close_time)
if (*symbol == list_element.symbol)
&& (filtered_data.closetime == list_element.close_time)
{
is_dupe = true;
break;
@ -163,8 +147,8 @@ pub async fn insert_pre_suggested_coins(
}
for list_element in &ordered_coin_list {
if (filtered_coin.symbol == list_element.symbol)
&& (filtered_coin.closetime == list_element.close_time)
if (*symbol == list_element.symbol)
&& (filtered_data.closetime == list_element.close_time)
{
is_dupe = true;
break;
@ -172,8 +156,8 @@ pub async fn insert_pre_suggested_coins(
}
for list_element in &pre_suggested_coin_list {
if (filtered_coin.symbol == list_element.symbol)
&& (filtered_coin.closetime == list_element.close_time)
if (*symbol == list_element.symbol)
&& (filtered_data.closetime == list_element.close_time)
{
is_dupe = true;
break;
@ -182,13 +166,13 @@ pub async fn insert_pre_suggested_coins(
if is_dupe == false {
let mut insert_values = vec![
filtered_coin.symbol.clone(), // symbol
filtered_coin.closetime.to_string(), // close_time
filtered_coin.current_price.to_string(), // suggested_price
filtered_coin.current_price.to_string(), // current_price
filtered_coin.stoploss.to_string(), // stoploss
filtered_coin.target_price.to_string(), // target_price
server_epoch().await.to_string(), // registered_server_epoch
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
0.0.to_string(), // profit_percent
0.0.to_string(), // minimum_profit_percent
0.0.to_string(), // maximum_profit_percent
@ -245,12 +229,12 @@ pub async fn insert_pre_suggested_coins(
.await?;
// insert record without duplicate(registerer, closetime, symbol) into [pre_suggested_coin_list]
for filtered_coin in filtered_coins {
for (symbol, filtered_data) in filtered_coins {
let mut is_dupe = false; // initialize
for list_element in &suggested_coin_list {
if (filtered_coin.symbol == list_element.symbol)
&& (filtered_coin.closetime == list_element.close_time)
if (*symbol == list_element.symbol)
&& (filtered_data.closetime == list_element.close_time)
{
is_dupe = true;
break;
@ -258,8 +242,8 @@ pub async fn insert_pre_suggested_coins(
}
for list_element in &ordered_coin_list {
if (filtered_coin.symbol == list_element.symbol)
&& (filtered_coin.closetime == list_element.close_time)
if (*symbol == list_element.symbol)
&& (filtered_data.closetime == list_element.close_time)
{
is_dupe = true;
break;
@ -268,13 +252,13 @@ pub async fn insert_pre_suggested_coins(
if is_dupe == false {
let mut insert_values = vec![
filtered_coin.symbol.clone(), // symbol
filtered_coin.closetime.to_string(), // close_time
filtered_coin.current_price.to_string(), // suggested_price
filtered_coin.current_price.to_string(), // current_price
filtered_coin.stoploss.to_string(), // stoploss
filtered_coin.target_price.to_string(), // target_price
server_epoch().await.to_string(), // registered_server_epoch
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
0.0.to_string(), // profit_percent
0.0.to_string(), // minimum_profit_percent
0.0.to_string(), // maximum_profit_percent
@ -321,12 +305,12 @@ pub async fn insert_pre_suggested_coins(
.await?;
// insert record without duplicate(registerer, closetime, symbol) into [pre_suggested_coin_list]
for filtered_coin in filtered_coins {
for (symbol, filtered_data) in filtered_coins {
let mut is_dupe = false; // initialize
for list_element in &suggested_coin_list {
if (filtered_coin.symbol == list_element.symbol)
&& (filtered_coin.closetime == list_element.close_time)
if (*symbol == list_element.symbol)
&& (filtered_data.closetime == list_element.close_time)
{
is_dupe = true;
break;
@ -334,8 +318,8 @@ pub async fn insert_pre_suggested_coins(
}
for list_element in &pre_suggested_coin_list {
if (filtered_coin.symbol == list_element.symbol)
&& (filtered_coin.closetime == list_element.close_time)
if (*symbol == list_element.symbol)
&& (filtered_data.closetime == list_element.close_time)
{
is_dupe = true;
break;
@ -344,13 +328,13 @@ pub async fn insert_pre_suggested_coins(
if is_dupe == false {
let mut insert_values = vec![
filtered_coin.symbol.clone(), // symbol
filtered_coin.closetime.to_string(), // close_time
filtered_coin.current_price.to_string(), // suggested_price
filtered_coin.current_price.to_string(), // current_price
filtered_coin.stoploss.to_string(), // stoploss
filtered_coin.target_price.to_string(), // target_price
server_epoch().await.to_string(), // registered_server_epoch
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
0.0.to_string(), // profit_percent
0.0.to_string(), // minimum_profit_percent
0.0.to_string(), // maximum_profit_percent
@ -390,12 +374,12 @@ pub async fn insert_pre_suggested_coins(
.await?;
// insert record without duplicate(registerer, closetime, symbol) into [pre_suggested_coin_list]
for filtered_coin in filtered_coins {
for (symbol, filtered_data) in filtered_coins {
let mut is_dupe = false; // initialize
for list_element in &suggested_coin_list {
if (filtered_coin.symbol == list_element.symbol)
&& (filtered_coin.closetime == list_element.close_time)
if (*symbol == list_element.symbol)
&& (filtered_data.closetime == list_element.close_time)
{
is_dupe = true;
break;
@ -404,13 +388,13 @@ pub async fn insert_pre_suggested_coins(
if is_dupe == false {
let mut insert_values = vec![
filtered_coin.symbol.clone(), // symbol
filtered_coin.closetime.to_string(), // close_time
filtered_coin.current_price.to_string(), // suggested_price
filtered_coin.current_price.to_string(), // current_price
filtered_coin.stoploss.to_string(), // stoploss
filtered_coin.target_price.to_string(), // target_price
server_epoch().await.to_string(), // registered_server_epoch
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
0.0.to_string(), // profit_percent
0.0.to_string(), // minimum_profit_percent
0.0.to_string(), // maximum_profit_percent
@ -467,12 +451,12 @@ pub async fn insert_pre_suggested_coins(
.await?;
// insert record without duplicate(registerer, close_time, symbol) into [pre_suggested_coin_list]
for filtered_coin in filtered_coins {
for (symbol, filtered_data) in filtered_coins {
let mut is_dupe = false; // initialize
for list_element in &ordered_coin_list {
if (filtered_coin.symbol == list_element.symbol)
&& (filtered_coin.closetime == list_element.close_time)
if (*symbol == list_element.symbol)
&& (filtered_data.closetime == list_element.close_time)
{
is_dupe = true;
break;
@ -480,8 +464,8 @@ pub async fn insert_pre_suggested_coins(
}
for list_element in &pre_suggested_coin_list {
if (filtered_coin.symbol == list_element.symbol)
&& (filtered_coin.closetime == list_element.close_time)
if (*symbol == list_element.symbol)
&& (filtered_data.closetime == list_element.close_time)
{
is_dupe = true;
break;
@ -490,13 +474,13 @@ pub async fn insert_pre_suggested_coins(
if is_dupe == false {
let mut insert_values = vec![
filtered_coin.symbol.clone(), // symbol
filtered_coin.closetime.to_string(), // close_time
filtered_coin.current_price.to_string(), // suggested_price
filtered_coin.current_price.to_string(), // current_price
filtered_coin.stoploss.to_string(), // stoploss
filtered_coin.target_price.to_string(), // target_price
server_epoch().await.to_string(), // registered_server_epoch
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
0.0.to_string(), // profit_percent
0.0.to_string(), // minimum_profit_percent
0.0.to_string(), // maximum_profit_percent
@ -546,12 +530,12 @@ pub async fn insert_pre_suggested_coins(
.await?;
// insert record without duplicate(registerer, closetime, symbol) into [pre_suggested_coin_list]
for filtered_coin in filtered_coins {
for (symbol, filtered_data) in filtered_coins {
let mut is_dupe = false; // initialize
for list_element in &ordered_coin_list {
if (filtered_coin.symbol == list_element.symbol)
&& (filtered_coin.closetime == list_element.close_time)
if (*symbol == list_element.symbol)
&& (filtered_data.closetime == list_element.close_time)
{
is_dupe = true;
break;
@ -560,13 +544,13 @@ pub async fn insert_pre_suggested_coins(
if is_dupe == false {
let mut insert_values = vec![
filtered_coin.symbol.clone(), // symbol
filtered_coin.closetime.to_string(), // close_time
filtered_coin.current_price.to_string(), // suggested_price
filtered_coin.current_price.to_string(), // current_price
filtered_coin.stoploss.to_string(), // stoploss
filtered_coin.target_price.to_string(), // target_price
server_epoch().await.to_string(), // registered_server_epoch
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
0.0.to_string(), // profit_percent
0.0.to_string(), // minimum_profit_percent
0.0.to_string(), // maximum_profit_percent
@ -606,12 +590,12 @@ pub async fn insert_pre_suggested_coins(
.await?;
// insert record without duplicate(registerer, closetime, symbol) into [pre_suggested_coin_list]
for filtered_coin in filtered_coins {
for (symbol, filtered_data) in filtered_coins {
let mut is_dupe = false; // initialize
for list_element in &pre_suggested_coin_list {
if (filtered_coin.symbol == list_element.symbol)
&& (filtered_coin.closetime == list_element.close_time)
if (*symbol == list_element.symbol)
&& (filtered_data.closetime == list_element.close_time)
{
is_dupe = true;
break;
@ -620,13 +604,13 @@ pub async fn insert_pre_suggested_coins(
if is_dupe == false {
let mut insert_values = vec![
filtered_coin.symbol.clone(), // symbol
filtered_coin.closetime.to_string(), // close_time
filtered_coin.current_price.to_string(), // suggested_price
filtered_coin.current_price.to_string(), // current_price
filtered_coin.stoploss.to_string(), // stoploss
filtered_coin.target_price.to_string(), // target_price
server_epoch().await.to_string(), // registered_server_epoch
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
0.0.to_string(), // profit_percent
0.0.to_string(), // minimum_profit_percent
0.0.to_string(), // maximum_profit_percent
@ -643,15 +627,15 @@ pub async fn insert_pre_suggested_coins(
}
}
} else {
for filtered_coin in filtered_coins {
for (symbol, filtered_data) in filtered_coins {
let mut insert_values = vec![
filtered_coin.symbol.clone(), // symbol
filtered_coin.closetime.to_string(), // close_time
filtered_coin.current_price.to_string(), // suggested_price
filtered_coin.current_price.to_string(), // current_price
filtered_coin.stoploss.to_string(), // stoploss
filtered_coin.target_price.to_string(), // target_price
server_epoch().await.to_string(), // registered_server_epoch
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
0.0.to_string(), // profit_percent
0.0.to_string(), // minimum_profit_percent
0.0.to_string(), // maximum_profit_percent

View File

@ -2,7 +2,6 @@
#![allow(warnings)]
use crate::coin_health_check_team::request_candles::CandleData;
use crate::coin_health_check_team::request_others::CoinPriceData;
use crate::database_control::*;
use csv::{DeserializeRecordsIter, StringRecord};
use rust_decimal::{prelude::ToPrimitive, Decimal};
@ -10,6 +9,10 @@ use serde::Deserialize;
use sqlx::FromRow;
use std::sync::Arc;
use tokio::{fs::*, sync::Mutex, time::*};
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone, PartialEq)]
pub enum CandleType { UP, DOWN }
#[derive(Debug, Clone, PartialEq)]
pub struct RealtimePriceData {
@ -20,7 +23,7 @@ pub struct RealtimePriceData {
pub low_price: f64,
pub close_time: i64,
pub quote_asset_volume: f64,
pub candle_type: String,
pub candle_type: CandleType,
}
impl RealtimePriceData {
@ -33,7 +36,7 @@ impl RealtimePriceData {
low_price: 0.0,
close_time: 0,
quote_asset_volume: 0.0,
candle_type: String::new(),
candle_type: CandleType::DOWN,
};
data
}
@ -41,194 +44,170 @@ impl RealtimePriceData {
pub async fn update_realtime_price_data(
interval: &String,
read_candle_for_opclo: &Vec<(String, Vec<CandleData>)>,
read_candle_for_rt: &Vec<(String, Vec<RealtimePriceData>)>,
write_rt_data: &mut Vec<(String, Vec<RealtimePriceData>)>,
read_price: &Vec<CoinPriceData>,
read_symbol: &Vec<String>,
read_candle: &HashMap<String, Vec<CandleData>>,
read_candle_for_rt: &HashMap<String, Vec<RealtimePriceData>>,
write_rt_data: &mut HashMap<String, Vec<RealtimePriceData>>,
read_price: &HashMap<String, f64>,
read_symbol: &HashSet<String>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let instant = Instant::now();
let mut rt_price_vec: Vec<RealtimePriceData> = Vec::new();
let mut rt_data_vec: Vec<(String, Vec<RealtimePriceData>)> = Vec::new();
let mut rt_data_map: HashMap<String, Vec<RealtimePriceData>> = HashMap::new();
for element in read_symbol {
let candle_search_result = read_candle_for_opclo.iter().position(|x| x.0 == *element);
match candle_search_result {
Some(T) => {
for element in &read_candle_for_opclo[T].1 {
let mut realtime_price_data_builder = RealtimePriceData::new();
realtime_price_data_builder.opclo_price =
(element.open_price + element.close_price) / 2.0;
realtime_price_data_builder.open_price = element.open_price;
realtime_price_data_builder.close_price = element.close_price;
realtime_price_data_builder.high_price = element.high_price;
realtime_price_data_builder.low_price = element.low_price;
realtime_price_data_builder.close_time = element.close_time;
realtime_price_data_builder.quote_asset_volume = element.quote_asset_volume;
if element.open_price < element.close_price {
realtime_price_data_builder.candle_type = String::from("UP");
} else {
realtime_price_data_builder.candle_type = String::from("DOWN");
}
rt_price_vec.push(realtime_price_data_builder);
}
// reflect realtime data to the last element in rt_price_vec
if interval.contains("1m") {
let price_search_result = read_price.iter().position(|x| x.symbol == *element);
if price_search_result.is_some() {
// update close_price
rt_price_vec.last_mut().unwrap().close_price =
read_price[price_search_result.unwrap()].current_price;
// update opclo_price
rt_price_vec.last_mut().unwrap().opclo_price =
(rt_price_vec.last_mut().unwrap().open_price
+ rt_price_vec.last_mut().unwrap().close_price)
/ 2.0;
// update candle_type
if rt_price_vec.last_mut().unwrap().close_price
>= rt_price_vec.last_mut().unwrap().open_price
{
rt_price_vec.last_mut().unwrap().candle_type = String::from("UP");
} else {
rt_price_vec.last_mut().unwrap().candle_type = String::from("DOWN");
}
// update high_price
if rt_price_vec.last_mut().unwrap().high_price
< read_price[price_search_result.unwrap()].current_price
{
rt_price_vec.last_mut().unwrap().high_price =
read_price[price_search_result.unwrap()].current_price;
}
// update low_price
if rt_price_vec.last_mut().unwrap().low_price
> read_price[price_search_result.unwrap()].current_price
{
rt_price_vec.last_mut().unwrap().low_price =
read_price[price_search_result.unwrap()].current_price;
}
}
if let Some(candle_data) = read_candle.get(element) {
for element in candle_data {
let mut realtime_price_data_builder = RealtimePriceData::new();
realtime_price_data_builder.opclo_price =
(element.open_price + element.close_price) / 2.0;
realtime_price_data_builder.open_price = element.open_price;
realtime_price_data_builder.close_price = element.close_price;
realtime_price_data_builder.high_price = element.high_price;
realtime_price_data_builder.low_price = element.low_price;
realtime_price_data_builder.close_time = element.close_time;
realtime_price_data_builder.quote_asset_volume = element.quote_asset_volume;
if element.open_price < element.close_price {
realtime_price_data_builder.candle_type = CandleType::UP;
} else {
// for 30m, uses 1m candle
// for 1d, uses 30m candle
// for 1w, uses 1d candle
// for 1mon, uses 1w candle
realtime_price_data_builder.candle_type = CandleType::DOWN;
}
rt_price_vec.push(realtime_price_data_builder);
}
// reflect realtime data to the last element in rt_price_vec
if interval.contains("1m") {
if let Some(current_price) = read_price.get(element) {
// update close_price
rt_price_vec.last_mut().unwrap().close_price = *current_price;
// update opclo_price
rt_price_vec.last_mut().unwrap().opclo_price =
(rt_price_vec.last_mut().unwrap().open_price
+ rt_price_vec.last_mut().unwrap().close_price)
/ 2.0;
// update candle_type
if rt_price_vec.last_mut().unwrap().close_price
>= rt_price_vec.last_mut().unwrap().open_price
{
rt_price_vec.last_mut().unwrap().candle_type = CandleType::UP;
} else {
rt_price_vec.last_mut().unwrap().candle_type = CandleType::DOWN;
}
// update high_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
{
rt_price_vec.last_mut().unwrap().low_price = *current_price;
}
}
} else {
// for 30m, uses 1m candle
// for 1d, uses 30m candle
// for 1w, uses 1d candle
// for 1mon, uses 1w candle
if let Some(rt_vec) = read_candle_for_rt.get(element) {
if rt_vec.len() >= 2 {
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;
let mut update_lowprice = 0.0;
let mut update_quote_asset_volume = 0.0;
// search symbol
let candle_search_result =
read_candle_for_rt.iter().position(|x| x.0 == *element);
match candle_search_result {
Some(T) => {
if read_candle_for_rt[T].1.len() >= 2 {
let mut candle_vec_clone = read_candle_for_rt[T].1.clone();
let mut rt_price_vec_clone = rt_price_vec.clone();
rt_price_vec_clone.reverse();
rt_price_vec_clone.truncate(2);
rt_price_vec_clone.reverse();
// update realtime information for the latest candle
let mut update_closeprice = 0.0;
let mut update_highprice = 0.0;
let mut update_lowprice = 0.0;
let mut update_quote_asset_volume = 0.0;
// search close time
let prev_closetime_result = candle_vec_clone.binary_search_by_key(
&rt_price_vec_clone.first().unwrap().close_time,
|RealtimePriceData {
opclo_price,
open_price,
close_price,
high_price,
low_price,
close_time,
quote_asset_volume,
candle_type,
}| *close_time,
);
if prev_closetime_result.is_ok() {
let result =
candle_vec_clone.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;
}
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;
}
for element in result.unwrap() {
update_quote_asset_volume += element.quote_asset_volume;
}
}
// search close time
let prev_closetime_result = rt_vec.binary_search_by_key(
&previous_close_time,
|RealtimePriceData {
opclo_price,
open_price,
close_price,
high_price,
low_price,
close_time,
quote_asset_volume,
candle_type,
}| *close_time,
);
if prev_closetime_result.is_ok() {
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;
}
let price_search_result =
read_price.iter().position(|x| x.symbol == *element);
if price_search_result.is_some() {
update_closeprice =
read_price[price_search_result.unwrap()].current_price;
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 the latest candle with values
if update_highprice != 0.0
&& !update_highprice.is_nan()
&& update_highprice.is_finite()
{
rt_price_vec.last_mut().unwrap().high_price = update_highprice;
}
if update_lowprice != 0.0
&& !update_lowprice.is_nan()
&& update_lowprice.is_finite()
{
rt_price_vec.last_mut().unwrap().low_price = update_lowprice;
}
if update_quote_asset_volume != 0.0
&& !update_quote_asset_volume.is_nan()
&& update_quote_asset_volume.is_finite()
{
rt_price_vec.last_mut().unwrap().quote_asset_volume =
update_quote_asset_volume;
}
if update_closeprice != 0.0
&& !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;
for element in result.unwrap() {
update_quote_asset_volume += element.quote_asset_volume;
}
}
}
None => {}
if let Some(current_price) = read_price.get(element) {
update_closeprice = *current_price;
}
// update the latest candle with values
if update_highprice != 0.0
&& !update_highprice.is_nan()
&& update_highprice.is_finite()
{
rt_price_vec.last_mut().unwrap().high_price = update_highprice;
}
if update_lowprice != 0.0
&& !update_lowprice.is_nan()
&& update_lowprice.is_finite()
{
rt_price_vec.last_mut().unwrap().low_price = update_lowprice;
}
if update_quote_asset_volume != 0.0
&& !update_quote_asset_volume.is_nan()
&& update_quote_asset_volume.is_finite()
{
rt_price_vec.last_mut().unwrap().quote_asset_volume =
update_quote_asset_volume;
}
if update_closeprice != 0.0
&& !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_data_vec.push((element.clone(), rt_price_vec.clone()));
rt_price_vec.clear();
}
None => {}
if let Some(price_vec) = write_rt_data.get_mut(element) {
*price_vec = rt_price_vec.clone();
} else {
write_rt_data.insert(element.clone(), rt_price_vec.clone());
}
rt_price_vec.clear();
}
}
*write_rt_data = rt_data_vec;
// println!(" datapoints/price_{} 완료 elapsed:{:.2}s", interval, instant.elapsed().as_secs_f32());
Ok(())
}

View File

@ -1,4 +1,4 @@
use super::{FilteredData, RealtimePriceData, try_join_all, Arc, Mutex};
use super::{FilteredDataValue, RealtimePriceData, try_join_all, Arc, Mutex, HashMap};
#[derive(Clone, Debug)]
pub struct AdxData {
@ -19,109 +19,109 @@ struct DiData {
close_time: i64,
}
pub async fn adx(adx_len: usize, di_len: usize, input_rt_data: &Vec<(String, Vec<RealtimePriceData>)>,
filtered_symbols: &Vec<FilteredData>,) -> Result<Vec<(String, Vec<AdxData>)>, Box<dyn std::error::Error + Send + Sync>> {
pub async fn adx(adx_len: usize, di_len: usize, input_rt_data: &HashMap<String, Vec<RealtimePriceData>>,
filtered_symbols: &HashMap<String, FilteredDataValue>,) -> Result<HashMap<String, Vec<AdxData>>, Box<dyn std::error::Error + Send + Sync>> {
if filtered_symbols.is_empty() {
Err(("Err"))?;
}
let mut adx_vec: Vec<(String, Vec<AdxData>)> = Vec::new();
let mut adx_vec: HashMap<String, Vec<AdxData>> = HashMap::new();
let mut adx_vec_arc = Arc::new(Mutex::new(adx_vec));
let mut task_vec = Vec::new();
for element in filtered_symbols {
let mut rt_data_vec = input_rt_data.clone();
for (filtered_symbol, filtered_data) in filtered_symbols {
let mut rt_data_map = input_rt_data.clone();
let adx_vec_arc_c = Arc::clone(&adx_vec_arc);
let symbol = element.symbol.clone();
task_vec.push(tokio::spawn(async move {
let idx_result = rt_data_vec.iter().position(|a| a.0 == symbol);
if idx_result.is_some() {
// step 1: calculate +DM, -DM, TR
let windows = rt_data_vec[idx_result.unwrap()].1.windows(2);
let mut basic_data_vec: Vec<BasicData> = Vec::new();
for window in windows {
let prev_rt_data = window.first().unwrap();
let current_rt_data = window.last().unwrap();
let mut up = current_rt_data.high_price - prev_rt_data.high_price;
let mut down = prev_rt_data.low_price - current_rt_data.low_price;
let basic_data = BasicData {
dm_plus: if up > 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),
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 mut smoothed_basic_data_vec: Vec<BasicData> = 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
let symbol = filtered_symbol.clone();
task_vec.push(tokio::spawn(async move {
if let Some(rt_price_data) = rt_data_map.get(&symbol) {
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<BasicData> = Vec::new();
for window in windows {
let prev_rt_data = window.first().unwrap();
let current_rt_data = window.last().unwrap();
let mut up = current_rt_data.high_price - prev_rt_data.high_price;
let mut down = prev_rt_data.low_price - current_rt_data.low_price;
let basic_data = BasicData {
dm_plus: if up > 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),
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 mut smoothed_basic_data_vec: Vec<BasicData> = 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
let mut dm_plus_calculated = 0.0;
let mut dm_minus_calculated = 0.0;
let mut tr_calculated = 0.0;
for element in partial_vec1 {
dm_plus_calculated += element.dm_plus;
dm_minus_calculated += element.dm_minus;
tr_calculated += element.true_range;
}
dm_plus_calculated /= di_len as f64;
dm_minus_calculated /= di_len as f64;
tr_calculated /= di_len as f64;
let basic_data = BasicData { dm_plus: dm_plus_calculated, dm_minus: dm_minus_calculated, true_range: tr_calculated, close_time: partial_vec1.last().unwrap().close_time };
smoothed_basic_data_vec.push(basic_data);
for element in partial_vec2 {
dm_plus_calculated = alpha * element.dm_plus + (1.0 - alpha) * dm_plus_calculated;
dm_minus_calculated = alpha * element.dm_minus + (1.0 - alpha) * dm_minus_calculated;
tr_calculated = alpha * element.true_range + (1.0 - alpha) * tr_calculated;
let basic_data = BasicData { dm_plus: dm_plus_calculated, dm_minus: dm_minus_calculated, true_range: tr_calculated, close_time: element.close_time };
let mut dm_plus_calculated = 0.0;
let mut dm_minus_calculated = 0.0;
let mut tr_calculated = 0.0;
for element in partial_vec1 {
dm_plus_calculated += element.dm_plus;
dm_minus_calculated += element.dm_minus;
tr_calculated += element.true_range;
}
dm_plus_calculated /= di_len as f64;
dm_minus_calculated /= di_len as f64;
tr_calculated /= di_len as f64;
let basic_data = BasicData { dm_plus: dm_plus_calculated, dm_minus: dm_minus_calculated, true_range: tr_calculated, close_time: partial_vec1.last().unwrap().close_time };
smoothed_basic_data_vec.push(basic_data);
}
// step 3: calculate DI
let mut di_data_vec: Vec<DiData> = 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};
di_data_vec.push(di_data);
}
for element in partial_vec2 {
dm_plus_calculated = alpha * element.dm_plus + (1.0 - alpha) * dm_plus_calculated;
dm_minus_calculated = alpha * element.dm_minus + (1.0 - alpha) * dm_minus_calculated;
tr_calculated = alpha * element.true_range + (1.0 - alpha) * tr_calculated;
let basic_data = BasicData { dm_plus: dm_plus_calculated, dm_minus: dm_minus_calculated, true_range: tr_calculated, close_time: element.close_time };
smoothed_basic_data_vec.push(basic_data);
}
// step 4: calculate ADX
let mut initial_adx_vec: Vec<AdxData> = Vec::new();
for di_data in di_data_vec {
let sum = di_data.di_plus + di_data.di_minus;
let difference = (di_data.di_plus - di_data.di_minus).abs();
let divisor = if sum <= 0.00000001 { 1.0 } else { sum };
let adx_data = AdxData { adx: difference.abs()/divisor, close_time: di_data.close_time };
initial_adx_vec.push(adx_data);
}
let partial_vec1 = initial_adx_vec.get(..adx_len).unwrap(); // for calculation of initial value
let partial_vec2 = initial_adx_vec.get(adx_len..).unwrap(); // for calculation of the rest
// step 3: calculate DI
let mut di_data_vec: Vec<DiData> = 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};
di_data_vec.push(di_data);
}
let mut smoothed_adx_vec: Vec<AdxData> = Vec::new();
let mut adx_calculated = 0.0;
for element in partial_vec1 {
adx_calculated += element.adx;
}
adx_calculated /= adx_len as f64;
let adx_data = AdxData { adx: adx_calculated, close_time: partial_vec1.last().unwrap().close_time };
smoothed_adx_vec.push(adx_data);
let alpha: f64 = 1.0 /(adx_len as f64);
for element in partial_vec2 {
adx_calculated = alpha * element.adx + (1.0 - alpha) * adx_calculated;
let adx_data = AdxData { adx: 100.0 * adx_calculated, close_time: element.close_time };
// step 4: calculate ADX
let mut initial_adx_vec: Vec<AdxData> = Vec::new();
for di_data in di_data_vec {
let sum = di_data.di_plus + di_data.di_minus;
let difference = (di_data.di_plus - di_data.di_minus).abs();
let divisor = if sum <= 0.00000001 { 1.0 } else { sum };
let adx_data = AdxData { adx: difference.abs()/divisor, close_time: di_data.close_time };
initial_adx_vec.push(adx_data);
}
let partial_vec1 = initial_adx_vec.get(..adx_len).unwrap(); // for calculation of initial value
let partial_vec2 = initial_adx_vec.get(adx_len..).unwrap(); // for calculation of the rest
let mut smoothed_adx_vec: Vec<AdxData> = Vec::new();
let mut adx_calculated = 0.0;
for element in partial_vec1 {
adx_calculated += element.adx;
}
adx_calculated /= adx_len as f64;
let adx_data = AdxData { adx: adx_calculated, close_time: partial_vec1.last().unwrap().close_time };
smoothed_adx_vec.push(adx_data);
}
let alpha: f64 = 1.0 /(adx_len as f64);
for element in partial_vec2 {
adx_calculated = alpha * element.adx + (1.0 - alpha) * adx_calculated;
let adx_data = AdxData { adx: 100.0 * adx_calculated, close_time: element.close_time };
smoothed_adx_vec.push(adx_data);
}
let mut adx_vec_arc_lock = adx_vec_arc_c.lock().await;
adx_vec_arc_lock.push((symbol.clone(), smoothed_adx_vec.clone()));
let mut adx_vec_arc_lock = adx_vec_arc_c.lock().await;
adx_vec_arc_lock.insert(symbol.clone(), smoothed_adx_vec.clone());
}
}
}));
}

View File

@ -4,12 +4,13 @@
use crate::database_control::*;
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use crate::value_estimation_team::indicators::sma::{SmaData, sma};
use crate::strategy_team::FilteredData;
use crate::strategy_team::FilteredDataValue;
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 BollingerBandData {
@ -35,35 +36,31 @@ impl BollingerBandData {
pub async fn bollingerband(
period: usize,
sd_factor: f64,
input_rt_data: &Vec<(String, Vec<RealtimePriceData>)>,
filtered_symbols: &Vec<FilteredData>,
) -> Result<Vec<(String, Vec<BollingerBandData>)>, Box<dyn std::error::Error + Send + Sync>> {
input_rt_data: &HashMap<String, Vec<RealtimePriceData>>,
filtered_symbols: &HashMap<String, FilteredDataValue>,
) -> Result<HashMap<String, Vec<BollingerBandData>>, Box<dyn std::error::Error + Send + Sync>> {
if filtered_symbols.is_empty() {
Err(("Err"))?;
}
let mut sma_data_vec: Vec<(String, Vec<SmaData>)> = sma(period, input_rt_data, filtered_symbols).await?;
let mut bb_data_wrapper: Vec<(String, Vec<BollingerBandData>)> = Vec::new();
let mut sma_data_map: HashMap<String, Vec<SmaData>> = sma(period, input_rt_data, filtered_symbols).await?;
let mut bb_data_wrapper: HashMap<String, Vec<BollingerBandData>> = HashMap::new();
let mut bb_data_wrapper_arc = Arc::new(Mutex::new(bb_data_wrapper));
let mut task_vec = Vec::new();
for filtered_symbol in filtered_symbols {
let filtered_symbol_c = filtered_symbol.clone();
let rt_data_vec_c: Vec<(String, Vec<RealtimePriceData>)> = input_rt_data.clone();
let sma_data_vec_c = sma_data_vec.clone();
let bb_data_wrapper_arc_c = Arc::clone(&bb_data_wrapper_arc);
task_vec.push(tokio::spawn(async move {
let search_result1 = rt_data_vec_c.clone().iter().position(|x| x.0 == *filtered_symbol_c.symbol);
let search_result2 = sma_data_vec_c.iter().position(|x| x.0 == *filtered_symbol_c.symbol);
if search_result1.is_some() && search_result2.is_some() {
for (symbol, filtered_data) in filtered_symbols {
if input_rt_data.contains_key(symbol) && sma_data_map.contains_key(symbol) {
let symbol_c = symbol.clone();
let rt_data_vec_c = input_rt_data.get(symbol).unwrap().clone();
let sma_data_vec_c = sma_data_map.get(symbol).unwrap().clone();
let bb_data_wrapper_arc_c = Arc::clone(&bb_data_wrapper_arc);
task_vec.push(tokio::spawn(async move {
let mut bb_data = BollingerBandData::new();
let mut bb_data_vec: Vec<BollingerBandData> = Vec::new();
// if the data has shorter than buffer
if sma_data_vec_c[search_result2.unwrap()].1.len() > period {
let result = rt_data_vec_c[search_result1.unwrap()].1.binary_search_by_key(
&sma_data_vec_c[search_result2.unwrap()].1.first().unwrap().close_time,
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,
@ -75,17 +72,17 @@ pub async fn bollingerband(
candle_type,
}| *close_time,
);
match result {
Ok(T) => {
if T <= period - 1 {
let mut read_data_iter =
sma_data_vec_c[search_result2.unwrap()].1.iter();
sma_data_vec_c.iter();
for _ in T..period - 1 {
read_data_iter.next();
}
let window_iter =
rt_data_vec_c[search_result1.unwrap()].1.windows(period);
rt_data_vec_c.windows(period);
for buffer in window_iter {
let mut sd_mean = 0.0;
let mut standard_deviation = 0.0;
@ -99,7 +96,7 @@ pub async fn bollingerband(
}
standard_deviation = sd_factor
* ((standard_deviation / period as f64).sqrt());
match read_data_iter.next() {
Some(T) => {
bb_data.sma = T.sma_value;
@ -119,10 +116,10 @@ pub async fn bollingerband(
}
let mut bb_data_wrapper_lock = bb_data_wrapper_arc_c.lock().await;
bb_data_wrapper_lock
.push((filtered_symbol_c.symbol.clone(), bb_data_vec.clone()));
.insert(symbol_c, bb_data_vec.clone());
}
}
}));
}));
}
}
try_join_all(task_vec).await?;
let a = bb_data_wrapper_arc.lock().await.to_owned();

View File

@ -7,7 +7,8 @@ use serde::Deserialize;
use sqlx::FromRow;
use std::sync::Arc;
use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*};
use super::FilteredData;
use super::FilteredDataValue;
use std::collections::HashMap;
#[derive(Clone, Debug)]
pub struct EmaData {
@ -27,9 +28,9 @@ impl EmaData {
// Binance EMA (closeprice)
pub async fn ema(
moving_number: usize,
input_rt_data: &Vec<(String, Vec<RealtimePriceData>)>,
filtered_symbols: &Vec<FilteredData>,
) -> Result<Vec<(String, Vec<EmaData>)>, Box<dyn std::error::Error + Send + Sync>> {
input_rt_data: &HashMap<String, Vec<RealtimePriceData>>,
filtered_symbols: &HashMap<String, FilteredDataValue>,
) -> Result<HashMap<String, Vec<EmaData>>, Box<dyn std::error::Error + Send + Sync>> {
if filtered_symbols.is_empty() {
Err("Err")?;
}
@ -38,55 +39,50 @@ pub async fn ema(
let mut ema_t: f64 = 0.0;
let mut ema_prev: f64 = 0.0;
let mut ema_data_wrapper: Vec<(String, Vec<EmaData>)> = Vec::new();
let mut ema_data_wrapper: HashMap<String, Vec<EmaData>> = HashMap::new();
let mut ema_data_wrapper_arc = Arc::new(Mutex::new(ema_data_wrapper));
let mut task_vec = Vec::new();
for filtered_elem in filtered_symbols {
let symbol_search_result = input_rt_data.iter().position(|x| x.0 == *filtered_elem.symbol);
for (symbol, filtered_data) in filtered_symbols {
if let Some(rt_data_vec) = input_rt_data.get(symbol) {
let ema_data_wrapper_arc_c = Arc::clone(&ema_data_wrapper_arc);
let mut ema_data = EmaData::new();
let mut ema_data_vec: Vec<EmaData> = Vec::new();
let symbol_c = symbol.clone();
let rt_data_vec_c = rt_data_vec.clone();
task_vec.push(tokio::spawn(async move {
if rt_data_vec_c.len() < moving_number {
ema_data.ema_value = 0.0;
ema_data.close_time = 0;
ema_data_vec.push(ema_data.clone());
} else {
let partial_vec1 = rt_data_vec_c.get(..moving_number).unwrap();
let partial_vec2 = rt_data_vec_c.get(moving_number..).unwrap();
match symbol_search_result {
Some(T) => {
let ema_data_wrapper_arc_c = Arc::clone(&ema_data_wrapper_arc);
let filtered_elem_c = filtered_elem.clone();
let mut ema_data = EmaData::new();
let mut ema_data_vec: Vec<EmaData> = Vec::new();
let input_rt_data_c = input_rt_data.clone();
task_vec.push(tokio::spawn(async move {
if input_rt_data_c[T].1.len() < moving_number {
ema_data.ema_value = 0.0;
ema_data.close_time = 0;
ema_data_vec.push(ema_data.clone());
} else {
let partial_vec1 = input_rt_data_c[T].1.get(..moving_number).unwrap();
let partial_vec2 = input_rt_data_c[T].1.get(moving_number..).unwrap();
let mut sma_for_initial_value = 0.0;
for element in partial_vec1 {
sma_for_initial_value += element.close_price;
}
sma_for_initial_value /= moving_number as f64;
ema_data.ema_value = sma_for_initial_value;
ema_data.close_time = partial_vec1.last().unwrap().close_time;
ema_data_vec.push(ema_data.clone());
ema_prev = sma_for_initial_value;
for element in partial_vec2 {
ema_t = (1.0 - alpha) * ema_prev + alpha * element.close_price;
ema_data.ema_value = ema_t;
ema_data.close_time = element.close_time;
ema_data_vec.push(ema_data.clone());
ema_prev = ema_t;
}
let mut sma_for_initial_value = 0.0;
for element in partial_vec1 {
sma_for_initial_value += element.close_price;
}
let mut ema_data_wrapper_lock = ema_data_wrapper_arc_c.lock().await;
ema_data_wrapper_lock.push((filtered_elem_c.symbol.clone(), ema_data_vec.clone()));
}));
}
None => {}
sma_for_initial_value /= moving_number as f64;
ema_data.ema_value = sma_for_initial_value;
ema_data.close_time = partial_vec1.last().unwrap().close_time;
ema_data_vec.push(ema_data.clone());
ema_prev = sma_for_initial_value;
for element in partial_vec2 {
ema_t = (1.0 - alpha) * ema_prev + alpha * element.close_price;
ema_data.ema_value = ema_t;
ema_data.close_time = element.close_time;
ema_data_vec.push(ema_data.clone());
ema_prev = ema_t;
}
}
let mut ema_data_wrapper_lock = ema_data_wrapper_arc_c.lock().await;
ema_data_wrapper_lock.insert(symbol_c, ema_data_vec.clone());
}));
}
}
try_join_all(task_vec).await?;
@ -97,9 +93,9 @@ pub async fn ema(
// Binance EMA ((open+close)/2)
pub async fn ema_opclo(
moving_number: usize,
input_rt_data: &Vec<(String, Vec<RealtimePriceData>)>,
filtered_symbols: &Vec<FilteredData>,
) -> Result<Vec<(String, Vec<EmaData>)>, Box<dyn std::error::Error + Send + Sync>> {
input_rt_data: &HashMap<String, Vec<RealtimePriceData>>,
filtered_symbols: &HashMap<String, FilteredDataValue>,
) -> Result<HashMap<String, Vec<EmaData>>, Box<dyn std::error::Error + Send + Sync>> {
if filtered_symbols.is_empty() {
Err("Err")?;
}
@ -108,58 +104,53 @@ pub async fn ema_opclo(
let mut ema_t: f64 = 0.0;
let mut ema_prev: f64 = 0.0;
let mut ema_data_wrapper: Vec<(String, Vec<EmaData>)> = Vec::new();
let mut ema_data_wrapper: HashMap<String, Vec<EmaData>> = HashMap::new();
let mut ema_data_wrapper_arc = Arc::new(Mutex::new(ema_data_wrapper));
let mut task_vec = Vec::new();
for filtered_elem in filtered_symbols {
let symbol_search_result = input_rt_data.iter().position(|x| x.0 == *filtered_elem.symbol);
for (symbol, filtered_data) in filtered_symbols {
if let Some(rt_data_vec) = input_rt_data.get(symbol) {
let ema_data_wrapper_arc_c = Arc::clone(&ema_data_wrapper_arc);
let mut ema_data = EmaData::new();
let mut ema_data_vec: Vec<EmaData> = Vec::new();
let symbol_c = symbol.clone();
let rt_data_vec_c = rt_data_vec.clone();
task_vec.push(tokio::spawn(async move {
if rt_data_vec_c.len() < moving_number {
ema_data.ema_value = 0.0;
ema_data.close_time = 0;
ema_data_vec.push(ema_data.clone());
} else {
let partial_vec1 = rt_data_vec_c.get(..moving_number).unwrap();
let partial_vec2 = rt_data_vec_c.get(moving_number..).unwrap();
match symbol_search_result {
Some(T) => {
let ema_data_wrapper_arc_c = Arc::clone(&ema_data_wrapper_arc);
let filtered_elem_c = filtered_elem.clone();
let mut ema_data = EmaData::new();
let mut ema_data_vec: Vec<EmaData> = Vec::new();
let input_rt_data_c = input_rt_data.clone();
task_vec.push(tokio::spawn(async move {
if input_rt_data_c[T].1.len() < moving_number {
ema_data.ema_value = 0.0;
ema_data.close_time = 0;
ema_data_vec.push(ema_data.clone());
} else {
let partial_vec1 = input_rt_data_c[T].1.get(..moving_number).unwrap();
let partial_vec2 = input_rt_data_c[T].1.get(moving_number..).unwrap();
let mut sma_for_initial_value = 0.0;
for element in partial_vec1 {
sma_for_initial_value += element.opclo_price;
}
sma_for_initial_value /= moving_number as f64;
ema_data.ema_value = sma_for_initial_value;
ema_data.close_time = partial_vec1.last().unwrap().close_time;
ema_data_vec.push(ema_data.clone());
ema_prev = sma_for_initial_value;
for element in partial_vec2 {
ema_t = (1.0 - alpha) * ema_prev + alpha * element.opclo_price;
ema_data.ema_value = ema_t;
ema_data.close_time = element.close_time;
ema_data_vec.push(ema_data.clone());
ema_prev = ema_t;
}
let mut sma_for_initial_value = 0.0;
for element in partial_vec1 {
sma_for_initial_value += element.opclo_price;
}
let mut ema_data_wrapper_lock = ema_data_wrapper_arc_c.lock().await;
ema_data_wrapper_lock.push((filtered_elem_c.symbol.clone(), ema_data_vec.clone()));
}));
}
None => {}
sma_for_initial_value /= moving_number as f64;
ema_data.ema_value = sma_for_initial_value;
ema_data.close_time = partial_vec1.last().unwrap().close_time;
ema_data_vec.push(ema_data.clone());
ema_prev = sma_for_initial_value;
for element in partial_vec2 {
ema_t = (1.0 - alpha) * ema_prev + alpha * element.close_price;
ema_data.ema_value = ema_t;
ema_data.close_time = element.close_time;
ema_data_vec.push(ema_data.clone());
ema_prev = ema_t;
}
}
let mut ema_data_wrapper_lock = ema_data_wrapper_arc_c.lock().await;
ema_data_wrapper_lock.insert(symbol_c, ema_data_vec.clone());
}));
}
}
try_join_all(task_vec).await?;
let a = ema_data_wrapper_arc.lock().await.to_owned();
Ok(a)
}
}

View File

@ -1,8 +1,9 @@
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use super::FilteredData;
use super::FilteredDataValue;
use std::sync::Arc;
use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*};
use tokio::sync::Mutex;
use futures::future::try_join_all;
use super::HashMap;
#[derive(Debug, Clone, PartialEq)]
pub enum HeatMapLevel {
@ -28,148 +29,150 @@ pub async fn heatmap_volume(
high_thold: f64,
medium_thold: f64,
normal_thold: f64,
filtered_symbols: &Vec<FilteredData>,
input_rt_data: &Vec<(String, Vec<RealtimePriceData>)>,
) -> Result<Vec<(String, Vec<HeatmapVolumeData>)>, Box<dyn std::error::Error + Send + Sync>> {
let mut heatmap_data_wrapper: Vec<(String, Vec<HeatmapVolumeData>)> = Vec::new();
filtered_symbols: &HashMap<String, FilteredDataValue>,
input_rt_data: &HashMap<String, Vec<RealtimePriceData>>,
) -> Result<HashMap<String, Vec<HeatmapVolumeData>>, Box<dyn std::error::Error + Send + Sync>> {
let mut heatmap_data_wrapper: HashMap<String, Vec<HeatmapVolumeData>> = HashMap::new();
let mut heatmap_data_wrapper_arc = Arc::new(Mutex::new(heatmap_data_wrapper));
let mut task_vec = Vec::new();
for element in filtered_symbols {
let heatmap_data_wrapper_arc_c = Arc::clone(&heatmap_data_wrapper_arc);
let element_c = element.clone();
let input_rt_data_c = input_rt_data.clone();
task_vec.push(tokio::spawn(async move {
let search_result = input_rt_data_c.iter().position(|x| x.0 == element_c.symbol);
if search_result.is_some_and(|a| input_rt_data_c[a].1.len() >= ma_len && input_rt_data_c[a].1.len() >= std_len) {
// calc mean
#[derive(Debug, Clone)]
struct MeanData {
mean_value: f64,
close_time: i64,
}
let mut mean_vec: Vec<MeanData> = Vec::new();
let mut mean_data = MeanData {
mean_value: 0.0,
close_time: 0,
};
let window = input_rt_data_c[search_result.unwrap()].1.windows(ma_len);
for buffer in window {
// calculate SMA of volume
mean_data.mean_value = 0.0;
mean_data.close_time = 0;
for element in buffer {
mean_data.mean_value += element.quote_asset_volume;
for (symbol, filtered_data) in filtered_symbols {
if let Some(rt_price_vec) = input_rt_data.get(symbol) {
let heatmap_data_wrapper_arc_c = Arc::clone(&heatmap_data_wrapper_arc);
let symbol_c = symbol.clone();
let rt_price_vec_c = rt_price_vec.clone();
task_vec.push(tokio::spawn(async move {
if rt_price_vec_c.len() >= ma_len && rt_price_vec_c.len() >= std_len {
// calc mean
#[derive(Debug, Clone)]
struct MeanData {
mean_value: f64,
close_time: i64,
}
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 {
pstdev_value: f64, // population standard deviation value
close_time: i64,
}
let mut pstdev_vec: Vec<PstdevData> = Vec::new();
let mut pstdev_data = PstdevData {
pstdev_value: 0.0,
close_time: 0,
};
let mut mean = 0.0;
let mut summation = 0.0;
let mut sample_minus_mean = 0.0;
let window = input_rt_data_c[search_result.unwrap()].1.windows(std_len);
for buffer in window {
pstdev_data.pstdev_value = 0.0;
pstdev_data.close_time = 0;
mean = 0.0;
summation = 0.0;
sample_minus_mean = 0.0;
for element in buffer {
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<HeatmapVolumeData> = Vec::new();
let mut heatmap_vol_data = HeatmapVolumeData {
heatmap_value: 0.0,
heatmap_level: HeatMapLevel::Normal,
close_time: 0,
};
if ma_len == std_len {
let mut rt_data_trunc_vec =
input_rt_data_c[search_result.unwrap()].1.get(ma_len - 1..).unwrap().iter();
let zipped = mean_vec.iter().zip(pstdev_vec.iter());
for element in zipped {
heatmap_vol_data.heatmap_value =
(rt_data_trunc_vec.next().unwrap().quote_asset_volume
- element.0.mean_value)
/ element.1.pstdev_value;
heatmap_vol_data.close_time = element.0.close_time;
if heatmap_vol_data.heatmap_value >= extra_high_thold {
heatmap_vol_data.heatmap_level = HeatMapLevel::ExtraHigh;
} else if heatmap_vol_data.heatmap_value > high_thold {
heatmap_vol_data.heatmap_level = HeatMapLevel::High;
} else if heatmap_vol_data.heatmap_value > medium_thold {
heatmap_vol_data.heatmap_level = HeatMapLevel::Medium;
} else if heatmap_vol_data.heatmap_value > normal_thold {
heatmap_vol_data.heatmap_level = HeatMapLevel::Normal;
} else {
heatmap_vol_data.heatmap_level = HeatMapLevel::Low;
let mut mean_vec: Vec<MeanData> = Vec::new();
let mut mean_data = MeanData {
mean_value: 0.0,
close_time: 0,
};
let window = rt_price_vec_c.windows(ma_len);
for buffer in window {
// calculate SMA of volume
mean_data.mean_value = 0.0;
mean_data.close_time = 0;
for element in buffer {
mean_data.mean_value += element.quote_asset_volume;
}
heatmap_vol_vec.push(heatmap_vol_data.clone());
mean_data.mean_value /= ma_len as f64;
mean_data.close_time = buffer.last().unwrap().close_time;
mean_vec.push(mean_data.clone());
}
} else if ma_len > std_len {
let mut rt_data_trunc_vec =
input_rt_data_c[search_result.unwrap()].1.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());
for element in zipped {
heatmap_vol_data.heatmap_value =
(rt_data_trunc_vec.next().unwrap().quote_asset_volume
- element.0.mean_value)
/ element.1.pstdev_value;
heatmap_vol_data.close_time = element.0.close_time;
heatmap_vol_vec.push(heatmap_vol_data.clone());
// calc pstdev
#[derive(Debug, Clone)]
struct PstdevData {
pstdev_value: f64, // population standard deviation value
close_time: i64,
}
} else {
let mut rt_data_trunc_vec =
input_rt_data_c[search_result.unwrap()].1.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);
for element in zipped {
heatmap_vol_data.heatmap_value =
(rt_data_trunc_vec.next().unwrap().quote_asset_volume
- element.0.mean_value)
/ element.1.pstdev_value;
heatmap_vol_data.close_time = element.0.close_time;
heatmap_vol_vec.push(heatmap_vol_data.clone());
} // level 구현
let mut pstdev_vec: Vec<PstdevData> = Vec::new();
let mut pstdev_data = PstdevData {
pstdev_value: 0.0,
close_time: 0,
};
let mut mean = 0.0;
let mut summation = 0.0;
let mut sample_minus_mean = 0.0;
let window = rt_price_vec_c.windows(std_len);
for buffer in window {
pstdev_data.pstdev_value = 0.0;
pstdev_data.close_time = 0;
mean = 0.0;
summation = 0.0;
sample_minus_mean = 0.0;
for element in buffer {
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<HeatmapVolumeData> = Vec::new();
let mut heatmap_vol_data = HeatmapVolumeData {
heatmap_value: 0.0,
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();
let zipped = mean_vec.iter().zip(pstdev_vec.iter());
for element in zipped {
heatmap_vol_data.heatmap_value =
(rt_data_trunc_vec.next().unwrap().quote_asset_volume
- element.0.mean_value)
/ element.1.pstdev_value;
heatmap_vol_data.close_time = element.0.close_time;
if heatmap_vol_data.heatmap_value >= extra_high_thold {
heatmap_vol_data.heatmap_level = HeatMapLevel::ExtraHigh;
} else if heatmap_vol_data.heatmap_value > high_thold {
heatmap_vol_data.heatmap_level = HeatMapLevel::High;
} else if heatmap_vol_data.heatmap_value > medium_thold {
heatmap_vol_data.heatmap_level = HeatMapLevel::Medium;
} else if heatmap_vol_data.heatmap_value > normal_thold {
heatmap_vol_data.heatmap_level = HeatMapLevel::Normal;
} else {
heatmap_vol_data.heatmap_level = HeatMapLevel::Low;
}
heatmap_vol_vec.push(heatmap_vol_data.clone());
}
} else if ma_len > std_len {
let mut rt_data_trunc_vec =
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());
for element in zipped {
heatmap_vol_data.heatmap_value =
(rt_data_trunc_vec.next().unwrap().quote_asset_volume
- element.0.mean_value)
/ element.1.pstdev_value;
heatmap_vol_data.close_time = element.0.close_time;
heatmap_vol_vec.push(heatmap_vol_data.clone());
}
} else {
let mut rt_data_trunc_vec =
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);
for element in zipped {
heatmap_vol_data.heatmap_value =
(rt_data_trunc_vec.next().unwrap().quote_asset_volume
- element.0.mean_value)
/ element.1.pstdev_value;
heatmap_vol_data.close_time = element.0.close_time;
heatmap_vol_vec.push(heatmap_vol_data.clone());
} // level 구현
}
let mut heatmap_data_wrapper_lock = heatmap_data_wrapper_arc_c.lock().await;
heatmap_data_wrapper_lock.insert(symbol_c, heatmap_vol_vec.clone());
}
let mut heatmap_data_wrapper_lock = heatmap_data_wrapper_arc_c.lock().await;
heatmap_data_wrapper_lock.push((element_c.symbol.clone(), heatmap_vol_vec.clone()));
}
}));
}));
}
}
try_join_all(task_vec).await?;
let a = heatmap_data_wrapper_arc.lock().await.to_owned();

View File

@ -1,6 +1,6 @@
use crate::value_estimation_team::indicators::ema::{EmaData, ema};
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use super::FilteredData;
use super::{FilteredDataValue, HashMap};
use std::sync::Arc;
use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*};
use futures::future::try_join_all;
@ -43,116 +43,114 @@ pub async fn ema_macd(
fast_len: usize,
slow_len: usize,
signal_smoothing: usize,
input_rt_data: &Vec<(String, Vec<RealtimePriceData>)>,
filtered_symbols: &Vec<FilteredData>
) -> Result<Vec<(String, Vec<MacdData>)>, Box<dyn std::error::Error + Send + Sync>> {
let mut macd_oscil_vec: Vec<(String, Vec<MacdData>)> = Vec::new();
input_rt_data: &HashMap<String, Vec<RealtimePriceData>>,
filtered_symbols: &HashMap<String, FilteredDataValue>
) -> Result<HashMap<String, Vec<MacdData>>, Box<dyn std::error::Error + Send + Sync>> {
let mut macd_oscil_vec: HashMap<String, Vec<MacdData>> = HashMap::new();
let fast_emas = ema(fast_len, input_rt_data, filtered_symbols).await?;
let slow_emas = ema(slow_len, input_rt_data, filtered_symbols).await?;
let mut macd_data_wrapper: Vec<(String, Vec<MacdData>)> = Vec::new();
let mut macd_data_wrapper: HashMap<String, Vec<MacdData>> = HashMap::new();
let mut macd_data_wrapper_arc = Arc::new(Mutex::new(macd_data_wrapper));
let mut task_vec = Vec::new();
for filtered_elem in filtered_symbols {
for (symbol, filtered_data) in filtered_symbols {
let fast_emas_c = fast_emas.clone();
let slow_emas_c = slow_emas.clone();
let filtered_elem_c = filtered_elem.clone();
let macd_data_wrapper_arc_c = Arc::clone(&macd_data_wrapper_arc);
task_vec.push(tokio::spawn(async move {
let fast_search_result = fast_emas_c.iter().position(|x| x.0 == filtered_elem_c.symbol);
let slow_search_result = slow_emas_c.iter().position(|x| x.0 == filtered_elem_c.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 {
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,
);
if result.is_ok() {
// making MACD
let temp_vec = fast_ema_vec_c.get(result.unwrap()..).unwrap();
let zipped = temp_vec.iter().zip(slow_ema_vec_c);
let mut macd = TempData::new();
let mut macd_vec: Vec<TempData> = Vec::new();
for element in zipped {
macd.value = element.0.ema_value - element.1.ema_value;
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);
// let mut macd_signal_vec: Vec<TempData> = Vec::new();
// for window in macd_vec_window {
// let mut sum_value = 0.0;
// for element in window {
// sum_value += element.value;
// }
// macd.value = sum_value / signal_smoothing as f64;
// macd.close_time = window.last().unwrap().close_time;
// macd_signal_vec.push(macd.clone());
// }
let mut macd_signal = TempData::new();
let mut macd_signal_vec: Vec<TempData> = Vec::new();
let partial_vec1 = macd_vec.get(..signal_smoothing).unwrap();
let partial_vec2 = macd_vec.get(signal_smoothing..).unwrap();
if fast_search_result.is_some_and(|a| fast_emas_c[a].1.len() >= signal_smoothing) &&
slow_search_result.is_some_and(|a| slow_emas_c[a].1.len() >= signal_smoothing) {
let fast_ema = fast_emas_c[fast_search_result.unwrap()].1.clone();
let slow_ema = slow_emas_c[fast_search_result.unwrap()].1.clone();
let result = fast_ema.binary_search_by_key(
&slow_ema.first().unwrap().close_time,
|&EmaData {
ema_value,
close_time,
}| close_time,
);
if result.is_ok() {
// making MACD
let temp_vec = fast_ema.get(result.unwrap()..).unwrap();
let zipped = temp_vec.iter().zip(slow_ema);
let mut macd = TempData::new();
let mut macd_vec: Vec<TempData> = Vec::new();
for element in zipped {
macd.value = element.0.ema_value - element.1.ema_value;
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);
// let mut macd_signal_vec: Vec<TempData> = Vec::new();
// for window in macd_vec_window {
// let mut sum_value = 0.0;
// for element in window {
// sum_value += element.value;
// }
// macd.value = sum_value / signal_smoothing as f64;
// macd.close_time = window.last().unwrap().close_time;
// macd_signal_vec.push(macd.clone());
// }
let mut macd_signal = TempData::new();
let mut macd_signal_vec: Vec<TempData> = Vec::new();
let partial_vec1 = macd_vec.get(..signal_smoothing).unwrap();
let partial_vec2 = macd_vec.get(signal_smoothing..).unwrap();
let mut sma_for_initial_value = 0.0;
for element in partial_vec1 {
sma_for_initial_value += element.value;
}
sma_for_initial_value /= signal_smoothing as f64;
let mut sma_for_initial_value = 0.0;
for element in partial_vec1 {
sma_for_initial_value += element.value;
}
sma_for_initial_value /= signal_smoothing as f64;
macd_signal.value = sma_for_initial_value;
macd_signal.close_time = partial_vec1.last().unwrap().close_time;
macd_signal_vec.push(macd_signal.clone());
let alpha: f64 = 2.0 / (signal_smoothing as f64 + 1.0);
let mut ema_prev = sma_for_initial_value;
for element in partial_vec2 {
let ema_t = (1.0 - alpha) * ema_prev + alpha * element.value;
macd_signal.value = ema_t;
macd_signal.close_time = element.close_time;
macd_signal.value = sma_for_initial_value;
macd_signal.close_time = partial_vec1.last().unwrap().close_time;
macd_signal_vec.push(macd_signal.clone());
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,
);
let alpha: f64 = 2.0 / (signal_smoothing as f64 + 1.0);
let mut ema_prev = sma_for_initial_value;
for element in partial_vec2 {
let ema_t = (1.0 - alpha) * ema_prev + alpha * element.value;
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<MacdData> = Vec::new();
for element in zipped {
let mut macd = MacdData::new();
macd.macd_value = element.0.value;
macd.signal_value = element.1.value;
macd.close_time = element.0.close_time;
macd_vec.push(macd);
macd_signal.value = ema_t;
macd_signal.close_time = element.close_time;
macd_signal_vec.push(macd_signal.clone());
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,
);
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<MacdData> = Vec::new();
for element in zipped {
let mut macd = MacdData::new();
macd.macd_value = element.0.value;
macd.signal_value = element.1.value;
macd.close_time = element.0.close_time;
macd_vec.push(macd);
}
let mut macd_data_wrapper_lock = macd_data_wrapper_arc_c.lock().await;
macd_data_wrapper_lock.insert(symbol_c, macd_vec.clone());
}
let mut macd_data_wrapper_lock = macd_data_wrapper_arc_c.lock().await;
macd_data_wrapper_lock.push((filtered_elem_c.symbol.clone(), macd_vec.clone()));
}
}
}
}
}));
}));
}
}
try_join_all(task_vec).await?;
let a = macd_data_wrapper_arc.lock().await.to_owned();

View File

@ -9,8 +9,9 @@ pub mod stoch_rsi;
pub mod supertrend;
pub mod tema;
use crate::strategy_team::FilteredData;
use crate::strategy_team::FilteredDataValue;
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use futures::future::try_join_all;
use std::sync::Arc;
use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*};
use std::collections::HashMap;

View File

@ -3,13 +3,14 @@
use crate::database_control::*;
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use crate::strategy_team::FilteredData;
use crate::strategy_team::FilteredDataValue;
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 {
@ -29,21 +30,20 @@ impl RsiData {
// Binance RSI (EMA, Closeprice, Wilder's weight, 150 candles)
pub async fn rsi(
rsi_number: usize,
input_rt_data: &Vec<(String, Vec<RealtimePriceData>)>,
filtered_symbols: &Vec<FilteredData>,
) -> Result<Vec<(String, Vec<RsiData>)>, Box<dyn std::error::Error + Send + Sync>> {
input_rt_data: &HashMap<String, Vec<RealtimePriceData>>,
filtered_symbols: &HashMap<String, FilteredDataValue>,
) -> Result<HashMap<String, Vec<RsiData>>, Box<dyn std::error::Error + Send + Sync>> {
let instant = Instant::now();
let mut rsi_data_wrapper: Vec<(String, Vec<RsiData>)> = Vec::new();
let mut rsi_data_wrapper: HashMap<String, Vec<RsiData>> = HashMap::new();
let mut rsi_data_wrapper_arc = Arc::new(Mutex::new(rsi_data_wrapper));
let mut task_vec = Vec::new();
for element in filtered_symbols {
let element_c = element.clone();
let rsi_data_wrapper_arc_c = Arc::clone(&rsi_data_wrapper_arc);
let search_result = input_rt_data.iter().position(|x| x.0 == *element_c.symbol);
if search_result.is_some() {
let input_rt_data_c = input_rt_data.clone();
for (symbol, filtered_data) in filtered_symbols {
if let Some(rt_price_vec) = input_rt_data.get(symbol) {
let rsi_data_wrapper_arc_c = Arc::clone(&rsi_data_wrapper_arc);
let symbol_c = symbol.clone();
let mut input_rt_data_c = rt_price_vec.clone();
task_vec.push(tokio::spawn(async move {
let mut rsi_data_vec: Vec<RsiData> = Vec::new();
let mut rsi_data = RsiData::new();
@ -56,18 +56,17 @@ pub async fn rsi(
let mut rsi: f64 = 0.0;
let mut last_close_time = 0;
if input_rt_data_c[search_result.unwrap()].1.len() < rsi_number + 1 {
if input_rt_data_c.len() < rsi_number + 1 {
rsi_data.rsi_value = 0.0;
rsi_data.close_time = 0;
rsi_data_vec.push(rsi_data.clone());
} else {
read_data_vec = input_rt_data_c[search_result.unwrap()].1.clone();
if read_data_vec.len() >= (150 + rsi_number) as usize {
read_data_vec.reverse();
read_data_vec.truncate((150 + rsi_number) as usize);
read_data_vec.reverse();
if input_rt_data_c.len() >= (150 + rsi_number) as usize {
input_rt_data_c.reverse();
input_rt_data_c.truncate((150 + rsi_number) as usize);
input_rt_data_c.reverse();
}
let window_iter = read_data_vec.windows(rsi_number + 1);
let window_iter = input_rt_data_c.windows(rsi_number + 1);
let mut prev_avg_ups: Option<f64> = None;
let mut prev_avg_downs: Option<f64> = None;
@ -143,7 +142,7 @@ pub async fn rsi(
rsi_data_vec.push(rsi_data.clone());
}
let mut rsi_data_wrapper_lock = rsi_data_wrapper_arc_c.lock().await;
rsi_data_wrapper_lock.push((element_c.symbol.clone(), rsi_data_vec.clone()));
rsi_data_wrapper_lock.insert(symbol_c, rsi_data_vec.clone());
}
}));
}

View File

@ -3,12 +3,13 @@
use crate::database_control::*;
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use crate::strategy_team::FilteredData;
use crate::strategy_team::FilteredDataValue;
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 {
@ -28,29 +29,28 @@ impl SmaData {
// Binance MA (closeprice)
pub async fn sma(
moving_number: usize,
input_rt_data: &Vec<(String, Vec<RealtimePriceData>)>,
filtered_symbols: &Vec<FilteredData>,
) -> Result<Vec<(String, Vec<SmaData>)>, Box<dyn std::error::Error + Send + Sync>> {
input_rt_data: &HashMap<String, Vec<RealtimePriceData>>,
filtered_symbols: &HashMap<String, FilteredDataValue>,
) -> Result<HashMap<String, Vec<SmaData>>, Box<dyn std::error::Error + Send + Sync>> {
if filtered_symbols.is_empty() {
Err("Err")?;
}
let mut sma_data_wrapper: Vec<(String, Vec<SmaData>)> = Vec::new();
let mut sma_data_wrapper: HashMap<String, Vec<SmaData>> = HashMap::new();
let mut sma_data_wrapper_arc = Arc::new(Mutex::new(sma_data_wrapper));
let mut task_vec = Vec::new();
for filtered_symbol in filtered_symbols {
let filtered_symbol_c = filtered_symbol.clone();
let sma_data_wrapper_arc_c = Arc::clone(&sma_data_wrapper_arc);
let input_rt_data_c = input_rt_data.clone();
task_vec.push(tokio::spawn(async move {
let search_result = input_rt_data_c.iter().position(|x| x.0 == *filtered_symbol_c.symbol);
if search_result.is_some() {
for (symbol, filtered_data) in filtered_symbols {
if let Some(vec) = input_rt_data.get(symbol) {
let sma_data_wrapper_arc_c = Arc::clone(&sma_data_wrapper_arc);
let symbol_c = symbol.clone();
let rt_price_data = vec.clone();
task_vec.push(tokio::spawn(async move {
let mut sma_data = SmaData::new();
let mut sma_data_vec: Vec<SmaData> = Vec::new();
if input_rt_data_c[search_result.unwrap()].1.len() >= moving_number {
let mut iter = input_rt_data_c[search_result.unwrap()].1.windows(moving_number);
if rt_price_data.len() >= moving_number {
let mut iter = rt_price_data.windows(moving_number);
for buffer in iter {
let mut avg = 0.0;
for element in buffer {
@ -64,9 +64,9 @@ pub async fn sma(
}
}
let mut sma_data_wrapper_lock = sma_data_wrapper_arc_c.lock().await;
sma_data_wrapper_lock.push((filtered_symbol_c.symbol.clone(), sma_data_vec.clone()));
}
}));
sma_data_wrapper_lock.insert(symbol_c, sma_data_vec.clone());
}));
}
}
try_join_all(task_vec).await?;
let a = sma_data_wrapper_arc.lock().await.to_owned();
@ -76,29 +76,28 @@ pub async fn sma(
// Binance MA ((open+close)/2)
pub async fn sma_opclo(
moving_number: usize,
input_rt_data: &Vec<(String, Vec<RealtimePriceData>)>,
filtered_symbols: &Vec<FilteredData>,
) -> Result<Vec<(String, Vec<SmaData>)>, Box<dyn std::error::Error + Send + Sync>> {
input_rt_data: &HashMap<String, Vec<RealtimePriceData>>,
filtered_symbols: &HashMap<String, FilteredDataValue>,
) -> Result<HashMap<String, Vec<SmaData>>, Box<dyn std::error::Error + Send + Sync>> {
if filtered_symbols.is_empty() {
Err("Err")?;
}
let mut sma_data_wrapper: Vec<(String, Vec<SmaData>)> = Vec::new();
let mut sma_data_wrapper: HashMap<String, Vec<SmaData>> = HashMap::new();
let mut sma_data_wrapper_arc = Arc::new(Mutex::new(sma_data_wrapper));
let mut task_vec = Vec::new();
for filtered_symbol in filtered_symbols {
let filtered_symbol_c = filtered_symbol.clone();
let sma_data_wrapper_arc_c = Arc::clone(&sma_data_wrapper_arc);
let input_rt_data_c = input_rt_data.clone();
task_vec.push(tokio::spawn(async move {
let search_result = input_rt_data_c.iter().position(|x| x.0 == *filtered_symbol_c.symbol);
if search_result.is_some() {
for (symbol, filtered_data) in filtered_symbols {
if let Some(vec) = input_rt_data.get(symbol) {
let sma_data_wrapper_arc_c = Arc::clone(&sma_data_wrapper_arc);
let symbol_c = symbol.clone();
let rt_price_data = vec.clone();
task_vec.push(tokio::spawn(async move {
let mut sma_data = SmaData::new();
let mut sma_data_vec: Vec<SmaData> = Vec::new();
if input_rt_data_c[search_result.unwrap()].1.len() >= moving_number {
let mut iter = input_rt_data_c[search_result.unwrap()].1.windows(moving_number);
if rt_price_data.len() >= moving_number {
let mut iter = rt_price_data.windows(moving_number);
for buffer in iter {
let mut avg = 0.0;
for element in buffer {
@ -112,9 +111,9 @@ pub async fn sma_opclo(
}
}
let mut sma_data_wrapper_lock = sma_data_wrapper_arc_c.lock().await;
sma_data_wrapper_lock.push((filtered_symbol_c.symbol.clone(), sma_data_vec.clone()));
}
}));
sma_data_wrapper_lock.insert(symbol_c, sma_data_vec.clone());
}));
}
}
try_join_all(task_vec).await?;
let a = sma_data_wrapper_arc.lock().await.to_owned();

View File

@ -4,13 +4,14 @@
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::FilteredData;
use crate::strategy_team::FilteredDataValue;
use futures::{future::try_join_all, lock::Mutex};
use serde::Deserialize;
use sqlx::FromRow;
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 {
@ -50,39 +51,31 @@ pub async fn stoch_rsi(
stoch_rsi_length: usize,
smooth_k: usize,
smooth_d: usize,
input_rt_data: &Vec<(String, Vec<RealtimePriceData>)>,
filtered_symbols: &Vec<FilteredData>
) -> Result<Vec<(String, Vec<StochRsiData>)>, Box<dyn std::error::Error + Send + Sync>> {
let mut stoch_rsi_data_wrapper: Vec<(String, Vec<StochRsiData>)> = Vec::new();
input_rt_data: &HashMap<String, Vec<RealtimePriceData>>,
filtered_symbols: &HashMap<String, FilteredDataValue>,
) -> Result<HashMap<String, Vec<StochRsiData>>, Box<dyn std::error::Error + Send + Sync>> {
let mut stoch_rsi_data_wrapper: HashMap<String, Vec<StochRsiData>> = HashMap::new();
let mut stoch_rsi_data_wrapper_arc = Arc::new(Mutex::new(stoch_rsi_data_wrapper));
let mut stoch_rsi_data_vec: Vec<StochRsiData> = Vec::new();
if rsi_length == 0 || stoch_rsi_length == 0 || smooth_k == 0 || smooth_d == 0 {
panic!(
"rsi_length or stoch_rsi_length or smooth_k or smooth_d can't be 0! rsi_length: {}, stoch_rsi_length: {}, k:{}, d:{}",
rsi_length, stoch_rsi_length, smooth_k, smooth_d
);
}
let rsi_data_vec = rsi(rsi_length, input_rt_data, filtered_symbols).await?;
let rsi_data_map = rsi(rsi_length, input_rt_data, filtered_symbols).await?;
let mut task_vec = Vec::new();
for element in rsi_data_vec {
for (symbol, rsi_vec) in rsi_data_map {
let mut stoch_rsi_data = StochRsiData::new();
let mut stoch_rsi_k_data = StochRsiKData::new();
let stoch_rsi_data_wrapper_arc_c = Arc::clone(&stoch_rsi_data_wrapper_arc);
let element_c = element.clone();
let symbol_c = symbol.clone();
task_vec.push(tokio::spawn(async move {
let mut stoch_rsi_data_vec: Vec<StochRsiData> = Vec::new();
let mut stoch_rsi_k_data_vec: Vec<StochRsiKData> = Vec::new();
let mut stoch_rsi_vec: Vec<RsiData> = Vec::new();
let mut stoch_rsi = RsiData::new();
if element_c.1.len() >= stoch_rsi_length
&& element_c.1.len() >= smooth_k
&& element_c.1.len() >= smooth_d
if rsi_vec.len() >= stoch_rsi_length
&& rsi_vec.len() >= smooth_k
&& rsi_vec.len() >= smooth_d
{
let mut read_data_vec = element_c.1;
let window_iter = read_data_vec.windows(stoch_rsi_length);
let window_iter = rsi_vec.windows(stoch_rsi_length);
for buffer_window in window_iter {
let max_value = buffer_window
@ -131,7 +124,7 @@ pub async fn stoch_rsi(
stoch_rsi_data_vec.push(stoch_rsi_data.clone());
}
let mut stoch_rsi_data_wrapper_lock = stoch_rsi_data_wrapper_arc_c.lock().await;
stoch_rsi_data_wrapper_lock.push((element_c.0.clone(), stoch_rsi_data_vec.clone()));
stoch_rsi_data_wrapper_lock.insert(symbol_c, stoch_rsi_data_vec.clone());
}
}));
}

View File

@ -1,11 +1,21 @@
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use super::{HashMap, FilteredDataValue};
use std::sync::Arc;
use futures::future::try_join_all;
use tokio::sync::Mutex;
#[derive(PartialEq, Clone, Debug)]
pub enum SuperTrendArea { UP, DOWN }
#[derive(PartialEq, Clone, Debug)]
pub enum SuperTrendSignal { BUY, SELL }
#[derive(Clone, Debug)]
pub struct SupertrendData {
pub band_value: f64,
pub signal: Option<String>, // BUY or SELL
pub area: String, // UP or DOWN
pub close_time: i64,
pub signal: Option<SuperTrendSignal>, // BUY or SELL
pub area: SuperTrendArea, // UP or DOWN
pub close_time: i64
}
impl SupertrendData {
@ -13,8 +23,8 @@ impl SupertrendData {
let a = SupertrendData {
band_value: 0.0,
signal: None,
area: String::new(),
close_time: 0,
area: SuperTrendArea::DOWN,
close_time: 0
};
a
@ -55,184 +65,193 @@ impl ATRData {
}
}
// TODO: should return type be Option?
// Implementation from TradingView Script (SuperTrend by KivancOzbilgic, source price: closeprice)
pub async fn supertrend(
symbol: &String,
input_rt_data: &Vec<(String, Vec<RealtimePriceData>)>,
// return key: close_time
pub async fn supertrend(
atr_period: usize,
multiplier: f64,
is_original_method: bool,
) -> Option<Vec<SupertrendData>> {
let symbol_search_result = input_rt_data.iter().position(|x| x.0 == *symbol);
match symbol_search_result {
Some(T) => {
if input_rt_data[T].1.len() >= atr_period && atr_period >= 2 {
// making True Range
let mut true_range_vec: Vec<TrueRangeData> = Vec::new();
let mut tr_data = TrueRangeData::new();
let mut tr_val_hl = 0.0; // High - Low
let mut tr_val_hcp = 0.0; // Abs(High - Close_previous)
let mut tr_val_lcp = 0.0; // Abs(Low - Close_previous)
let window_vec = input_rt_data[T].1.windows(2);
tr_data.tr_value = input_rt_data[T].1.first().unwrap().high_price
- input_rt_data[T].1.first().unwrap().low_price;
tr_data.close_time = input_rt_data[T].1.first().unwrap().close_time;
true_range_vec.push(tr_data.clone());
for buffer in window_vec {
tr_val_hl = buffer[1].high_price - buffer[1].low_price;
tr_val_hcp = (buffer[1].high_price - buffer[0].close_price).abs();
tr_val_lcp = (buffer[1].low_price - buffer[0].close_price).abs();
tr_data.tr_value = tr_val_hl.max(tr_val_hcp.max(tr_val_lcp));
tr_data.close_time = buffer[1].close_time;
input_rt_data: &HashMap<String, Vec<RealtimePriceData>>,
filtered_symbols: &HashMap<String, FilteredDataValue>,
) -> Result<HashMap<String, Vec<SupertrendData>>, Box<dyn std::error::Error + Send + Sync>> {
let mut supertrend_data_wrapper: HashMap<String, Vec<SupertrendData>> = HashMap::new();
let mut supertrend_data_wrapper_arc = Arc::new(Mutex::new(supertrend_data_wrapper));
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 {
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();
task_vec.push(tokio::spawn(async move {
// making True Range
let mut true_range_vec: Vec<TrueRangeData> = Vec::new();
let mut tr_data = TrueRangeData::new();
let mut tr_val_hl = 0.0; // High - Low
let mut tr_val_hcp = 0.0; // Abs(High - Close_previous)
let mut tr_val_lcp = 0.0; // Abs(Low - Close_previous)
let window_vec = 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());
}
// making Average True Range
let mut average_true_range_vec: Vec<ATRData> = Vec::new();
let mut atr_data = ATRData::new();
if is_original_method == true {
// original calculation of ATR
let mut first_value = 0.0;
let mut first_vec = &true_range_vec[0..atr_period];
for element in first_vec {
first_value += element.tr_value;
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());
}
first_value /= (atr_period) as f64;
atr_data.atr_value = first_value;
atr_data.close_time = first_vec.last().unwrap().close_time;
average_true_range_vec.push(atr_data.clone());
let mut temp_prev_atr_value = first_value;
for element in &true_range_vec[atr_period..] {
atr_data.atr_value = ((temp_prev_atr_value * ((atr_period - 1) as f64))
+ element.tr_value)
/ atr_period as f64;
atr_data.close_time = element.close_time;
average_true_range_vec.push(atr_data.clone());
temp_prev_atr_value = atr_data.atr_value;
}
} else {
// Calculation of ATR from SMA True Range with atr_period
let window = true_range_vec.windows(atr_period);
let mut sum = 0.0;
for buffer in window {
for element in buffer {
sum += element.tr_value;
// making Average True Range
let mut average_true_range_vec: Vec<ATRData> = Vec::new();
let mut atr_data = ATRData::new();
if is_original_method == true {
// original calculation of ATR
let mut first_value = 0.0;
let mut first_vec = &true_range_vec[0..atr_period];
for element in first_vec {
first_value += element.tr_value;
}
atr_data.atr_value = sum / atr_period as f64;
atr_data.close_time = buffer.last().unwrap().close_time;
first_value /= (atr_period) as f64;
atr_data.atr_value = first_value;
atr_data.close_time = first_vec.last().unwrap().close_time;
average_true_range_vec.push(atr_data.clone());
sum = 0.0;
}
}
// making Supertrend
#[derive(Clone)]
struct BandData {
basic_upperband: f64,
basic_lowerband: f64,
close_time: i64,
}
let mut supertrend_vec: Vec<SupertrendData> = Vec::new();
let mut supertrend_data = SupertrendData::new();
let mut band_vec: Vec<BandData> = Vec::new();
let mut band_data = BandData {
basic_upperband: 0.0,
basic_lowerband: 0.0,
close_time: 0,
};
let closetime_search_result = input_rt_data[T].1.iter().position(|x| {
x.close_time == average_true_range_vec.first().unwrap().close_time
});
let mut rt_data = &input_rt_data[T].1[closetime_search_result.unwrap()..];
let zipped = rt_data.iter().zip(average_true_range_vec);
for element in zipped {
band_data.basic_upperband = ((element.0.high_price + element.0.low_price)
/ 2.0)
- (multiplier * element.1.atr_value);
band_data.basic_lowerband = ((element.0.high_price + element.0.low_price)
/ 2.0)
+ (multiplier * element.1.atr_value);
band_data.close_time = element.1.close_time;
band_vec.push(band_data.clone());
}
let mut zipped = rt_data.iter().zip(band_vec);
let first_element = zipped.next().unwrap();
let mut prev_close_price = first_element.0.close_price;
let mut final_upperband = 0.0;
let mut final_lowerband = 0.0;
let mut prev_final_upperband = first_element.1.basic_upperband;
let mut prev_final_lowerband = first_element.1.basic_lowerband;
let mut trend = 1; // 1 means up trend, -1 means down trend
let mut prev_trend = 1;
for element in zipped {
// set final upperband
if prev_close_price > prev_final_upperband {
final_upperband = prev_final_upperband.max(element.1.basic_upperband);
let mut temp_prev_atr_value = first_value;
for element in &true_range_vec[atr_period..] {
atr_data.atr_value = ((temp_prev_atr_value * ((atr_period - 1) as f64))
+ element.tr_value)
/ atr_period as f64;
atr_data.close_time = element.close_time;
average_true_range_vec.push(atr_data.clone());
temp_prev_atr_value = atr_data.atr_value;
}
} else {
final_upperband = element.1.basic_upperband;
// 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;
}
}
// 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;
// making Supertrend
#[derive(Clone)]
struct BandData {
basic_upperband: f64,
basic_lowerband: f64,
close_time: i64,
}
// set supertrend
if trend == -1 && element.0.close_price > prev_final_lowerband {
supertrend_data.area = String::from("UP");
trend = 1;
} else if trend == 1 && element.0.close_price < prev_final_upperband {
supertrend_data.area = String::from("DOWN");
trend = -1;
let mut supertrend_vec: Vec<SupertrendData> = Vec::new();
let mut supertrend_data = SupertrendData::new();
let mut band_vec: Vec<BandData> = Vec::new();
let mut band_data = BandData {
basic_upperband: 0.0,
basic_lowerband: 0.0,
close_time: 0,
};
let closetime_search_result = 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)
- (multiplier * element.1.atr_value);
band_data.basic_lowerband = ((element.0.high_price + element.0.low_price)
/ 2.0)
+ (multiplier * element.1.atr_value);
band_data.close_time = element.1.close_time;
band_vec.push(band_data.clone());
}
if supertrend_data.area.contains("UP") {
supertrend_data.band_value = final_upperband;
} else {
supertrend_data.band_value = final_lowerband;
let mut zipped = rt_data.iter().zip(band_vec);
let first_element = zipped.next().unwrap();
let mut prev_close_price = first_element.0.close_price;
let mut final_upperband = 0.0;
let mut final_lowerband = 0.0;
let mut prev_final_upperband = first_element.1.basic_upperband;
let mut prev_final_lowerband = first_element.1.basic_lowerband;
let mut trend = 1; // 1 means up trend, -1 means down trend
let mut prev_trend = 1;
for element in zipped {
// set final upperband
if prev_close_price > prev_final_upperband {
final_upperband = prev_final_upperband.max(element.1.basic_upperband);
} else {
final_upperband = element.1.basic_upperband;
}
// set final lowerband
if prev_close_price < prev_final_lowerband {
final_lowerband = prev_final_lowerband.min(element.1.basic_lowerband);
} else {
final_lowerband = element.1.basic_lowerband;
}
// set supertrend
if trend == -1 && element.0.close_price > prev_final_lowerband {
supertrend_data.area = SuperTrendArea::UP;
trend = 1;
} else if trend == 1 && element.0.close_price < prev_final_upperband {
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 {
supertrend_data.signal = Some(SuperTrendSignal::SELL);
} else {
supertrend_data.signal = None;
}
supertrend_data.close_time = element.1.close_time;
supertrend_vec.push(supertrend_data.clone());
prev_close_price = element.0.close_price;
prev_final_upperband = final_upperband;
prev_final_lowerband = final_lowerband;
prev_trend = trend;
}
if trend == 1 && prev_trend == -1 {
supertrend_data.signal = Some(String::from("BUY"));
} else if trend == -1 && prev_trend == 1 {
supertrend_data.signal = Some(String::from("SELL"));
} else {
supertrend_data.signal = None;
}
supertrend_data.close_time = element.1.close_time;
supertrend_vec.push(supertrend_data.clone());
prev_close_price = element.0.close_price;
prev_final_upperband = final_upperband;
prev_final_lowerband = final_lowerband;
prev_trend = trend;
}
Some(supertrend_vec)
} else {
None
let mut supertrend_data_wrapper_lock = supertrend_data_wrapper_arc_c.lock().await;
supertrend_data_wrapper_lock.insert(symbol_c, supertrend_vec.clone());
}));
}
}
None => None,
}
try_join_all(task_vec).await?;
let a = supertrend_data_wrapper_arc.lock().await.to_owned();
Ok(a)
}