tradingbot/src/coex/assets_managing_team.rs
2024-04-13 14:04:09 +09:00

612 lines
22 KiB
Rust

// 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::<Vec<_>>();
let win_collection_vec = sell_history_vec
.iter()
.filter(|&a| a.pure_profit_percent >= 0.0)
.collect::<Vec<_>>();
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
}