Formatting

This commit is contained in:
Sik Yoon 2024-04-13 14:04:09 +09:00
parent e85690fd41
commit 23066cf0f3
33 changed files with 1689 additions and 1030 deletions

View File

@ -6,12 +6,12 @@ use crate::database_control::*;
use crate::decimal_funcs::*; use crate::decimal_funcs::*;
use crate::RunningMode::*; use crate::RunningMode::*;
use crate::RUNNING_MODE; use crate::RUNNING_MODE;
use log;
use reqwest::Client; use reqwest::Client;
use rust_decimal::{prelude::ToPrimitive, Decimal, RoundingStrategy}; use rust_decimal::{prelude::ToPrimitive, Decimal, RoundingStrategy};
use rust_decimal_macros::dec; use rust_decimal_macros::dec;
use serde_json::{Result, Value}; use serde_json::{Result, Value};
use sqlx::FromRow; use sqlx::FromRow;
use log;
#[derive(Debug, FromRow)] #[derive(Debug, FromRow)]
pub struct AchievementEvaluationInfo { pub struct AchievementEvaluationInfo {
@ -156,7 +156,7 @@ pub async fn add_extra_usdt(extra_usdt: Decimal) {
// add additional_usdt into [wallet_simul] // add additional_usdt into [wallet_simul]
let mut update_table_name = String::new(); let mut update_table_name = String::new();
unsafe{ unsafe {
if RUNNING_MODE == SIMUL { if RUNNING_MODE == SIMUL {
let update_table_name = String::from("wallet"); let update_table_name = String::from("wallet");
let mut value_build = String::from("free + "); let mut value_build = String::from("free + ");
@ -186,7 +186,7 @@ pub async fn update_current_total_usdt() {
} else { } else {
profit = decimal_sub(decimal_div(free_usdt, initial_usdt), dec!(1)); profit = decimal_sub(decimal_div(free_usdt, initial_usdt), dec!(1));
} }
update_values = vec![ update_values = vec![
(String::from("current_total_usdt"), free_usdt.to_string()), (String::from("current_total_usdt"), free_usdt.to_string()),
(String::from("profit"), profit.to_string()), (String::from("profit"), profit.to_string()),
@ -198,19 +198,19 @@ pub async fn update_current_total_usdt() {
]; ];
} else { } else {
let asset_info = select_asset_manage_announcement().await; let asset_info = select_asset_manage_announcement().await;
let achievement_evaluation = select_achievement_evaluation().await; let achievement_evaluation = select_achievement_evaluation().await;
let balance = decimal_sub( let balance = decimal_sub(
achievement_evaluation.usdt_profit, achievement_evaluation.usdt_profit,
achievement_evaluation.invested_usdt, achievement_evaluation.invested_usdt,
); );
let current_total_usdt = decimal_add(asset_info.initial_usdt, balance); let current_total_usdt = decimal_add(asset_info.initial_usdt, balance);
let profit = decimal_sub( let profit = decimal_sub(
decimal_div(current_total_usdt, asset_info.initial_usdt), decimal_div(current_total_usdt, asset_info.initial_usdt),
dec!(1), dec!(1),
); );
update_values = vec![ update_values = vec![
( (
String::from("current_total_usdt"), String::from("current_total_usdt"),
@ -225,7 +225,7 @@ pub async fn update_current_total_usdt() {
]; ];
} }
} }
let update_table_name = String::from("asset_manage_announcement"); let update_table_name = String::from("asset_manage_announcement");
let update_condition = vec![(String::from("id"), String::from("1"))]; let update_condition = vec![(String::from("id"), String::from("1"))];
update_record3(&update_table_name, &update_values, &update_condition) update_record3(&update_table_name, &update_values, &update_condition)
@ -541,7 +541,7 @@ pub async fn select_asset_manage_announcement() -> AssetInfo {
pub async fn fetch_free_usdt() -> Decimal { pub async fn fetch_free_usdt() -> Decimal {
let wallet_info = WalletInfo::new(); let wallet_info = WalletInfo::new();
let select_table_name = { let select_table_name = {
unsafe{ unsafe {
if RUNNING_MODE == SIMUL || RUNNING_MODE == REAL { if RUNNING_MODE == SIMUL || RUNNING_MODE == REAL {
String::from("wallet") String::from("wallet")
} else { } else {

View File

@ -257,7 +257,9 @@ pub async fn buy_coin(
.unwrap(); .unwrap();
for element in &filtered_suggested_coin_vec { for element in &filtered_suggested_coin_vec {
if is_tradable == true { if is_tradable == true {
if exchange_info_map.contains_key(&element.symbol) && trade_fee_map.contains_key(&element.symbol) { 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 exchange_info = exchange_info_map.get(&element.symbol).unwrap();
let lot_step_size = exchange_info.stepsize; let lot_step_size = exchange_info.stepsize;
let tick_size = exchange_info.ticksize; let tick_size = exchange_info.ticksize;
@ -375,10 +377,15 @@ pub async fn buy_coin_for_test(
let insert_table_name = String::from("buy_ordered_coin_list"); let insert_table_name = String::from("buy_ordered_coin_list");
for element in &filtered_suggested_coin_vec { for element in &filtered_suggested_coin_vec {
if exchange_info_map.contains_key(&element.symbol) && trade_fee_map.contains_key(&element.symbol){ 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 lot_step_size = exchange_info_map.get(&element.symbol).unwrap().stepsize;
let tick_size = exchange_info_map.get(&element.symbol).unwrap().ticksize; 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 base_commission_precision = exchange_info_map
.get(&element.symbol)
.unwrap()
.base_commission_precision;
let trade_fee = trade_fee_map.get(&element.symbol).unwrap().takercommission; let trade_fee = trade_fee_map.get(&element.symbol).unwrap().takercommission;
// buy the suggested coin and transfer it into [buy_ordered_coin_list] // buy the suggested coin and transfer it into [buy_ordered_coin_list]
@ -387,8 +394,10 @@ pub async fn buy_coin_for_test(
// let order_price = current_price; // let order_price = current_price;
let order_price = element.suggested_price; let order_price = element.suggested_price;
base_qty_ordered = decimal_div(unit_trade_usdt, order_price) base_qty_ordered = decimal_div(unit_trade_usdt, order_price).round_dp_with_strategy(
.round_dp_with_strategy(lot_step_size.normalize().scale(), RoundingStrategy::ToZero); lot_step_size.normalize().scale(),
RoundingStrategy::ToZero,
);
base_qty_fee_adjusted = decimal_mul(base_qty_ordered, decimal_sub(dec!(1), trade_fee)) base_qty_fee_adjusted = decimal_mul(base_qty_ordered, decimal_sub(dec!(1), trade_fee))
.round_dp_with_strategy(base_commission_precision, RoundingStrategy::ToZero); .round_dp_with_strategy(base_commission_precision, RoundingStrategy::ToZero);
used_usdt = decimal_mul(base_qty_ordered, order_price) used_usdt = decimal_mul(base_qty_ordered, order_price)
@ -865,7 +874,8 @@ pub async fn update_profit_percent() {
profit_percent = (decimal_sub( profit_percent = (decimal_sub(
decimal_div(element.usdt_profit, element.invested_usdt), decimal_div(element.usdt_profit, element.invested_usdt),
dec!(1), dec!(1),
)).round_dp(2) ))
.round_dp(2)
.to_f64() .to_f64()
.unwrap() .unwrap()
* 100.0; * 100.0;

View File

@ -24,14 +24,14 @@ use crate::value_estimation_team::indicators::supertrend::{supertrend, Supertren
use futures::future::try_join_all; use futures::future::try_join_all;
use hex::ToHex; use hex::ToHex;
use hmac_sha256::HMAC; use hmac_sha256::HMAC;
use log;
use reqwest::{Client, ClientBuilder}; use reqwest::{Client, ClientBuilder};
use rust_decimal::{prelude::FromPrimitive, prelude::ToPrimitive, Decimal, RoundingStrategy}; use rust_decimal::{prelude::FromPrimitive, prelude::ToPrimitive, Decimal, RoundingStrategy};
use rust_decimal_macros::dec; use rust_decimal_macros::dec;
use serde_json::Value; use serde_json::Value;
use sqlx::FromRow; use sqlx::FromRow;
use tokio::time::*;
use std::collections::HashMap; use std::collections::HashMap;
use log; use tokio::time::*;
pub enum OrderSide { pub enum OrderSide {
Buy, Buy,
@ -199,7 +199,7 @@ pub async fn limit_order_buy_test(
// building URL and API-keys // building URL and API-keys
let mut url = String::new(); let mut url = String::new();
let mut api_key = String::new(); let mut api_key = String::new();
unsafe{ unsafe {
if RUNNING_MODE == TEST { if RUNNING_MODE == TEST {
url.push_str(URL_TEST); url.push_str(URL_TEST);
api_key = API_KEY_TESTNET.to_string(); api_key = API_KEY_TESTNET.to_string();
@ -598,18 +598,26 @@ async fn update_repeat_task(
for element in buy_ordered_coin_vec { for element in buy_ordered_coin_vec {
// build update values // build update values
update_record_build.clear(); update_record_build.clear();
if coin_price_map.contains_key(&element.symbol) && exchange_info_map.contains_key(&element.symbol) && trade_fee_map.contains_key(&element.symbol) { if coin_price_map.contains_key(&element.symbol)
price = rust_decimal::prelude::FromPrimitive::from_f64(*coin_price_map.get(&element.symbol).unwrap()).unwrap(); && 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() { if !price.is_zero() {
// to get quote_commission_precision // to get quote_commission_precision
let trade_fee = trade_fee_map.get(&element.symbol).unwrap().takercommission; let trade_fee = trade_fee_map.get(&element.symbol).unwrap().takercommission;
let lot_step_size = exchange_info_map.get(&element.symbol).unwrap().stepsize; 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 quote_commission_precision = exchange_info_map
let base_qty_to_be_ordered = .get(&element.symbol)
element.base_qty_fee_adjusted.round_dp_with_strategy( .unwrap()
lot_step_size.normalize().scale(), .quote_commission_precision;
RoundingStrategy::ToZero, 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( let expected_get_usdt = decimal_mul(
decimal_mul(base_qty_to_be_ordered, price).round_dp_with_strategy( decimal_mul(base_qty_to_be_ordered, price).round_dp_with_strategy(
quote_commission_precision, quote_commission_precision,
@ -617,9 +625,9 @@ async fn update_repeat_task(
), ),
decimal_sub(dec!(1), trade_fee), decimal_sub(dec!(1), trade_fee),
); );
// TODO: sell_count >=1 이면 expected_get_usdt 는 한번만 tradefee만 적용하여 업데이트 할 것. 현재는 수수료를 2번 (매수,매도)를 계산함. 아래 변수에 든 값으로 업데이트 하면 됨 // TODO: sell_count >=1 이면 expected_get_usdt 는 한번만 tradefee만 적용하여 업데이트 할 것. 현재는 수수료를 2번 (매수,매도)를 계산함. 아래 변수에 든 값으로 업데이트 하면 됨
// let expected_get_usdt = // let expected_get_usdt =
// decimal_mul(base_qty_to_be_ordered, price).round_dp_with_strategy( // decimal_mul(base_qty_to_be_ordered, price).round_dp_with_strategy(
// quote_commission_precision, // quote_commission_precision,
// RoundingStrategy::ToZero, // RoundingStrategy::ToZero,
@ -628,7 +636,7 @@ async fn update_repeat_task(
/ element.used_usdt.to_f64().unwrap()) / element.used_usdt.to_f64().unwrap())
- 1.0) - 1.0)
* 100.0; * 100.0;
pure_profit_percent = (pure_profit_percent * 100.0).round() / 100.0; // Rounding pure_profit_percent = (pure_profit_percent * 100.0).round() / 100.0; // Rounding
update_record_build.push(element.id.to_string()); // id update_record_build.push(element.id.to_string()); // id
update_record_build.push(price.to_string()); // current_price 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(expected_get_usdt.to_string()); //expected_get_usdt
@ -705,9 +713,17 @@ pub async fn limit_order_sell(
let mut insert_value_container: Vec<String> = Vec::new(); let mut insert_value_container: Vec<String> = Vec::new();
unsafe { unsafe {
if RUNNING_MODE == SIMUL && buy_ordered_coin.status == "SIMUL" { if RUNNING_MODE == SIMUL && buy_ordered_coin.status == "SIMUL" {
if exchange_info_map.contains_key(&buy_ordered_coin.symbol) && trade_fee_map.contains_key(&buy_ordered_coin.symbol) { if exchange_info_map.contains_key(&buy_ordered_coin.symbol)
let quote_asset_precision = exchange_info_map.get(&buy_ordered_coin.symbol).unwrap().quote_asset_precision; && trade_fee_map.contains_key(&buy_ordered_coin.symbol)
let trade_fee = trade_fee_map.get(&buy_ordered_coin.symbol).unwrap().takercommission; {
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) let get_usdt = decimal_mul(sell_base_quantity, sell_base_price)
.round_dp_with_strategy(quote_asset_precision, RoundingStrategy::ToZero); .round_dp_with_strategy(quote_asset_precision, RoundingStrategy::ToZero);
@ -735,7 +751,8 @@ pub async fn limit_order_sell(
dec!(1), dec!(1),
), ),
dec!(100), dec!(100),
).round_dp(2); )
.round_dp(2);
insert_value_container.push(pure_profit_percent.to_string()); // pure_profit_percent 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.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.registerer.to_string()); // registerer
@ -811,9 +828,17 @@ pub async fn limit_order_sell(
.push(T.get("status").unwrap().as_str().unwrap().to_string()); // status .push(T.get("status").unwrap().as_str().unwrap().to_string()); // status
insert_value_container.push(buy_ordered_coin.used_usdt.to_string()); // used_usdt insert_value_container.push(buy_ordered_coin.used_usdt.to_string()); // used_usdt
if exchange_info_map.contains_key(&buy_ordered_coin.symbol) && trade_fee_map.contains_key(&buy_ordered_coin.symbol) { if exchange_info_map.contains_key(&buy_ordered_coin.symbol)
let quote_asset_precision = exchange_info_map.get(&buy_ordered_coin.symbol).unwrap().quote_asset_precision; && trade_fee_map.contains_key(&buy_ordered_coin.symbol)
let trade_fee = trade_fee_map.get(&buy_ordered_coin.symbol).unwrap().takercommission; {
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( let get_usdt = rust_decimal::prelude::FromStr::from_str(
T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(), T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(),
) )
@ -859,11 +884,15 @@ pub async fn limit_order_sell(
if T.get("status").unwrap().as_str().unwrap() == "FILLED" { if T.get("status").unwrap().as_str().unwrap() == "FILLED" {
let pure_profit_percent = decimal_mul( let pure_profit_percent = decimal_mul(
decimal_sub( decimal_sub(
decimal_div(get_usdt_fee_adjusted, buy_ordered_coin.used_usdt), decimal_div(
get_usdt_fee_adjusted,
buy_ordered_coin.used_usdt,
),
dec!(1), dec!(1),
), ),
dec!(100), dec!(100),
).round_dp(2); )
.round_dp(2);
insert_value_container insert_value_container
.push(buy_ordered_coin.pure_profit_percent.to_string()); .push(buy_ordered_coin.pure_profit_percent.to_string());
// pure_profit_percent // pure_profit_percent
@ -876,7 +905,8 @@ pub async fn limit_order_sell(
insert_value_container.push(buy_ordered_coin.is_long.to_string()); // is_long insert_value_container.push(buy_ordered_coin.is_long.to_string()); // is_long
insert_values.push(insert_value_container.clone()); insert_values.push(insert_value_container.clone());
insert_records(&insert_table_name, &insert_columns, &insert_values).await; insert_records(&insert_table_name, &insert_columns, &insert_values)
.await;
// delete record in buy_ordered_coin_list // delete record in buy_ordered_coin_list
let delete_table_name = String::from("buy_ordered_coin_list"); let delete_table_name = String::from("buy_ordered_coin_list");
@ -962,7 +992,9 @@ pub async fn monitoring_filled_sell_order(
for element in filled_sell_orders { for element in filled_sell_orders {
// build insert value // build insert value
let pure_profit_usdt = decimal_sub(element.get_usdt_fee_adjusted, element.used_usdt); let pure_profit_usdt = decimal_sub(element.get_usdt_fee_adjusted, element.used_usdt);
let pure_profit_percent = decimal_mul(decimal_div(pure_profit_usdt, element.used_usdt), dec!(100)).round_dp(2); let pure_profit_percent =
decimal_mul(decimal_div(pure_profit_usdt, element.used_usdt), dec!(100))
.round_dp(2);
insert_value_build.clear(); insert_value_build.clear();
insert_value_build.push(element.symbol.clone()); // symbol insert_value_build.push(element.symbol.clone()); // symbol
insert_value_build.push(server_epoch.to_string()); // soldtime insert_value_build.push(server_epoch.to_string()); // soldtime
@ -1164,7 +1196,8 @@ pub async fn cancel_buy_order(
// calculate values to be updated // calculate values to be updated
if trade_fee_map.contains_key(&order.symbol) { if trade_fee_map.contains_key(&order.symbol) {
let trade_fee = trade_fee_map.get(&order.symbol).unwrap().takercommission; let trade_fee =
trade_fee_map.get(&order.symbol).unwrap().takercommission;
let base_qty_ordered = rust_decimal::prelude::FromStr::from_str( let base_qty_ordered = rust_decimal::prelude::FromStr::from_str(
T.get("executedQty").unwrap().as_str().unwrap(), T.get("executedQty").unwrap().as_str().unwrap(),
) )
@ -1344,9 +1377,15 @@ pub async fn cancel_sell_order(
insert_values.push(insert_value_container.clone()); insert_values.push(insert_value_container.clone());
insert_records(&insert_table_name, &insert_columns, &insert_values).await; insert_records(&insert_table_name, &insert_columns, &insert_values).await;
} else { } else {
if exchange_info_map.contains_key(&order.symbol) && trade_fee_map.contains_key(&order.symbol) { if exchange_info_map.contains_key(&order.symbol)
let quote_asset_precision = exchange_info_map.get(&order.symbol).unwrap().quote_asset_precision; && trade_fee_map.contains_key(&order.symbol)
let trade_fee = trade_fee_map.get(&order.symbol).unwrap().takercommission; {
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 { if base_qty_executed == base_qty_ordered {
// FILLED case // FILLED case
// update status FILLED // update status FILLED
@ -1371,7 +1410,8 @@ pub async fn cancel_sell_order(
dec!(1), dec!(1),
), ),
dec!(100), dec!(100),
).round_dp(2); )
.round_dp(2);
let table_name = String::from("sell_ordered_coin_list"); let table_name = String::from("sell_ordered_coin_list");
let mut value_build = String::from("\'"); let mut value_build = String::from("\'");
@ -1422,7 +1462,8 @@ pub async fn cancel_sell_order(
dec!(1), dec!(1),
), ),
dec!(100), dec!(100),
).round_dp(2); )
.round_dp(2);
let table_name = String::from("sell_ordered_coin_list"); let table_name = String::from("sell_ordered_coin_list");
let mut value_build = String::from("\'"); let mut value_build = String::from("\'");
@ -1756,11 +1797,18 @@ pub async fn query_sell_order(
match v { match v {
Ok(T) => { Ok(T) => {
if T.get("status").is_some_and(|a| a.as_str().unwrap() == "FILLED") if T.get("status")
|| T.get("status").is_some_and(|a| a.as_str().unwrap() == "PARTIALLY_FILLED") .is_some_and(|a| a.as_str().unwrap() == "FILLED")
|| T.get("status")
.is_some_and(|a| a.as_str().unwrap() == "PARTIALLY_FILLED")
{ {
if exchange_info_map.contains_key(&order.symbol) && trade_fee_map.contains_key(&order.symbol) { if exchange_info_map.contains_key(&order.symbol)
let quote_asset_precision = exchange_info_map.get(&order.symbol).unwrap().quote_asset_precision; && 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 trade_fee = trade_fee_map.get(&order.symbol).unwrap().takercommission;
let get_usdt = rust_decimal::prelude::FromStr::from_str( let get_usdt = rust_decimal::prelude::FromStr::from_str(
T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(), T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(),
@ -1781,7 +1829,8 @@ pub async fn query_sell_order(
let pure_profit_percent = decimal_mul( let pure_profit_percent = decimal_mul(
decimal_sub(decimal_div(get_usdt_fee_adjusted, order.used_usdt), dec!(1)), decimal_sub(decimal_div(get_usdt_fee_adjusted, order.used_usdt), dec!(1)),
dec!(100), dec!(100),
).round_dp(2); )
.round_dp(2);
let table_name = String::from("sell_ordered_coin_list"); let table_name = String::from("sell_ordered_coin_list");
let mut value_build = String::from("\'"); let mut value_build = String::from("\'");

View File

@ -11,9 +11,9 @@ use serde::Deserialize;
use serde_json::Value; use serde_json::Value;
use sqlx::{Error, FromRow}; use sqlx::{Error, FromRow};
use std::borrow::{Borrow, BorrowMut}; use std::borrow::{Borrow, BorrowMut};
use std::collections::{HashMap, HashSet};
use std::sync::Arc; use std::sync::Arc;
use tokio::{join, sync::Mutex, time::*}; use tokio::{join, sync::Mutex, time::*};
use std::collections::{HashMap, HashSet};
#[derive(Debug, FromRow)] #[derive(Debug, FromRow)]
struct AllCoinProfitChangeAvgList { struct AllCoinProfitChangeAvgList {
@ -115,13 +115,13 @@ pub async fn collect_valid_usde_trade(
// get valid usdt trades // get valid usdt trades
let usdt_trades = let usdt_trades =
select_record(&fetch_table_name, &column_name, &condition, &usdt_trades).await?; select_record(&fetch_table_name, &column_name, &condition, &usdt_trades).await?;
// get banned usdt trades // get banned usdt trades
#[derive(Debug, FromRow)] #[derive(Debug, FromRow)]
struct Symbols { struct Symbols {
symbol: String, symbol: String,
} }
let table_name = String::from("banned_usdt_trades"); let table_name = String::from("banned_usdt_trades");
let column_name = String::from("symbol"); let column_name = String::from("symbol");
let condition = None; let condition = None;
@ -152,7 +152,8 @@ pub async fn collect_valid_usde_trade(
.unwrap(); .unwrap();
let step_size = value.stepsize; let step_size = value.stepsize;
let step_price = decimal_mul(step_size, avg_price); let step_price = decimal_mul(step_size, avg_price);
let unit_trade_usdt = crate::coex::assets_managing_team::get_unit_trade_usdt().await; 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 // 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)) { if step_price > decimal_mul(unit_trade_usdt, dec!(0.01)) {

View File

@ -6,9 +6,9 @@ use serde::Deserialize;
use serde_json::Value; use serde_json::Value;
use sqlx::{Error, FromRow}; use sqlx::{Error, FromRow};
use std::borrow::{Borrow, BorrowMut}; use std::borrow::{Borrow, BorrowMut};
use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use tokio::{join, sync::Mutex, time::*}; use tokio::{join, sync::Mutex, time::*};
use std::collections::HashMap;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct CandleData { pub struct CandleData {
@ -166,7 +166,7 @@ async fn de_candle_json2(
} }
if let Some(value) = candle_map.get_mut(&symbol) { if let Some(value) = candle_map.get_mut(&symbol) {
*value = candle_vec; *value = candle_vec;
} else { } else {
candle_map.insert(symbol, candle_vec); candle_map.insert(symbol, candle_vec);
} }

View File

@ -1,17 +1,17 @@
use crate::database_control::*; use crate::database_control::*;
use hex::ToHex; use hex::ToHex;
use hmac_sha256::HMAC; use hmac_sha256::HMAC;
use log;
use reqwest::{Client, ClientBuilder, Response}; use reqwest::{Client, ClientBuilder, Response};
use rust_decimal::{prelude::FromPrimitive, prelude::ToPrimitive, Decimal, RoundingStrategy}; use rust_decimal::{prelude::FromPrimitive, prelude::ToPrimitive, Decimal, RoundingStrategy};
use serde::Deserialize; use serde::Deserialize;
use serde_json::Value; use serde_json::Value;
use sqlx::{Error, FromRow}; use sqlx::{Error, FromRow};
use std::borrow::{Borrow, BorrowMut}; use std::borrow::{Borrow, BorrowMut};
use std::collections::HashMap;
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::Arc; use std::sync::Arc;
use std::collections::HashMap;
use tokio::{join, sync::Mutex, time::*}; use tokio::{join, sync::Mutex, time::*};
use log;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct TradeFee { pub struct TradeFee {
@ -184,7 +184,7 @@ async fn de_trade_fee_json(
object_map = element.as_object().unwrap(); object_map = element.as_object().unwrap();
let mut object_map_iter = object_map.iter(); let mut object_map_iter = object_map.iter();
for element in object_map_iter { for element in object_map_iter {
match element.0.as_str() { match element.0.as_str() {
"symbol" => symbol = element.1.as_str().unwrap().to_string(), "symbol" => symbol = element.1.as_str().unwrap().to_string(),
@ -203,7 +203,7 @@ async fn de_trade_fee_json(
} }
} }
} }
tradefee_map_build.insert(symbol.clone(), tradefee_data.clone()); tradefee_map_build.insert(symbol.clone(), tradefee_data.clone());
} }
*tradefee_map = tradefee_map_build; *tradefee_map = tradefee_map_build;
Ok(()) Ok(())
@ -374,8 +374,7 @@ pub async fn request_exchange_infomation(
.unwrap() .unwrap()
.ends_with("USDT") .ends_with("USDT")
{ {
symbol = symbol = (element.get("symbol").unwrap().as_str().unwrap().to_string());
(element.get("symbol").unwrap().as_str().unwrap().to_string());
exchange_info.base_asset_precision = exchange_info.base_asset_precision =
(element.get("baseAssetPrecision").unwrap().as_u64().unwrap()) as u32; (element.get("baseAssetPrecision").unwrap().as_u64().unwrap()) as u32;
exchange_info.base_commission_precision = (element exchange_info.base_commission_precision = (element
@ -442,7 +441,7 @@ pub async fn request_delist_symbols(
secret_key: &str, secret_key: &str,
local_epoch: u128, local_epoch: u128,
difference_epoch: i64, difference_epoch: i64,
client: &Client client: &Client,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> { ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut base_url = String::from("https://api.binance.com/sapi/v1/spot/delist-schedule?"); let mut base_url = String::from("https://api.binance.com/sapi/v1/spot/delist-schedule?");
@ -512,28 +511,26 @@ async fn de_deilst_symbol_json(
for element in into_vec.unwrap() { for element in into_vec.unwrap() {
object_map = element.as_object().unwrap(); object_map = element.as_object().unwrap();
let mut object_map_iter = object_map.iter(); let mut object_map_iter = object_map.iter();
for element in object_map_iter { for element in object_map_iter {
match element.0.as_str() { match element.0.as_str() {
"delistTime" => { "delistTime" => {}
},
"symbols" => { "symbols" => {
if let Some(array) = element.1.as_array() { if let Some(array) = element.1.as_array() {
for delist_symbol in array { for delist_symbol in array {
if let Some(symbol) = delist_symbol.as_str() { if let Some(symbol) = delist_symbol.as_str() {
if symbol.ends_with("USDT") && !delist_hashset.contains(symbol) { if symbol.ends_with("USDT") && !delist_hashset.contains(symbol) {
delist_hashset.insert(symbol.to_string()); delist_hashset.insert(symbol.to_string());
} }
} }
} }
} }
}, }
_ => { _ => {
log::error!("Elements in body msg are changed. Please update both your delist table and vectors."); log::error!("Elements in body msg are changed. Please update both your delist table and vectors.");
} }
} }
} }
} }
if delist_hashset.len() != 0 { if delist_hashset.len() != 0 {
@ -541,7 +538,7 @@ async fn de_deilst_symbol_json(
struct Symbols { struct Symbols {
symbol: String, symbol: String,
} }
let table_name = String::from("banned_usdt_trades"); let table_name = String::from("banned_usdt_trades");
let column_name = String::from("symbol"); let column_name = String::from("symbol");
let condition = None; let condition = None;
@ -550,8 +547,8 @@ async fn de_deilst_symbol_json(
}; };
let mut select_result = try_select_record(&table_name, &column_name, &condition, &symbols) let mut select_result = try_select_record(&table_name, &column_name, &condition, &symbols)
.await .await
.unwrap(); .unwrap();
let mut banned_usdt_trades_set: HashSet<String> = HashSet::new(); let mut banned_usdt_trades_set: HashSet<String> = HashSet::new();
for element in select_result { for element in select_result {
banned_usdt_trades_set.insert(element.symbol.clone()); banned_usdt_trades_set.insert(element.symbol.clone());
@ -562,11 +559,11 @@ async fn de_deilst_symbol_json(
if !banned_usdt_trades_set.contains(element) { if !banned_usdt_trades_set.contains(element) {
let insert_values = vec![element.clone()]; let insert_values = vec![element.clone()];
insert_one_record(&table_name, &insert_column, &insert_values) insert_one_record(&table_name, &insert_column, &insert_values)
.await .await
.unwrap(); .unwrap();
} }
} }
} }
Ok(()) Ok(())
} }

View File

@ -292,8 +292,8 @@ async fn initialize_database() {
vec![String::from("ANCUSDT")], vec![String::from("ANCUSDT")],
]; ];
insert_records(&table_name, &columns, &value_wrapper) insert_records(&table_name, &columns, &value_wrapper)
.await .await
.unwrap(); .unwrap();
} }
println!("Ok"); println!("Ok");
} }
@ -907,9 +907,10 @@ async fn initialize_database() {
let mut symbols = Symbols { let mut symbols = Symbols {
symbol: String::new(), symbol: String::new(),
}; };
let symbols_vec = select_record(&fetch_table_name, &column_name, &condition, &symbols) let symbols_vec =
.await select_record(&fetch_table_name, &column_name, &condition, &symbols)
.expect("Failed to fetch records!"); .await
.expect("Failed to fetch records!");
let insert_table_name = String::from("wallet"); let insert_table_name = String::from("wallet");
let insert_columns = vec!["asset", "free", "locked"]; let insert_columns = vec!["asset", "free", "locked"];
let mut insert_values: Vec<Vec<String>> = Vec::new(); let mut insert_values: Vec<Vec<String>> = Vec::new();

View File

@ -8,8 +8,11 @@ use crate::server_health_check_team::ServerHealth;
use crate::strategy_team::AllData; use crate::strategy_team::AllData;
use crate::time_checking_team::UserTime; use crate::time_checking_team::UserTime;
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use clap::{arg, Command};
use log::Level;
use reqwest::{Client, ClientBuilder}; use reqwest::{Client, ClientBuilder};
use rust_decimal::Decimal; use rust_decimal::Decimal;
use simple_logger::set_up_color_terminal;
use sqlx::{mysql::*, Connection, Executor, FromRow, Row}; use sqlx::{mysql::*, Connection, Executor, FromRow, Row};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::{ use std::{
@ -18,9 +21,6 @@ use std::{
}; };
use tokio::{fs::*, join, sync::mpsc, sync::watch, sync::Mutex, task::*, time::*}; use tokio::{fs::*, join, sync::mpsc, sync::watch, sync::Mutex, task::*, time::*};
use tradingbot::{RunningMode::*, *}; use tradingbot::{RunningMode::*, *};
use clap::{arg, Command};
use log::Level;
use simple_logger::set_up_color_terminal;
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
@ -301,9 +301,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
loop { loop {
let instant = Instant::now(); let instant = Instant::now();
let client = ClientBuilder::new() let client = ClientBuilder::new()
.timeout(Duration::from_millis(1000)) .timeout(Duration::from_millis(1000))
.build() .build()
.unwrap(); .unwrap();
let mut usertime = UserTime::new(); let mut usertime = UserTime::new();
let mut serverhealth = ServerHealth::new(); let mut serverhealth = ServerHealth::new();
let mut epoch_difference_vec: Vec<f64> = Vec::new(); let mut epoch_difference_vec: Vec<f64> = Vec::new();
@ -330,14 +330,18 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
match result { match result {
Ok(T) => { Ok(T) => {
local_epoch_tx1.send(usertime.local_epoch) local_epoch_tx1
.send(usertime.local_epoch)
.expect("local_epoch_tx1-local_epoch_rx1 channel has been closed."); .expect("local_epoch_tx1-local_epoch_rx1 channel has been closed.");
epoch_difference_tx1.send(usertime.epoch_difference) epoch_difference_tx1.send(usertime.epoch_difference).expect(
.expect("epoch_difference_tx1-epoch_difference_rx1 channel has been closed."); "epoch_difference_tx1-epoch_difference_rx1 channel has been closed.",
local_epoch_tx2.send(usertime.local_epoch) );
local_epoch_tx2
.send(usertime.local_epoch)
.expect("local_epoch_tx2-local_epoch_rx2 channel has been closed."); .expect("local_epoch_tx2-local_epoch_rx2 channel has been closed.");
epoch_difference_tx2.send(usertime.epoch_difference) epoch_difference_tx2.send(usertime.epoch_difference).expect(
.expect("epoch_difference_tx2-epoch_difference_rx2 channel has been closed."); "epoch_difference_tx2-epoch_difference_rx2 channel has been closed.",
);
tx_task1.send(1).expect("The mpsc channel has been closed."); tx_task1.send(1).expect("The mpsc channel has been closed.");
} }
Err(E) => {} Err(E) => {}
@ -398,9 +402,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
loop { loop {
let instant = Instant::now(); let instant = Instant::now();
let client = ClientBuilder::new() let client = ClientBuilder::new()
.timeout(tokio::time::Duration::from_millis(3000)) .timeout(tokio::time::Duration::from_millis(3000))
.build() .build()
.unwrap(); .unwrap();
let tx1_changed = local_epoch_rx1.changed().await; let tx1_changed = local_epoch_rx1.changed().await;
let tx2_changed = epoch_difference_rx1.changed().await; let tx2_changed = epoch_difference_rx1.changed().await;
let local_epoch = *local_epoch_rx1.borrow(); let local_epoch = *local_epoch_rx1.borrow();
@ -429,7 +433,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
tx_tradefee_map.send_modify(|vec| *vec = tradefee_vec_temp); tx_tradefee_map.send_modify(|vec| *vec = tradefee_vec_temp);
tx_task2.send(2).expect("The mpsc channel has been closed."); tx_task2.send(2).expect("The mpsc channel has been closed.");
} }
Err(E) => { Err(E) => {
panic!("tx2-rx2 channel has been closed.") panic!("tx2-rx2 channel has been closed.")
@ -452,9 +455,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
tokio::task::spawn(async move { tokio::task::spawn(async move {
loop { loop {
let client = ClientBuilder::new() let client = ClientBuilder::new()
.timeout(tokio::time::Duration::from_millis(3000)) .timeout(tokio::time::Duration::from_millis(3000))
.build() .build()
.unwrap(); .unwrap();
let mut exchange_info_map_temp: HashMap<String, ExchangeInfo> = HashMap::new(); let mut exchange_info_map_temp: HashMap<String, ExchangeInfo> = HashMap::new();
let mut result; let mut result;
loop { loop {
@ -472,7 +475,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
} }
tx_exchange_info_map.send_modify(|vec| *vec = exchange_info_map_temp); tx_exchange_info_map.send_modify(|vec| *vec = exchange_info_map_temp);
tx_task3.send(3).expect("The mpsc channel has been closed."); tx_task3.send(3).expect("The mpsc channel has been closed.");
@ -492,9 +494,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
loop { loop {
let instant = Instant::now(); let instant = Instant::now();
let client = ClientBuilder::new() let client = ClientBuilder::new()
.timeout(tokio::time::Duration::from_millis(3000)) .timeout(tokio::time::Duration::from_millis(3000))
.build() .build()
.unwrap(); .unwrap();
let result = request_others::request_24hr_ticker_price_change_statistics(&client).await; let result = request_others::request_24hr_ticker_price_change_statistics(&client).await;
match result { match result {
Ok(T) => { Ok(T) => {
@ -506,9 +508,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
) )
.await; .await;
tx_valid_usdt_trade_set tx_valid_usdt_trade_set.send_modify(|vec| *vec = valid_usdt_trade_set_temp);
.send_modify(|vec| *vec = valid_usdt_trade_set_temp); tx_task4.send(4).expect("The mpsc channel has been closed.");
tx_task4.send(4).expect("The mpsc channel has been closed.");
} }
Err(E) => { Err(E) => {
log::warn!(">>> Failed to monitor usdt_24h_change_profit_index."); log::warn!(">>> Failed to monitor usdt_24h_change_profit_index.");
@ -560,9 +561,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
loop { loop {
let instant = Instant::now(); let instant = Instant::now();
let client = ClientBuilder::new() let client = ClientBuilder::new()
.timeout(tokio::time::Duration::from_millis(1000)) .timeout(tokio::time::Duration::from_millis(1000))
.build() .build()
.unwrap(); .unwrap();
let mut price_vec_temp: HashMap<String, f64> = HashMap::new(); let mut price_vec_temp: HashMap<String, f64> = HashMap::new();
let mut price_vec_temp_c: 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; request_others::request_all_coin_price(&client, &mut price_vec_temp).await;
@ -576,8 +577,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let interval = String::from("1m"); let interval = String::from("1m");
let candle_1m_vec = rx_candle_1m_map.borrow().clone(); let candle_1m_vec = rx_candle_1m_map.borrow().clone();
let dummy_data: HashMap<String, Vec<RealtimePriceData>> = HashMap::new(); 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: HashMap<String, Vec<RealtimePriceData>> =
let mut rt_price_1m_map_write_temp_c: HashMap<String, Vec<RealtimePriceData>> = HashMap::new(); 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( let result = value_estimation_team::datapoints::price_data::update_realtime_price_data(
&interval, &interval,
&candle_1m_vec, &candle_1m_vec,
@ -597,8 +600,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 30m // 30m
let interval = String::from("30m"); let interval = String::from("30m");
let candle_30m_map = rx_candle_30m_map.borrow().clone(); 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: HashMap<String, Vec<RealtimePriceData>> =
let mut rt_price_30m_map_write_temp_c: HashMap<String, Vec<RealtimePriceData>> = HashMap::new(); 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() { if !rt_price_1m_map_write_temp_c.is_empty() {
let result = let result =
value_estimation_team::datapoints::price_data::update_realtime_price_data( value_estimation_team::datapoints::price_data::update_realtime_price_data(
@ -614,15 +619,19 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
if tx_rt_price_30m_map.is_closed() { if tx_rt_price_30m_map.is_closed() {
log::error!("tx_rt_price_30m_vec has been closed!"); log::error!("tx_rt_price_30m_vec has been closed!");
} else { } else {
tx_rt_price_30m_map tx_rt_price_30m_map.send_modify(
.send_modify(|map: &mut HashMap<String, Vec<RealtimePriceData>>| *map = rt_price_30m_map_write_temp); |map: &mut HashMap<String, Vec<RealtimePriceData>>| {
} *map = rt_price_30m_map_write_temp
},
);
}
} }
// 1d // 1d
let interval = String::from("1d"); let interval = String::from("1d");
let candle_1d_vec = rx_candle_1d_map.borrow().clone(); let candle_1d_vec = rx_candle_1d_map.borrow().clone();
let mut rt_price_1d_map_write_temp: HashMap<String, Vec<RealtimePriceData>> = HashMap::new(); let mut rt_price_1d_map_write_temp: HashMap<String, Vec<RealtimePriceData>> =
HashMap::new();
if !rt_price_30m_map_write_temp_c.is_empty() { if !rt_price_30m_map_write_temp_c.is_empty() {
let result = let result =
@ -1055,8 +1064,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let instant = Instant::now(); let instant = Instant::now();
let exchange_info_map = rx_exchange_info_map.borrow().clone(); let exchange_info_map = rx_exchange_info_map.borrow().clone();
let trade_fee_map = rx_tradefee_map.borrow().clone(); let trade_fee_map = rx_tradefee_map.borrow().clone();
let result = let result = coex::exchange_team::buy_coin(&exchange_info_map, &trade_fee_map).await;
coex::exchange_team::buy_coin(&exchange_info_map, &trade_fee_map).await;
// send Task#0 a message to notify running on // send Task#0 a message to notify running on
match result { match result {
@ -1087,15 +1095,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
loop { loop {
let instant = Instant::now(); let instant = Instant::now();
let client = ClientBuilder::new() let client = ClientBuilder::new()
.timeout(tokio::time::Duration::from_millis(3000)) .timeout(tokio::time::Duration::from_millis(3000))
.build() .build()
.unwrap(); .unwrap();
let trade_fee_map = rx2_tradefee_map.borrow().clone(); let trade_fee_map = rx2_tradefee_map.borrow().clone();
let result = coex::order_team::monitoring_open_buy_order( let result =
&client, coex::order_team::monitoring_open_buy_order(&client, &trade_fee_map).await;
&trade_fee_map,
)
.await;
// send Task#0 a message to notify running on // send Task#0 a message to notify running on
match result { match result {
@ -1162,9 +1167,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
loop { loop {
let instant = Instant::now(); let instant = Instant::now();
let client = ClientBuilder::new() let client = ClientBuilder::new()
.timeout(tokio::time::Duration::from_millis(3000)) .timeout(tokio::time::Duration::from_millis(3000))
.build() .build()
.unwrap(); .unwrap();
let exchange_info_vec = rx4_exchange_info_map.borrow().clone(); let exchange_info_vec = rx4_exchange_info_map.borrow().clone();
let trade_fee_vec = rx4_tradefee_map.borrow().clone(); let trade_fee_vec = rx4_tradefee_map.borrow().clone();
let result = coex::order_team::monitoring_open_sell_order( let result = coex::order_team::monitoring_open_sell_order(
@ -1200,9 +1205,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
loop { loop {
let instant = Instant::now(); let instant = Instant::now();
let client = ClientBuilder::new() let client = ClientBuilder::new()
.timeout(tokio::time::Duration::from_millis(3000)) .timeout(tokio::time::Duration::from_millis(3000))
.build() .build()
.unwrap(); .unwrap();
let result = coex::order_team::monitoring_filled_sell_order(&client).await; let result = coex::order_team::monitoring_filled_sell_order(&client).await;
// send Task#0 a message to notify running on // send Task#0 a message to notify running on
@ -1260,9 +1265,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
loop { loop {
let instant = Instant::now(); let instant = Instant::now();
let client = ClientBuilder::new() let client = ClientBuilder::new()
.timeout(tokio::time::Duration::from_millis(3000)) .timeout(tokio::time::Duration::from_millis(3000))
.build() .build()
.unwrap(); .unwrap();
let result = let result =
coex::assets_managing_team::monitoring_asset_usdt(&mut previous_result, &client) coex::assets_managing_team::monitoring_asset_usdt(&mut previous_result, &client)
.await; .await;
@ -1320,25 +1325,26 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
loop { loop {
let instant = Instant::now(); let instant = Instant::now();
let client = ClientBuilder::new() let client = ClientBuilder::new()
.timeout(tokio::time::Duration::from_millis(3000)) .timeout(tokio::time::Duration::from_millis(3000))
.build() .build()
.unwrap(); .unwrap();
let tx1_changed = local_epoch_rx2.changed().await; let tx1_changed = local_epoch_rx2.changed().await;
let tx2_changed = epoch_difference_rx2.changed().await; let tx2_changed = epoch_difference_rx2.changed().await;
let local_epoch = *local_epoch_rx2.borrow(); let local_epoch = *local_epoch_rx2.borrow();
let difference_epoch = *epoch_difference_rx2.borrow(); let difference_epoch = *epoch_difference_rx2.borrow();
let result = request_others::request_delist_symbols( let result = request_others::request_delist_symbols(
API_KEY, API_KEY,
SECRET_KEY, SECRET_KEY,
local_epoch, local_epoch,
difference_epoch, difference_epoch,
&client &client,
) )
.await; .await;
tx_task27.send(27).expect("The mpsc channel has been closed."); tx_task27
.send(27)
.expect("The mpsc channel has been closed.");
// sleep as much as the loop recurs per 300 seconds if all operation finished within 300 seconds. // sleep as much as the loop recurs per 300 seconds if all operation finished within 300 seconds.
elapsed_time = instant.elapsed().as_secs(); elapsed_time = instant.elapsed().as_secs();
@ -1348,18 +1354,20 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
}); });
loop { loop {}
}
Ok(()) Ok(())
} }
fn program_setting() { fn program_setting() {
let matches = Command::new("Tradingbot") let matches = Command::new("Tradingbot")
.arg(arg!(log_level: -l --log <level> "Select log level: trace, debug, info, warn, error").default_value("error")) .arg(
arg!(log_level: -l --log <level> "Select log level: trace, debug, info, warn, error")
.default_value("error"),
)
.arg(arg!(mode: -m --mode <mode> "Select mode: real, simul, test").required(true)) .arg(arg!(mode: -m --mode <mode> "Select mode: real, simul, test").required(true))
.get_matches(); .get_matches();
// set log level // set log level
set_up_color_terminal(); set_up_color_terminal();
if let Some(level) = matches.get_one::<String>("log_level") { if let Some(level) = matches.get_one::<String>("log_level") {
@ -1374,7 +1382,6 @@ fn program_setting() {
log::error!("wrong log level argument."); log::error!("wrong log level argument.");
std::process::exit(0); std::process::exit(0);
} }
} }
} }
@ -1385,15 +1392,15 @@ fn program_setting() {
"real" => { "real" => {
RUNNING_MODE = RunningMode::REAL; RUNNING_MODE = RunningMode::REAL;
println!("*** REAL MODE ***"); println!("*** REAL MODE ***");
}, }
"simul" => { "simul" => {
RUNNING_MODE = RunningMode::SIMUL; RUNNING_MODE = RunningMode::SIMUL;
println!("*** SIMULATION MODE ***"); println!("*** SIMULATION MODE ***");
}, }
"test" => { "test" => {
RUNNING_MODE = RunningMode::TEST; RUNNING_MODE = RunningMode::TEST;
println!("*** TEST MODE ***"); println!("*** TEST MODE ***");
}, }
_ => { _ => {
log::error!("wrong mode argument."); log::error!("wrong mode argument.");
std::process::exit(0); std::process::exit(0);

View File

@ -3,11 +3,11 @@
use crate::database_control::*; use crate::database_control::*;
use crate::time_checking_team::UserTime; use crate::time_checking_team::UserTime;
use log;
use rand::*; use rand::*;
use reqwest::{Client, ClientBuilder, Response}; use reqwest::{Client, ClientBuilder, Response};
use tokio::join; use tokio::join;
use tokio::time::*; use tokio::time::*;
use log;
#[derive(Debug)] #[derive(Debug)]
pub struct ServerHealth { pub struct ServerHealth {
@ -55,7 +55,7 @@ pub async fn execute_server_health_check(
if waiting_time > 2 { if waiting_time > 2 {
log::warn!(">>> retry connection after {} second(s).", waiting_time); log::warn!(">>> retry connection after {} second(s).", waiting_time);
} }
sleep(Duration::from_secs(waiting_time as u64)).await; sleep(Duration::from_secs(waiting_time as u64)).await;
} }
} }

View File

@ -9,13 +9,15 @@ pub mod strategy_008;
// pub mod strategy_test; // pub mod strategy_test;
pub mod strategy_manager; pub mod strategy_manager;
use crate::coex::order_team::{limit_order_sell, select_filled_buy_orders};
use crate::coex::exchange_team::get_server_epoch; use crate::coex::exchange_team::get_server_epoch;
use crate::coex::order_team::{limit_order_sell, select_filled_buy_orders};
use crate::coin_health_check_team::request_others::{ExchangeInfo, TradeFee}; use crate::coin_health_check_team::request_others::{ExchangeInfo, TradeFee};
use crate::database_control::*; use crate::database_control::*;
use crate::decimal_funcs::*; use crate::decimal_funcs::*;
use crate::value_estimation_team::datapoints::price_data::{RealtimePriceData, CandleType}; use crate::value_estimation_team::datapoints::price_data::{CandleType, RealtimePriceData};
use crate::value_estimation_team::indicators::adx::{adx, AdxData};
use crate::value_estimation_team::indicators::bollingerband::{bollingerband, BollingerBandData}; use crate::value_estimation_team::indicators::bollingerband::{bollingerband, BollingerBandData};
use crate::value_estimation_team::indicators::dema::{dema, DemaData};
use crate::value_estimation_team::indicators::ema::{ema, ema_opclo, EmaData}; use crate::value_estimation_team::indicators::ema::{ema, ema_opclo, EmaData};
use crate::value_estimation_team::indicators::heatmap_volume::{ use crate::value_estimation_team::indicators::heatmap_volume::{
heatmap_volume, HeatMapLevel, HeatmapVolumeData, heatmap_volume, HeatMapLevel, HeatmapVolumeData,
@ -24,19 +26,19 @@ 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::rsi::{rsi, RsiData};
use crate::value_estimation_team::indicators::sma::{sma, sma_opclo, SmaData}; 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::stoch_rsi::{stoch_rsi, StochRsiData};
use crate::value_estimation_team::indicators::supertrend::{supertrend, SupertrendData, SuperTrendArea, SuperTrendSignal}; use crate::value_estimation_team::indicators::supertrend::{
use crate::value_estimation_team::indicators::adx::{AdxData, adx}; supertrend, SuperTrendArea, SuperTrendSignal, SupertrendData,
use crate::value_estimation_team::indicators::dema::{DemaData, dema}; };
use crate::value_estimation_team::indicators::tema::{TemaData, tema}; use crate::value_estimation_team::indicators::tema::{tema, TemaData};
use futures::future::try_join_all; use futures::future::try_join_all;
use reqwest::{Client, ClientBuilder}; use reqwest::{Client, ClientBuilder};
use rust_decimal::{prelude::FromPrimitive, prelude::ToPrimitive, Decimal, RoundingStrategy}; use rust_decimal::{prelude::FromPrimitive, prelude::ToPrimitive, Decimal, RoundingStrategy};
use rust_decimal_macros::dec; use rust_decimal_macros::dec;
use sqlx::FromRow; use sqlx::FromRow;
use std::collections::{HashMap, HashSet};
use std::sync::Arc; use std::sync::Arc;
use strategy_manager::insert_pre_suggested_coins; use strategy_manager::insert_pre_suggested_coins;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AllData { pub struct AllData {
@ -92,9 +94,10 @@ impl FilteredDataValue {
} }
} }
pub async fn duplicate_filter(registerer: i32, original_filtered_data: &HashMap<String, FilteredDataValue>) pub async fn duplicate_filter(
-> Result<HashMap<String, FilteredDataValue>, Box<dyn std::error::Error + Send + Sync>> 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_1 = String::from("buy_ordered_coin_list");
let inspect_table_name_2 = String::from("sell_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_3 = String::from("pre_suggested_coin_list");
@ -146,14 +149,17 @@ pub async fn duplicate_filter(registerer: i32, original_filtered_data: &HashMap<
Ok(filtered_data_c) Ok(filtered_data_c)
} }
pub async fn remove_keys(filtered_data: &mut HashMap<String, FilteredDataValue>, keys_to_remove: HashSet<String>) { pub async fn remove_keys(
filtered_data: &mut HashMap<String, FilteredDataValue>,
keys_to_remove: HashSet<String>,
) {
let len_prev = filtered_data.len(); let len_prev = filtered_data.len();
// remove key-value in filtered_data // remove key-value in filtered_data
for symbol in keys_to_remove { for symbol in keys_to_remove {
filtered_data.remove(&symbol); filtered_data.remove(&symbol);
} }
let len_now = filtered_data.len(); let len_now = filtered_data.len();
// shrink capacity of filtered_data // shrink capacity of filtered_data
if len_now != len_prev { if len_now != len_prev {
filtered_data.shrink_to_fit(); filtered_data.shrink_to_fit();

View File

@ -1,11 +1,12 @@
use crate::value_estimation_team::indicators::bollingerband::bollingerband; use crate::value_estimation_team::indicators::bollingerband::bollingerband;
use super::{ use super::{
dec, decimal_add, decimal_sub, decimal_div, ema, exists_record, insert_pre_suggested_coins, adx, dec, decimal_add, decimal_div, decimal_sub, duplicate_filter, ema, ema_macd,
limit_order_sell, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, AllData, exists_record, get_server_epoch, insert_pre_suggested_coins, limit_order_sell, remove_keys,
Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredDataValue, Mutex, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, update_record3, AdxData,
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, get_server_epoch, MacdData, ema_macd, AllData, Arc, BollingerBandData, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo,
BollingerBandData, ToPrimitive, duplicate_filter, HashMap, HashSet, remove_keys, SuperTrendArea, SuperTrendSignal FilteredDataValue, HashMap, HashSet, MacdData, Mutex, RealtimePriceData, RoundingStrategy,
RsiData, StochRsiData, SuperTrendArea, SuperTrendSignal, SupertrendData, ToPrimitive, TradeFee,
}; };
// BB lowerband + SuperTrend + StochRSI // BB lowerband + SuperTrend + StochRSI
@ -23,7 +24,7 @@ pub async fn list_up_for_buy(
for symbol in &alldata.valid_symbol_vec { for symbol in &alldata.valid_symbol_vec {
filtered_data.insert(symbol.clone(), FilteredDataValue::new()); filtered_data.insert(symbol.clone(), FilteredDataValue::new());
} }
// 4th filtering: 0.5% <= the average amplitude of the latest 10 30m candles <= 1.0% // 4th filtering: 0.5% <= the average amplitude of the latest 10 30m candles <= 1.0%
let mut keys_to_remove: HashSet<String> = HashSet::new(); let mut keys_to_remove: HashSet<String> = HashSet::new();
for (symbol, values) in &mut filtered_data { for (symbol, values) in &mut filtered_data {
@ -31,12 +32,14 @@ pub async fn list_up_for_buy(
let rt_price_30m = alldata.rt_price_30m_vec.get(symbol).unwrap(); let rt_price_30m = alldata.rt_price_30m_vec.get(symbol).unwrap();
let vec_len = rt_price_30m.len(); let vec_len = rt_price_30m.len();
if let Some(candles) = rt_price_30m.get(vec_len-12..vec_len-1) { if let Some(candles) = rt_price_30m.get(vec_len - 12..vec_len - 1) {
let windows = candles.windows(2); let windows = candles.windows(2);
let mut average_amplitude = 0.0; let mut average_amplitude = 0.0;
for window in windows { for window in windows {
average_amplitude += (window.last().unwrap().high_price - window.last().unwrap().low_price) / window.first().unwrap().close_price; average_amplitude += (window.last().unwrap().high_price
- window.last().unwrap().low_price)
/ window.first().unwrap().close_price;
} }
average_amplitude /= 10.0; average_amplitude /= 10.0;
@ -52,61 +55,48 @@ pub async fn list_up_for_buy(
} }
} }
remove_keys(&mut filtered_data, keys_to_remove).await; remove_keys(&mut filtered_data, keys_to_remove).await;
// 2nd filtering: supertrend(ATR period 20, multiplier: 2, 30m close price) // 2nd filtering: supertrend(ATR period 20, multiplier: 2, 30m close price)
let mut keys_to_remove: HashSet<String> = HashSet::new(); let mut keys_to_remove: HashSet<String> = HashSet::new();
let server_epoch = get_server_epoch().await; let server_epoch = get_server_epoch().await;
let supertrend_30m_map = supertrend(20, 2.0, true, &alldata.rt_price_30m_vec, &filtered_data).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 { 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 let (Some(supertrend_vec), Some(rt_price_vec)) = (
if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && supertrend_30m_map.get(symbol),
rt_price_vec.last().unwrap().close_time > server_epoch { alldata.rt_price_30m_vec.get(symbol),
// input closetime, current_price ) {
values.closetime = rt_price_vec.last().unwrap().close_time; if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time
values.current_price = rust_decimal::prelude::FromPrimitive::from_f64(rt_price_vec.last().unwrap().close_price).unwrap(); && rt_price_vec.last().unwrap().close_time > server_epoch
{
// input stoploss // input closetime, current_price
let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap(); values.closetime = rt_price_vec.last().unwrap().close_time;
if supertrend_vec.last().unwrap().area == SuperTrendArea::DOWN && values.current_price = rust_decimal::prelude::FromPrimitive::from_f64(
supertrend_vec.last().unwrap().band_value > values.current_price.to_f64().unwrap() rt_price_vec.last().unwrap().close_price,
{ )
values.stoploss = decimal_sub(values.current_price, decimal_sub(band_value, values.current_price)); .unwrap();
} else if supertrend_vec.last().unwrap().area == SuperTrendArea::UP && // input stoploss
supertrend_vec.last().unwrap().band_value < values.current_price.to_f64().unwrap() let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(
{ supertrend_vec.last().unwrap().band_value,
values.stoploss = band_value; )
} else { .unwrap();
keys_to_remove.insert(symbol.clone()); 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 { } else {
keys_to_remove.insert(symbol.clone()); 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 { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -115,15 +105,65 @@ pub async fn list_up_for_buy(
} }
} }
remove_keys(&mut filtered_data, keys_to_remove).await; 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 // 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 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?; let stoch_rsis = stoch_rsi(10, 10, 3, 3, &alldata.rt_price_30m_vec, &filtered_data).await?;
for (symbol, values) in &mut filtered_data { for (symbol, values) in &mut filtered_data {
if alldata.rt_price_30m_vec.contains_key(symbol) && stoch_rsis.contains_key(symbol) { if alldata.rt_price_30m_vec.contains_key(symbol) && stoch_rsis.contains_key(symbol) {
let stoch_rsi_vec = stoch_rsis.get(symbol).unwrap(); let stoch_rsi_vec = stoch_rsis.get(symbol).unwrap();
let search_result = stoch_rsi_vec.iter().position(|x| x.close_time == values.closetime); let search_result = stoch_rsi_vec
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) { .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
}) {
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -132,17 +172,26 @@ pub async fn list_up_for_buy(
} }
} }
remove_keys(&mut filtered_data, keys_to_remove).await; remove_keys(&mut filtered_data, keys_to_remove).await;
// filtering: 1d MACD (3, 7, 30) current MACD-signal > prev MACD-signal // filtering: 1d MACD (3, 7, 30) current MACD-signal > prev MACD-signal
let mut keys_to_remove: HashSet<String> = HashSet::new(); 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?; let macd_1d_map = ema_macd(3, 7, 30, &alldata.rt_price_1d_vec, &filtered_data).await?;
for (symbol, values) in &mut filtered_data { 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 let (Some(macd_vec), Some(rt_price_vec)) =
if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && (macd_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol))
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 if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time
{ && rt_price_vec.last().unwrap().close_time > server_epoch
values.current_price = rust_decimal::prelude::FromPrimitive::from_f64(rt_price_vec.last().unwrap().close_price).unwrap(); {
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; values.closetime = rt_price_vec.last().unwrap().close_time;
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
@ -161,10 +210,13 @@ pub async fn list_up_for_buy(
let adx_vec = adx(10, 10, &alldata.rt_price_30m_vec, &filtered_data).await?; let adx_vec = adx(10, 10, &alldata.rt_price_30m_vec, &filtered_data).await?;
for (symbol, values) in &mut filtered_data { for (symbol, values) in &mut filtered_data {
if let Some(adx_vec) = adx_vec.get(symbol) { 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 let Some(last_idx) = adx_vec
if .iter()
adx_vec[last_idx].adx < adx_vec[last_idx-1].adx && .position(|elem| elem.close_time == values.closetime)
adx_vec[last_idx-1].adx < adx_vec[last_idx-2].adx { {
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 { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -173,7 +225,7 @@ pub async fn list_up_for_buy(
} }
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
} }
remove_keys(&mut filtered_data, keys_to_remove).await; remove_keys(&mut filtered_data, keys_to_remove).await;
@ -202,37 +254,49 @@ pub async fn list_up_for_sell(
for element in &filled_buy_orders { for element in &filled_buy_orders {
filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new()); filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new());
} }
let supertrend_30m = supertrend(20, 2.0, true, &all_data.rt_price_30m_vec, &filtered_symbols).await?; let supertrend_30m =
let stoch_rsis = stoch_rsi(10, 10, 3, 3, &all_data.rt_price_30m_vec, &filtered_symbols).await?; 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 { for element in filled_buy_orders {
if element.used_usdt >= dec!(10.0) { if element.used_usdt >= dec!(10.0) {
if let (Some(exchange_info), Some(tradefee), Some(stoch_rsi), Some(supertrend_vec)) = if let (
(exchange_info_map.get(&element.symbol), trade_fee_map.get(&element.symbol), stoch_rsis.get(&element.symbol), supertrend_30m.get(&element.symbol)) { 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 // update stoploss
let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap(); let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(
supertrend_vec.last().unwrap().band_value,
)
.unwrap();
if supertrend_vec.last().unwrap().area == SuperTrendArea::UP if supertrend_vec.last().unwrap().area == SuperTrendArea::UP
&& band_value > element.stoploss { && band_value > element.stoploss
let update_table_name = String::from("buy_ordered_coin_list"); {
let update_value = vec![ let update_table_name = String::from("buy_ordered_coin_list");
(String::from("stoploss"), band_value.to_string()), let update_value = vec![(String::from("stoploss"), band_value.to_string())];
]; let update_condition = vec![(String::from("id"), element.id.to_string())];
let update_condition = vec![(String::from("id"), element.id.to_string())]; update_record3(&update_table_name, &update_value, &update_condition)
update_record3(&update_table_name, &update_value, &update_condition) .await
.await .unwrap();
.unwrap(); }
}
let lot_step_size = exchange_info.stepsize; let lot_step_size = exchange_info.stepsize;
let quote_commission_precision = exchange_info.quote_commission_precision; let quote_commission_precision = exchange_info.quote_commission_precision;
let base_qty_to_be_ordered = let base_qty_to_be_ordered = element.base_qty_ordered.round_dp_with_strategy(
element.base_qty_ordered.round_dp_with_strategy( lot_step_size.normalize().scale(),
lot_step_size.normalize().scale(), RoundingStrategy::ToZero,
RoundingStrategy::ToZero, );
);
let stoch_rsi_k = stoch_rsi.last().unwrap().k; let stoch_rsi_k = stoch_rsi.last().unwrap().k;
let stoch_rsi_k_prev = stoch_rsi[stoch_rsi.len()-2].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 = stoch_rsi.last().unwrap().d;
let stoch_rsi_d_prev = stoch_rsi[stoch_rsi.len()-2].d; let stoch_rsi_d_prev = stoch_rsi[stoch_rsi.len() - 2].d;
if (element.is_long == 0 || element.is_long == 1) if (element.is_long == 0 || element.is_long == 1)
&& !element.current_price.is_zero() && !element.current_price.is_zero()
{ {
@ -266,9 +330,9 @@ pub async fn list_up_for_sell(
&trade_fee_map, &trade_fee_map,
) )
.await; .await;
} else if stoch_rsi_k >= 90.0 && } else if stoch_rsi_k >= 90.0
(stoch_rsi_k < stoch_rsi_d && && (stoch_rsi_k < stoch_rsi_d && stoch_rsi_k_prev > stoch_rsi_d_prev)
stoch_rsi_k_prev > stoch_rsi_d_prev) { {
limit_order_sell( limit_order_sell(
&element, &element,
element.current_price, element.current_price,
@ -280,7 +344,7 @@ pub async fn list_up_for_sell(
.await; .await;
} }
// TODO: sell_count가 1일 때 적용하기 // TODO: sell_count가 1일 때 적용하기
// else if (supertrend_vec // else if (supertrend_vec
// .last() // .last()
// .unwrap() // .unwrap()
// .signal // .signal
@ -305,7 +369,6 @@ pub async fn list_up_for_sell(
// } // }
} }
} }
} }
} }
} }

View File

@ -1,11 +1,12 @@
use crate::value_estimation_team::indicators::bollingerband::bollingerband; use crate::value_estimation_team::indicators::bollingerband::bollingerband;
use super::{ use super::{
dec, decimal_add, decimal_sub, decimal_div, ema, exists_record, insert_pre_suggested_coins, adx, dec, decimal_add, decimal_div, decimal_sub, duplicate_filter, ema, ema_macd,
limit_order_sell, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, AllData, exists_record, get_server_epoch, insert_pre_suggested_coins, limit_order_sell, remove_keys,
Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredDataValue, Mutex, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, update_record3, AdxData,
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, get_server_epoch, MacdData, ema_macd, AllData, Arc, BollingerBandData, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo,
BollingerBandData, ToPrimitive, duplicate_filter, HashMap, HashSet, remove_keys, SuperTrendArea, SuperTrendSignal FilteredDataValue, HashMap, HashSet, MacdData, Mutex, RealtimePriceData, RoundingStrategy,
RsiData, StochRsiData, SuperTrendArea, SuperTrendSignal, SupertrendData, ToPrimitive, TradeFee,
}; };
// BB 30m lowerband + BB 1m lowerband // BB 30m lowerband + BB 1m lowerband
@ -29,12 +30,14 @@ pub async fn list_up_for_buy(
if let Some(rt_price_30m_vec) = alldata.rt_price_30m_vec.get(symbol) { if let Some(rt_price_30m_vec) = alldata.rt_price_30m_vec.get(symbol) {
let vec_len = rt_price_30m_vec.len(); let vec_len = rt_price_30m_vec.len();
if vec_len >= 11 { if vec_len >= 11 {
let candles = rt_price_30m_vec.get(vec_len-12..vec_len-1).unwrap(); let candles = rt_price_30m_vec.get(vec_len - 12..vec_len - 1).unwrap();
let windows = candles.windows(2); let windows = candles.windows(2);
let mut average_amplitude = 0.0; let mut average_amplitude = 0.0;
for window in windows { for window in windows {
average_amplitude += (window.last().unwrap().high_price - window.last().unwrap().low_price) / window.first().unwrap().close_price; average_amplitude += (window.last().unwrap().high_price
- window.last().unwrap().low_price)
/ window.first().unwrap().close_price;
} }
average_amplitude /= 10.0; average_amplitude /= 10.0;
@ -50,30 +53,46 @@ pub async fn list_up_for_buy(
} }
} }
remove_keys(&mut filtered_data, keys_to_remove).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 // 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 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_30m_map =
let bollingerbands_1m_map = bollingerband(30, 3.0, &alldata.rt_price_1m_vec, &filtered_data).await?; 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; let server_epoch = get_server_epoch().await;
for (symbol, filtered_data) in &mut filtered_data { 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 let (Some(bb_30m_vec), Some(bb_1m_vec), Some(rt_30m_vec)) = (
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 { 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( let bb_30m_search_result = bb_30m_vec.binary_search_by_key(
&rt_30m_vec.last().unwrap().close_time, &rt_30m_vec.last().unwrap().close_time,
|BollingerBandData { |BollingerBandData {
sma, sma,
upperband, upperband,
lowerband, lowerband,
close_time, close_time,
}| *close_time, }| *close_time,
); );
if bb_30m_search_result.is_ok() { 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 && if bb_30m_vec[bb_30m_search_result.unwrap()].lowerband
bb_1m_vec.last().unwrap().lowerband > rt_30m_vec[rt_30m_vec.len()-1].close_price > 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.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.current_price =
rust_decimal::prelude::FromPrimitive::from_f64(
rt_30m_vec.last().unwrap().close_price,
)
.unwrap();
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -91,26 +110,48 @@ pub async fn list_up_for_buy(
// 3rd filtering: supertrend(ATR period 7, multiplier: 1.5, 30m close price), area should be DOWN // 3rd filtering: supertrend(ATR period 7, multiplier: 1.5, 30m close price), area should be DOWN
let mut keys_to_remove: HashSet<String> = HashSet::new(); 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 supertrend_30m_map =
supertrend(7, 1.5, true, &alldata.rt_price_30m_vec, &filtered_data).await?;
let server_epoch = get_server_epoch().await; let server_epoch = get_server_epoch().await;
for (symbol, filtered_data) in &mut filtered_data { 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 let (Some(rt_30m_vec), Some(supertrend_vec)) = (
if rt_30m_vec.len() >= 3 && supertrend_vec.len() >= 3 && rt_30m_vec.last().unwrap().close_time > server_epoch { 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( let supertrend_search_result = supertrend_vec.binary_search_by_key(
&rt_30m_vec.last().unwrap().close_time, &rt_30m_vec.last().unwrap().close_time,
|SupertrendData { |SupertrendData {
band_value, band_value,
signal, signal,
area, area,
close_time, close_time,
}| *close_time, }| *close_time,
); );
if supertrend_search_result.is_ok() { if supertrend_search_result.is_ok() {
if supertrend_vec[supertrend_search_result.unwrap()].area == SuperTrendArea::DOWN if supertrend_vec[supertrend_search_result.unwrap()].area
&& supertrend_vec[supertrend_search_result.unwrap()].band_value > filtered_data.current_price.to_f64().unwrap() == SuperTrendArea::DOWN
&& supertrend_vec[supertrend_search_result.unwrap()].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(); filtered_data.target_price =
let stop_loss = decimal_sub(filtered_data.current_price, decimal_div(decimal_sub(filtered_data.target_price, filtered_data.current_price), dec!(2))); 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.stoploss = stop_loss;
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
@ -131,18 +172,31 @@ pub async fn list_up_for_buy(
let mut keys_to_remove: HashSet<String> = HashSet::new(); let mut keys_to_remove: HashSet<String> = HashSet::new();
let ema_map = ema(200, &alldata.rt_price_30m_vec, &filtered_data).await?; let ema_map = ema(200, &alldata.rt_price_30m_vec, &filtered_data).await?;
for (symbol, filtered_data) in &mut filtered_data { 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)) { 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( let search_result = ema_vec.binary_search_by_key(
&rt_30m_vec.last().unwrap().close_time, &rt_30m_vec.last().unwrap().close_time,
|EmaData { |EmaData {
ema_value, ema_value,
close_time, close_time,
}| *close_time); }| *close_time,
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) && if search_result
search_result.is_ok_and(|x| ema_vec[x-2].ema_value < rt_30m_vec[rt_30m_vec.len()-3].close_price) && .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-3].ema_value < rt_30m_vec[rt_30m_vec.len()-4].close_price) && && search_result.is_ok_and(|x| {
search_result.is_ok_and(|x| ema_vec[x-4].ema_value < rt_30m_vec[rt_30m_vec.len()-5].close_price) { 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 { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -156,12 +210,21 @@ pub async fn list_up_for_buy(
let mut keys_to_remove: HashSet<String> = HashSet::new(); 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?; let macd_1d_map = ema_macd(3, 7, 30, &alldata.rt_price_1d_vec, &filtered_data).await?;
for (symbol, values) in &mut filtered_data { 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 let (Some(macd_vec), Some(rt_price_vec)) =
if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && (macd_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol))
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 if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time
{ && rt_price_vec.last().unwrap().close_time > server_epoch
values.current_price = rust_decimal::prelude::FromPrimitive::from_f64(rt_price_vec.last().unwrap().close_price).unwrap(); {
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; values.closetime = rt_price_vec.last().unwrap().close_time;
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
@ -174,7 +237,7 @@ pub async fn list_up_for_buy(
} }
} }
remove_keys(&mut filtered_data, keys_to_remove).await; remove_keys(&mut filtered_data, keys_to_remove).await;
let final_filtered_data = duplicate_filter(2, &filtered_data).await?; let final_filtered_data = duplicate_filter(2, &filtered_data).await?;
insert_pre_suggested_coins(2, false, &final_filtered_data, &alldata).await; insert_pre_suggested_coins(2, false, &final_filtered_data, &alldata).await;
@ -199,17 +262,15 @@ pub async fn list_up_for_sell(
if let Some(exchange_info) = exchange_info_map.get(&element.symbol) { if let Some(exchange_info) = exchange_info_map.get(&element.symbol) {
let lot_step_size = exchange_info.stepsize; let lot_step_size = exchange_info.stepsize;
let quote_commission_precision = exchange_info.quote_commission_precision; let quote_commission_precision = exchange_info.quote_commission_precision;
let base_qty_to_be_ordered = let base_qty_to_be_ordered = element.base_qty_ordered.round_dp_with_strategy(
element.base_qty_ordered.round_dp_with_strategy( lot_step_size.normalize().scale(),
lot_step_size.normalize().scale(), RoundingStrategy::ToZero,
RoundingStrategy::ToZero, );
);
if (element.is_long == 0 || element.is_long == 1) if (element.is_long == 0 || element.is_long == 1)
&& !element.current_price.is_zero() && !element.current_price.is_zero()
{ {
if element.current_price >= element.target_price if element.current_price >= element.target_price {
{
limit_order_sell( limit_order_sell(
&element, &element,
element.current_price, element.current_price,
@ -241,7 +302,7 @@ pub async fn list_up_for_sell(
.await; .await;
} }
// TODO: sell_count가 1일 때 적용하기 // TODO: sell_count가 1일 때 적용하기
// else if (supertrend_vec // else if (supertrend_vec
// .last() // .last()
// .unwrap() // .unwrap()
// .signal // .signal

View File

@ -1,11 +1,13 @@
use crate::value_estimation_team::indicators::bollingerband::bollingerband; use crate::value_estimation_team::indicators::bollingerband::bollingerband;
use super::{ use super::{
dec, decimal_add, decimal_sub, decimal_div, ema, ema_opclo, sma, sma_opclo, exists_record, insert_pre_suggested_coins, adx, dec, decimal_add, decimal_div, decimal_sub, duplicate_filter, ema, ema_macd, ema_opclo,
limit_order_sell, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, AllData, exists_record, get_server_epoch, insert_pre_suggested_coins, limit_order_sell, remove_keys,
Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredDataValue, Mutex, SmaData, rsi, select_filled_buy_orders, sma, sma_opclo, stoch_rsi, supertrend, try_join_all,
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, get_server_epoch, MacdData, ema_macd, update_record3, AdxData, AllData, Arc, BollingerBandData, Client, ClientBuilder, Decimal,
BollingerBandData, ToPrimitive, duplicate_filter, HashMap, HashSet, remove_keys, SuperTrendArea, SuperTrendSignal EmaData, ExchangeInfo, FilteredDataValue, HashMap, HashSet, MacdData, Mutex, RealtimePriceData,
RoundingStrategy, RsiData, SmaData, StochRsiData, SuperTrendArea, SuperTrendSignal,
SupertrendData, ToPrimitive, TradeFee,
}; };
// BUY: 30m SMA5 (opclo_price) < 30m EMA3 (opclo_price) // BUY: 30m SMA5 (opclo_price) < 30m EMA3 (opclo_price)
@ -25,23 +27,31 @@ pub async fn list_up_for_buy(
// 3rd filtering: supertrend(ATR period 20, multiplier: 4.0, 30m close price), area should be UP // 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 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 supertrend_30m_map =
supertrend(20, 4.0, true, &alldata.rt_price_30m_vec, &filtered_data).await?;
let server_epoch = get_server_epoch().await; let server_epoch = get_server_epoch().await;
for (symbol, filtered_data) in &mut filtered_data { 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 let (Some(rt_30m_vec), Some(supertrend_vec)) = (
if rt_30m_vec.len() >= 3 && supertrend_vec.len() >= 3 && rt_30m_vec.last().unwrap().close_time > server_epoch { 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( let supertrend_search_result = supertrend_vec.binary_search_by_key(
&rt_30m_vec.last().unwrap().close_time, &rt_30m_vec.last().unwrap().close_time,
|SupertrendData { |SupertrendData {
band_value, band_value,
signal, signal,
area, area,
close_time, close_time,
}| *close_time, }| *close_time,
); );
if supertrend_search_result.is_ok() { if supertrend_search_result.is_ok() {
if supertrend_vec[supertrend_search_result.unwrap()].area == SuperTrendArea::UP 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() && supertrend_vec[supertrend_search_result.unwrap()].band_value
< filtered_data.current_price.to_f64().unwrap()
{ {
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
@ -57,17 +67,24 @@ pub async fn list_up_for_buy(
} }
} }
remove_keys(&mut filtered_data, keys_to_remove).await; remove_keys(&mut filtered_data, keys_to_remove).await;
// 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 // 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 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?; let stoch_rsis = stoch_rsi(30, 30, 3, 3, &alldata.rt_price_30m_vec, &filtered_data).await?;
for (symbol, values) in &mut filtered_data { for (symbol, values) in &mut filtered_data {
if alldata.rt_price_30m_vec.contains_key(symbol) && stoch_rsis.contains_key(symbol) { if alldata.rt_price_30m_vec.contains_key(symbol) && stoch_rsis.contains_key(symbol) {
let stoch_rsi_vec = stoch_rsis.get(symbol).unwrap(); let stoch_rsi_vec = stoch_rsis.get(symbol).unwrap();
let search_result = stoch_rsi_vec.iter().position(|x| x.close_time == values.closetime); let search_result = stoch_rsi_vec
if search_result.is_some_and(|a| stoch_rsi_vec[a-1].k < 10.0 && stoch_rsi_vec[a].k < 10.0 .iter()
&& stoch_rsi_vec[a-1].d < 10.0 && stoch_rsi_vec[a].d< 10.0 && .position(|x| x.close_time == values.closetime);
stoch_rsi_vec[a-1].k <= stoch_rsi_vec[a-1].d && stoch_rsi_vec[a].k > stoch_rsi_vec[a].d) { 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 { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -80,19 +97,31 @@ pub async fn list_up_for_buy(
// 3rd filtering: the latest 5 30m candle close prices > EMA 200 // 3rd filtering: the latest 5 30m candle close prices > EMA 200
let mut keys_to_remove: HashSet<String> = HashSet::new(); let mut keys_to_remove: HashSet<String> = HashSet::new();
let emas = ema(200, &alldata.rt_price_30m_vec, &filtered_data).await?; let emas = ema(200, &alldata.rt_price_30m_vec, &filtered_data).await?;
for (symbol, values) in &mut filtered_data { for (symbol, values) in &mut filtered_data {
if emas.contains_key(symbol) && alldata.rt_price_30m_vec.contains_key(symbol) { if emas.contains_key(symbol) && alldata.rt_price_30m_vec.contains_key(symbol) {
let ema = emas.get(symbol).unwrap(); let ema = emas.get(symbol).unwrap();
let rt_price_30m = alldata.rt_price_30m_vec.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 rt_price_30m_len = rt_price_30m.len();
let search_result = ema.binary_search_by_key( let search_result = ema.binary_search_by_key(
&alldata.rt_price_30m_vec.get(symbol).unwrap().last().unwrap().close_time, &alldata
.rt_price_30m_vec
.get(symbol)
.unwrap()
.last()
.unwrap()
.close_time,
|EmaData { |EmaData {
ema_value, ema_value,
close_time, close_time,
}| *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) { 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 { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -106,12 +135,21 @@ pub async fn list_up_for_buy(
let mut keys_to_remove: HashSet<String> = HashSet::new(); 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?; let macd_1d_map = ema_macd(3, 7, 30, &alldata.rt_price_1d_vec, &filtered_data).await?;
for (symbol, values) in &mut filtered_data { 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 let (Some(macd_vec), Some(rt_price_vec)) =
if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && (macd_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol))
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 if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time
{ && rt_price_vec.last().unwrap().close_time > server_epoch
values.current_price = rust_decimal::prelude::FromPrimitive::from_f64(rt_price_vec.last().unwrap().close_price).unwrap(); {
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; values.closetime = rt_price_vec.last().unwrap().close_time;
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
@ -124,7 +162,7 @@ pub async fn list_up_for_buy(
} }
} }
remove_keys(&mut filtered_data, keys_to_remove).await; remove_keys(&mut filtered_data, keys_to_remove).await;
let final_filtered_data = duplicate_filter(3, &filtered_data).await?; let final_filtered_data = duplicate_filter(3, &filtered_data).await?;
insert_pre_suggested_coins(3, false, &final_filtered_data, &alldata).await; insert_pre_suggested_coins(3, false, &final_filtered_data, &alldata).await;
@ -148,27 +186,29 @@ pub async fn list_up_for_sell(
filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new()); filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new());
} }
let server_epoch = get_server_epoch().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?; let stoch_rsis =
stoch_rsi(30, 30, 3, 3, &all_data.rt_price_30m_vec, &filtered_symbols).await?;
for element in filled_buy_orders { for element in filled_buy_orders {
if element.used_usdt >= dec!(10.0) { 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)) { 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 lot_step_size = exchange_info.stepsize;
let quote_commission_precision = exchange_info.quote_commission_precision; let quote_commission_precision = exchange_info.quote_commission_precision;
let base_qty_to_be_ordered = let base_qty_to_be_ordered = element.base_qty_ordered.round_dp_with_strategy(
element.base_qty_ordered.round_dp_with_strategy( lot_step_size.normalize().scale(),
lot_step_size.normalize().scale(), RoundingStrategy::ToZero,
RoundingStrategy::ToZero, );
);
let stoch_rsi_k = stoch_rsi.last().unwrap().k; let stoch_rsi_k = stoch_rsi.last().unwrap().k;
let stoch_rsi_k_prev = stoch_rsi[stoch_rsi.len()-2].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 = stoch_rsi.last().unwrap().d;
let stoch_rsi_d_prev = stoch_rsi[stoch_rsi.len()-2].d; let stoch_rsi_d_prev = stoch_rsi[stoch_rsi.len() - 2].d;
if (element.is_long == 0 || element.is_long == 1) if (element.is_long == 0 || element.is_long == 1)
&& !element.current_price.is_zero() && !element.current_price.is_zero()
{ {
if element.current_price >= element.target_price if element.current_price >= element.target_price {
{
limit_order_sell( limit_order_sell(
&element, &element,
element.current_price, element.current_price,
@ -208,10 +248,12 @@ pub async fn list_up_for_sell(
&trade_fee_map, &trade_fee_map,
) )
.await; .await;
} else if stoch_rsi_k >= 20.0 && stoch_rsi_k >= 20.0 && } else if stoch_rsi_k >= 20.0
stoch_rsi_k_prev >= 20.0 && stoch_rsi_k_prev >= 20.0 && && stoch_rsi_k >= 20.0
stoch_rsi_k < stoch_rsi_d && && stoch_rsi_k_prev >= 20.0
stoch_rsi_k_prev >= stoch_rsi_d_prev && stoch_rsi_k_prev >= 20.0
&& stoch_rsi_k < stoch_rsi_d
&& stoch_rsi_k_prev >= stoch_rsi_d_prev
{ {
limit_order_sell( limit_order_sell(
&element, &element,
@ -222,9 +264,9 @@ pub async fn list_up_for_sell(
&trade_fee_map, &trade_fee_map,
) )
.await; .await;
} }
// TODO: sell_count가 1일 때 적용하기 // TODO: sell_count가 1일 때 적용하기
// else if (supertrend_vec // else if (supertrend_vec
// .last() // .last()
// .unwrap() // .unwrap()
// .signal // .signal

View File

@ -1,11 +1,13 @@
use crate::value_estimation_team::indicators::bollingerband::bollingerband; use crate::value_estimation_team::indicators::bollingerband::bollingerband;
use super::{ use super::{
dec, decimal_add, decimal_sub, decimal_div, ema, exists_record, insert_pre_suggested_coins, adx, dec, decimal_add, decimal_div, decimal_sub, duplicate_filter, ema, ema_macd,
limit_order_sell, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, AllData, exists_record, get_server_epoch, insert_pre_suggested_coins, limit_order_sell, remove_keys,
Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredDataValue, Mutex, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, update_record3, AdxData,
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, get_server_epoch, MacdData, ema_macd, AllData, Arc, BollingerBandData, CandleType, Client, ClientBuilder, Decimal, EmaData,
BollingerBandData, ToPrimitive, duplicate_filter, HashMap, HashSet, remove_keys, SuperTrendArea, SuperTrendSignal, CandleType ExchangeInfo, FilteredDataValue, HashMap, HashSet, MacdData, Mutex, RealtimePriceData,
RoundingStrategy, RsiData, StochRsiData, SuperTrendArea, SuperTrendSignal, SupertrendData,
ToPrimitive, TradeFee,
}; };
// BB lowerband + SuperTrend + StochRSI // BB lowerband + SuperTrend + StochRSI
@ -30,12 +32,14 @@ pub async fn list_up_for_buy(
if let Some(rt_price_30m_vec) = alldata.rt_price_30m_vec.get(symbol) { if let Some(rt_price_30m_vec) = alldata.rt_price_30m_vec.get(symbol) {
let vec_len = rt_price_30m_vec.len(); let vec_len = rt_price_30m_vec.len();
if vec_len >= 11 { if vec_len >= 11 {
let candles = rt_price_30m_vec.get(vec_len-12..vec_len-1).unwrap(); let candles = rt_price_30m_vec.get(vec_len - 12..vec_len - 1).unwrap();
let windows = candles.windows(2); let windows = candles.windows(2);
let mut average_amplitude = 0.0; let mut average_amplitude = 0.0;
for window in windows { for window in windows {
average_amplitude += (window.last().unwrap().high_price - window.last().unwrap().low_price) / window.first().unwrap().close_price; average_amplitude += (window.last().unwrap().high_price
- window.last().unwrap().low_price)
/ window.first().unwrap().close_price;
} }
average_amplitude /= 10.0; average_amplitude /= 10.0;
@ -51,31 +55,45 @@ pub async fn list_up_for_buy(
} }
} }
remove_keys(&mut filtered_data, keys_to_remove).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 // 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 mut keys_to_remove: HashSet<String> = HashSet::new();
let bollingerband_map = bollingerband(30, 3.0, &alldata.rt_price_30m_vec, &filtered_data).await?; let bollingerband_map =
bollingerband(30, 3.0, &alldata.rt_price_30m_vec, &filtered_data).await?;
let server_epoch = get_server_epoch().await; let server_epoch = get_server_epoch().await;
for (symbol, filtered_data) in &mut filtered_data { 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 let (Some(bb_vec), Some(rt_30m_vec)) = (
if rt_30m_vec.len() >= 3 && bb_vec.len() >= 3 && rt_30m_vec.last().unwrap().close_time > server_epoch { 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( let bb_search_result = bb_vec.binary_search_by_key(
&rt_30m_vec.last().unwrap().close_time, &rt_30m_vec.last().unwrap().close_time,
|BollingerBandData { |BollingerBandData {
sma, sma,
upperband, upperband,
lowerband, lowerband,
close_time, close_time,
}| *close_time, }| *close_time,
); );
if bb_search_result.is_ok() { 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 && if bb_vec[bb_search_result.unwrap() - 1].lowerband
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].low_price
rt_30m_vec[rt_30m_vec.len()-2].candle_type == CandleType::DOWN && && rt_30m_vec[rt_30m_vec.len() - 2].opclo_price
rt_30m_vec[rt_30m_vec.len()-2].high_price < bb_vec[bb_search_result.unwrap()-1].sma > 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.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.current_price =
rust_decimal::prelude::FromPrimitive::from_f64(
rt_30m_vec.last().unwrap().close_price,
)
.unwrap();
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -94,24 +112,43 @@ pub async fn list_up_for_buy(
// 3rd filtering: supertrend(ATR period 10, multiplier: 2, 30m close price), area should be DOWN // 3rd filtering: supertrend(ATR period 10, multiplier: 2, 30m close price), area should be DOWN
let mut keys_to_remove: HashSet<String> = HashSet::new(); let mut keys_to_remove: HashSet<String> = HashSet::new();
let server_epoch = get_server_epoch().await; let server_epoch = get_server_epoch().await;
let supertrend_30m_map = supertrend( 10, 2.0, true, &alldata.rt_price_30m_vec, &filtered_data).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 { 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 let (Some(supertrend_vec), Some(rt_30m_vec)) = (
if rt_30m_vec.len() >= 3 && supertrend_vec.len() >= 3 && rt_30m_vec.last().unwrap().close_time > server_epoch { 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( let supertrend_search_result = supertrend_vec.binary_search_by_key(
&rt_30m_vec.last().unwrap().close_time, &rt_30m_vec.last().unwrap().close_time,
|SupertrendData { |SupertrendData {
band_value, band_value,
signal, signal,
area, area,
close_time, close_time,
}| *close_time, }| *close_time,
); );
if supertrend_search_result.is_ok_and(|x| if supertrend_search_result.is_ok_and(|x| {
supertrend_vec[x].area == SuperTrendArea::DOWN && supertrend_vec[x].band_value > filtered_data.current_price.to_f64().unwrap()) { supertrend_vec[x].area == SuperTrendArea::DOWN
filtered_data.target_price = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec[supertrend_search_result.unwrap()].band_value).unwrap(); && supertrend_vec[x].band_value
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.current_price.to_f64().unwrap()
filtered_data.stoploss = stop_loss; }) {
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;
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -128,18 +165,32 @@ pub async fn list_up_for_buy(
let mut keys_to_remove: HashSet<String> = HashSet::new(); let mut keys_to_remove: HashSet<String> = HashSet::new();
let ema_map = ema(200, &alldata.rt_price_30m_vec, &filtered_data).await?; let ema_map = ema(200, &alldata.rt_price_30m_vec, &filtered_data).await?;
for (symbol, filtered_data) in &mut filtered_data { 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)) { 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( let search_result = ema_vec.binary_search_by_key(
&rt_30m_vec.last().unwrap().close_time, &rt_30m_vec.last().unwrap().close_time,
|EmaData { |EmaData {
ema_value, ema_value,
close_time, close_time,
}| *close_time); }| *close_time,
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) && if search_result.is_ok_and(|x| {
search_result.is_ok_and(|x| ema_vec[search_result.unwrap()-2].ema_value < rt_30m_vec[rt_30m_vec.len()-3].close_price) && ema_vec[search_result.unwrap()].ema_value
search_result.is_ok_and(|x| ema_vec[search_result.unwrap()-3].ema_value < rt_30m_vec[rt_30m_vec.len()-4].close_price) && < rt_30m_vec[rt_30m_vec.len() - 1].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) { }) && 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 { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -152,10 +203,13 @@ pub async fn list_up_for_buy(
// 6th filtering: 30m StochRSI (RSI_len: 10, StochRSI_len: 10, K: 3, D: 3) current K, D < 20 // 6th filtering: 30m StochRSI (RSI_len: 10, StochRSI_len: 10, K: 3, D: 3) current K, D < 20
let mut keys_to_remove: HashSet<String> = HashSet::new(); 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?; 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 { for (symbol, filtered_data) in &mut filtered_data {
if let Some(stoch_rsi_vec) = stoch_rsi_map.get(symbol) { 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); let search_result = stoch_rsi_vec
if search_result.is_some_and(|a| stoch_rsi_vec[a].k < 15.0 && stoch_rsi_vec[a].d < 15.0) { .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)
{
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -169,12 +223,21 @@ pub async fn list_up_for_buy(
let mut keys_to_remove: HashSet<String> = HashSet::new(); 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?; let macd_1d_map = ema_macd(3, 7, 30, &alldata.rt_price_1d_vec, &filtered_data).await?;
for (symbol, values) in &mut filtered_data { 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 let (Some(macd_vec), Some(rt_price_vec)) =
if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && (macd_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol))
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 if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time
{ && rt_price_vec.last().unwrap().close_time > server_epoch
values.current_price = rust_decimal::prelude::FromPrimitive::from_f64(rt_price_vec.last().unwrap().close_price).unwrap(); {
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; values.closetime = rt_price_vec.last().unwrap().close_time;
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
@ -213,17 +276,15 @@ pub async fn list_up_for_sell(
if let Some(exchange_info) = exchange_info_map.get(&element.symbol) { if let Some(exchange_info) = exchange_info_map.get(&element.symbol) {
let lot_step_size = exchange_info.stepsize; let lot_step_size = exchange_info.stepsize;
let quote_commission_precision = exchange_info.quote_commission_precision; let quote_commission_precision = exchange_info.quote_commission_precision;
let base_qty_to_be_ordered = let base_qty_to_be_ordered = element.base_qty_ordered.round_dp_with_strategy(
element.base_qty_ordered.round_dp_with_strategy( lot_step_size.normalize().scale(),
lot_step_size.normalize().scale(), RoundingStrategy::ToZero,
RoundingStrategy::ToZero, );
);
if (element.is_long == 0 || element.is_long == 1) if (element.is_long == 0 || element.is_long == 1)
&& !element.current_price.is_zero() && !element.current_price.is_zero()
{ {
if element.current_price >= element.target_price if element.current_price >= element.target_price {
{
limit_order_sell( limit_order_sell(
&element, &element,
element.current_price, element.current_price,
@ -255,7 +316,7 @@ pub async fn list_up_for_sell(
.await; .await;
} }
// TODO: sell_count가 1일 때 적용하기 // TODO: sell_count가 1일 때 적용하기
// else if (supertrend_vec // else if (supertrend_vec
// .last() // .last()
// .unwrap() // .unwrap()
// .signal // .signal

View File

@ -1,15 +1,17 @@
use super::{ use super::{
dec, decimal_add, decimal_sub, decimal_div, decimal_mul, ema, exists_record, insert_pre_suggested_coins, adx, dec, decimal_add, decimal_div, decimal_mul, decimal_sub, duplicate_filter, ema, ema_macd,
limit_order_sell, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, AllData, exists_record, get_current_price, get_server_epoch, insert_pre_suggested_coins,
Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredDataValue, Mutex, limit_order_sell, remove_keys, rsi, select_filled_buy_orders, stoch_rsi, supertrend,
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, get_server_epoch, MacdData, ema_macd, try_join_all, update_record3, AdxData, AllData, Arc, BollingerBandData, Client, ClientBuilder,
BollingerBandData, ToPrimitive, duplicate_filter, HashMap, HashSet, remove_keys, SuperTrendArea, SuperTrendSignal, get_current_price Decimal, EmaData, ExchangeInfo, FilteredDataValue, HashMap, HashSet, MacdData, Mutex,
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SuperTrendArea, SuperTrendSignal,
SupertrendData, ToPrimitive, TradeFee,
}; };
// BUY conditions // BUY conditions
// (1) 1d MACD (3, 7, 30) cross // (1) 1d MACD (3, 7, 30) cross
// (2) supertrend (30, 3): UP area // (2) supertrend (30, 3): UP area
// stoploss: (update) supertrend(10, 1.5) lowerband // stoploss: (update) supertrend(10, 1.5) lowerband
// target price: (fixed) stoploss inverse x 3 times profit // target price: (fixed) stoploss inverse x 3 times profit
pub async fn list_up_for_buy( pub async fn list_up_for_buy(
alldata: &AllData, alldata: &AllData,
@ -27,12 +29,17 @@ pub async fn list_up_for_buy(
// 2nd filtering: supertrend(ATR period 14, multiplier: 1.2, 1d close price) // 2nd filtering: supertrend(ATR period 14, multiplier: 1.2, 1d close price)
let mut keys_to_remove: HashSet<String> = HashSet::new(); let mut keys_to_remove: HashSet<String> = HashSet::new();
let server_epoch = get_server_epoch().await; let server_epoch = get_server_epoch().await;
let supertrend_1d_map = supertrend(14, 1.2, true, &alldata.rt_price_1d_vec, &filtered_data).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 { 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 let (Some(supertrend_vec), Some(rt_price_vec)) = (
if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && supertrend_1d_map.get(symbol),
rt_price_vec.last().unwrap().close_time > server_epoch { alldata.rt_price_1d_vec.get(symbol),
if supertrend_vec.last().unwrap().area == SuperTrendArea::UP{ ) {
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 { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -49,12 +56,21 @@ pub async fn list_up_for_buy(
let mut keys_to_remove: HashSet<String> = HashSet::new(); 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?; let macd_1d_map = ema_macd(3, 5, 30, &alldata.rt_price_1d_vec, &filtered_data).await?;
for (symbol, values) in &mut filtered_data { 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 let (Some(macd_vec), Some(rt_price_vec)) =
if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && (macd_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol))
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 && if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time
macd_vec[macd_vec.len()-2].macd_value < macd_vec[macd_vec.len()-2].signal_value { && rt_price_vec.last().unwrap().close_time > server_epoch
values.current_price = rust_decimal::prelude::FromPrimitive::from_f64(rt_price_vec.last().unwrap().close_price).unwrap(); {
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; values.closetime = rt_price_vec.last().unwrap().close_time;
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
@ -71,24 +87,48 @@ pub async fn list_up_for_buy(
// 2nd filtering: supertrend(ATR period 14, multiplier: 1.2, 1d close price) // 2nd filtering: supertrend(ATR period 14, multiplier: 1.2, 1d close price)
let mut keys_to_remove: HashSet<String> = HashSet::new(); let mut keys_to_remove: HashSet<String> = HashSet::new();
let server_epoch = get_server_epoch().await; let server_epoch = get_server_epoch().await;
let supertrend_1d_map = supertrend(14, 1.2, true, &alldata.rt_price_1d_vec, &filtered_data).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 { 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 let (Some(supertrend_vec), Some(rt_price_vec)) = (
if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && supertrend_1d_map.get(symbol),
rt_price_vec.last().unwrap().close_time > server_epoch { 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 // input stoploss, target_price
let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap(); let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(
if supertrend_vec.last().unwrap().area == SuperTrendArea::DOWN && supertrend_vec.last().unwrap().band_value,
supertrend_vec.last().unwrap().band_value > values.current_price.to_f64().unwrap() )
.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.stoploss = decimal_sub(
values.target_price = decimal_add(decimal_mul(decimal_sub(values.current_price, values.stoploss), dec!(2.0)), values.current_price); 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.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.stoploss = band_value;
values.target_price = decimal_add(decimal_mul(decimal_sub(values.current_price, values.stoploss), dec!(2.0)), values.current_price); values.target_price = decimal_add(
decimal_mul(
decimal_sub(values.current_price, values.stoploss),
dec!(2.0),
),
values.current_price,
);
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -99,16 +139,18 @@ pub async fn list_up_for_buy(
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
} }
remove_keys(&mut filtered_data, keys_to_remove).await; remove_keys(&mut filtered_data, keys_to_remove).await;
// filtering: the 1 previous ADX(3, 5)s increase // filtering: the 1 previous ADX(3, 5)s increase
let mut keys_to_remove: HashSet<String> = HashSet::new(); let mut keys_to_remove: HashSet<String> = HashSet::new();
let adx_vec = adx(3, 5, &alldata.rt_price_1d_vec, &filtered_data).await?; let adx_vec = adx(3, 5, &alldata.rt_price_1d_vec, &filtered_data).await?;
for (symbol, values) in &mut filtered_data { for (symbol, values) in &mut filtered_data {
if let Some(adx_vec) = adx_vec.get(symbol) { 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 let Some(last_idx) = adx_vec
if .iter()
adx_vec[last_idx].adx > adx_vec[last_idx-1].adx { .position(|elem| elem.close_time == values.closetime)
{
if adx_vec[last_idx].adx > adx_vec[last_idx - 1].adx {
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -117,7 +159,7 @@ pub async fn list_up_for_buy(
} }
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
} }
remove_keys(&mut filtered_data, keys_to_remove).await; remove_keys(&mut filtered_data, keys_to_remove).await;
@ -126,14 +168,19 @@ pub async fn list_up_for_buy(
let server_epoch = get_server_epoch().await; let server_epoch = get_server_epoch().await;
for (symbol, values) in &mut filtered_data { for (symbol, values) in &mut filtered_data {
if let Some(rt_price_vec) = alldata.rt_price_1d_vec.get(symbol) { 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 { if rt_price_vec.last().unwrap().close_time > server_epoch && rt_price_vec.len() >= 6 {
let mut opclo_vec: Vec<f64> = Vec::new(); 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() - 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() - 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() - 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() - 5].opclo_price);
opclo_vec.push(rt_price_vec[rt_price_vec.len()-6].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()); 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()); opclo_vec.remove(max_idx.unwrap());
let mut mean = 0.0; let mut mean = 0.0;
@ -155,7 +202,7 @@ pub async fn list_up_for_buy(
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
} }
remove_keys(&mut filtered_data, keys_to_remove).await; remove_keys(&mut filtered_data, keys_to_remove).await;
let final_filtered_data = duplicate_filter(5, &filtered_data).await?; let final_filtered_data = duplicate_filter(5, &filtered_data).await?;
insert_pre_suggested_coins(5, false, &final_filtered_data, &alldata).await; insert_pre_suggested_coins(5, false, &final_filtered_data, &alldata).await;
@ -181,33 +228,38 @@ pub async fn list_up_for_sell(
for element in &filled_buy_orders { for element in &filled_buy_orders {
filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new()); 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?; let supertrend_1d =
supertrend(14, 1.2, true, &all_data.rt_price_1d_vec, &filtered_symbols).await?;
for element in filled_buy_orders { for element in filled_buy_orders {
if element.used_usdt >= dec!(10.0) { if element.used_usdt >= dec!(10.0) {
if let (Some(exchange_info), Some(tradefee), Some(supertrend_vec)) = 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)) { exchange_info_map.get(&element.symbol),
trade_fee_map.get(&element.symbol),
supertrend_1d.get(&element.symbol),
) {
// update stoploss // update stoploss
let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap(); let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(
supertrend_vec.last().unwrap().band_value,
)
.unwrap();
if supertrend_vec.last().unwrap().area == SuperTrendArea::UP if supertrend_vec.last().unwrap().area == SuperTrendArea::UP
&& band_value > element.stoploss { && band_value > element.stoploss
let update_table_name = String::from("buy_ordered_coin_list"); {
let update_value = vec![ let update_table_name = String::from("buy_ordered_coin_list");
(String::from("stoploss"), band_value.to_string()), let update_value = vec![(String::from("stoploss"), band_value.to_string())];
]; let update_condition = vec![(String::from("id"), element.id.to_string())];
let update_condition = vec![(String::from("id"), element.id.to_string())]; update_record3(&update_table_name, &update_value, &update_condition)
update_record3(&update_table_name, &update_value, &update_condition) .await
.await .unwrap();
.unwrap(); }
}
let lot_step_size = exchange_info.stepsize; let lot_step_size = exchange_info.stepsize;
let quote_commission_precision = exchange_info.quote_commission_precision; let quote_commission_precision = exchange_info.quote_commission_precision;
let base_qty_to_be_ordered = let base_qty_to_be_ordered = element.base_qty_ordered.round_dp_with_strategy(
element.base_qty_ordered.round_dp_with_strategy( lot_step_size.normalize().scale(),
lot_step_size.normalize().scale(), RoundingStrategy::ToZero,
RoundingStrategy::ToZero, );
);
if (element.is_long == 0 || element.is_long == 1) if (element.is_long == 0 || element.is_long == 1)
&& !element.current_price.is_zero() && !element.current_price.is_zero()
{ {
@ -231,7 +283,8 @@ pub async fn list_up_for_sell(
&trade_fee_map, &trade_fee_map,
) )
.await; .await;
} else if server_epoch - element.transact_time > (86_400_000) * 7 { // 7 days timeout selling } else if server_epoch - element.transact_time > (86_400_000) * 7 {
// 7 days timeout selling
limit_order_sell( limit_order_sell(
&element, &element,
element.current_price, element.current_price,
@ -243,7 +296,7 @@ pub async fn list_up_for_sell(
.await; .await;
} }
// TODO: sell_count가 1일 때 적용하기 // TODO: sell_count가 1일 때 적용하기
// else if (supertrend_vec // else if (supertrend_vec
// .last() // .last()
// .unwrap() // .unwrap()
// .signal // .signal

View File

@ -1,15 +1,17 @@
use super::{ use super::{
dec, decimal_add, decimal_sub, decimal_div, decimal_mul, ema, exists_record, insert_pre_suggested_coins, adx, dec, decimal_add, decimal_div, decimal_mul, decimal_sub, dema, duplicate_filter, ema,
limit_order_sell, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, AllData, ema_macd, exists_record, get_current_price, get_server_epoch, insert_pre_suggested_coins,
Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredDataValue, Mutex, limit_order_sell, remove_keys, rsi, select_filled_buy_orders, stoch_rsi, supertrend, tema,
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, get_server_epoch, MacdData, ema_macd, try_join_all, update_record3, AdxData, AllData, Arc, BollingerBandData, Client, ClientBuilder,
BollingerBandData, ToPrimitive, duplicate_filter, HashMap, HashSet, remove_keys, SuperTrendArea, SuperTrendSignal, get_current_price, dema, DemaData, tema, TemaData Decimal, DemaData, EmaData, ExchangeInfo, FilteredDataValue, HashMap, HashSet, MacdData, Mutex,
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SuperTrendArea, SuperTrendSignal,
SupertrendData, TemaData, ToPrimitive, TradeFee,
}; };
// BUY conditions // BUY conditions
// (1) 1d MACD (3, 7, 30): MACD_current - Signal_current < 0, MACD_prev - Signal_prev < MACD_current - Signal_current // (1) 1d MACD (3, 7, 30): MACD_current - Signal_current < 0, MACD_prev - Signal_prev < MACD_current - Signal_current
// (2) stoch RSI(30, 30, 2, 2): K_prev < 10, K_prev < K_current // (2) stoch RSI(30, 30, 2, 2): K_prev < 10, K_prev < K_current
// stoploss: (update) supertrend(10, 1.5) lowerband // stoploss: (update) supertrend(10, 1.5) lowerband
// target price: (fixed) stoploss inverse x 3 times profit // target price: (fixed) stoploss inverse x 3 times profit
pub async fn list_up_for_buy( pub async fn list_up_for_buy(
alldata: &AllData, alldata: &AllData,
@ -29,15 +31,28 @@ pub async fn list_up_for_buy(
let macd_1d_map = ema_macd(3, 7, 30, &alldata.rt_price_1d_vec, &filtered_data).await?; let macd_1d_map = ema_macd(3, 7, 30, &alldata.rt_price_1d_vec, &filtered_data).await?;
let server_epoch = get_server_epoch().await; let server_epoch = get_server_epoch().await;
for (symbol, values) in &mut filtered_data { 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 let (Some(macd_vec), Some(rt_price_vec)) =
if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && (macd_1d_map.get(symbol), alldata.rt_price_1d_vec.get(symbol))
rt_price_vec.last().unwrap().close_time > server_epoch { {
if macd_vec.len() >= 30 && if macd_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time
(macd_vec[macd_vec.len()-1].macd_value - macd_vec[macd_vec.len()-1].signal_value).is_sign_negative() && && rt_price_vec.last().unwrap().close_time > server_epoch
(macd_vec[macd_vec.len()-2].macd_value - macd_vec[macd_vec.len()-2].signal_value).is_sign_negative() && {
(macd_vec[macd_vec.len()-1].macd_value - macd_vec[macd_vec.len()-1].signal_value > if macd_vec.len() >= 30
macd_vec[macd_vec.len()-2].macd_value - macd_vec[macd_vec.len()-2].signal_value) { && (macd_vec[macd_vec.len() - 1].macd_value
values.current_price = rust_decimal::prelude::FromPrimitive::from_f64(rt_price_vec.last().unwrap().close_price).unwrap(); - macd_vec[macd_vec.len() - 1].signal_value)
.is_sign_negative()
&& (macd_vec[macd_vec.len() - 2].macd_value
- macd_vec[macd_vec.len() - 2].signal_value)
.is_sign_negative()
&& (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; values.closetime = rt_price_vec.last().unwrap().close_time;
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
@ -51,20 +66,26 @@ pub async fn list_up_for_buy(
} }
remove_keys(&mut filtered_data, keys_to_remove).await; remove_keys(&mut filtered_data, keys_to_remove).await;
// 1d StochRSI (RSI_len: 30, StochRSI_len: 30, K: 3, D: 3) K_prev < 10, K_prev < K_current // 1d StochRSI (RSI_len: 30, StochRSI_len: 30, K: 3, D: 3) K_prev < 10, K_prev < K_current
let mut keys_to_remove: HashSet<String> = HashSet::new(); let mut keys_to_remove: HashSet<String> = HashSet::new();
let stoch_rsis = stoch_rsi(30, 30, 3, 3, &alldata.rt_price_1d_vec, &filtered_data).await?; let stoch_rsis = stoch_rsi(30, 30, 3, 3, &alldata.rt_price_1d_vec, &filtered_data).await?;
for (symbol, values) in &mut filtered_data { for (symbol, values) in &mut filtered_data {
if stoch_rsis.contains_key(symbol) { if stoch_rsis.contains_key(symbol) {
let stoch_rsi_vec = stoch_rsis.get(symbol).unwrap(); let stoch_rsi_vec = stoch_rsis.get(symbol).unwrap();
let search_result = stoch_rsi_vec.iter().position(|x| x.close_time == values.closetime); let search_result = stoch_rsi_vec
if stoch_rsi_vec.len() > 10 && search_result.is_some_and(|a| stoch_rsi_vec[a-3].k < 20.0 && .iter()
stoch_rsi_vec[a-2].k < 15.0 && .position(|x| x.close_time == values.closetime);
stoch_rsi_vec[a-1].k < 10.0 && if stoch_rsi_vec.len() > 10
stoch_rsi_vec[a-1].k < stoch_rsi_vec[a].k && && search_result.is_some_and(|a| {
stoch_rsi_vec[a].k < stoch_rsi_vec[a].d && stoch_rsi_vec[a - 3].k < 20.0
!stoch_rsi_vec[a].d.is_subnormal() && && stoch_rsi_vec[a - 2].k < 15.0
stoch_rsi_vec[a].d > 0.00000001) { && stoch_rsi_vec[a - 1].k < 10.0
&& stoch_rsi_vec[a - 1].k < stoch_rsi_vec[a].k
&& stoch_rsi_vec[a].k < stoch_rsi_vec[a].d
&& !stoch_rsi_vec[a].d.is_subnormal()
&& stoch_rsi_vec[a].d > 0.00000001
})
{
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -77,11 +98,16 @@ pub async fn list_up_for_buy(
// 2nd filtering: supertrend(ATR period 30, multiplier: 2.0, 1d close price) UP area // 2nd filtering: supertrend(ATR period 30, multiplier: 2.0, 1d close price) UP area
let mut keys_to_remove: HashSet<String> = HashSet::new(); let mut keys_to_remove: HashSet<String> = HashSet::new();
let server_epoch = get_server_epoch().await; let server_epoch = get_server_epoch().await;
let supertrend_1d_map = supertrend(30, 2.0, true, &alldata.rt_price_1d_vec, &filtered_data).await?; let supertrend_1d_map =
supertrend(30, 2.0, true, &alldata.rt_price_1d_vec, &filtered_data).await?;
for (symbol, values) in &mut filtered_data { 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 let (Some(supertrend_vec), Some(rt_price_vec)) = (
if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && supertrend_1d_map.get(symbol),
rt_price_vec.last().unwrap().close_time > server_epoch { 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 { if supertrend_vec.last().unwrap().area == SuperTrendArea::UP {
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
@ -93,29 +119,53 @@ pub async fn list_up_for_buy(
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
} }
remove_keys(&mut filtered_data, keys_to_remove).await; remove_keys(&mut filtered_data, keys_to_remove).await;
// 2nd filtering: supertrend(ATR period 14, multiplier: 1.2, 1d close price) // 2nd filtering: supertrend(ATR period 14, multiplier: 1.2, 1d close price)
let mut keys_to_remove: HashSet<String> = HashSet::new(); let mut keys_to_remove: HashSet<String> = HashSet::new();
let server_epoch = get_server_epoch().await; let server_epoch = get_server_epoch().await;
let supertrend_1d_map = supertrend(14, 1.2, true, &alldata.rt_price_1d_vec, &filtered_data).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 { 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 let (Some(supertrend_vec), Some(rt_price_vec)) = (
if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && supertrend_1d_map.get(symbol),
rt_price_vec.last().unwrap().close_time > server_epoch { 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 // input stoploss, target_price
let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap(); let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(
if supertrend_vec.last().unwrap().area == SuperTrendArea::DOWN && supertrend_vec.last().unwrap().band_value,
supertrend_vec.last().unwrap().band_value > values.current_price.to_f64().unwrap() )
.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.stoploss = decimal_sub(
values.target_price = decimal_add(decimal_mul(decimal_sub(values.current_price, values.stoploss), dec!(2.0)), values.current_price); 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.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.stoploss = band_value;
values.target_price = decimal_add(decimal_mul(decimal_sub(values.current_price, values.stoploss), dec!(2.0)), values.current_price); values.target_price = decimal_add(
decimal_mul(
decimal_sub(values.current_price, values.stoploss),
dec!(2.0),
),
values.current_price,
);
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -126,21 +176,26 @@ pub async fn list_up_for_buy(
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
} }
remove_keys(&mut filtered_data, keys_to_remove).await; remove_keys(&mut filtered_data, keys_to_remove).await;
// limit buy price: 3 * abs(이전 3 개 중 최대값 제거 한 opclo 값 평균 - 현재 open 값) + 현재 open 값 > current_price // limit buy price: 3 * abs(이전 3 개 중 최대값 제거 한 opclo 값 평균 - 현재 open 값) + 현재 open 값 > current_price
let mut keys_to_remove: HashSet<String> = HashSet::new(); let mut keys_to_remove: HashSet<String> = HashSet::new();
let server_epoch = get_server_epoch().await; let server_epoch = get_server_epoch().await;
for (symbol, values) in &mut filtered_data { for (symbol, values) in &mut filtered_data {
if let Some(rt_price_vec) = alldata.rt_price_1d_vec.get(symbol) { 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 { if rt_price_vec.last().unwrap().close_time > server_epoch && rt_price_vec.len() >= 6 {
let mut opclo_vec: Vec<f64> = Vec::new(); 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() - 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() - 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() - 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() - 5].opclo_price);
opclo_vec.push(rt_price_vec[rt_price_vec.len()-6].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()); 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()); opclo_vec.remove(max_idx.unwrap());
let mut mean = 0.0; let mut mean = 0.0;
@ -162,7 +217,7 @@ pub async fn list_up_for_buy(
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
} }
remove_keys(&mut filtered_data, keys_to_remove).await; remove_keys(&mut filtered_data, keys_to_remove).await;
let final_filtered_data = duplicate_filter(6, &filtered_data).await?; let final_filtered_data = duplicate_filter(6, &filtered_data).await?;
insert_pre_suggested_coins(6, false, &final_filtered_data, &alldata).await; insert_pre_suggested_coins(6, false, &final_filtered_data, &alldata).await;
@ -188,63 +243,76 @@ pub async fn list_up_for_sell(
for element in &filled_buy_orders { for element in &filled_buy_orders {
filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new()); 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?; let supertrend_1d =
supertrend(14, 1.2, true, &all_data.rt_price_1d_vec, &filtered_symbols).await?;
for element in filled_buy_orders { for element in filled_buy_orders {
let mut is_sell = false; let mut is_sell = false;
let opclo_sample_length: usize = 15; // 15 candle samsples let opclo_sample_length: usize = 15; // 15 candle samsples
let mut target_profit_percent = 0.0; let mut target_profit_percent = 0.0;
if let Some(price_1d_vec) = all_data if let Some(price_1d_vec) = all_data.rt_price_1d_vec.get(&element.symbol) {
.rt_price_1d_vec
.get(&element.symbol)
{
let vec_len = price_1d_vec.len(); let vec_len = price_1d_vec.len();
if let Some(candles) = price_1d_vec.get(vec_len-opclo_sample_length-2..vec_len-1) { if let Some(candles) =
price_1d_vec.get(vec_len - opclo_sample_length - 2..vec_len - 1)
{
let windows = candles.windows(2); let windows = candles.windows(2);
let mut sum_amplitude_candles = 0.0; let mut sum_amplitude_candles = 0.0;
let mut sum_ratio_amp_body = 0.0; let mut sum_ratio_amp_body = 0.0;
let mut average_amplitude = 0.0; let mut average_amplitude = 0.0;
for window in windows { for window in windows {
sum_amplitude_candles += ((window.last().unwrap().high_price - window.last().unwrap().low_price) * 100.0) / window.first().unwrap().close_price; sum_amplitude_candles += ((window.last().unwrap().high_price
- window.last().unwrap().low_price)
* 100.0)
/ window.first().unwrap().close_price;
} }
let average_amplitude = sum_amplitude_candles / opclo_sample_length as f64; // percent unit let average_amplitude = sum_amplitude_candles / opclo_sample_length as f64; // percent unit
let mut amplitude_variance = 0.0; let mut amplitude_variance = 0.0;
let windows = candles.windows(2); let windows = candles.windows(2);
for window in windows { for window in windows {
amplitude_variance += ((((window.last().unwrap().high_price - window.last().unwrap().low_price) * 100.0) / window.first().unwrap().close_price) - average_amplitude).powi(2); amplitude_variance += ((((window.last().unwrap().high_price
- window.last().unwrap().low_price)
* 100.0)
/ window.first().unwrap().close_price)
- average_amplitude)
.powi(2);
} }
amplitude_variance = amplitude_variance / (opclo_sample_length - 1) as f64; amplitude_variance = amplitude_variance / (opclo_sample_length - 1) as f64;
let standard_deviation_amplitude = amplitude_variance.sqrt(); let standard_deviation_amplitude = amplitude_variance.sqrt();
target_profit_percent = average_amplitude + (standard_deviation_amplitude * 0.5); target_profit_percent =
average_amplitude + (standard_deviation_amplitude * 0.5);
} }
} }
if element.used_usdt >= dec!(10.0) { if element.used_usdt >= dec!(10.0) {
if let (Some(exchange_info), Some(tradefee), Some(supertrend_vec)) = 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)) { exchange_info_map.get(&element.symbol),
trade_fee_map.get(&element.symbol),
supertrend_1d.get(&element.symbol),
) {
// update stoploss // update stoploss
let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap(); let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(
supertrend_vec.last().unwrap().band_value,
)
.unwrap();
if supertrend_vec.last().unwrap().area == SuperTrendArea::UP if supertrend_vec.last().unwrap().area == SuperTrendArea::UP
&& band_value > element.stoploss { && band_value > element.stoploss
let update_table_name = String::from("buy_ordered_coin_list"); {
let update_value = vec![ let update_table_name = String::from("buy_ordered_coin_list");
(String::from("stoploss"), band_value.to_string()), let update_value = vec![(String::from("stoploss"), band_value.to_string())];
]; let update_condition = vec![(String::from("id"), element.id.to_string())];
let update_condition = vec![(String::from("id"), element.id.to_string())]; update_record3(&update_table_name, &update_value, &update_condition)
update_record3(&update_table_name, &update_value, &update_condition) .await
.await .unwrap();
.unwrap(); }
}
let lot_step_size = exchange_info.stepsize; let lot_step_size = exchange_info.stepsize;
let quote_commission_precision = exchange_info.quote_commission_precision; let quote_commission_precision = exchange_info.quote_commission_precision;
let base_qty_to_be_ordered = let base_qty_to_be_ordered = element.base_qty_ordered.round_dp_with_strategy(
element.base_qty_ordered.round_dp_with_strategy( lot_step_size.normalize().scale(),
lot_step_size.normalize().scale(), RoundingStrategy::ToZero,
RoundingStrategy::ToZero, );
);
if (element.is_long == 0 || element.is_long == 1) if (element.is_long == 0 || element.is_long == 1)
&& !element.current_price.is_zero() && !element.current_price.is_zero()
{ {
@ -252,19 +320,32 @@ pub async fn list_up_for_sell(
is_sell = true; is_sell = true;
} else if element.current_price >= element.target_price { } else if element.current_price >= element.target_price {
is_sell = true; is_sell = true;
} else if (element.pure_profit_percent >= target_profit_percent * 2.5) && (target_profit_percent != 0.0 && target_profit_percent.is_sign_positive()) { // absolute sell profit percent } else if (element.pure_profit_percent >= target_profit_percent * 2.5)
&& (target_profit_percent != 0.0
&& target_profit_percent.is_sign_positive())
{
// absolute sell profit percent
is_sell = true; is_sell = true;
} else if server_epoch - element.transact_time > (86_400_000) * 5 && } else if server_epoch - element.transact_time > (86_400_000) * 5
(target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && target_profit_percent * 2.0 <= element.pure_profit_percent){ // scaled selling with time up selling (5 days) && (target_profit_percent != 0.0
&& target_profit_percent.is_sign_positive()
&& target_profit_percent * 2.0 <= element.pure_profit_percent)
{
// scaled selling with time up selling (5 days)
is_sell = true; is_sell = true;
} else if server_epoch - element.transact_time > (86_400_000) * 6 && } else if server_epoch - element.transact_time > (86_400_000) * 6
(target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && target_profit_percent * 1.5 <= element.pure_profit_percent){ // scaled selling with time up selling (6 days) && (target_profit_percent != 0.0
&& target_profit_percent.is_sign_positive()
&& target_profit_percent * 1.5 <= element.pure_profit_percent)
{
// scaled selling with time up selling (6 days)
is_sell = true; is_sell = true;
} else if server_epoch - element.transact_time > (86_400_000) * 7 { // time up selling } else if server_epoch - element.transact_time > (86_400_000) * 7 {
// time up selling
is_sell = true; is_sell = true;
} }
// TODO: sell_count가 1일 때 적용하기 // TODO: sell_count가 1일 때 적용하기
// else if (supertrend_vec // else if (supertrend_vec
// .last() // .last()
// .unwrap() // .unwrap()
// .signal // .signal

View File

@ -1,9 +1,11 @@
use super::{ use super::{
dec, decimal_add, decimal_sub, decimal_div, decimal_mul, ema, exists_record, insert_pre_suggested_coins, adx, dec, decimal_add, decimal_div, decimal_mul, decimal_sub, duplicate_filter, ema, ema_macd,
limit_order_sell, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, AllData, exists_record, get_current_price, get_server_epoch, insert_pre_suggested_coins,
Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredDataValue, Mutex, limit_order_sell, remove_keys, rsi, select_filled_buy_orders, stoch_rsi, supertrend,
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, get_server_epoch, MacdData, ema_macd, try_join_all, update_record3, AdxData, AllData, Arc, BollingerBandData, Client, ClientBuilder,
BollingerBandData, ToPrimitive, duplicate_filter, HashMap, HashSet, remove_keys, SuperTrendArea, SuperTrendSignal, get_current_price Decimal, EmaData, ExchangeInfo, FilteredDataValue, HashMap, HashSet, MacdData, Mutex,
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SuperTrendArea, SuperTrendSignal,
SupertrendData, ToPrimitive, TradeFee,
}; };
// BUY conditions // BUY conditions
@ -31,80 +33,15 @@ pub async fn list_up_for_buy(
let adx_vec = adx(10, 10, &alldata.rt_price_1d_vec, &filtered_data).await?; let adx_vec = adx(10, 10, &alldata.rt_price_1d_vec, &filtered_data).await?;
for (symbol, values) in &mut filtered_data { for (symbol, values) in &mut filtered_data {
if let Some(adx_vec) = adx_vec.get(symbol) { 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 let Some(last_idx) = adx_vec
if .iter()
adx_vec[last_idx].adx > adx_vec[last_idx-1].adx && .position(|elem| elem.close_time == values.closetime)
adx_vec[last_idx-1].adx > adx_vec[last_idx-2].adx && {
adx_vec[last_idx].adx < 25.0 { if adx_vec.len() > 10
} else { && adx_vec[last_idx].adx > adx_vec[last_idx - 1].adx
keys_to_remove.insert(symbol.clone()); && adx_vec[last_idx - 1].adx > adx_vec[last_idx - 2].adx
} && adx_vec[last_idx].adx < 25.0
} else {
keys_to_remove.insert(symbol.clone());
}
} else {
keys_to_remove.insert(symbol.clone());
}
}
remove_keys(&mut filtered_data, keys_to_remove).await;
// 2nd filtering: the 2 previous ADX(5, 5)s increase, ADX < 40
let mut keys_to_remove: HashSet<String> = HashSet::new();
let adx_vec = adx(10, 10, &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 &&
adx_vec[last_idx-1].adx > adx_vec[last_idx-2].adx &&
adx_vec[last_idx].adx < 40.0 {
} 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: RSI 5 < 75.0
let mut keys_to_remove: HashSet<String> = HashSet::new();
let rsi_map = rsi(5, &alldata.rt_price_1d_vec, &filtered_data).await?;
for (symbol, values) in &mut filtered_data {
if let Some(rsi_vec) = rsi_map.get(symbol) {
if let Some(last_idx) = rsi_vec.iter().position(|elem| elem.close_time == values.closetime) {
if rsi_vec[last_idx].rsi_value > 75.0 {
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: 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::UP &&
supertrend_vec.last().unwrap().band_value < values.current_price.to_f64().unwrap()
{ {
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;
values.stoploss = band_value;
values.target_price = decimal_add(decimal_mul(decimal_sub(values.current_price, values.stoploss), dec!(3.0)), values.current_price);
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -115,21 +52,120 @@ pub async fn list_up_for_buy(
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
} }
remove_keys(&mut filtered_data, keys_to_remove).await; remove_keys(&mut filtered_data, keys_to_remove).await;
println!("{}", filtered_data.keys().len());
// 2nd filtering: the 2 previous ADX(5, 5)s increase, ADX < 40
let mut keys_to_remove: HashSet<String> = HashSet::new();
let adx_vec = adx(10, 10, &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.len() > 10
&& adx_vec[last_idx].adx > adx_vec[last_idx - 1].adx
&& adx_vec[last_idx - 1].adx > adx_vec[last_idx - 2].adx
&& adx_vec[last_idx].adx < 40.0
{
} 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: RSI 5 < 75.0
let mut keys_to_remove: HashSet<String> = HashSet::new();
let rsi_map = rsi(5, &alldata.rt_price_1d_vec, &filtered_data).await?;
for (symbol, values) in &mut filtered_data {
if let Some(rsi_vec) = rsi_map.get(symbol) {
if let Some(last_idx) = rsi_vec
.iter()
.position(|elem| elem.close_time == values.closetime)
{
if rsi_vec[last_idx].rsi_value > 75.0 {
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: 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::UP
&& supertrend_vec.last().unwrap().band_value
< values.current_price.to_f64().unwrap()
{
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;
values.stoploss = band_value;
values.target_price = decimal_add(
decimal_mul(
decimal_sub(values.current_price, values.stoploss),
dec!(3.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());
}
}
remove_keys(&mut filtered_data, keys_to_remove).await;
// limit buy price: 3 * abs(이전 3 개 중 최대값 제거 한 opclo 값 평균 - 현재 open 값) + 현재 open 값 > current_price // limit buy price: 3 * abs(이전 3 개 중 최대값 제거 한 opclo 값 평균 - 현재 open 값) + 현재 open 값 > current_price
let mut keys_to_remove: HashSet<String> = HashSet::new(); let mut keys_to_remove: HashSet<String> = HashSet::new();
let server_epoch = get_server_epoch().await; let server_epoch = get_server_epoch().await;
for (symbol, values) in &mut filtered_data { for (symbol, values) in &mut filtered_data {
if let Some(rt_price_vec) = alldata.rt_price_1d_vec.get(symbol) { 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 { if rt_price_vec.last().unwrap().close_time > server_epoch && rt_price_vec.len() >= 6 {
let mut opclo_vec: Vec<f64> = Vec::new(); 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() - 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() - 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() - 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() - 5].opclo_price);
opclo_vec.push(rt_price_vec[rt_price_vec.len()-6].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()); 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()); opclo_vec.remove(max_idx.unwrap());
let mut mean = 0.0; let mut mean = 0.0;
@ -151,7 +187,7 @@ pub async fn list_up_for_buy(
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
} }
remove_keys(&mut filtered_data, keys_to_remove).await; remove_keys(&mut filtered_data, keys_to_remove).await;
let final_filtered_data = duplicate_filter(7, &filtered_data).await?; let final_filtered_data = duplicate_filter(7, &filtered_data).await?;
insert_pre_suggested_coins(7, false, &final_filtered_data, &alldata).await; insert_pre_suggested_coins(7, false, &final_filtered_data, &alldata).await;
@ -180,36 +216,45 @@ pub async fn list_up_for_sell(
for element in &filled_buy_orders { for element in &filled_buy_orders {
filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new()); 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?; let supertrend_1d =
supertrend(14, 1.2, true, &all_data.rt_price_1d_vec, &filtered_symbols).await?;
for element in filled_buy_orders { for element in filled_buy_orders {
let mut is_sell = false; let mut is_sell = false;
if element.used_usdt >= dec!(10.0) { if element.used_usdt >= dec!(10.0) {
if let (Some(exchange_info), Some(tradefee), Some(supertrend_vec)) = 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)) { exchange_info_map.get(&element.symbol),
trade_fee_map.get(&element.symbol),
supertrend_1d.get(&element.symbol),
) {
// update stoploss // update stoploss
let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap(); let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(
supertrend_vec.last().unwrap().band_value,
)
.unwrap();
if supertrend_vec.last().unwrap().area == SuperTrendArea::UP if supertrend_vec.last().unwrap().area == SuperTrendArea::UP
&& band_value > element.stoploss { && band_value > element.stoploss
let update_table_name = String::from("buy_ordered_coin_list"); {
let update_value = vec![ let update_table_name = String::from("buy_ordered_coin_list");
(String::from("stoploss"), band_value.to_string()), let update_value = vec![(String::from("stoploss"), band_value.to_string())];
]; let update_condition = vec![(String::from("id"), element.id.to_string())];
let update_condition = vec![(String::from("id"), element.id.to_string())]; update_record3(&update_table_name, &update_value, &update_condition)
update_record3(&update_table_name, &update_value, &update_condition) .await
.await .unwrap();
.unwrap(); }
}
let lot_step_size = exchange_info.stepsize; let lot_step_size = exchange_info.stepsize;
let quote_commission_precision = exchange_info.quote_commission_precision; let quote_commission_precision = exchange_info.quote_commission_precision;
let base_qty_to_be_ordered = let base_qty_to_be_ordered = element.base_qty_ordered.round_dp_with_strategy(
element.base_qty_ordered.round_dp_with_strategy( lot_step_size.normalize().scale(),
lot_step_size.normalize().scale(), RoundingStrategy::ToZero,
RoundingStrategy::ToZero, );
); let target_profit_percent = decimal_div(
let target_profit_percent = decimal_div(decimal_sub(element.stoploss, element.buy_price), element.buy_price).to_f64().unwrap(); decimal_sub(element.target_price, element.buy_price),
element.buy_price,
)
.to_f64()
.unwrap();
if (element.is_long == 0 || element.is_long == 1) if (element.is_long == 0 || element.is_long == 1)
&& !element.current_price.is_zero() && !element.current_price.is_zero()
{ {
@ -217,32 +262,62 @@ pub async fn list_up_for_sell(
is_sell = true; is_sell = true;
} else if element.current_price >= element.target_price { } else if element.current_price >= element.target_price {
is_sell = true; is_sell = true;
} else if server_epoch - element.transact_time > (86_400_000) * 8 && } else if server_epoch - element.transact_time > (86_400_000) * 8
(target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && target_profit_percent * (13.0/14.0) <= element.pure_profit_percent) { && (target_profit_percent != 0.0
&& target_profit_percent.is_sign_positive()
&& target_profit_percent * (13.0 / 14.0)
<= element.pure_profit_percent)
{
is_sell = true; is_sell = true;
} else if server_epoch - element.transact_time > (86_400_000) * 9 && } else if server_epoch - element.transact_time > (86_400_000) * 9
(target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && target_profit_percent * (12.0/14.0) <= element.pure_profit_percent) { && (target_profit_percent != 0.0
&& target_profit_percent.is_sign_positive()
&& target_profit_percent * (12.0 / 14.0)
<= element.pure_profit_percent)
{
is_sell = true; is_sell = true;
} else if server_epoch - element.transact_time > (86_400_000) * 10 && } else if server_epoch - element.transact_time > (86_400_000) * 10
(target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && target_profit_percent * (11.0/14.0) <= element.pure_profit_percent) { && (target_profit_percent != 0.0
&& target_profit_percent.is_sign_positive()
&& target_profit_percent * (11.0 / 14.0)
<= element.pure_profit_percent)
{
is_sell = true; is_sell = true;
} else if server_epoch - element.transact_time > (86_400_000) * 11 && } else if server_epoch - element.transact_time > (86_400_000) * 11
(target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && target_profit_percent * (10.0/14.0) <= element.pure_profit_percent) { && (target_profit_percent != 0.0
&& target_profit_percent.is_sign_positive()
&& target_profit_percent * (10.0 / 14.0)
<= element.pure_profit_percent)
{
is_sell = true; is_sell = true;
} else if server_epoch - element.transact_time > (86_400_000) * 12 && } else if server_epoch - element.transact_time > (86_400_000) * 12
(target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && target_profit_percent * (9.0/14.0) <= element.pure_profit_percent) { && (target_profit_percent != 0.0
&& target_profit_percent.is_sign_positive()
&& target_profit_percent * (9.0 / 14.0)
<= element.pure_profit_percent)
{
is_sell = true; is_sell = true;
} else if server_epoch - element.transact_time > (86_400_000) * 13 && } else if server_epoch - element.transact_time > (86_400_000) * 13
(target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && target_profit_percent * (8.0/14.0) <= element.pure_profit_percent) { && (target_profit_percent != 0.0
&& target_profit_percent.is_sign_positive()
&& target_profit_percent * (8.0 / 14.0)
<= element.pure_profit_percent)
{
is_sell = true; is_sell = true;
} else if server_epoch - element.transact_time > (86_400_000) * 14 && } else if server_epoch - element.transact_time > (86_400_000) * 14
(target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && target_profit_percent * (1.0/2.0) <= element.pure_profit_percent) { // scaled selling with time up selling (6 days){ && (target_profit_percent != 0.0
&& target_profit_percent.is_sign_positive()
&& target_profit_percent * (1.0 / 2.0)
<= element.pure_profit_percent)
{
// scaled selling with time up selling (6 days){
is_sell = true; is_sell = true;
} else if server_epoch - element.transact_time > (86_400_000) * 15 { // time up selling } else if server_epoch - element.transact_time > (86_400_000) * 15 {
// time up selling
is_sell = true; is_sell = true;
} }
// TODO: sell_count가 1일 때 적용하기 // TODO: sell_count가 1일 때 적용하기
// else if (supertrend_vec // else if (supertrend_vec
// .last() // .last()
// .unwrap() // .unwrap()
// .signal // .signal

View File

@ -1,10 +1,12 @@
use super::{ use super::{
dec, decimal_add, decimal_sub, decimal_div, decimal_mul, ema, exists_record, insert_pre_suggested_coins, adx, dec, decimal_add, decimal_div, decimal_mul, decimal_sub, dema, duplicate_filter, ema,
limit_order_sell, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, AllData, ema_macd, exists_record, get_current_price, get_server_epoch, heatmap_volume,
Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredDataValue, Mutex, insert_pre_suggested_coins, limit_order_sell, remove_keys, rsi, select_filled_buy_orders,
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, get_server_epoch, MacdData, ema_macd, stoch_rsi, supertrend, tema, try_join_all, update_record3, AdxData, AllData, Arc,
BollingerBandData, ToPrimitive, duplicate_filter, HashMap, HashSet, remove_keys, SuperTrendArea, SuperTrendSignal, get_current_price, dema, DemaData, tema, TemaData, BollingerBandData, Client, ClientBuilder, Decimal, DemaData, EmaData, ExchangeInfo,
heatmap_volume, HeatMapLevel, HeatmapVolumeData FilteredDataValue, HashMap, HashSet, HeatMapLevel, HeatmapVolumeData, MacdData, Mutex,
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SuperTrendArea, SuperTrendSignal,
SupertrendData, TemaData, ToPrimitive, TradeFee,
}; };
// BUY conditions // BUY conditions
@ -28,10 +30,12 @@ pub async fn list_up_for_buy(
let server_epoch = get_server_epoch().await; let server_epoch = get_server_epoch().await;
for (symbol, values) in &mut filtered_data { for (symbol, values) in &mut filtered_data {
if let (Some(tema5_vec), Some(tema10_vec)) = (tema_3.get(symbol), tema_10.get(symbol)) { if let (Some(tema5_vec), Some(tema10_vec)) = (tema_3.get(symbol), tema_10.get(symbol)) {
if tema5_vec.len() > 2 && tema10_vec.len() > 2 && if tema5_vec.len() > 2
tema5_vec.last().unwrap().close_time == tema10_vec.last().unwrap().close_time && && tema10_vec.len() > 2
tema5_vec.last().unwrap().close_time > server_epoch && && tema5_vec.last().unwrap().close_time == tema10_vec.last().unwrap().close_time
tema10_vec.last().unwrap().close_time > server_epoch { && tema5_vec.last().unwrap().close_time > server_epoch
&& tema10_vec.last().unwrap().close_time > server_epoch
{
if tema5_vec.last().unwrap().tema_value > tema10_vec.last().unwrap().tema_value { if tema5_vec.last().unwrap().tema_value > tema10_vec.last().unwrap().tema_value {
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
@ -53,17 +57,30 @@ pub async fn list_up_for_buy(
let tema_200 = tema(200, &alldata.rt_price_30m_vec, &filtered_data).await?; let tema_200 = tema(200, &alldata.rt_price_30m_vec, &filtered_data).await?;
let server_epoch = get_server_epoch().await; let server_epoch = get_server_epoch().await;
for (symbol, values) in &mut filtered_data { for (symbol, values) in &mut filtered_data {
if let (Some(tema30_vec), Some(tema5_vec), Some(tema_200_vec), Some(rt_price_vec)) = if let (Some(tema30_vec), Some(tema5_vec), Some(tema_200_vec), Some(rt_price_vec)) = (
(tema_30.get(symbol), tema_5.get(symbol), tema_200.get(symbol), alldata.rt_price_30m_vec.get(symbol)) { tema_30.get(symbol),
if (tema30_vec.len() > 10 && tema5_vec.len() > 10 && tema_200_vec.len() > 10 && rt_price_vec.len() > 10) && tema_5.get(symbol),
tema30_vec.last().unwrap().close_time == tema5_vec.last().unwrap().close_time && tema_200.get(symbol),
tema_200_vec.last().unwrap().close_time == tema5_vec.last().unwrap().close_time && alldata.rt_price_30m_vec.get(symbol),
tema5_vec.last().unwrap().close_time > server_epoch { ) {
if tema30_vec.last().unwrap().tema_value < tema5_vec.last().unwrap().tema_value && if (tema30_vec.len() > 10
tema30_vec[tema30_vec.len()-2].tema_value > tema5_vec[tema5_vec.len()-2].tema_value && && tema5_vec.len() > 10
tema30_vec[tema30_vec.len()-3].tema_value > tema5_vec[tema5_vec.len()-3].tema_value && && tema_200_vec.len() > 10
tema_200_vec.last().unwrap().tema_value < rt_price_vec.last().unwrap().opclo_price && && rt_price_vec.len() > 10)
tema_200_vec[tema_200_vec.len()-2].tema_value < rt_price_vec[rt_price_vec.len()-2].opclo_price{ && tema30_vec.last().unwrap().close_time == tema5_vec.last().unwrap().close_time
&& tema_200_vec.last().unwrap().close_time == tema5_vec.last().unwrap().close_time
&& tema5_vec.last().unwrap().close_time > server_epoch
{
if tema30_vec.last().unwrap().tema_value < tema5_vec.last().unwrap().tema_value
&& tema30_vec[tema30_vec.len() - 2].tema_value
> tema5_vec[tema5_vec.len() - 2].tema_value
&& tema30_vec[tema30_vec.len() - 3].tema_value
> tema5_vec[tema5_vec.len() - 3].tema_value
&& tema_200_vec.last().unwrap().tema_value
< rt_price_vec.last().unwrap().opclo_price
&& tema_200_vec[tema_200_vec.len() - 2].tema_value
< rt_price_vec[rt_price_vec.len() - 2].opclo_price
{
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -79,32 +96,51 @@ pub async fn list_up_for_buy(
// supertrend(ATR period 10, multiplier: 2.0, 30m close price) // supertrend(ATR period 10, multiplier: 2.0, 30m close price)
let mut keys_to_remove: HashSet<String> = HashSet::new(); let mut keys_to_remove: HashSet<String> = HashSet::new();
let server_epoch = get_server_epoch().await; let server_epoch = get_server_epoch().await;
let supertrend_30m_map = supertrend(10, 2.0, true, &alldata.rt_price_30m_vec, &filtered_data).await?; let supertrend_30m_map =
supertrend(10, 2.0, true, &alldata.rt_price_30m_vec, &filtered_data).await?;
for (symbol, values) in &mut filtered_data { 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 let (Some(supertrend_vec), Some(rt_price_vec)) = (
if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time && supertrend_30m_map.get(symbol),
rt_price_vec.last().unwrap().close_time > server_epoch { 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 stoploss, target_price // input stoploss, target_price
let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap(); let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(
let open_price: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(rt_price_vec.last().unwrap().open_price).unwrap(); supertrend_vec.last().unwrap().band_value,
let current_price: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(rt_price_vec.last().unwrap().close_price).unwrap(); )
if supertrend_vec.last().unwrap().area == SuperTrendArea::UP && .unwrap();
band_value < current_price && let open_price: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(
band_value < open_price rt_price_vec.last().unwrap().open_price,
)
.unwrap();
let current_price: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(
rt_price_vec.last().unwrap().close_price,
)
.unwrap();
if supertrend_vec.last().unwrap().area == SuperTrendArea::UP
&& band_value < current_price
&& band_value < open_price
{ {
values.current_price = current_price; values.current_price = current_price;
values.closetime = rt_price_vec.last().unwrap().close_time; values.closetime = rt_price_vec.last().unwrap().close_time;
values.stoploss = band_value; values.stoploss = band_value;
values.target_price = decimal_add(decimal_mul(decimal_sub(current_price, values.stoploss), dec!(3.0)), current_price); values.target_price = decimal_add(
} else if supertrend_vec.last().unwrap().area == SuperTrendArea::DOWN && decimal_mul(decimal_sub(current_price, values.stoploss), dec!(3.0)),
band_value > current_price && current_price,
band_value > open_price );
} else if supertrend_vec.last().unwrap().area == SuperTrendArea::DOWN
&& band_value > current_price
&& band_value > open_price
{ {
values.current_price = current_price; values.current_price = current_price;
values.closetime = rt_price_vec.last().unwrap().close_time; values.closetime = rt_price_vec.last().unwrap().close_time;
values.stoploss = decimal_sub(open_price, decimal_sub(band_value, open_price)); values.stoploss = decimal_sub(open_price, decimal_sub(band_value, open_price));
values.target_price = decimal_add(decimal_mul(decimal_sub(open_price, values.stoploss), dec!(3.0)), current_price); values.target_price = decimal_add(
decimal_mul(decimal_sub(open_price, values.stoploss), dec!(3.0)),
current_price,
);
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -115,7 +151,7 @@ pub async fn list_up_for_buy(
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
} }
remove_keys(&mut filtered_data, keys_to_remove).await; remove_keys(&mut filtered_data, keys_to_remove).await;
// current ADX(15, 15) < 25 // current ADX(15, 15) < 25
// let mut keys_to_remove: HashSet<String> = HashSet::new(); // let mut keys_to_remove: HashSet<String> = HashSet::new();
@ -133,21 +169,27 @@ pub async fn list_up_for_buy(
// } // }
// } else { // } else {
// keys_to_remove.insert(symbol.clone()); // keys_to_remove.insert(symbol.clone());
// } // }
// } // }
// remove_keys(&mut filtered_data, keys_to_remove).await; // remove_keys(&mut filtered_data, keys_to_remove).await;
// StochRSI (RSI_len: 10, StochRSI_len: 10, K: 3, D: 3) K_current < 70, K_current > d_current // StochRSI (RSI_len: 10, StochRSI_len: 10, K: 3, D: 3) K_current < 70, K_current > d_current
let mut keys_to_remove: HashSet<String> = HashSet::new(); 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?; let stoch_rsis = stoch_rsi(10, 10, 3, 3, &alldata.rt_price_30m_vec, &filtered_data).await?;
for (symbol, values) in &mut filtered_data { for (symbol, values) in &mut filtered_data {
if stoch_rsis.contains_key(symbol) { if stoch_rsis.contains_key(symbol) {
let stoch_rsi_vec = stoch_rsis.get(symbol).unwrap(); let stoch_rsi_vec = stoch_rsis.get(symbol).unwrap();
let search_result = stoch_rsi_vec.iter().position(|x| x.close_time == values.closetime); let search_result = stoch_rsi_vec
if stoch_rsi_vec.len() > 10 && search_result.is_some_and(|a| stoch_rsi_vec[a].k > stoch_rsi_vec[a].d && .iter()
stoch_rsi_vec[a].k < 70.0 && .position(|x| x.close_time == values.closetime);
stoch_rsi_vec[a-1].k < 60.0 && if stoch_rsi_vec.len() > 10
stoch_rsi_vec[a-2].k < 50.0) { && search_result.is_some_and(|a| {
stoch_rsi_vec[a].k > stoch_rsi_vec[a].d
&& stoch_rsi_vec[a].k < 70.0
&& stoch_rsi_vec[a - 1].k < 60.0
&& stoch_rsi_vec[a - 2].k < 50.0
})
{
} else { } else {
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
@ -159,25 +201,52 @@ pub async fn list_up_for_buy(
// Heatmap volume: filtering close price with Extra High is over the previous candle from 30 previous candles // Heatmap volume: filtering close price with Extra High is over the previous candle from 30 previous candles
let mut keys_to_remove: HashSet<String> = HashSet::new(); let mut keys_to_remove: HashSet<String> = HashSet::new();
let heatmap_volumes = heatmap_volume(30, 30, 4.0, 2.5, 1.0, -0.5, &filtered_data, &alldata.rt_price_30m_vec).await?; let heatmap_volumes = heatmap_volume(
30,
30,
4.0,
2.5,
1.0,
-0.5,
&filtered_data,
&alldata.rt_price_30m_vec,
)
.await?;
for (symbol, values) in &mut filtered_data { for (symbol, values) in &mut filtered_data {
if stoch_rsis.contains_key(symbol) { if stoch_rsis.contains_key(symbol) {
let heatmap_volume_vec = heatmap_volumes.get(symbol).unwrap(); let heatmap_volume_vec = heatmap_volumes.get(symbol).unwrap();
if heatmap_volume_vec.len() > 50 { if heatmap_volume_vec.len() > 50 {
let heatmap_volume_trunc = heatmap_volume_vec.get(heatmap_volume_vec.len()-31..heatmap_volume_vec.len()-1).unwrap(); let heatmap_volume_trunc = heatmap_volume_vec
.get(heatmap_volume_vec.len() - 31..heatmap_volume_vec.len() - 1)
.unwrap();
let windows = heatmap_volume_trunc.windows(2); let windows = heatmap_volume_trunc.windows(2);
for slice in windows { for slice in windows {
if slice[1].heatmap_level == HeatMapLevel::ExtraHigh { if slice[1].heatmap_level == HeatMapLevel::ExtraHigh {
if let (prev_candle_idx, current_candle_idx) = ( if let (prev_candle_idx, current_candle_idx) = (
(&alldata.rt_price_30m_vec.get(symbol).unwrap().iter().position(|x| x.close_time == slice[0].close_time)).unwrap(), (&alldata
(&alldata.rt_price_30m_vec.get(symbol).unwrap().iter().position(|x| x.close_time == slice[1].close_time)).unwrap() .rt_price_30m_vec
.get(symbol)
.unwrap()
.iter()
.position(|x| x.close_time == slice[0].close_time))
.unwrap(),
(&alldata
.rt_price_30m_vec
.get(symbol)
.unwrap()
.iter()
.position(|x| x.close_time == slice[1].close_time))
.unwrap(),
) { ) {
let prev_candle = &alldata.rt_price_30m_vec.get(symbol).unwrap()[prev_candle_idx]; let prev_candle =
let current_candle = &alldata.rt_price_30m_vec.get(symbol).unwrap()[current_candle_idx]; &alldata.rt_price_30m_vec.get(symbol).unwrap()[prev_candle_idx];
if current_candle.close_price > prev_candle.close_price || let current_candle =
current_candle.close_price > prev_candle.open_price || &alldata.rt_price_30m_vec.get(symbol).unwrap()[current_candle_idx];
current_candle.close_price > prev_candle.high_price || if current_candle.close_price > prev_candle.close_price
current_candle.close_price > prev_candle.low_price { || current_candle.close_price > prev_candle.open_price
|| current_candle.close_price > prev_candle.high_price
|| current_candle.close_price > prev_candle.low_price
{
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
} }
@ -193,14 +262,19 @@ pub async fn list_up_for_buy(
let server_epoch = get_server_epoch().await; let server_epoch = get_server_epoch().await;
for (symbol, values) in &mut filtered_data { for (symbol, values) in &mut filtered_data {
if let Some(rt_price_vec) = alldata.rt_price_30m_vec.get(symbol) { if let Some(rt_price_vec) = alldata.rt_price_30m_vec.get(symbol) {
if rt_price_vec.last().unwrap().close_time > server_epoch && rt_price_vec.len() >= 6 { if rt_price_vec.last().unwrap().close_time > server_epoch && rt_price_vec.len() >= 6 {
let mut opclo_vec: Vec<f64> = Vec::new(); 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() - 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() - 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() - 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() - 5].opclo_price);
opclo_vec.push(rt_price_vec[rt_price_vec.len()-6].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()); 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()); opclo_vec.remove(max_idx.unwrap());
let mut mean = 0.0; let mut mean = 0.0;
@ -222,7 +296,7 @@ pub async fn list_up_for_buy(
keys_to_remove.insert(symbol.clone()); keys_to_remove.insert(symbol.clone());
} }
} }
remove_keys(&mut filtered_data, keys_to_remove).await; remove_keys(&mut filtered_data, keys_to_remove).await;
let final_filtered_data = duplicate_filter(8, &filtered_data).await?; let final_filtered_data = duplicate_filter(8, &filtered_data).await?;
insert_pre_suggested_coins(8, false, &final_filtered_data, &alldata).await; insert_pre_suggested_coins(8, false, &final_filtered_data, &alldata).await;
@ -248,43 +322,56 @@ pub async fn list_up_for_sell(
for element in &filled_buy_orders { for element in &filled_buy_orders {
filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new()); filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new());
} }
let supertrend_30m = supertrend(10, 2.0, true, &all_data.rt_price_30m_vec, &filtered_symbols).await?; let supertrend_30m =
supertrend(10, 2.0, true, &all_data.rt_price_30m_vec, &filtered_symbols).await?;
let tema_30 = tema(30, &all_data.rt_price_30m_vec, &filtered_symbols).await?; let tema_30 = tema(30, &all_data.rt_price_30m_vec, &filtered_symbols).await?;
let tema_5 = tema(5, &all_data.rt_price_30m_vec, &filtered_symbols).await?; let tema_5 = tema(5, &all_data.rt_price_30m_vec, &filtered_symbols).await?;
for element in filled_buy_orders { for element in filled_buy_orders {
let mut is_sell = false; let mut is_sell = false;
let mut is_overturned = false; let mut is_overturned = false;
if element.used_usdt >= dec!(10.0) { if element.used_usdt >= dec!(10.0) {
if let (Some(tema30_vec), Some(tema5_vec)) = (tema_30.get(&element.symbol), tema_5.get(&element.symbol)) { if let (Some(tema30_vec), Some(tema5_vec)) =
if tema30_vec.len() > 2 && tema5_vec.len() > 2 && (tema_30.get(&element.symbol), tema_5.get(&element.symbol))
tema30_vec.last().unwrap().close_time == tema5_vec.last().unwrap().close_time && {
tema30_vec.last().unwrap().close_time > server_epoch && if tema30_vec.len() > 2
tema5_vec.last().unwrap().close_time > server_epoch && && tema5_vec.len() > 2
tema30_vec.last().unwrap().tema_value > tema5_vec.last().unwrap().tema_value && && tema30_vec.last().unwrap().close_time
tema30_vec[tema30_vec.len()-2].tema_value < tema5_vec[tema5_vec.len()-2].tema_value { == tema5_vec.last().unwrap().close_time
&& tema30_vec.last().unwrap().close_time > server_epoch
&& tema5_vec.last().unwrap().close_time > server_epoch
&& tema30_vec.last().unwrap().tema_value
> tema5_vec.last().unwrap().tema_value
&& tema30_vec[tema30_vec.len() - 2].tema_value
< tema5_vec[tema5_vec.len() - 2].tema_value
{
is_overturned = true; is_overturned = true;
} }
} }
if let (Some(exchange_info), Some(tradefee), Some(supertrend_vec)) = if let (Some(exchange_info), Some(tradefee), Some(supertrend_vec)) = (
(exchange_info_map.get(&element.symbol), trade_fee_map.get(&element.symbol), supertrend_30m.get(&element.symbol)) { exchange_info_map.get(&element.symbol),
trade_fee_map.get(&element.symbol),
supertrend_30m.get(&element.symbol),
) {
// update stoploss // update stoploss
let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(supertrend_vec.last().unwrap().band_value).unwrap(); let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(
supertrend_vec.last().unwrap().band_value,
)
.unwrap();
if supertrend_vec.last().unwrap().area == SuperTrendArea::UP if supertrend_vec.last().unwrap().area == SuperTrendArea::UP
&& band_value > element.stoploss { && band_value > element.stoploss
let update_table_name = String::from("buy_ordered_coin_list"); {
let update_value = vec![ let update_table_name = String::from("buy_ordered_coin_list");
(String::from("stoploss"), band_value.to_string()), let update_value = vec![(String::from("stoploss"), band_value.to_string())];
]; let update_condition = vec![(String::from("id"), element.id.to_string())];
let update_condition = vec![(String::from("id"), element.id.to_string())]; update_record3(&update_table_name, &update_value, &update_condition)
update_record3(&update_table_name, &update_value, &update_condition) .await
.await .unwrap();
.unwrap(); }
}
let lot_step_size = exchange_info.stepsize; let lot_step_size = exchange_info.stepsize;
let quote_commission_precision = exchange_info.quote_commission_precision; let quote_commission_precision = exchange_info.quote_commission_precision;
// TODO: BNB 코인이 있으면 // TODO: BNB 코인이 있으면
// let base_qty_to_be_ordered = // let base_qty_to_be_ordered =
// element.base_qty_ordered.round_dp_with_strategy( // element.base_qty_ordered.round_dp_with_strategy(
@ -293,40 +380,53 @@ pub async fn list_up_for_sell(
// ); // );
// TODO: BNB 코인이 없으면 // TODO: BNB 코인이 없으면
let base_qty_to_be_ordered = let base_qty_to_be_ordered =
element.base_qty_fee_adjusted.round_dp_with_strategy( element.base_qty_fee_adjusted.round_dp_with_strategy(
lot_step_size.normalize().scale(), lot_step_size.normalize().scale(),
RoundingStrategy::ToZero, RoundingStrategy::ToZero,
); );
let target_profit_percent = decimal_div(decimal_sub(element.target_price, element.buy_price), element.buy_price).to_f64().unwrap(); let target_profit_percent = decimal_div(
if !element.current_price.is_zero() decimal_sub(element.target_price, element.buy_price),
{ element.buy_price,
)
.to_f64()
.unwrap();
if !element.current_price.is_zero() {
if element.current_price <= element.stoploss { if element.current_price <= element.stoploss {
is_sell = true; is_sell = true;
} else if element.current_price >= element.target_price { } else if element.current_price >= element.target_price {
is_sell = true; is_sell = true;
} else if server_epoch - element.transact_time > (1_800_000) * 1 && is_overturned == true { } else if server_epoch - element.transact_time > (1_800_000) * 1
&& is_overturned == true
{
is_sell = true; is_sell = true;
} }
let minimum_candles = 5; let minimum_candles = 5;
let maximum_candles = 30; let maximum_candles = 30;
for count_candles in minimum_candles..=maximum_candles { for count_candles in minimum_candles..=maximum_candles {
if count_candles < maximum_candles && if count_candles < maximum_candles
server_epoch - element.transact_time > (1_800_000) * count_candles && && server_epoch - element.transact_time
(target_profit_percent != 0.0 && target_profit_percent.is_sign_positive() && > (1_800_000) * count_candles
target_profit_percent * ((maximum_candles - count_candles) as f64 / (maximum_candles - minimum_candles + 1) as f64) <= element.pure_profit_percent) { && (target_profit_percent != 0.0
&& target_profit_percent.is_sign_positive()
&& target_profit_percent
* ((maximum_candles - count_candles) as f64
/ (maximum_candles - minimum_candles + 1) as f64)
<= element.pure_profit_percent)
{
is_sell = true; is_sell = true;
break; break;
} else if count_candles == maximum_candles { // time up selling } else if count_candles == maximum_candles {
// time up selling
is_sell = true; is_sell = true;
break; break;
} else { } else {
break; break;
} }
} }
// TODO: sell_count가 1일 때 적용하기 // TODO: sell_count가 1일 때 적용하기
// else if (supertrend_vec // else if (supertrend_vec
// .last() // .last()
// .unwrap() // .unwrap()
// .signal // .signal

View File

@ -8,7 +8,7 @@ use serde::Deserialize;
// use super::strategy_test; // use super::strategy_test;
use super::{ use super::{
exists_record, insert_one_record, try_join_all, try_select_record, AllData, ExchangeInfo, exists_record, insert_one_record, try_join_all, try_select_record, AllData, ExchangeInfo,
FilteredDataValue, FromRow, RealtimePriceData, TradeFee, HashMap FilteredDataValue, FromRow, HashMap, RealtimePriceData, TradeFee,
}; };
use crate::signal_association::signal_decision::*; use crate::signal_association::signal_decision::*;
use tokio::time::{sleep, Duration, Instant}; use tokio::time::{sleep, Duration, Instant};
@ -39,7 +39,7 @@ pub async fn execute_list_up_for_buy(
crate::strategy_team::strategy_006::list_up_for_buy(all_data).await; crate::strategy_team::strategy_006::list_up_for_buy(all_data).await;
crate::strategy_team::strategy_007::list_up_for_buy(all_data).await; crate::strategy_team::strategy_007::list_up_for_buy(all_data).await;
crate::strategy_team::strategy_008::list_up_for_buy(all_data).await; crate::strategy_team::strategy_008::list_up_for_buy(all_data).await;
Ok(()) Ok(())
} }
@ -47,16 +47,31 @@ pub async fn execute_list_up_for_sell(
all_data: &AllData, all_data: &AllData,
exchange_info_map: &HashMap<String, ExchangeInfo>, exchange_info_map: &HashMap<String, ExchangeInfo>,
trade_fee_map: &HashMap<String, TradeFee>, trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> { ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// crate::strategy_team::strategy_001::list_up_for_sell(&all_data, &exchange_info_map, &trade_fee_map).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_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_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_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; // crate::strategy_team::strategy_005::list_up_for_sell(&all_data, &exchange_info_map, &trade_fee_map).await;
crate::strategy_team::strategy_006::list_up_for_sell(&all_data, &exchange_info_map, &trade_fee_map).await; crate::strategy_team::strategy_006::list_up_for_sell(
crate::strategy_team::strategy_007::list_up_for_sell(&all_data, &exchange_info_map, &trade_fee_map).await; &all_data,
crate::strategy_team::strategy_008::list_up_for_sell(&all_data, &exchange_info_map, &trade_fee_map).await; &exchange_info_map,
&trade_fee_map,
)
.await;
crate::strategy_team::strategy_007::list_up_for_sell(
&all_data,
&exchange_info_map,
&trade_fee_map,
)
.await;
crate::strategy_team::strategy_008::list_up_for_sell(
&all_data,
&exchange_info_map,
&trade_fee_map,
)
.await;
Ok(()) Ok(())
} }
@ -172,13 +187,13 @@ pub async fn insert_pre_suggested_coins(
if is_dupe == false { if is_dupe == false {
let mut insert_values = vec![ let mut insert_values = vec![
symbol.clone(), // symbol symbol.clone(), // symbol
filtered_data.closetime.to_string(), // close_time filtered_data.closetime.to_string(), // close_time
filtered_data.current_price.to_string(), // suggested_price filtered_data.current_price.to_string(), // suggested_price
filtered_data.current_price.to_string(), // current_price filtered_data.current_price.to_string(), // current_price
filtered_data.stoploss.to_string(), // stoploss filtered_data.stoploss.to_string(), // stoploss
filtered_data.target_price.to_string(), // target_price filtered_data.target_price.to_string(), // target_price
get_server_epoch().await.to_string(), // registered_server_epoch get_server_epoch().await.to_string(), // registered_server_epoch
0.0.to_string(), // profit_percent 0.0.to_string(), // profit_percent
0.0.to_string(), // minimum_profit_percent 0.0.to_string(), // minimum_profit_percent
0.0.to_string(), // maximum_profit_percent 0.0.to_string(), // maximum_profit_percent
@ -258,13 +273,13 @@ pub async fn insert_pre_suggested_coins(
if is_dupe == false { if is_dupe == false {
let mut insert_values = vec![ let mut insert_values = vec![
symbol.clone(), // symbol symbol.clone(), // symbol
filtered_data.closetime.to_string(), // close_time filtered_data.closetime.to_string(), // close_time
filtered_data.current_price.to_string(), // suggested_price filtered_data.current_price.to_string(), // suggested_price
filtered_data.current_price.to_string(), // current_price filtered_data.current_price.to_string(), // current_price
filtered_data.stoploss.to_string(), // stoploss filtered_data.stoploss.to_string(), // stoploss
filtered_data.target_price.to_string(), // target_price filtered_data.target_price.to_string(), // target_price
get_server_epoch().await.to_string(), // registered_server_epoch get_server_epoch().await.to_string(), // registered_server_epoch
0.0.to_string(), // profit_percent 0.0.to_string(), // profit_percent
0.0.to_string(), // minimum_profit_percent 0.0.to_string(), // minimum_profit_percent
0.0.to_string(), // maximum_profit_percent 0.0.to_string(), // maximum_profit_percent
@ -334,13 +349,13 @@ pub async fn insert_pre_suggested_coins(
if is_dupe == false { if is_dupe == false {
let mut insert_values = vec![ let mut insert_values = vec![
symbol.clone(), // symbol symbol.clone(), // symbol
filtered_data.closetime.to_string(), // close_time filtered_data.closetime.to_string(), // close_time
filtered_data.current_price.to_string(), // suggested_price filtered_data.current_price.to_string(), // suggested_price
filtered_data.current_price.to_string(), // current_price filtered_data.current_price.to_string(), // current_price
filtered_data.stoploss.to_string(), // stoploss filtered_data.stoploss.to_string(), // stoploss
filtered_data.target_price.to_string(), // target_price filtered_data.target_price.to_string(), // target_price
get_server_epoch().await.to_string(), // registered_server_epoch get_server_epoch().await.to_string(), // registered_server_epoch
0.0.to_string(), // profit_percent 0.0.to_string(), // profit_percent
0.0.to_string(), // minimum_profit_percent 0.0.to_string(), // minimum_profit_percent
0.0.to_string(), // maximum_profit_percent 0.0.to_string(), // maximum_profit_percent
@ -394,13 +409,13 @@ pub async fn insert_pre_suggested_coins(
if is_dupe == false { if is_dupe == false {
let mut insert_values = vec![ let mut insert_values = vec![
symbol.clone(), // symbol symbol.clone(), // symbol
filtered_data.closetime.to_string(), // close_time filtered_data.closetime.to_string(), // close_time
filtered_data.current_price.to_string(), // suggested_price filtered_data.current_price.to_string(), // suggested_price
filtered_data.current_price.to_string(), // current_price filtered_data.current_price.to_string(), // current_price
filtered_data.stoploss.to_string(), // stoploss filtered_data.stoploss.to_string(), // stoploss
filtered_data.target_price.to_string(), // target_price filtered_data.target_price.to_string(), // target_price
get_server_epoch().await.to_string(), // registered_server_epoch get_server_epoch().await.to_string(), // registered_server_epoch
0.0.to_string(), // profit_percent 0.0.to_string(), // profit_percent
0.0.to_string(), // minimum_profit_percent 0.0.to_string(), // minimum_profit_percent
0.0.to_string(), // maximum_profit_percent 0.0.to_string(), // maximum_profit_percent
@ -480,13 +495,13 @@ pub async fn insert_pre_suggested_coins(
if is_dupe == false { if is_dupe == false {
let mut insert_values = vec![ let mut insert_values = vec![
symbol.clone(), // symbol symbol.clone(), // symbol
filtered_data.closetime.to_string(), // close_time filtered_data.closetime.to_string(), // close_time
filtered_data.current_price.to_string(), // suggested_price filtered_data.current_price.to_string(), // suggested_price
filtered_data.current_price.to_string(), // current_price filtered_data.current_price.to_string(), // current_price
filtered_data.stoploss.to_string(), // stoploss filtered_data.stoploss.to_string(), // stoploss
filtered_data.target_price.to_string(), // target_price filtered_data.target_price.to_string(), // target_price
get_server_epoch().await.to_string(), // registered_server_epoch get_server_epoch().await.to_string(), // registered_server_epoch
0.0.to_string(), // profit_percent 0.0.to_string(), // profit_percent
0.0.to_string(), // minimum_profit_percent 0.0.to_string(), // minimum_profit_percent
0.0.to_string(), // maximum_profit_percent 0.0.to_string(), // maximum_profit_percent
@ -550,13 +565,13 @@ pub async fn insert_pre_suggested_coins(
if is_dupe == false { if is_dupe == false {
let mut insert_values = vec![ let mut insert_values = vec![
symbol.clone(), // symbol symbol.clone(), // symbol
filtered_data.closetime.to_string(), // close_time filtered_data.closetime.to_string(), // close_time
filtered_data.current_price.to_string(), // suggested_price filtered_data.current_price.to_string(), // suggested_price
filtered_data.current_price.to_string(), // current_price filtered_data.current_price.to_string(), // current_price
filtered_data.stoploss.to_string(), // stoploss filtered_data.stoploss.to_string(), // stoploss
filtered_data.target_price.to_string(), // target_price filtered_data.target_price.to_string(), // target_price
get_server_epoch().await.to_string(), // registered_server_epoch get_server_epoch().await.to_string(), // registered_server_epoch
0.0.to_string(), // profit_percent 0.0.to_string(), // profit_percent
0.0.to_string(), // minimum_profit_percent 0.0.to_string(), // minimum_profit_percent
0.0.to_string(), // maximum_profit_percent 0.0.to_string(), // maximum_profit_percent
@ -610,13 +625,13 @@ pub async fn insert_pre_suggested_coins(
if is_dupe == false { if is_dupe == false {
let mut insert_values = vec![ let mut insert_values = vec![
symbol.clone(), // symbol symbol.clone(), // symbol
filtered_data.closetime.to_string(), // close_time filtered_data.closetime.to_string(), // close_time
filtered_data.current_price.to_string(), // suggested_price filtered_data.current_price.to_string(), // suggested_price
filtered_data.current_price.to_string(), // current_price filtered_data.current_price.to_string(), // current_price
filtered_data.stoploss.to_string(), // stoploss filtered_data.stoploss.to_string(), // stoploss
filtered_data.target_price.to_string(), // target_price filtered_data.target_price.to_string(), // target_price
get_server_epoch().await.to_string(), // registered_server_epoch get_server_epoch().await.to_string(), // registered_server_epoch
0.0.to_string(), // profit_percent 0.0.to_string(), // profit_percent
0.0.to_string(), // minimum_profit_percent 0.0.to_string(), // minimum_profit_percent
0.0.to_string(), // maximum_profit_percent 0.0.to_string(), // maximum_profit_percent
@ -635,13 +650,13 @@ pub async fn insert_pre_suggested_coins(
} else { } else {
for (symbol, filtered_data) in filtered_coins { for (symbol, filtered_data) in filtered_coins {
let mut insert_values = vec![ let mut insert_values = vec![
symbol.clone(), // symbol symbol.clone(), // symbol
filtered_data.closetime.to_string(), // close_time filtered_data.closetime.to_string(), // close_time
filtered_data.current_price.to_string(), // suggested_price filtered_data.current_price.to_string(), // suggested_price
filtered_data.current_price.to_string(), // current_price filtered_data.current_price.to_string(), // current_price
filtered_data.stoploss.to_string(), // stoploss filtered_data.stoploss.to_string(), // stoploss
filtered_data.target_price.to_string(), // target_price filtered_data.target_price.to_string(), // target_price
get_server_epoch().await.to_string(), // registered_server_epoch get_server_epoch().await.to_string(), // registered_server_epoch
0.0.to_string(), // profit_percent 0.0.to_string(), // profit_percent
0.0.to_string(), // minimum_profit_percent 0.0.to_string(), // minimum_profit_percent
0.0.to_string(), // maximum_profit_percent 0.0.to_string(), // maximum_profit_percent

View File

@ -1,7 +1,7 @@
use super::{ use super::{
dec, decimal_add, decimal_sub, ema, exists_record, insert_pre_suggested_coins, dec, decimal_add, decimal_sub, ema, exists_record, insert_pre_suggested_coins,
limit_order_sell, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, AllData, limit_order_sell, rsi, select_filled_buy_orders, stoch_rsi, supertrend, try_join_all, AllData,
Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredData, Mutex, Arc, Client, ClientBuilder, Decimal, EmaData, ExchangeInfo, FilteredDataValue, Mutex,
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, MacdData, ema_macd RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SupertrendData, TradeFee, update_record3, adx, AdxData, MacdData, ema_macd
}; };
@ -14,13 +14,13 @@ pub async fn strategist_test(
// println!("BTCUSDT: {:?}", alldata.rt_price_30m_vec[a.unwrap()].1.last().unwrap()); // println!("BTCUSDT: {:?}", alldata.rt_price_30m_vec[a.unwrap()].1.last().unwrap());
// 1st filtering: lookup tables if the tradepair is already there // 1st filtering: lookup tables if the tradepair is already there
let mut symbol_1 = FilteredData::new(); let mut symbol_1 = FilteredDataValue::new();
// let mut symbol_2 = FilteredData::new(); // let mut symbol_2 = FilteredData::new();
// let mut symbol_3 = FilteredData::new(); // let mut symbol_3 = FilteredData::new();
symbol_1.symbol = String::from("BTCUSDT"); symbol_1.symbol = String::from("BTCUSDT");
// symbol_2.symbol = String::from("XRPUSDT"); // symbol_2.symbol = String::from("XRPUSDT");
// symbol_3.symbol = String::from("ETHUSDT"); // symbol_3.symbol = String::from("ETHUSDT");
let mut test_symbols: Vec<FilteredData> = Vec::new(); let mut test_symbols: Vec<FilteredDataValue> = Vec::new();
test_symbols.push(symbol_1); test_symbols.push(symbol_1);
// test_symbols.push(symbol_2); // test_symbols.push(symbol_2);
// test_symbols.push(symbol_3); // test_symbols.push(symbol_3);

View File

@ -7,12 +7,15 @@ use csv::{DeserializeRecordsIter, StringRecord};
use rust_decimal::{prelude::ToPrimitive, Decimal}; use rust_decimal::{prelude::ToPrimitive, Decimal};
use serde::Deserialize; use serde::Deserialize;
use sqlx::FromRow; use sqlx::FromRow;
use std::collections::{HashMap, HashSet};
use std::sync::Arc; use std::sync::Arc;
use tokio::{fs::*, sync::Mutex, time::*}; use tokio::{fs::*, sync::Mutex, time::*};
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum CandleType { UP, DOWN } pub enum CandleType {
UP,
DOWN,
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct RealtimePriceData { pub struct RealtimePriceData {
@ -93,15 +96,11 @@ pub async fn update_realtime_price_data(
rt_price_vec.last_mut().unwrap().candle_type = CandleType::DOWN; rt_price_vec.last_mut().unwrap().candle_type = CandleType::DOWN;
} }
// update high_price // update high_price
if rt_price_vec.last_mut().unwrap().high_price if rt_price_vec.last_mut().unwrap().high_price < *current_price {
< *current_price
{
rt_price_vec.last_mut().unwrap().high_price = *current_price; rt_price_vec.last_mut().unwrap().high_price = *current_price;
} }
// update low_price // update low_price
if rt_price_vec.last_mut().unwrap().low_price if rt_price_vec.last_mut().unwrap().low_price > *current_price {
> *current_price
{
rt_price_vec.last_mut().unwrap().low_price = *current_price; rt_price_vec.last_mut().unwrap().low_price = *current_price;
} }
} }
@ -112,7 +111,7 @@ pub async fn update_realtime_price_data(
// for 1mon, uses 1w candle // for 1mon, uses 1w candle
if let Some(rt_vec) = read_candle_for_rt.get(element) { if let Some(rt_vec) = read_candle_for_rt.get(element) {
if rt_vec.len() >= 2 && rt_price_vec.len() >= 2 { if rt_vec.len() >= 2 && rt_price_vec.len() >= 2 {
let previous_close_time = rt_price_vec[rt_price_vec.len()-2].close_time; let previous_close_time = rt_price_vec[rt_price_vec.len() - 2].close_time;
// update realtime information for the latest candle // update realtime information for the latest candle
let mut update_closeprice = 0.0; let mut update_closeprice = 0.0;
let mut update_highprice = 0.0; let mut update_highprice = 0.0;
@ -134,25 +133,22 @@ pub async fn update_realtime_price_data(
}| *close_time, }| *close_time,
); );
if prev_closetime_result.is_ok() { if prev_closetime_result.is_ok() {
let result = let result = rt_vec.get(prev_closetime_result.unwrap() + 1..);
rt_vec.get(prev_closetime_result.unwrap() + 1..);
if result.is_some() { if result.is_some() {
let update_highprice_result = let update_highprice_result =
result.unwrap().iter().max_by(|x, y| { result.unwrap().iter().max_by(|x, y| {
x.high_price.partial_cmp(&y.high_price).unwrap() x.high_price.partial_cmp(&y.high_price).unwrap()
}); });
if update_highprice_result.is_some() { if update_highprice_result.is_some() {
update_highprice = update_highprice = update_highprice_result.unwrap().high_price;
update_highprice_result.unwrap().high_price;
} }
let update_lowprice_result = let update_lowprice_result = result
result.unwrap().iter().min_by(|x, y| { .unwrap()
x.low_price.partial_cmp(&y.low_price).unwrap() .iter()
}); .min_by(|x, y| x.low_price.partial_cmp(&y.low_price).unwrap());
if update_lowprice_result.is_some() { if update_lowprice_result.is_some() {
update_lowprice = update_lowprice = update_lowprice_result.unwrap().low_price;
update_lowprice_result.unwrap().low_price;
} }
for element in result.unwrap() { for element in result.unwrap() {
@ -189,12 +185,10 @@ pub async fn update_realtime_price_data(
&& !update_closeprice.is_nan() && !update_closeprice.is_nan()
&& update_closeprice.is_finite() && update_closeprice.is_finite()
{ {
rt_price_vec.last_mut().unwrap().close_price = rt_price_vec.last_mut().unwrap().close_price = update_closeprice;
update_closeprice; rt_price_vec.last_mut().unwrap().opclo_price = (update_closeprice
rt_price_vec.last_mut().unwrap().opclo_price = + rt_price_vec.last_mut().unwrap().open_price)
(update_closeprice / 2.0;
+ rt_price_vec.last_mut().unwrap().open_price)
/ 2.0;
} }
} }
} }

View File

@ -1,9 +1,9 @@
use super::{FilteredDataValue, RealtimePriceData, try_join_all, Arc, Mutex, HashMap}; use super::{try_join_all, Arc, FilteredDataValue, HashMap, Mutex, RealtimePriceData};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct AdxData { pub struct AdxData {
pub adx: f64, pub adx: f64,
pub close_time: i64 pub close_time: i64,
} }
struct BasicData { struct BasicData {
@ -19,12 +19,16 @@ struct DiData {
close_time: i64, close_time: i64,
} }
pub async fn adx(adx_len: usize, di_len: usize, input_rt_data: &HashMap<String, Vec<RealtimePriceData>>, pub async fn adx(
filtered_symbols: &HashMap<String, FilteredDataValue>,) -> Result<HashMap<String, Vec<AdxData>>, Box<dyn std::error::Error + Send + Sync>> { 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() { if filtered_symbols.is_empty() {
Err(("Err"))?; Err(("Err"))?;
} }
let mut adx_vec: HashMap<String, Vec<AdxData>> = HashMap::new(); let mut adx_vec: HashMap<String, Vec<AdxData>> = HashMap::new();
let mut adx_vec_arc = Arc::new(Mutex::new(adx_vec)); let mut adx_vec_arc = Arc::new(Mutex::new(adx_vec));
let mut task_vec = Vec::new(); let mut task_vec = Vec::new();
@ -32,9 +36,9 @@ filtered_symbols: &HashMap<String, FilteredDataValue>,) -> Result<HashMap<String
let mut rt_data_map = input_rt_data.clone(); let mut rt_data_map = input_rt_data.clone();
let adx_vec_arc_c = Arc::clone(&adx_vec_arc); let adx_vec_arc_c = Arc::clone(&adx_vec_arc);
let symbol = filtered_symbol.clone(); let symbol = filtered_symbol.clone();
task_vec.push(tokio::spawn(async move { task_vec.push(tokio::spawn(async move {
if let Some(rt_price_data) = rt_data_map.get(&symbol) { 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 { if rt_price_data.len() - 1 > adx_len && rt_price_data.len() > di_len {
// step 1: calculate +DM, -DM, TR // step 1: calculate +DM, -DM, TR
let windows = rt_price_data.windows(2); let windows = rt_price_data.windows(2);
let mut basic_data_vec: Vec<BasicData> = Vec::new(); let mut basic_data_vec: Vec<BasicData> = Vec::new();
@ -46,17 +50,21 @@ filtered_symbols: &HashMap<String, FilteredDataValue>,) -> Result<HashMap<String
let basic_data = BasicData { let basic_data = BasicData {
dm_plus: if up > down && up > 0.0 { up } else { 0.0 }, dm_plus: if up > down && up > 0.0 { up } else { 0.0 },
dm_minus: if down > up && down > 0.0 { down } 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, true_range: f64::max(
current_rt_data.high_price - prev_rt_data.close_price), f64::max(
current_rt_data.low_price - prev_rt_data.close_price), 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, close_time: current_rt_data.close_time,
}; };
basic_data_vec.push(basic_data); basic_data_vec.push(basic_data);
} }
// step 2: smoothing +DM, -DM, TR // step 2: smoothing +DM, -DM, TR
let alpha: f64 = 1.0/(di_len as f64); let alpha: f64 = 1.0 / (di_len as f64);
let mut smoothed_basic_data_vec: Vec<BasicData> = Vec::new(); 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_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 partial_vec2 = basic_data_vec.get(di_len..).unwrap(); // for calculation of the rest
@ -71,22 +79,38 @@ filtered_symbols: &HashMap<String, FilteredDataValue>,) -> Result<HashMap<String
dm_plus_calculated /= di_len as f64; dm_plus_calculated /= di_len as f64;
dm_minus_calculated /= di_len as f64; dm_minus_calculated /= di_len as f64;
tr_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 }; 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); smoothed_basic_data_vec.push(basic_data);
for element in partial_vec2 { for element in partial_vec2 {
dm_plus_calculated = alpha * element.dm_plus + (1.0 - alpha) * dm_plus_calculated; dm_plus_calculated =
dm_minus_calculated = alpha * element.dm_minus + (1.0 - alpha) * dm_minus_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; 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 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); smoothed_basic_data_vec.push(basic_data);
} }
// step 3: calculate DI // step 3: calculate DI
let mut di_data_vec: Vec<DiData> = Vec::new(); let mut di_data_vec: Vec<DiData> = Vec::new();
for basic_data in smoothed_basic_data_vec { 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}; 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); di_data_vec.push(di_data);
} }
@ -96,11 +120,17 @@ filtered_symbols: &HashMap<String, FilteredDataValue>,) -> Result<HashMap<String
let sum = di_data.di_plus + di_data.di_minus; let sum = di_data.di_plus + di_data.di_minus;
let difference = (di_data.di_plus - di_data.di_minus).abs(); let difference = (di_data.di_plus - di_data.di_minus).abs();
let divisor = if sum <= 0.00000001 { 1.0 } else { sum }; let divisor = if sum <= 0.00000001 { 1.0 } else { sum };
let adx_data = AdxData { adx: difference.abs()/divisor, close_time: di_data.close_time }; let adx_data = AdxData {
adx: difference.abs() / divisor,
close_time: di_data.close_time,
};
initial_adx_vec.push(adx_data); initial_adx_vec.push(adx_data);
} }
if let (Some(partial_vec1), Some(partial_vec2)) = (initial_adx_vec.get(..adx_len), initial_adx_vec.get(adx_len..)) { if let (Some(partial_vec1), Some(partial_vec2)) = (
initial_adx_vec.get(..adx_len),
initial_adx_vec.get(adx_len..),
) {
// partial_vec1 is for calculation of initial value // partial_vec1 is for calculation of initial value
// partial_vec2 is for calculation of the rest // partial_vec2 is for calculation of the rest
let mut smoothed_adx_vec: Vec<AdxData> = Vec::new(); let mut smoothed_adx_vec: Vec<AdxData> = Vec::new();
@ -109,14 +139,20 @@ filtered_symbols: &HashMap<String, FilteredDataValue>,) -> Result<HashMap<String
adx_calculated += element.adx; adx_calculated += element.adx;
} }
adx_calculated /= adx_len as f64; adx_calculated /= adx_len as f64;
let adx_data = AdxData { adx: adx_calculated, close_time: partial_vec1.last().unwrap().close_time }; let adx_data = AdxData {
adx: adx_calculated,
close_time: partial_vec1.last().unwrap().close_time,
};
smoothed_adx_vec.push(adx_data); smoothed_adx_vec.push(adx_data);
let alpha: f64 = 1.0 /(adx_len as f64); let alpha: f64 = 1.0 / (adx_len as f64);
for element in partial_vec2 { for element in partial_vec2 {
adx_calculated = alpha * element.adx + (1.0 - alpha) * adx_calculated; adx_calculated = alpha * element.adx + (1.0 - alpha) * adx_calculated;
let adx_data = AdxData { adx: 100.0 * adx_calculated, close_time: element.close_time }; let adx_data = AdxData {
adx: 100.0 * adx_calculated,
close_time: element.close_time,
};
smoothed_adx_vec.push(adx_data); smoothed_adx_vec.push(adx_data);
} }
@ -128,8 +164,7 @@ filtered_symbols: &HashMap<String, FilteredDataValue>,) -> Result<HashMap<String
})); }));
} }
try_join_all(task_vec).await?; try_join_all(task_vec).await?;
let a = adx_vec_arc.lock().await.to_owned(); let a = adx_vec_arc.lock().await.to_owned();
Ok(a) Ok(a)
}
}

View File

@ -1,16 +1,16 @@
#![allow(unused)] #![allow(unused)]
#![allow(warnings)] #![allow(warnings)]
use super::HashMap;
use crate::database_control::*; 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::FilteredDataValue; use crate::strategy_team::FilteredDataValue;
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use crate::value_estimation_team::indicators::sma::{sma, SmaData};
use futures::future::try_join_all; use futures::future::try_join_all;
use serde::Deserialize; use serde::Deserialize;
use sqlx::FromRow; use sqlx::FromRow;
use std::sync::Arc; use std::sync::Arc;
use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*}; use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*};
use super::{HashMap};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct BollingerBandData { pub struct BollingerBandData {
@ -43,7 +43,8 @@ pub async fn bollingerband(
Err(("Err"))?; Err(("Err"))?;
} }
let mut sma_data_map: HashMap<String, Vec<SmaData>> = sma(period, input_rt_data, filtered_symbols).await?; 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: HashMap<String, Vec<BollingerBandData>> = HashMap::new();
let mut bb_data_wrapper_arc = Arc::new(Mutex::new(bb_data_wrapper)); let mut bb_data_wrapper_arc = Arc::new(Mutex::new(bb_data_wrapper));
let mut task_vec = Vec::new(); let mut task_vec = Vec::new();
@ -56,33 +57,31 @@ pub async fn bollingerband(
task_vec.push(tokio::spawn(async move { task_vec.push(tokio::spawn(async move {
let mut bb_data = BollingerBandData::new(); let mut bb_data = BollingerBandData::new();
let mut bb_data_vec: Vec<BollingerBandData> = Vec::new(); let mut bb_data_vec: Vec<BollingerBandData> = Vec::new();
// if the data has shorter than buffer // if the data has shorter than buffer
if sma_data_vec_c.len() > period { if sma_data_vec_c.len() > period {
let result = rt_data_vec_c.binary_search_by_key( let result = rt_data_vec_c.binary_search_by_key(
&sma_data_vec_c.first().unwrap().close_time, &sma_data_vec_c.first().unwrap().close_time,
|RealtimePriceData { |RealtimePriceData {
opclo_price, opclo_price,
open_price, open_price,
close_price, close_price,
high_price, high_price,
low_price, low_price,
close_time, close_time,
quote_asset_volume, quote_asset_volume,
candle_type, candle_type,
}| *close_time, }| *close_time,
); );
match result { match result {
Ok(T) => { Ok(T) => {
if T <= period - 1 { if T <= period - 1 {
let mut read_data_iter = let mut read_data_iter = sma_data_vec_c.iter();
sma_data_vec_c.iter();
for _ in T..period - 1 { for _ in T..period - 1 {
read_data_iter.next(); read_data_iter.next();
} }
let window_iter = let window_iter = rt_data_vec_c.windows(period);
rt_data_vec_c.windows(period);
for buffer in window_iter { for buffer in window_iter {
let mut sd_mean = 0.0; let mut sd_mean = 0.0;
let mut standard_deviation = 0.0; let mut standard_deviation = 0.0;
@ -94,16 +93,14 @@ pub async fn bollingerband(
standard_deviation += standard_deviation +=
(element.close_price - sd_mean).powi(2); (element.close_price - sd_mean).powi(2);
} }
standard_deviation = sd_factor standard_deviation =
* ((standard_deviation / period as f64).sqrt()); sd_factor * ((standard_deviation / period as f64).sqrt());
match read_data_iter.next() { match read_data_iter.next() {
Some(T) => { Some(T) => {
bb_data.sma = T.sma_value; bb_data.sma = T.sma_value;
bb_data.upperband = bb_data.upperband = T.sma_value + standard_deviation;
T.sma_value + standard_deviation; bb_data.lowerband = T.sma_value - standard_deviation;
bb_data.lowerband =
T.sma_value - standard_deviation;
bb_data.close_time = T.close_time; bb_data.close_time = T.close_time;
bb_data_vec.push(bb_data.clone()); bb_data_vec.push(bb_data.clone());
} }
@ -115,8 +112,7 @@ pub async fn bollingerband(
Err(E) => {} Err(E) => {}
} }
let mut bb_data_wrapper_lock = bb_data_wrapper_arc_c.lock().await; let mut bb_data_wrapper_lock = bb_data_wrapper_arc_c.lock().await;
bb_data_wrapper_lock bb_data_wrapper_lock.insert(symbol_c, bb_data_vec.clone());
.insert(symbol_c, bb_data_vec.clone());
} }
})); }));
} }

View File

@ -1,15 +1,15 @@
#![allow(unused)] #![allow(unused)]
#![allow(warnings)] #![allow(warnings)]
use super::ema::{ema, EmaData};
use super::FilteredDataValue;
use crate::database_control::*; use crate::database_control::*;
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use futures::future::try_join_all; use futures::future::try_join_all;
use serde::Deserialize; use serde::Deserialize;
use sqlx::FromRow; use sqlx::FromRow;
use std::collections::HashMap;
use std::{iter::zip, sync::Arc}; use std::{iter::zip, sync::Arc};
use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*}; use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*};
use super::FilteredDataValue;
use std::collections::HashMap;
use super::ema::{ema, EmaData};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct DemaData { pub struct DemaData {
@ -81,7 +81,6 @@ pub async fn dema(
ema_data_wrapper_lock.insert(symbol_c, ema_data_vec.clone()); ema_data_wrapper_lock.insert(symbol_c, ema_data_vec.clone());
} }
})); }));
} }
try_join_all(task_vec).await?; try_join_all(task_vec).await?;
let e2 = ema_data_wrapper_arc.lock().await.to_owned(); let e2 = ema_data_wrapper_arc.lock().await.to_owned();
@ -91,14 +90,16 @@ pub async fn dema(
let mut task_vec = Vec::new(); let mut task_vec = Vec::new();
for (symbol, e2_vec) in e2 { for (symbol, e2_vec) in e2 {
if let Some(e1_vec) = e1.get(&symbol) { if let Some(e1_vec) = e1.get(&symbol) {
let dema_data_wrapper_arc_c: Arc<Mutex<HashMap<String, Vec<DemaData>>>> = Arc::clone(&dema_data_wrapper_arc); let dema_data_wrapper_arc_c: Arc<Mutex<HashMap<String, Vec<DemaData>>>> =
Arc::clone(&dema_data_wrapper_arc);
let symbol_c = symbol.clone(); let symbol_c = symbol.clone();
let e1_vec_c = e1_vec.clone(); let e1_vec_c = e1_vec.clone();
task_vec.push(tokio::spawn(async move { task_vec.push(tokio::spawn(async move {
if e2_vec.last().unwrap().close_time == e1_vec_c.last().unwrap().close_time && if e2_vec.last().unwrap().close_time == e1_vec_c.last().unwrap().close_time
e2_vec.len() < e1_vec_c.len() { && e2_vec.len() < e1_vec_c.len()
{
let mut dema_data_vec: Vec<DemaData> = Vec::new(); let mut dema_data_vec: Vec<DemaData> = Vec::new();
let e1_vec_part = e1_vec_c.get(e1_vec_c.len()-e2_vec.len()..).unwrap(); let e1_vec_part = e1_vec_c.get(e1_vec_c.len() - e2_vec.len()..).unwrap();
let zipped = e1_vec_part.iter().zip(e2_vec.iter()); let zipped = e1_vec_part.iter().zip(e2_vec.iter());
for element in zipped { for element in zipped {
let mut dema_data = DemaData::new(); let mut dema_data = DemaData::new();

View File

@ -1,14 +1,14 @@
#![allow(unused)] #![allow(unused)]
#![allow(warnings)] #![allow(warnings)]
use super::FilteredDataValue;
use crate::database_control::*; use crate::database_control::*;
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use futures::future::try_join_all; use futures::future::try_join_all;
use serde::Deserialize; use serde::Deserialize;
use sqlx::FromRow; use sqlx::FromRow;
use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*}; use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*};
use super::FilteredDataValue;
use std::collections::HashMap;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct EmaData { pub struct EmaData {

View File

@ -1,9 +1,9 @@
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use super::FilteredDataValue; use super::FilteredDataValue;
use super::HashMap;
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use futures::future::try_join_all;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use futures::future::try_join_all;
use super::HashMap;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum HeatMapLevel { pub enum HeatMapLevel {
@ -49,7 +49,7 @@ pub async fn heatmap_volume(
mean_value: f64, mean_value: f64,
close_time: i64, close_time: i64,
} }
let mut mean_vec: Vec<MeanData> = Vec::new(); let mut mean_vec: Vec<MeanData> = Vec::new();
let mut mean_data = MeanData { let mut mean_data = MeanData {
mean_value: 0.0, mean_value: 0.0,
@ -65,10 +65,10 @@ pub async fn heatmap_volume(
} }
mean_data.mean_value /= ma_len as f64; mean_data.mean_value /= ma_len as f64;
mean_data.close_time = buffer.last().unwrap().close_time; mean_data.close_time = buffer.last().unwrap().close_time;
mean_vec.push(mean_data.clone()); mean_vec.push(mean_data.clone());
} }
// calc pstdev // calc pstdev
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct PstdevData { struct PstdevData {
@ -94,18 +94,18 @@ pub async fn heatmap_volume(
mean += element.quote_asset_volume; mean += element.quote_asset_volume;
} }
mean /= std_len as f64; mean /= std_len as f64;
for element in buffer { for element in buffer {
sample_minus_mean = element.quote_asset_volume - mean; sample_minus_mean = element.quote_asset_volume - mean;
summation += sample_minus_mean.powi(2); summation += sample_minus_mean.powi(2);
} }
pstdev_data.pstdev_value = (summation / std_len as f64).sqrt(); pstdev_data.pstdev_value = (summation / std_len as f64).sqrt();
pstdev_data.close_time = buffer.last().unwrap().close_time; pstdev_data.close_time = buffer.last().unwrap().close_time;
pstdev_vec.push(pstdev_data.clone()); pstdev_vec.push(pstdev_data.clone());
} }
// calc stdbar and heatmap volume // calc stdbar and heatmap volume
let mut heatmap_vol_vec: Vec<HeatmapVolumeData> = Vec::new(); let mut heatmap_vol_vec: Vec<HeatmapVolumeData> = Vec::new();
let mut heatmap_vol_data = HeatmapVolumeData { let mut heatmap_vol_data = HeatmapVolumeData {
@ -113,11 +113,11 @@ pub async fn heatmap_volume(
heatmap_level: HeatMapLevel::Normal, heatmap_level: HeatMapLevel::Normal,
close_time: 0, close_time: 0,
}; };
if ma_len == std_len { if ma_len == std_len {
let mut rt_data_trunc_vec = let mut rt_data_trunc_vec =
rt_price_vec_c.get(ma_len - 1..).unwrap().iter(); rt_price_vec_c.get(ma_len - 1..).unwrap().iter();
let zipped = mean_vec.iter().zip(pstdev_vec.iter()); let zipped = mean_vec.iter().zip(pstdev_vec.iter());
for element in zipped { for element in zipped {
heatmap_vol_data.heatmap_value = heatmap_vol_data.heatmap_value =
@ -140,7 +140,7 @@ pub async fn heatmap_volume(
} }
} else if ma_len > std_len { } else if ma_len > std_len {
let mut rt_data_trunc_vec = let mut rt_data_trunc_vec =
rt_price_vec_c.get(std_len - 1..).unwrap().iter(); rt_price_vec_c.get(std_len - 1..).unwrap().iter();
let mut mean_trunc_vec = let mut mean_trunc_vec =
mean_vec.get(mean_vec.len() - std_len..).unwrap().iter(); mean_vec.get(mean_vec.len() - std_len..).unwrap().iter();
let zipped = mean_trunc_vec.zip(pstdev_vec.iter()); let zipped = mean_trunc_vec.zip(pstdev_vec.iter());
@ -154,7 +154,7 @@ pub async fn heatmap_volume(
} }
} else { } else {
let mut rt_data_trunc_vec = let mut rt_data_trunc_vec =
rt_price_vec_c.get(ma_len - 1..).unwrap().iter(); rt_price_vec_c.get(ma_len - 1..).unwrap().iter();
let mut pstdev_trunc_vec = let mut pstdev_trunc_vec =
pstdev_vec.get(pstdev_vec.len() - ma_len..).unwrap().iter(); pstdev_vec.get(pstdev_vec.len() - ma_len..).unwrap().iter();
let zipped = mean_vec.iter().zip(pstdev_trunc_vec); let zipped = mean_vec.iter().zip(pstdev_trunc_vec);
@ -172,7 +172,6 @@ pub async fn heatmap_volume(
} }
})); }));
} }
} }
try_join_all(task_vec).await?; try_join_all(task_vec).await?;
let a = heatmap_data_wrapper_arc.lock().await.to_owned(); let a = heatmap_data_wrapper_arc.lock().await.to_owned();

View File

@ -1,9 +1,9 @@
use crate::value_estimation_team::indicators::ema::{EmaData, ema};
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use super::{FilteredDataValue, HashMap}; use super::{FilteredDataValue, HashMap};
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use crate::value_estimation_team::indicators::ema::{ema, EmaData};
use futures::future::try_join_all;
use std::sync::Arc; use std::sync::Arc;
use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*}; use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*};
use futures::future::try_join_all;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct MacdData { pub struct MacdData {
@ -44,7 +44,7 @@ pub async fn ema_macd(
slow_len: usize, slow_len: usize,
signal_smoothing: usize, signal_smoothing: usize,
input_rt_data: &HashMap<String, Vec<RealtimePriceData>>, input_rt_data: &HashMap<String, Vec<RealtimePriceData>>,
filtered_symbols: &HashMap<String, FilteredDataValue> filtered_symbols: &HashMap<String, FilteredDataValue>,
) -> Result<HashMap<String, Vec<MacdData>>, Box<dyn std::error::Error + Send + Sync>> { ) -> Result<HashMap<String, Vec<MacdData>>, Box<dyn std::error::Error + Send + Sync>> {
let mut macd_oscil_vec: HashMap<String, Vec<MacdData>> = HashMap::new(); let mut macd_oscil_vec: HashMap<String, Vec<MacdData>> = HashMap::new();
@ -56,19 +56,23 @@ pub async fn ema_macd(
for (symbol, filtered_data) in filtered_symbols { for (symbol, filtered_data) in filtered_symbols {
let fast_emas_c = fast_emas.clone(); let fast_emas_c = fast_emas.clone();
let slow_emas_c = slow_emas.clone(); let slow_emas_c = slow_emas.clone();
if let (Some(fast_ema_vec), Some(slow_ema_vec)) = (fast_emas.get(symbol), slow_emas_c.get(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 symbol_c = symbol.clone();
let fast_ema_vec_c = fast_ema_vec.clone(); let fast_ema_vec_c = fast_ema_vec.clone();
let slow_ema_vec_c = slow_ema_vec.clone(); let slow_ema_vec_c = slow_ema_vec.clone();
let macd_data_wrapper_arc_c = Arc::clone(&macd_data_wrapper_arc); let macd_data_wrapper_arc_c = Arc::clone(&macd_data_wrapper_arc);
task_vec.push(tokio::spawn(async move { task_vec.push(tokio::spawn(async move {
if fast_ema_vec_c.len() >= signal_smoothing && slow_ema_vec_c.len() >= signal_smoothing { 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( let result = fast_ema_vec_c.binary_search_by_key(
&slow_ema_vec_c.first().unwrap().close_time, &slow_ema_vec_c.first().unwrap().close_time,
|&EmaData { |&EmaData {
ema_value, ema_value,
close_time, close_time,
}| close_time, }| close_time,
); );
if result.is_ok() { if result.is_ok() {
// making MACD // making MACD
@ -81,7 +85,7 @@ pub async fn ema_macd(
macd.close_time = element.0.close_time; macd.close_time = element.0.close_time;
macd_vec.push(macd.clone()); macd_vec.push(macd.clone());
} }
// making signal (smoothed MACD) // making signal (smoothed MACD)
// TODO: this should be calculated in EMA (currently, SMA) // TODO: this should be calculated in EMA (currently, SMA)
// let macd_vec_window = macd_vec.windows(signal_smoothing); // let macd_vec_window = macd_vec.windows(signal_smoothing);
@ -121,20 +125,17 @@ pub async fn ema_macd(
ema_prev = ema_t; ema_prev = ema_t;
} }
let result = macd_vec.binary_search_by_key( let result = macd_vec.binary_search_by_key(
&macd_signal_vec.first().unwrap().close_time, &macd_signal_vec.first().unwrap().close_time,
|&TempData { |&TempData { value, close_time }| close_time,
value,
close_time,
}| close_time,
); );
if result.is_ok() { if result.is_ok() {
let result = macd_vec.get(result.unwrap()..); let result = macd_vec.get(result.unwrap()..);
if result.is_some() { if result.is_some() {
let zipped = result.unwrap().iter().zip(macd_signal_vec); let zipped = result.unwrap().iter().zip(macd_signal_vec);
let mut macd_vec: Vec<MacdData> = Vec::new(); let mut macd_vec: Vec<MacdData> = Vec::new();
for element in zipped { for element in zipped {
let mut macd = MacdData::new(); let mut macd = MacdData::new();
@ -143,14 +144,15 @@ pub async fn ema_macd(
macd.close_time = element.0.close_time; macd.close_time = element.0.close_time;
macd_vec.push(macd); macd_vec.push(macd);
} }
let mut macd_data_wrapper_lock = macd_data_wrapper_arc_c.lock().await; let mut macd_data_wrapper_lock =
macd_data_wrapper_arc_c.lock().await;
macd_data_wrapper_lock.insert(symbol_c, macd_vec.clone()); macd_data_wrapper_lock.insert(symbol_c, macd_vec.clone());
} }
} }
} }
} }
})); }));
} }
} }
try_join_all(task_vec).await?; try_join_all(task_vec).await?;
let a = macd_data_wrapper_arc.lock().await.to_owned(); let a = macd_data_wrapper_arc.lock().await.to_owned();

View File

@ -1,5 +1,6 @@
pub mod adx; pub mod adx;
pub mod bollingerband; pub mod bollingerband;
pub mod dema;
pub mod ema; pub mod ema;
pub mod heatmap_volume; pub mod heatmap_volume;
pub mod macd; pub mod macd;
@ -8,11 +9,10 @@ pub mod sma;
pub mod stoch_rsi; pub mod stoch_rsi;
pub mod supertrend; pub mod supertrend;
pub mod tema; pub mod tema;
pub mod dema;
use crate::strategy_team::FilteredDataValue; use crate::strategy_team::FilteredDataValue;
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use futures::future::try_join_all; use futures::future::try_join_all;
use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*}; use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*};
use std::collections::HashMap;

View File

@ -1,16 +1,16 @@
#![allow(unused)] #![allow(unused)]
#![allow(warnings)] #![allow(warnings)]
use super::HashMap;
use crate::database_control::*; use crate::database_control::*;
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use crate::strategy_team::FilteredDataValue; use crate::strategy_team::FilteredDataValue;
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use futures::future::try_join_all; use futures::future::try_join_all;
use serde::Deserialize; use serde::Deserialize;
use sqlx::FromRow; use sqlx::FromRow;
use std::f64::NAN; use std::f64::NAN;
use std::sync::Arc; use std::sync::Arc;
use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*}; use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*};
use super::HashMap;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct RsiData { pub struct RsiData {
@ -132,8 +132,8 @@ pub async fn rsi(
prev_avg_ups = current_avg_ups; prev_avg_ups = current_avg_ups;
prev_avg_downs = current_avg_downs; prev_avg_downs = current_avg_downs;
let rs = current_avg_ups.unwrap() let rs =
/ (current_avg_downs.unwrap() + 0.00000001); // 0.00000001 is used to avoid division by 0 current_avg_ups.unwrap() / (current_avg_downs.unwrap() + 0.00000001); // 0.00000001 is used to avoid division by 0
let rsi = 100.0 - (100.0 / (1.0 + rs)); let rsi = 100.0 - (100.0 / (1.0 + rs));

View File

@ -1,15 +1,15 @@
#![allow(unused)] #![allow(unused)]
#![allow(warnings)] #![allow(warnings)]
use super::HashMap;
use crate::database_control::*; use crate::database_control::*;
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use crate::strategy_team::FilteredDataValue; use crate::strategy_team::FilteredDataValue;
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use futures::future::try_join_all; use futures::future::try_join_all;
use serde::Deserialize; use serde::Deserialize;
use sqlx::FromRow; use sqlx::FromRow;
use std::sync::Arc; use std::sync::Arc;
use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*}; use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*};
use super::HashMap;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SmaData { pub struct SmaData {
@ -48,7 +48,7 @@ pub async fn sma(
task_vec.push(tokio::spawn(async move { task_vec.push(tokio::spawn(async move {
let mut sma_data = SmaData::new(); let mut sma_data = SmaData::new();
let mut sma_data_vec: Vec<SmaData> = Vec::new(); let mut sma_data_vec: Vec<SmaData> = Vec::new();
if rt_price_data.len() >= moving_number { if rt_price_data.len() >= moving_number {
let mut iter = rt_price_data.windows(moving_number); let mut iter = rt_price_data.windows(moving_number);
for buffer in iter { for buffer in iter {
@ -95,7 +95,7 @@ pub async fn sma_opclo(
task_vec.push(tokio::spawn(async move { task_vec.push(tokio::spawn(async move {
let mut sma_data = SmaData::new(); let mut sma_data = SmaData::new();
let mut sma_data_vec: Vec<SmaData> = Vec::new(); let mut sma_data_vec: Vec<SmaData> = Vec::new();
if rt_price_data.len() >= moving_number { if rt_price_data.len() >= moving_number {
let mut iter = rt_price_data.windows(moving_number); let mut iter = rt_price_data.windows(moving_number);
for buffer in iter { for buffer in iter {

View File

@ -2,16 +2,16 @@
#![allow(warnings)] #![allow(warnings)]
use crate::database_control::*; 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::FilteredDataValue; use crate::strategy_team::FilteredDataValue;
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use crate::value_estimation_team::indicators::rsi::{rsi, RsiData};
use futures::{future::try_join_all, lock::Mutex}; use futures::{future::try_join_all, lock::Mutex};
use serde::Deserialize; use serde::Deserialize;
use sqlx::FromRow; use sqlx::FromRow;
use std::collections::{HashMap, HashSet};
use std::f64::NAN; use std::f64::NAN;
use std::sync::Arc; use std::sync::Arc;
use tokio::{fs::*, io::AsyncWriteExt, time::*}; use tokio::{fs::*, io::AsyncWriteExt, time::*};
use std::collections::{HashMap, HashSet};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct StochRsiData { pub struct StochRsiData {

View File

@ -1,21 +1,27 @@
use super::{FilteredDataValue, HashMap};
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use super::{HashMap, FilteredDataValue};
use std::sync::Arc;
use futures::future::try_join_all; use futures::future::try_join_all;
use std::sync::Arc;
use tokio::sync::Mutex; use tokio::sync::Mutex;
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
pub enum SuperTrendArea { UP, DOWN } pub enum SuperTrendArea {
UP,
DOWN,
}
#[derive(PartialEq, Clone, Debug)] #[derive(PartialEq, Clone, Debug)]
pub enum SuperTrendSignal { BUY, SELL } pub enum SuperTrendSignal {
BUY,
SELL,
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SupertrendData { pub struct SupertrendData {
pub band_value: f64, pub band_value: f64,
pub signal: Option<SuperTrendSignal>, // BUY or SELL pub signal: Option<SuperTrendSignal>, // BUY or SELL
pub area: SuperTrendArea, // UP or DOWN pub area: SuperTrendArea, // UP or DOWN
pub close_time: i64 pub close_time: i64,
} }
impl SupertrendData { impl SupertrendData {
@ -24,7 +30,7 @@ impl SupertrendData {
band_value: 0.0, band_value: 0.0,
signal: None, signal: None,
area: SuperTrendArea::DOWN, area: SuperTrendArea::DOWN,
close_time: 0 close_time: 0,
}; };
a a
@ -68,7 +74,7 @@ impl ATRData {
// TODO: should return type be Option? // TODO: should return type be Option?
// Implementation from TradingView Script (SuperTrend by KivancOzbilgic, source price: closeprice) // Implementation from TradingView Script (SuperTrend by KivancOzbilgic, source price: closeprice)
// return key: close_time // return key: close_time
pub async fn supertrend( pub async fn supertrend(
atr_period: usize, atr_period: usize,
multiplier: f64, multiplier: f64,
is_original_method: bool, is_original_method: bool,
@ -81,7 +87,7 @@ pub async fn supertrend(
let mut task_vec = Vec::new(); let mut task_vec = Vec::new();
for (symbol, filtered_data) in filtered_symbols { for (symbol, filtered_data) in filtered_symbols {
if let Some(rt_price_vec) = input_rt_data.get(symbol) { if let Some(rt_price_vec) = input_rt_data.get(symbol) {
if atr_period < rt_price_vec.len()-1 { if atr_period < rt_price_vec.len() - 1 {
let symbol_c = symbol.clone(); let symbol_c = symbol.clone();
let supertrend_data_wrapper_arc_c = Arc::clone(&supertrend_data_wrapper_arc); let supertrend_data_wrapper_arc_c = Arc::clone(&supertrend_data_wrapper_arc);
let rt_price_vec_c = rt_price_vec.clone(); let rt_price_vec_c = rt_price_vec.clone();
@ -92,34 +98,34 @@ pub async fn supertrend(
let mut tr_val_hl = 0.0; // High - Low let mut tr_val_hl = 0.0; // High - Low
let mut tr_val_hcp = 0.0; // Abs(High - Close_previous) let mut tr_val_hcp = 0.0; // Abs(High - Close_previous)
let mut tr_val_lcp = 0.0; // Abs(Low - Close_previous) let mut tr_val_lcp = 0.0; // Abs(Low - Close_previous)
let window_vec = rt_price_vec_c.windows(2); let window_vec = rt_price_vec_c.windows(2);
tr_data.tr_value = rt_price_vec_c.first().unwrap().high_price tr_data.tr_value = rt_price_vec_c.first().unwrap().high_price
- rt_price_vec_c.first().unwrap().low_price; - rt_price_vec_c.first().unwrap().low_price;
tr_data.close_time = rt_price_vec_c.first().unwrap().close_time; tr_data.close_time = rt_price_vec_c.first().unwrap().close_time;
true_range_vec.push(tr_data.clone()); true_range_vec.push(tr_data.clone());
for buffer in window_vec { for buffer in window_vec {
tr_val_hl = buffer[1].high_price - buffer[1].low_price; 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_hcp = (buffer[1].high_price - buffer[0].close_price).abs();
tr_val_lcp = (buffer[1].low_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.tr_value = tr_val_hl.max(tr_val_hcp.max(tr_val_lcp));
tr_data.close_time = buffer[1].close_time; tr_data.close_time = buffer[1].close_time;
true_range_vec.push(tr_data.clone()); true_range_vec.push(tr_data.clone());
} }
// making Average True Range // making Average True Range
let mut average_true_range_vec: Vec<ATRData> = Vec::new(); let mut average_true_range_vec: Vec<ATRData> = Vec::new();
let mut atr_data = ATRData::new(); let mut atr_data = ATRData::new();
if is_original_method == true { if is_original_method == true {
// original calculation of ATR // original calculation of ATR
let mut first_value = 0.0; let mut first_value = 0.0;
let mut first_vec = &true_range_vec[0..atr_period]; let mut first_vec = &true_range_vec[0..atr_period];
for element in first_vec { for element in first_vec {
first_value += element.tr_value; first_value += element.tr_value;
} }
@ -127,35 +133,36 @@ pub async fn supertrend(
atr_data.atr_value = first_value; atr_data.atr_value = first_value;
atr_data.close_time = first_vec.last().unwrap().close_time; atr_data.close_time = first_vec.last().unwrap().close_time;
average_true_range_vec.push(atr_data.clone()); average_true_range_vec.push(atr_data.clone());
let mut temp_prev_atr_value = first_value; let mut temp_prev_atr_value = first_value;
for element in &true_range_vec[atr_period..] { for element in &true_range_vec[atr_period..] {
atr_data.atr_value = ((temp_prev_atr_value * ((atr_period - 1) as f64)) atr_data.atr_value = ((temp_prev_atr_value
* ((atr_period - 1) as f64))
+ element.tr_value) + element.tr_value)
/ atr_period as f64; / atr_period as f64;
atr_data.close_time = element.close_time; atr_data.close_time = element.close_time;
average_true_range_vec.push(atr_data.clone()); average_true_range_vec.push(atr_data.clone());
temp_prev_atr_value = atr_data.atr_value; temp_prev_atr_value = atr_data.atr_value;
} }
} else { } else {
// Calculation of ATR from SMA True Range with atr_period // Calculation of ATR from SMA True Range with atr_period
let window = true_range_vec.windows(atr_period); let window = true_range_vec.windows(atr_period);
let mut sum = 0.0; let mut sum = 0.0;
for buffer in window { for buffer in window {
for element in buffer { for element in buffer {
sum += element.tr_value; sum += element.tr_value;
} }
atr_data.atr_value = sum / atr_period as f64; atr_data.atr_value = sum / atr_period as f64;
atr_data.close_time = buffer.last().unwrap().close_time; atr_data.close_time = buffer.last().unwrap().close_time;
average_true_range_vec.push(atr_data.clone()); average_true_range_vec.push(atr_data.clone());
sum = 0.0; sum = 0.0;
} }
} }
// making Supertrend // making Supertrend
#[derive(Clone)] #[derive(Clone)]
struct BandData { struct BandData {
@ -163,7 +170,7 @@ pub async fn supertrend(
basic_lowerband: f64, basic_lowerband: f64,
close_time: i64, close_time: i64,
} }
let mut supertrend_vec: Vec<SupertrendData> = Vec::new(); let mut supertrend_vec: Vec<SupertrendData> = Vec::new();
let mut supertrend_data = SupertrendData::new(); let mut supertrend_data = SupertrendData::new();
let mut band_vec: Vec<BandData> = Vec::new(); let mut band_vec: Vec<BandData> = Vec::new();
@ -172,13 +179,13 @@ pub async fn supertrend(
basic_lowerband: 0.0, basic_lowerband: 0.0,
close_time: 0, close_time: 0,
}; };
let closetime_search_result = rt_price_vec_c.iter().position(|x| { let closetime_search_result = rt_price_vec_c.iter().position(|x| {
x.close_time == average_true_range_vec.first().unwrap().close_time x.close_time == average_true_range_vec.first().unwrap().close_time
}); });
let mut rt_data = &rt_price_vec_c[closetime_search_result.unwrap()..]; let mut rt_data = &rt_price_vec_c[closetime_search_result.unwrap()..];
let zipped = rt_data.iter().zip(average_true_range_vec); let zipped = rt_data.iter().zip(average_true_range_vec);
for element in zipped { for element in zipped {
band_data.basic_upperband = ((element.0.high_price + element.0.low_price) band_data.basic_upperband = ((element.0.high_price + element.0.low_price)
/ 2.0) / 2.0)
@ -189,7 +196,7 @@ pub async fn supertrend(
band_data.close_time = element.1.close_time; band_data.close_time = element.1.close_time;
band_vec.push(band_data.clone()); band_vec.push(band_data.clone());
} }
let mut zipped = rt_data.iter().zip(band_vec); let mut zipped = rt_data.iter().zip(band_vec);
let first_element = zipped.next().unwrap(); let first_element = zipped.next().unwrap();
let mut prev_close_price = first_element.0.close_price; let mut prev_close_price = first_element.0.close_price;
@ -206,14 +213,14 @@ pub async fn supertrend(
} else { } else {
final_upperband = element.1.basic_upperband; final_upperband = element.1.basic_upperband;
} }
// set final lowerband // set final lowerband
if prev_close_price < prev_final_lowerband { if prev_close_price < prev_final_lowerband {
final_lowerband = prev_final_lowerband.min(element.1.basic_lowerband); final_lowerband = prev_final_lowerband.min(element.1.basic_lowerband);
} else { } else {
final_lowerband = element.1.basic_lowerband; final_lowerband = element.1.basic_lowerband;
} }
// set supertrend // set supertrend
if trend == -1 && element.0.close_price > prev_final_lowerband { if trend == -1 && element.0.close_price > prev_final_lowerband {
supertrend_data.area = SuperTrendArea::UP; supertrend_data.area = SuperTrendArea::UP;
@ -222,13 +229,13 @@ pub async fn supertrend(
supertrend_data.area = SuperTrendArea::DOWN; supertrend_data.area = SuperTrendArea::DOWN;
trend = -1; trend = -1;
} }
if supertrend_data.area == SuperTrendArea::UP { if supertrend_data.area == SuperTrendArea::UP {
supertrend_data.band_value = final_upperband; supertrend_data.band_value = final_upperband;
} else { } else {
supertrend_data.band_value = final_lowerband; supertrend_data.band_value = final_lowerband;
} }
if trend == 1 && prev_trend == -1 { if trend == 1 && prev_trend == -1 {
supertrend_data.signal = Some(SuperTrendSignal::BUY); supertrend_data.signal = Some(SuperTrendSignal::BUY);
} else if trend == -1 && prev_trend == 1 { } else if trend == -1 && prev_trend == 1 {
@ -236,16 +243,17 @@ pub async fn supertrend(
} else { } else {
supertrend_data.signal = None; supertrend_data.signal = None;
} }
supertrend_data.close_time = element.1.close_time; supertrend_data.close_time = element.1.close_time;
supertrend_vec.push(supertrend_data.clone()); supertrend_vec.push(supertrend_data.clone());
prev_close_price = element.0.close_price; prev_close_price = element.0.close_price;
prev_final_upperband = final_upperband; prev_final_upperband = final_upperband;
prev_final_lowerband = final_lowerband; prev_final_lowerband = final_lowerband;
prev_trend = trend; prev_trend = trend;
} }
let mut supertrend_data_wrapper_lock = supertrend_data_wrapper_arc_c.lock().await; let mut supertrend_data_wrapper_lock =
supertrend_data_wrapper_arc_c.lock().await;
supertrend_data_wrapper_lock.insert(symbol_c, supertrend_vec.clone()); supertrend_data_wrapper_lock.insert(symbol_c, supertrend_vec.clone());
})); }));
} }

View File

@ -1,15 +1,15 @@
#![allow(unused)] #![allow(unused)]
#![allow(warnings)] #![allow(warnings)]
use super::ema::{ema, EmaData};
use super::FilteredDataValue;
use crate::database_control::*; use crate::database_control::*;
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData; use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use futures::future::try_join_all; use futures::future::try_join_all;
use serde::Deserialize; use serde::Deserialize;
use sqlx::FromRow; use sqlx::FromRow;
use std::collections::HashMap;
use std::{iter::zip, sync::Arc}; use std::{iter::zip, sync::Arc};
use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*}; use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*};
use super::FilteredDataValue;
use std::collections::HashMap;
use super::ema::{ema, EmaData};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct TemaData { pub struct TemaData {
@ -81,7 +81,6 @@ pub async fn tema(
ema_data_wrapper_lock.insert(symbol_c, ema_data_vec.clone()); ema_data_wrapper_lock.insert(symbol_c, ema_data_vec.clone());
} }
})); }));
} }
try_join_all(task_vec).await?; try_join_all(task_vec).await?;
let e2 = ema_data_wrapper_arc.lock().await.to_owned(); let e2 = ema_data_wrapper_arc.lock().await.to_owned();
@ -126,7 +125,6 @@ pub async fn tema(
ema_data_wrapper_lock.insert(symbol_c, ema_data_vec.clone()); ema_data_wrapper_lock.insert(symbol_c, ema_data_vec.clone());
} }
})); }));
} }
try_join_all(task_vec).await?; try_join_all(task_vec).await?;
let e3 = ema_data_wrapper_arc.lock().await.to_owned(); let e3 = ema_data_wrapper_arc.lock().await.to_owned();
@ -136,25 +134,29 @@ pub async fn tema(
let mut task_vec = Vec::new(); let mut task_vec = Vec::new();
for (symbol, e3_vec) in e3 { for (symbol, e3_vec) in e3 {
if let (Some(e1_vec), Some(e2_vec)) = (e1.get(&symbol), e2.get(&symbol)) { if let (Some(e1_vec), Some(e2_vec)) = (e1.get(&symbol), e2.get(&symbol)) {
let tema_data_wrapper_arc_c: Arc<Mutex<HashMap<String, Vec<TemaData>>>> = Arc::clone(&tema_data_wrapper_arc); let tema_data_wrapper_arc_c: Arc<Mutex<HashMap<String, Vec<TemaData>>>> =
Arc::clone(&tema_data_wrapper_arc);
let symbol_c = symbol.clone(); let symbol_c = symbol.clone();
let e1_vec_c = e1_vec.clone(); let e1_vec_c = e1_vec.clone();
let e2_vec_c = e2_vec.clone(); let e2_vec_c = e2_vec.clone();
task_vec.push(tokio::spawn(async move { task_vec.push(tokio::spawn(async move {
if e3_vec.last().unwrap().close_time == e1_vec_c.last().unwrap().close_time && if e3_vec.last().unwrap().close_time == e1_vec_c.last().unwrap().close_time
e3_vec.last().unwrap().close_time == e2_vec_c.last().unwrap().close_time && && e3_vec.last().unwrap().close_time == e2_vec_c.last().unwrap().close_time
e3_vec.len() < e1_vec_c.len() && && e3_vec.len() < e1_vec_c.len()
e3_vec.len() < e2_vec_c.len() && && e3_vec.len() < e2_vec_c.len()
e2_vec_c.len() < e1_vec_c.len() { && e2_vec_c.len() < e1_vec_c.len()
{
let mut tema_data_vec: Vec<TemaData> = Vec::new(); let mut tema_data_vec: Vec<TemaData> = Vec::new();
let e1_vec_part = e1_vec_c.get(e1_vec_c.len()-e3_vec.len()..).unwrap(); let e1_vec_part = e1_vec_c.get(e1_vec_c.len() - e3_vec.len()..).unwrap();
let e2_vec_part = e2_vec_c.get(e2_vec_c.len()-e3_vec.len()..).unwrap(); let e2_vec_part = e2_vec_c.get(e2_vec_c.len() - e3_vec.len()..).unwrap();
let zipped_e1_e2 = e1_vec_part.iter().zip(e2_vec_part.iter()); let zipped_e1_e2 = e1_vec_part.iter().zip(e2_vec_part.iter());
let zipped_e1_e2_e3 = zipped_e1_e2.zip(e3_vec.iter()); let zipped_e1_e2_e3 = zipped_e1_e2.zip(e3_vec.iter());
for element in zipped_e1_e2_e3 { for element in zipped_e1_e2_e3 {
let mut tema_data = TemaData::new(); let mut tema_data = TemaData::new();
tema_data.close_time = element.0.0.close_time; tema_data.close_time = element.0 .0.close_time;
tema_data.tema_value = (3.0 * (element.0.0.ema_value - element.0.1.ema_value)) + element.1.ema_value; tema_data.tema_value = (3.0
* (element.0 .0.ema_value - element.0 .1.ema_value))
+ element.1.ema_value;
tema_data_vec.push(tema_data); tema_data_vec.push(tema_data);
} }
let mut dema_data_wrapper_lock = tema_data_wrapper_arc_c.lock().await; let mut dema_data_wrapper_lock = tema_data_wrapper_arc_c.lock().await;