// assets_managing_team manages [asset_manage_announcement], [kelly_criterion], and[wallet] ([wallet_testnet] as well) use crate::coex::exchange_team::*; use crate::coex::order_team::{DBlist, SellHistoryList}; use crate::database_control::*; use crate::decimal_funcs::*; use crate::RunningMode::*; use crate::RUNNING_MODE; use log; use reqwest::Client; use rust_decimal::{prelude::ToPrimitive, Decimal, RoundingStrategy}; use rust_decimal_macros::dec; use serde_json::{Result, Value}; use sqlx::FromRow; #[derive(Debug, FromRow)] pub struct AchievementEvaluationInfo { pub strategist: u16, pub invested_usdt: Decimal, pub usdt_profit: Decimal, pub profit_percent: f64, } impl DBlist for AchievementEvaluationInfo { fn new() -> AchievementEvaluationInfo { let a = AchievementEvaluationInfo { strategist: 0, invested_usdt: Decimal::new(0, 8), usdt_profit: Decimal::new(0, 8), profit_percent: 0.0, }; a } } #[derive(Debug, FromRow)] pub struct AssetInfo { pub initial_usdt: Decimal, pub current_total_usdt: Decimal, pub profit: Decimal, pub realtime_expected_total_usdt: f64, pub realtime_profit: f64, pub available_usdt: Decimal, pub is_tradable: u8, pub unit_trade_usdt: Decimal, } impl DBlist for AssetInfo { fn new() -> AssetInfo { let a = AssetInfo { initial_usdt: Decimal::new(0, 8), current_total_usdt: Decimal::new(0, 8), profit: Decimal::new(0, 8), realtime_expected_total_usdt: 0.0, realtime_profit: 0.0, available_usdt: Decimal::new(0, 8), is_tradable: 0, unit_trade_usdt: Decimal::new(0, 8), }; a } } #[derive(Debug, FromRow)] pub struct WalletInfo { pub asset: String, pub free: Decimal, pub locked: Decimal, } impl DBlist for WalletInfo { fn new() -> WalletInfo { let a = WalletInfo { asset: String::new(), free: Decimal::new(0, 8), locked: Decimal::new(0, 8), }; a } } #[derive(Debug, FromRow)] pub struct KellyCriterionInfo { pub betting_rate: f64, pub win_rate: f64, pub lose_rate: f64, pub profit_rate: f64, pub loss_rate: f64, } impl DBlist for KellyCriterionInfo { fn new() -> KellyCriterionInfo { let a = KellyCriterionInfo { betting_rate: 0.0, win_rate: 0.0, lose_rate: 0.0, profit_rate: 0.0, loss_rate: 0.0, }; a } } // set unit_trade_usdt pub async fn set_unit_usdt() { let asset_info = select_asset_manage_announcement().await; let mut set_unit_trade_usdt = Decimal::new(0, 8); set_unit_trade_usdt = dec!(50.0); // $50 for each trade // update fields in [asset_manage_announcement] table let update_table_name = String::from("asset_manage_announcement"); let update_values = vec![( String::from("unit_trade_usdt"), set_unit_trade_usdt.to_string(), )]; let update_condition = vec![(String::from("id"), String::from("1"))]; update_record3(&update_table_name, &update_values, &update_condition) .await .unwrap(); } // add extra usdt to initial usdt pub async fn add_extra_usdt(extra_usdt: Decimal) { // update fields in [asset_manage_announcement] table let update_table_name = String::from("asset_manage_announcement"); let mut value_build = String::from("initial_usdt + "); value_build.push_str( extra_usdt .round_dp_with_strategy(8, RoundingStrategy::ToZero) .to_string() .as_str(), ); let update_values = vec![(String::from("initial_usdt"), value_build)]; let update_condition = vec![(String::from("id"), String::from("1"))]; update_record3(&update_table_name, &update_values, &update_condition) .await .unwrap(); let mut value_build = String::from("current_total_usdt + "); value_build.push_str( extra_usdt .round_dp_with_strategy(8, RoundingStrategy::ToZero) .to_string() .as_str(), ); let update_values = vec![(String::from("current_total_usdt"), value_build)]; let update_condition = vec![(String::from("id"), String::from("1"))]; update_record3(&update_table_name, &update_values, &update_condition) .await .unwrap(); add_available_usdt(extra_usdt.round_dp_with_strategy(8, RoundingStrategy::ToZero)).await; // add additional_usdt into [wallet_simul] let mut update_table_name = String::new(); unsafe { if RUNNING_MODE == SIMUL { let update_table_name = String::from("wallet"); let mut value_build = String::from("free + "); value_build.push_str(extra_usdt.to_string().as_str()); let update_values = vec![(String::from("free"), value_build)]; let update_condition = vec![(String::from("asset"), String::from("USDT"))]; update_record3(&update_table_name, &update_values, &update_condition) .await .unwrap(); } } println!("add_extra_usdt 완료"); } // update current_total_usdt and profit of asset pub async fn update_current_total_usdt() { let mut update_values: Vec<(String, String)> = Vec::new(); unsafe { if RUNNING_MODE == REAL || RUNNING_MODE == TEST { let free_usdt = fetch_free_usdt().await; let initial_usdt = select_asset_manage_announcement().await.initial_usdt; // println!("update_current_total_usdt실행 free_usdt: {}, initial_usdt: {}", free_usdt, initial_usdt); let mut profit: Decimal; if initial_usdt.is_zero() { profit = dec!(0.0); } else { profit = decimal_sub(decimal_div(free_usdt, initial_usdt), dec!(1)); } update_values = vec![ (String::from("current_total_usdt"), free_usdt.to_string()), (String::from("profit"), profit.to_string()), ( String::from("realtime_expected_total_usdt"), 0.0.to_string(), ), (String::from("realtime_profit"), 0.0.to_string()), ]; } else { let asset_info = select_asset_manage_announcement().await; let achievement_evaluation = select_achievement_evaluation().await; let balance = decimal_sub( achievement_evaluation.usdt_profit, achievement_evaluation.invested_usdt, ); let current_total_usdt = decimal_add(asset_info.initial_usdt, balance); let profit = decimal_sub( decimal_div(current_total_usdt, asset_info.initial_usdt), dec!(1), ); update_values = vec![ ( String::from("current_total_usdt"), current_total_usdt.to_string(), ), (String::from("profit"), profit.to_string()), ( String::from("realtime_expected_total_usdt"), 0.0.to_string(), ), (String::from("realtime_profit"), 0.0.to_string()), ]; } } let update_table_name = String::from("asset_manage_announcement"); let update_condition = vec![(String::from("id"), String::from("1"))]; update_record3(&update_table_name, &update_values, &update_condition) .await .unwrap(); } // monitor whether [buy_ordered_coin_list] is empty to update current_total_usdt and available_usdt in [asset_manage_announcement] pub async fn monitoring_asset_usdt(previous_result: &mut bool, client: &Client) -> Result<()> { let table_name = String::from("buy_ordered_coin_list"); let condition = None; let buy_list_result = exists_record(&table_name, &condition).await; let table_name = String::from("sell_ordered_coin_list"); let condition = None; let sell_list_result = exists_record(&table_name, &condition).await; if buy_list_result == false && sell_list_result == false { unsafe { if buy_list_result == false && sell_list_result == false && *previous_result == true && (RUNNING_MODE == TEST || RUNNING_MODE == REAL) { let mut result = crate::coex::order_team::account_information(&client).await; // (/api, Weight(IP) 10) loop { if result.is_ok() { break; } else { log::warn!("retry account_information()"); tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; result = crate::coex::order_team::account_information(&client).await; // (/api, Weight(IP) 10) } } // FIXME: 아래 함수들을 고쳐야함. 순환 오류 빠지는 듯 update_current_total_usdt().await; set_available_usdt().await; set_unit_usdt().await; } else if buy_list_result == false && sell_list_result == false && *previous_result == true && (RUNNING_MODE == SIMUL) { update_current_total_usdt().await; set_is_tradable().await; } } *previous_result = false; } else { update_realtime_asset().await; *previous_result = true; } Ok(()) } // update kelly_criterion table and kelly_betting_usdt pub async fn update_kelly_criterion() -> Result<()> { let table_name = String::from("sell_history"); let condition = None; let is_exist = exists_record(&table_name, &condition).await; if is_exist == true { let select_columns = String::from("*"); let select_condition = None; let data_struct = SellHistoryList::new(); let sell_history_vec = select_record( &table_name, &select_columns, &select_condition, &data_struct, ) .await .unwrap(); // FIXME: let least_number = 50; let least_number = 50; // the least number of sell hostory to calculate kelly criterion if sell_history_vec.len() >= least_number { let lose_collection_vec = sell_history_vec .iter() .filter(|&a| a.pure_profit_percent < 0.0) .collect::>(); let win_collection_vec = sell_history_vec .iter() .filter(|&a| a.pure_profit_percent >= 0.0) .collect::>(); let win_rate: f64 = (win_collection_vec.len() as f64) / (sell_history_vec.len() as f64); let lose_rate: f64 = 1.0 - win_rate; let loss_sum = lose_collection_vec .iter() .fold(0.0, |acc, x| acc + x.pure_profit_percent); let loss_rate_avg = (loss_sum * 0.01) / (lose_collection_vec.len() as f64) * -1.0; let profit_sum = win_collection_vec .iter() .fold(0.0, |acc, x| acc + x.pure_profit_percent); let profit_rate_avg = (profit_sum * 0.01) / (win_collection_vec.len() as f64); let betting_rate = (win_rate / loss_rate_avg) - (lose_rate / profit_rate_avg); // update kelly_criterion let update_table_name = String::from("kelly_criterion"); let update_value = vec![ (String::from("betting_rate"), betting_rate.to_string()), (String::from("win_rate"), win_rate.to_string()), (String::from("lose_rate"), lose_rate.to_string()), (String::from("profit_rate"), profit_rate_avg.to_string()), (String::from("loss_rate"), loss_rate_avg.to_string()), ]; let update_condition = vec![(String::from("id"), String::from("1"))]; update_record3(&update_table_name, &update_value, &update_condition) .await .unwrap(); } } Ok(()) } // update realtime_expected_total_usdt and realtime_profit pub async fn update_realtime_asset() { let asset_info = select_asset_manage_announcement().await; let scoreboard_list = select_scoreboard().await; let scoreboard_short = scoreboard_list.first().unwrap(); let scoreboard_long = scoreboard_list.last().unwrap(); if scoreboard_short.total_number_of_coin != 0 || scoreboard_long.total_number_of_coin != 0 { // calculate expect_realtime_asset let available_usdt = asset_info.available_usdt.to_f64().unwrap(); let total_used_usdt = scoreboard_short.total_used_usdt + scoreboard_long.total_used_usdt; let current_pos_profit_usdt = scoreboard_short.total_pos_profit_usdt + scoreboard_long.total_pos_profit_usdt; let current_neg_profit_usdt = scoreboard_short.total_neg_profit_usdt + scoreboard_long.total_neg_profit_usdt; let expect_realtime_expected_total_usdt = available_usdt + total_used_usdt + current_pos_profit_usdt + current_neg_profit_usdt; // calculate realtime_profit let mut realtime_profit = 0.0; let current_total_usdt = asset_info.current_total_usdt; if current_total_usdt.is_zero() { realtime_profit = 0.0; } else { realtime_profit = ((expect_realtime_expected_total_usdt / current_total_usdt.to_f64().unwrap()) - 1.0) * 100.0; } // update record let update_table_name = String::from("asset_manage_announcement"); let update_values = vec![ ( String::from("realtime_expected_total_usdt"), expect_realtime_expected_total_usdt.to_string(), ), (String::from("realtime_profit"), realtime_profit.to_string()), ]; let update_condition = vec![(String::from("id"), String::from("1"))]; update_record3(&update_table_name, &update_values, &update_condition) .await .unwrap(); } } // set available_usdt pub async fn set_available_usdt() { let update_table_name = String::from("asset_manage_announcement"); // fetch current_total_usdt let mut asset_info = select_asset_manage_announcement().await; let kelly_criterion_info = select_kelly_criterion().await; let mut available_usdt: Decimal; if kelly_criterion_info.betting_rate >= 5.0 && kelly_criterion_info.betting_rate < 100.0 { available_usdt = decimal_mul( asset_info.current_total_usdt, rust_decimal::prelude::FromPrimitive::from_f64( kelly_criterion_info.betting_rate * 0.01, ) .unwrap(), ); } else if kelly_criterion_info.betting_rate >= 100.0 { available_usdt = asset_info.current_total_usdt; } else { // default: 5% of current_total_usdt // FIXME: dec!(0.05) available_usdt = decimal_mul(asset_info.current_total_usdt, dec!(1)); } let unit_trade_usdt = asset_info.unit_trade_usdt; // set available_usdt with current_total_usdt and update is_tradable let update_value = { if available_usdt > unit_trade_usdt { vec![ (String::from("available_usdt"), available_usdt.to_string()), (String::from("is_tradable"), (String::from("1"))), ] } else { vec![ (String::from("available_usdt"), available_usdt.to_string()), (String::from("is_tradable"), (String::from("0"))), ] } }; let update_condition = vec![(String::from("id"), String::from("1"))]; update_record3(&update_table_name, &update_value, &update_condition) .await .unwrap(); } // get available_usdt pub async fn get_available_usdt() -> Decimal { let update_table_name = String::from("asset_manage_announcement"); let mut asset_info = select_asset_manage_announcement().await; asset_info.available_usdt } // add available_usdt pub async fn add_available_usdt(add_usdt: Decimal) { let update_table_name = String::from("asset_manage_announcement"); let mut value_build = String::from("available_usdt + "); value_build.push_str(add_usdt.to_string().as_str()); let update_values = vec![(String::from("available_usdt"), value_build)]; let update_condition = vec![(String::from("id"), String::from("1"))]; update_record3(&update_table_name, &update_values, &update_condition) .await .unwrap(); } // subtract available_usdt pub async fn sub_available_usdt(sub_usdt: Decimal) { let update_table_name = String::from("asset_manage_announcement"); let mut value_build = String::from("available_usdt - "); value_build.push_str(sub_usdt.to_string().as_str()); let update_values = vec![(String::from("available_usdt"), value_build)]; let update_condition = vec![(String::from("id"), String::from("1"))]; update_record3(&update_table_name, &update_values, &update_condition) .await .unwrap(); } // set is_tradable as available_usdt pub async fn set_is_tradable() { let update_table_name = String::from("asset_manage_announcement"); let mut asset_info = select_asset_manage_announcement().await; let unit_trade_usdt = asset_info.unit_trade_usdt; let available_usdt = asset_info.available_usdt; let update_value = { if available_usdt > unit_trade_usdt { vec![(String::from("is_tradable"), (String::from("1")))] } else { vec![(String::from("is_tradable"), (String::from("0")))] } }; let update_condition = vec![(String::from("id"), String::from("1"))]; update_record3(&update_table_name, &update_value, &update_condition) .await .unwrap(); } // get is_tradable pub async fn get_is_tradable() -> bool { let asset_info = select_asset_manage_announcement().await; let is_tradable = asset_info.is_tradable; if asset_info.is_tradable == 1 { true } else { false } } pub async fn get_unit_trade_usdt() -> Decimal { let asset_info = select_asset_manage_announcement().await; asset_info.unit_trade_usdt } pub async fn get_current_total_usdt() -> Decimal { let asset_info = select_asset_manage_announcement().await; asset_info.current_total_usdt } pub async fn select_asset_manage_announcement() -> AssetInfo { let mut asset_info = AssetInfo::new(); let table_name = String::from("asset_manage_announcement"); let columns = String::from("*"); let condition = None; let select_result = select_record(&table_name, &columns, &condition, &asset_info) .await .unwrap(); let result_vec = select_result.first().unwrap(); asset_info.initial_usdt = result_vec.initial_usdt; asset_info.current_total_usdt = result_vec.current_total_usdt; asset_info.profit = result_vec.profit; asset_info.realtime_expected_total_usdt = result_vec.realtime_expected_total_usdt; asset_info.realtime_profit = result_vec.realtime_profit; asset_info.available_usdt = result_vec.available_usdt; asset_info.is_tradable = result_vec.is_tradable; asset_info.unit_trade_usdt = result_vec.unit_trade_usdt; asset_info } // fetch free usdt from wallet pub async fn fetch_free_usdt() -> Decimal { let wallet_info = WalletInfo::new(); let select_table_name = { unsafe { if RUNNING_MODE == SIMUL || RUNNING_MODE == REAL { String::from("wallet") } else { String::from("wallet_testnet") } } }; let select_columns_name = String::from("*"); let condition = Some(String::from("WHERE asset = 'USDT'")); let mut select_result = select_record( &select_table_name, &select_columns_name, &condition, &wallet_info, ) .await .unwrap(); select_result.first().unwrap().free } pub async fn select_kelly_criterion() -> KellyCriterionInfo { let mut kelly_criterion_info = KellyCriterionInfo::new(); let table_name = String::from("kelly_criterion"); let columns = String::from("*"); let condition = None; let select_result = select_record(&table_name, &columns, &condition, &kelly_criterion_info) .await .unwrap(); let result_vec = select_result.first().unwrap(); kelly_criterion_info.betting_rate = result_vec.betting_rate; kelly_criterion_info.win_rate = result_vec.win_rate; kelly_criterion_info.lose_rate = result_vec.lose_rate; kelly_criterion_info.profit_rate = result_vec.profit_rate; kelly_criterion_info.loss_rate = result_vec.loss_rate; kelly_criterion_info } pub async fn select_achievement_evaluation() -> AchievementEvaluationInfo { let mut achievement_evaluation_info = AchievementEvaluationInfo::new(); let table_name = String::from("achievement_evaluation"); let columns = String::from("*"); let condition = None; // FIXME: this code should be general let select_result = select_record( &table_name, &columns, &condition, &achievement_evaluation_info, ) .await .unwrap(); let result_vec = &select_result[2]; //strategist #3 achievement_evaluation_info.strategist = result_vec.strategist; achievement_evaluation_info.invested_usdt = result_vec.invested_usdt; achievement_evaluation_info.usdt_profit = result_vec.usdt_profit; achievement_evaluation_info.profit_percent = result_vec.profit_percent; achievement_evaluation_info }