diff --git a/src/coex/order_team.rs b/src/coex/order_team.rs index c26415a..fe0b6de 100644 --- a/src/coex/order_team.rs +++ b/src/coex/order_team.rs @@ -90,6 +90,61 @@ pub struct SellOrderedCoinList { pub is_long: u8, } +pub trait DBlist { + fn new() -> Self; +} + +impl DBlist for SellOrderedCoinList { + fn new() -> SellOrderedCoinList { + let a = SellOrderedCoinList { + id: 0, + symbol: String::new(), + buy_order_id: 0, + sell_order_id: 0, + transact_time: 0, + close_time: 0, + status: String::new(), + used_usdt: Decimal::new(0, 8), + get_usdt: Decimal::new(0, 8), + get_usdt_fee_adjusted: Decimal::new(0, 8), + buy_price: Decimal::new(0, 8), + sell_price: Decimal::new(0, 8), + base_qty_ordered: Decimal::new(0, 8), + pure_profit_percent: Decimal::new(0, 8), + maximum_profit_percent: 0.0, + registerer: 0, + is_long: 0, + }; + a + } +} + +impl DBlist for BuyOrderedCoinList { + fn new() -> BuyOrderedCoinList { + let a = BuyOrderedCoinList { + id: 0, + symbol: String::new(), + order_id: 0, + transact_time: 0, + close_time: 0, + status: String::new(), + used_usdt: Decimal::new(0, 8), + expected_get_usdt: 0.0, + expected_usdt_profit: 0.0, + buy_price: Decimal::new(0, 8), + current_price: Decimal::new(0, 8), + base_qty_ordered: Decimal::new(0, 8), + base_qty_fee_adjusted: Decimal::new(0, 8), + pure_profit_percent: 0.0, + minimum_profit_percent: 0.0, + maximum_profit_percent: 0.0, + registerer: 0, + is_long: 0, + }; + a + } +} + #[derive(Debug, FromRow)] pub struct SignalDecisionInfo { pub decision: String, @@ -258,6 +313,8 @@ pub async fn limit_order_buy( // reflect available_usdt in [asset_manage_announcement] sub_available_usdt(used_usdt).await; + + println!("SIMUL buy coin: {}", element.symbol); } else { // building URL and API-keys let mut url = String::new(); @@ -436,7 +493,8 @@ pub async fn monitoring_filled_buy_order( // let scoreboard_list = select_scoreboard().await; // let signal_decision = select_signal_decision().await; - let filled_buy_orders = select_filled_buy_orders().await; + let filled_buy_orders = select_filled_buy_orders().await?; + if !filled_buy_orders.is_empty() { // 심볼들을 2개씩 청스로 나누어 테스크를 생성하고 각 태스크 별로 병렬로 처리한다. // update real-time current price to each record through chunks @@ -463,7 +521,7 @@ pub async fn monitoring_filled_buy_order( let result = try_join_all(task_vec).await; // sell coin if its sell condition is satisfied - let filled_buy_orders = select_filled_buy_orders().await; + let filled_buy_orders = select_filled_buy_orders().await?; let client = ClientBuilder::new() .timeout(tokio::time::Duration::from_millis(5000)) .build() @@ -506,11 +564,15 @@ pub async fn monitoring_filled_buy_order( let mut opclo_30m_vec = all_data.rt_price_30m_vec[opclo_30m_option.unwrap()] .1 .clone(); - let opclo_sample_length: usize = 50; // 50 candle samsples + opclo_30m_vec.pop(); opclo_30m_vec.reverse(); + let mut opclo_sample_length: usize = 50; // 50 candle samsples + let nbr_of_exclusive: usize = 5; opclo_30m_vec.truncate(opclo_sample_length); - opclo_30m_vec.reverse(); + opclo_30m_vec.sort_by(|a, b| (a.high_price-a.low_price).total_cmp(&(b.high_price-b.low_price))); + opclo_30m_vec.truncate(opclo_sample_length - nbr_of_exclusive); + opclo_sample_length -= nbr_of_exclusive; let mut sum_amplitude_candles = 0.0; let mut sum_ratio_amp_body = 0.0; @@ -536,35 +598,42 @@ pub async fn monitoring_filled_buy_order( // let target_profit_percent = average_amplitude + (standard_deviation_amplitude * (average_ratio_amp_body)); let target_profit_percent = |multiplier: f64| -> f64 { if multiplier < 0.0 { - ((average_amplitude / 2.0) * multiplier) - (standard_deviation_amplitude * 2.0) // 2.0 sigma (recommand: 0.5 ~ 2.0(patient & greedy)) + ((average_amplitude) * multiplier) - (standard_deviation_amplitude * 2.0) // 2.0 sigma (recommand: 0.5 ~ 2.0(patient & greedy)) } else { - ((average_amplitude / 2.0) * multiplier) + (standard_deviation_amplitude * 2.0) // 2.0 sigma (recommand: 0.5 ~ 2.0(patient & greedy)) + ((average_amplitude) * multiplier) + (standard_deviation_amplitude * 2.0) // 2.0 sigma (recommand: 0.5 ~ 2.0(patient & greedy)) } }; + + if server_epoch - element.close_time >= (1_800_000 * 2) * 12 // (30min * 2) * 12 + { + println!( + "selling {} due to time up {:.3}", + element.symbol, + element.pure_profit_percent + ); - if element.is_long == 0 || element.is_long == 1 { + limit_order_sell( + &element, + element.current_price, + base_qty_to_be_ordered, + &client, + &exchange_info_vec, + &trade_fee_vec, + ) + .await; + } else { if element.pure_profit_percent >= 0.0 { let mut is_sell = false; - if element.maximum_profit_percent >= target_profit_percent(2.0) - && element.pure_profit_percent >= target_profit_percent(2.0) + if element.maximum_profit_percent >= target_profit_percent(2.0) + 0.2 + && element.pure_profit_percent >= target_profit_percent(2.0) + 0.2 { println!( "Selling {} 200% target_profit_percent: {:.3}", element.symbol, - target_profit_percent(2.0) + element.pure_profit_percent ); is_sell = true; - } else if element.maximum_profit_percent < target_profit_percent(2.0) - && element.maximum_profit_percent >= target_profit_percent(1.5) - && element.pure_profit_percent < target_profit_percent(0.2) - { - println!( - "selling {} 20% target_profit_percent: {:.3}", - element.symbol, - target_profit_percent(0.2) - ); - is_sell = true; - } else if server_epoch - element.close_time > (1_800_000) * 10 // 30min * 10 + } else if server_epoch - element.close_time >= (1_800_000 * 2) * 6 // (30min * 2) * 6 { println!( "selling {} due to time up {:.3}", @@ -572,8 +641,14 @@ pub async fn monitoring_filled_buy_order( element.pure_profit_percent ); is_sell = true; + } else if element.pure_profit_percent >= 1.5 { + println!( + "selling {} 1.5% profit", + element.symbol + ); + is_sell = true; } - + if is_sell == true { // let mut sell_price_ahead: Decimal = Decimal::new(14, 8); // sell_price_ahead = decimal_mul(decimal_add(decimal_mul(decimal_mul(rust_decimal::Decimal::from_f64(element.pure_profit_percent).unwrap(), dec!(0.01)), dec!(0.97)), dec!(1)), element.buy_price).round_dp_with_strategy(2, RoundingStrategy::ToZero); @@ -588,14 +663,26 @@ pub async fn monitoring_filled_buy_order( .await; } } else { - if element.pure_profit_percent < target_profit_percent(-1.333) - 0.2 // -0.2 means about total trade fees. + let mut is_sell = false; + if element.pure_profit_percent <= target_profit_percent(-1.5) - 0.2 // -0.2 means about total trade fees. { println!( - "selling {} due to lower limit {:.3}", + "Selling {} -150% target_profit_percent: {:.3}", element.symbol, element.pure_profit_percent ); - limit_order_sell( + is_sell = true; + } else if element.pure_profit_percent <= -1.0 && server_epoch - element.close_time >= (1_800_000 * 2) * 2 // (30min * 2) * 2 + { + println!( + "selling {} -1.0% profit", + element.symbol + ); + is_sell = true; + } + + if is_sell == true { + limit_order_sell( &element, element.current_price, base_qty_to_be_ordered, @@ -607,6 +694,7 @@ pub async fn monitoring_filled_buy_order( } } } + } } @@ -796,6 +884,7 @@ pub async fn limit_order_sell( dec!(100), ); insert_value_container.push(pure_profit_percent.to_string()); // pure_profit_percent + insert_value_container.push(buy_ordered_coin.maximum_profit_percent.to_string()); // maximum_profit_percent insert_value_container.push(buy_ordered_coin.registerer.to_string()); // registerer insert_value_container.push(buy_ordered_coin.is_long.to_string()); // is_long @@ -2140,30 +2229,11 @@ async fn get_timestamp() -> String { timestamp } -pub async fn select_filled_buy_orders() -> Vec { +pub async fn select_filled_buy_orders() -> Result, Box> { let select_table_name = String::from("buy_ordered_coin_list"); let select_columns = String::from("*"); let select_condition = Some(String::from("WHERE status = 'FILLED' or status = 'SIMUL'")); - let data_struct = BuyOrderedCoinList { - id: 0, - symbol: String::new(), - order_id: 0, - transact_time: 0, - close_time: 0, - status: String::new(), - used_usdt: Decimal::new(0, 8), - expected_get_usdt: 0.0, - expected_usdt_profit: 0.0, - buy_price: Decimal::new(0, 8), - current_price: Decimal::new(0, 8), - base_qty_ordered: Decimal::new(0, 8), - base_qty_fee_adjusted: Decimal::new(0, 8), - pure_profit_percent: 0.0, - minimum_profit_percent: 0.0, - maximum_profit_percent: 0.0, - registerer: 0, - is_long: 0, - }; + let data_struct = BuyOrderedCoinList::new(); let select_result = try_select_record( &select_table_name, @@ -2171,10 +2241,15 @@ pub async fn select_filled_buy_orders() -> Vec { &select_condition, &data_struct, ) - .await - .unwrap(); + .await; - select_result + if select_result.is_ok() { + Ok(select_result.unwrap()) + } else { + eprint!("select_filled_buy_order() error!"); + Err("error")? + } + } // select open buy orders (NEW, Partially Filled) @@ -2184,26 +2259,7 @@ async fn select_open_buy_orders() -> Vec { let select_condition = Some(String::from( "WHERE status = 'NEW' or status = 'PARTIALLY_FILLED'", )); - let data_struct = BuyOrderedCoinList { - id: 0, - symbol: String::new(), - order_id: 0, - transact_time: 0, - close_time: 0, - status: String::new(), - used_usdt: Decimal::new(0, 8), - expected_get_usdt: 0.0, - expected_usdt_profit: 0.0, - buy_price: Decimal::new(0, 8), - current_price: Decimal::new(0, 8), - base_qty_ordered: Decimal::new(0, 8), - base_qty_fee_adjusted: Decimal::new(0, 8), - pure_profit_percent: 0.0, - minimum_profit_percent: 0.0, - maximum_profit_percent: 0.0, - registerer: 0, - is_long: 0, - }; + let data_struct = BuyOrderedCoinList::new(); let select_result = try_select_record( &select_table_name, &select_columns, @@ -2220,25 +2276,7 @@ pub async fn select_filled_sell_orders() -> Vec { let select_table_name = String::from("sell_ordered_coin_list"); let select_columns = String::from("*"); let select_condition = Some(String::from("WHERE status = 'FILLED' or status = 'SIMUL'")); - let data_struct = SellOrderedCoinList { - id: 0, - symbol: String::new(), - buy_order_id: 0, - sell_order_id: 0, - transact_time: 0, - close_time: 0, - status: String::new(), - used_usdt: Decimal::new(0, 8), - get_usdt: Decimal::new(0, 8), - get_usdt_fee_adjusted: Decimal::new(0, 8), - buy_price: Decimal::new(0, 8), - sell_price: Decimal::new(0, 8), - base_qty_ordered: Decimal::new(0, 8), - pure_profit_percent: Decimal::new(0, 8), - maximum_profit_percent: 0.0, - registerer: 0, - is_long: 0, - }; + let data_struct = SellOrderedCoinList::new(); let select_result = try_select_record( &select_table_name, @@ -2259,25 +2297,7 @@ async fn select_open_sell_orders() -> Vec { let select_condition = Some(String::from( "WHERE status = 'NEW' or status = 'PARTIALLY_FILLED'", )); - let data_struct = SellOrderedCoinList { - id: 0, - symbol: String::new(), - buy_order_id: 0, - sell_order_id: 0, - transact_time: 0, - close_time: 0, - status: String::new(), - used_usdt: Decimal::new(0, 8), - get_usdt: Decimal::new(0, 8), - get_usdt_fee_adjusted: Decimal::new(0, 8), - buy_price: Decimal::new(0, 8), - sell_price: Decimal::new(0, 8), - base_qty_ordered: Decimal::new(0, 8), - pure_profit_percent: Decimal::new(0, 8), - maximum_profit_percent: 0.0, - registerer: 0, - is_long: 0, - }; + let data_struct = SellOrderedCoinList::new(); let select_result = try_select_record( &select_table_name,