From bc1e429377f80f871c2deb78483588563665b933 Mon Sep 17 00:00:00 2001 From: Sik Yoon Date: Sat, 18 Nov 2023 21:21:43 +0900 Subject: [PATCH] Add kelly criterion --- src/coex/assets_managing_team.rs | 211 +++++++++++++++++++++++++------ 1 file changed, 170 insertions(+), 41 deletions(-) diff --git a/src/coex/assets_managing_team.rs b/src/coex/assets_managing_team.rs index 420e8a2..b072a48 100644 --- a/src/coex/assets_managing_team.rs +++ b/src/coex/assets_managing_team.rs @@ -1,4 +1,4 @@ -// assets_managing_team manages [asset_manage_announcement] and [wallet] ([wallet_testnet] as well) +// assets_managing_team manages [asset_manage_announcement], [kelly_criterion], and[wallet] ([wallet_testnet] as well) use crate::coex::exchange_team::*; use crate::database_control::*; @@ -10,7 +10,27 @@ use rust_decimal::{prelude::ToPrimitive, Decimal, RoundingStrategy}; use rust_decimal_macros::dec; use serde_json::{Result, Value}; use sqlx::FromRow; -use crate::coex::order_team::DBlist; +use crate::coex::order_team::{DBlist, SellHistoryList}; + +#[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 { @@ -58,37 +78,34 @@ impl DBlist for WalletInfo { } } +#[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); - if RUNNING_MODE == SIMUL { - set_unit_trade_usdt = dec!(100.0); // $100 for each trade - } else if RUNNING_MODE == REAL { - // define protect_rate and unit_trade_usdt as high as total_usdt_amount - if dec!(0.0) <= asset_info.current_total_usdt && asset_info.current_total_usdt < dec!(1000.0) { - set_unit_trade_usdt = dec!(50.0); // $100 for each trade - // set_unit_trade_usdt = decimal_mul(asset_info.current_total_usdt, dec!(0.8)); // 80% of total usdt - } else if dec!(1000.0) <= asset_info.current_total_usdt - { - // set_unit_trade_usdt = dec!(150.0); // $150 for each trade - set_unit_trade_usdt = decimal_mul(asset_info.current_total_usdt, dec!(0.05)); - - if set_unit_trade_usdt <= dec!(20) { - set_unit_trade_usdt = dec!(0); - } else { - let truncated = decimal_mul(decimal_mul(set_unit_trade_usdt, dec!(0.1)).trunc(), dec!(10)); - let difference = decimal_sub(set_unit_trade_usdt, truncated); - - if difference >= dec!(5) { - set_unit_trade_usdt = decimal_add(truncated, dec!(5)); - } else { - set_unit_trade_usdt = truncated; - } - } - } - } + set_unit_trade_usdt = dec!(30.0); // $30 for each trade // update fields in [asset_manage_announcement] table let update_table_name = String::from("asset_manage_announcement"); @@ -177,20 +194,19 @@ pub async fn update_current_total_usdt() { ]; } else { let asset_info = select_asset_manage_announcement().await; - let mut profit: Decimal; - if asset_info.initial_usdt.is_zero() { - profit = dec!(0.0); - } else { - profit = decimal_sub( - decimal_div(asset_info.current_total_usdt, asset_info.initial_usdt), - dec!(1), - ); - } + + 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"), - asset_info.available_usdt.to_string(), + current_total_usdt.to_string(), ), (String::from("profit"), profit.to_string()), ( @@ -205,7 +221,6 @@ pub async fn update_current_total_usdt() { update_record3(&update_table_name, &update_values, &update_condition) .await .unwrap(); - set_unit_usdt().await; } // monitor whether [buy_ordered_coin_list] is empty to update current_total_usdt and available_usdt in [asset_manage_announcement] @@ -235,8 +250,10 @@ pub async fn monitoring_asset_usdt(previous_result: &mut bool, client: &Client) // (/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 @@ -253,6 +270,60 @@ pub async fn monitoring_asset_usdt(previous_result: &mut bool, client: &Client) 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(); + + 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; @@ -304,8 +375,20 @@ 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 available_usdt = asset_info.current_total_usdt; + 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 + available_usdt = decimal_mul(asset_info.current_total_usdt, dec!(0.05)); + } + let unit_trade_usdt = asset_info.unit_trade_usdt; // set available_usdt with current_total_usdt and update is_tradable @@ -398,6 +481,11 @@ pub async fn get_unit_trade_usdt() -> Decimal { 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(); @@ -446,3 +534,44 @@ pub async fn fetch_free_usdt() -> Decimal { .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 +}