Fork from tradingbot_sandbox
This commit is contained in:
commit
0c4e8ada55
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/target
|
||||||
2803
Cargo.lock
generated
Normal file
2803
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
24
Cargo.toml
Normal file
24
Cargo.toml
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[package]
|
||||||
|
name = "tradingbot"
|
||||||
|
version = "0.10.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
reqwest = { version = "0.11.4", features = ["json"]}
|
||||||
|
serde = { version ="1.0.126", features = ["derive"] }
|
||||||
|
serde_json = "1.0.64"
|
||||||
|
hmac-sha256 = "0.1.7"
|
||||||
|
tokio = { version = "1.28.2", features = ["full"] }
|
||||||
|
futures = "0.3.17"
|
||||||
|
chrono = "0.4.19"
|
||||||
|
hex = "0.4.3"
|
||||||
|
rand = { version = "0.8.4", features = ["getrandom"]}
|
||||||
|
sqlx = { version = "0.6.3", features = ["runtime-tokio-native-tls", "mysql", "macros", "migrate", "decimal", "chrono"] }
|
||||||
|
csv = "1.1.6"
|
||||||
|
thirtyfour = { version = "0.28.1", features = ["tokio-runtime"]}
|
||||||
|
ord_subset = "3.1.1"
|
||||||
|
rust_decimal = "1.29.1"
|
||||||
|
rust_decimal_macros = "1.29.1"
|
||||||
|
plotters = "0.3.1"
|
||||||
4
src/coex.rs
Normal file
4
src/coex.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
pub mod assets_managing_team;
|
||||||
|
pub mod exchange_team;
|
||||||
|
pub mod order_team;
|
||||||
|
pub mod strategy_team;
|
||||||
427
src/coex/assets_managing_team.rs
Normal file
427
src/coex/assets_managing_team.rs
Normal file
|
|
@ -0,0 +1,427 @@
|
||||||
|
// assets_managing_team manages [asset_manage_announcement] and [wallet] ([wallet_testnet] as well)
|
||||||
|
|
||||||
|
use crate::coex::exchange_team::*;
|
||||||
|
use crate::database_control::*;
|
||||||
|
use crate::decimal_funcs::*;
|
||||||
|
use crate::RunningMode::*;
|
||||||
|
use crate::RUNNING_MODE;
|
||||||
|
use reqwest::Client;
|
||||||
|
use rust_decimal::{prelude::ToPrimitive, Decimal, RoundingStrategy};
|
||||||
|
use rust_decimal_macros::dec;
|
||||||
|
use serde_json::{Result, Value};
|
||||||
|
use sqlx::FromRow;
|
||||||
|
|
||||||
|
#[derive(Debug, FromRow)]
|
||||||
|
pub struct AssetInfo {
|
||||||
|
pub initial_usdt: Decimal,
|
||||||
|
pub current_total_usdt: Decimal,
|
||||||
|
pub profit: Decimal,
|
||||||
|
pub realtime_expected_total_usdt: f64,
|
||||||
|
pub realtime_profit: f64,
|
||||||
|
pub available_usdt: Decimal,
|
||||||
|
pub is_tradable: u8,
|
||||||
|
pub unit_trade_usdt: Decimal,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, FromRow)]
|
||||||
|
pub struct WalletInfo {
|
||||||
|
pub asset: String,
|
||||||
|
pub free: Decimal,
|
||||||
|
pub locked: Decimal,
|
||||||
|
}
|
||||||
|
|
||||||
|
// set unit_trade_usdt
|
||||||
|
pub async fn set_unit_usdt() {
|
||||||
|
let asset_info = select_asset_manage_announcement().await;
|
||||||
|
let mut set_unit_trade_usdt = Decimal::new(0, 8);
|
||||||
|
|
||||||
|
// define protect_rate and unit_trade_usdt as high as total_usdt_amount
|
||||||
|
if dec!(0.0) <= asset_info.current_total_usdt && asset_info.current_total_usdt < dec!(500.0) {
|
||||||
|
set_unit_trade_usdt = dec!(100.0); // $100 for each trade
|
||||||
|
// set_unit_trade_usdt = decimal_mul(asset_info.current_total_usdt, dec!(0.8)); // 80% of total usdt
|
||||||
|
} else if dec!(500.0) <= asset_info.current_total_usdt
|
||||||
|
&& asset_info.current_total_usdt < dec!(2000.0)
|
||||||
|
{
|
||||||
|
// set_unit_trade_usdt = dec!(150.0); // $150 for each trade
|
||||||
|
set_unit_trade_usdt = decimal_mul(asset_info.current_total_usdt, dec!(0.1)); // 10% of total usdt
|
||||||
|
} else if dec!(2000.0) <= asset_info.current_total_usdt
|
||||||
|
&& asset_info.current_total_usdt < dec!(5000.0)
|
||||||
|
{
|
||||||
|
set_unit_trade_usdt = dec!(200.0); // $200 for each trade
|
||||||
|
} else if dec!(5000.0) <= asset_info.current_total_usdt
|
||||||
|
&& asset_info.current_total_usdt < dec!(10000.0)
|
||||||
|
{
|
||||||
|
set_unit_trade_usdt = dec!(300.0); // $300 for each trade
|
||||||
|
} else {
|
||||||
|
set_unit_trade_usdt = dec!(500.0); // $500 for each trade
|
||||||
|
}
|
||||||
|
|
||||||
|
// update fields in [asset_manage_announcement] table
|
||||||
|
let update_table_name = String::from("asset_manage_announcement");
|
||||||
|
let update_values = vec![(
|
||||||
|
String::from("unit_trade_usdt"),
|
||||||
|
set_unit_trade_usdt.to_string(),
|
||||||
|
)];
|
||||||
|
let update_condition = vec![(String::from("id"), String::from("1"))];
|
||||||
|
update_record3(&update_table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// add extra usdt to initial usdt
|
||||||
|
pub async fn add_extra_usdt(extra_usdt: Decimal) {
|
||||||
|
// update fields in [asset_manage_announcement] table
|
||||||
|
let update_table_name = String::from("asset_manage_announcement");
|
||||||
|
let mut value_build = String::from("initial_usdt + ");
|
||||||
|
value_build.push_str(
|
||||||
|
extra_usdt
|
||||||
|
.round_dp_with_strategy(8, RoundingStrategy::ToZero)
|
||||||
|
.to_string()
|
||||||
|
.as_str(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let update_values = vec![(String::from("initial_usdt"), value_build)];
|
||||||
|
let update_condition = vec![(String::from("id"), String::from("1"))];
|
||||||
|
update_record3(&update_table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut value_build = String::from("current_total_usdt + ");
|
||||||
|
value_build.push_str(
|
||||||
|
extra_usdt
|
||||||
|
.round_dp_with_strategy(8, RoundingStrategy::ToZero)
|
||||||
|
.to_string()
|
||||||
|
.as_str(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let update_values = vec![(String::from("current_total_usdt"), value_build)];
|
||||||
|
let update_condition = vec![(String::from("id"), String::from("1"))];
|
||||||
|
update_record3(&update_table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
add_available_usdt(extra_usdt.round_dp_with_strategy(8, RoundingStrategy::ToZero)).await;
|
||||||
|
|
||||||
|
// add additional_usdt into [wallet_simul]
|
||||||
|
let mut update_table_name = String::new();
|
||||||
|
if RUNNING_MODE == SIMUL {
|
||||||
|
let update_table_name = String::from("wallet");
|
||||||
|
let mut value_build = String::from("free + ");
|
||||||
|
value_build.push_str(extra_usdt.to_string().as_str());
|
||||||
|
let update_values = vec![(String::from("free"), value_build)];
|
||||||
|
let update_condition = vec![(String::from("asset"), String::from("USDT"))];
|
||||||
|
update_record3(&update_table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("add_extra_usdt 완료");
|
||||||
|
}
|
||||||
|
|
||||||
|
// update current_total_usdt and profit of asset
|
||||||
|
pub async fn update_current_total_usdt() {
|
||||||
|
let mut update_values: Vec<(String, String)> = Vec::new();
|
||||||
|
if RUNNING_MODE == REAL || RUNNING_MODE == TEST {
|
||||||
|
let free_usdt = fetch_free_usdt().await;
|
||||||
|
let initial_usdt = select_asset_manage_announcement().await.initial_usdt;
|
||||||
|
// println!("update_current_total_usdt실행 free_usdt: {}, initial_usdt: {}", free_usdt, initial_usdt);
|
||||||
|
let mut profit: Decimal;
|
||||||
|
if initial_usdt.is_zero() {
|
||||||
|
profit = dec!(0.0);
|
||||||
|
} else {
|
||||||
|
profit = decimal_sub(decimal_div(free_usdt, initial_usdt), dec!(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
update_values = vec![
|
||||||
|
(String::from("current_total_usdt"), free_usdt.to_string()),
|
||||||
|
(String::from("profit"), profit.to_string()),
|
||||||
|
(
|
||||||
|
String::from("realtime_expected_total_usdt"),
|
||||||
|
0.0.to_string(),
|
||||||
|
),
|
||||||
|
(String::from("realtime_profit"), 0.0.to_string()),
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
let asset_info = select_asset_manage_announcement().await;
|
||||||
|
let mut profit: Decimal;
|
||||||
|
if asset_info.initial_usdt.is_zero() {
|
||||||
|
profit = dec!(0.0);
|
||||||
|
} else {
|
||||||
|
profit = decimal_sub(
|
||||||
|
decimal_div(asset_info.current_total_usdt, asset_info.initial_usdt),
|
||||||
|
dec!(1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
update_values = vec![
|
||||||
|
(
|
||||||
|
String::from("current_total_usdt"),
|
||||||
|
asset_info.available_usdt.to_string(),
|
||||||
|
),
|
||||||
|
(String::from("profit"), profit.to_string()),
|
||||||
|
(
|
||||||
|
String::from("realtime_expected_total_usdt"),
|
||||||
|
0.0.to_string(),
|
||||||
|
),
|
||||||
|
(String::from("realtime_profit"), 0.0.to_string()),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
let update_table_name = String::from("asset_manage_announcement");
|
||||||
|
let update_condition = vec![(String::from("id"), String::from("1"))];
|
||||||
|
update_record3(&update_table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
set_unit_usdt().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// monitor whether [buy_ordered_coin_list] is empty to update current_total_usdt and available_usdt in [asset_manage_announcement]
|
||||||
|
pub async fn monitoring_asset_usdt(previous_result: &mut bool, client: &Client) -> Result<()> {
|
||||||
|
let table_name = String::from("buy_ordered_coin_list");
|
||||||
|
let condition = None;
|
||||||
|
let buy_list_result = exists_record(&table_name, &condition).await;
|
||||||
|
|
||||||
|
let table_name = String::from("sell_ordered_coin_list");
|
||||||
|
let condition = None;
|
||||||
|
let sell_list_result = exists_record(&table_name, &condition).await;
|
||||||
|
|
||||||
|
if buy_list_result == false && sell_list_result == false {
|
||||||
|
if buy_list_result == false
|
||||||
|
&& sell_list_result == false
|
||||||
|
&& *previous_result == true
|
||||||
|
&& (RUNNING_MODE == TEST || RUNNING_MODE == REAL)
|
||||||
|
{
|
||||||
|
let mut result = crate::coex::order_team::account_information(&client).await; // (/api, Weight(IP) 10)
|
||||||
|
loop {
|
||||||
|
if result.is_ok() {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
println!("retry account_information()");
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
|
||||||
|
result = crate::coex::order_team::account_information(&client).await;
|
||||||
|
// (/api, Weight(IP) 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update_current_total_usdt().await;
|
||||||
|
set_available_usdt().await;
|
||||||
|
} else if buy_list_result == false
|
||||||
|
&& sell_list_result == false
|
||||||
|
&& *previous_result == true
|
||||||
|
&& (RUNNING_MODE == SIMUL)
|
||||||
|
{
|
||||||
|
update_current_total_usdt().await;
|
||||||
|
set_is_tradable().await;
|
||||||
|
}
|
||||||
|
*previous_result = false;
|
||||||
|
} else {
|
||||||
|
update_realtime_asset().await;
|
||||||
|
*previous_result = true;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// update realtime_expected_total_usdt and realtime_profit
|
||||||
|
pub async fn update_realtime_asset() {
|
||||||
|
let asset_info = select_asset_manage_announcement().await;
|
||||||
|
let scoreboard_list = select_scoreboard().await;
|
||||||
|
let scoreboard_short = scoreboard_list.first().unwrap();
|
||||||
|
let scoreboard_long = scoreboard_list.last().unwrap();
|
||||||
|
|
||||||
|
if scoreboard_short.total_number_of_coin != 0 || scoreboard_long.total_number_of_coin != 0 {
|
||||||
|
// calculate expect_realtime_asset
|
||||||
|
let available_usdt = asset_info.available_usdt.to_f64().unwrap();
|
||||||
|
let total_used_usdt = scoreboard_short.total_used_usdt + scoreboard_long.total_used_usdt;
|
||||||
|
let current_pos_profit_usdt =
|
||||||
|
scoreboard_short.total_pos_profit_usdt + scoreboard_long.total_pos_profit_usdt;
|
||||||
|
let current_neg_profit_usdt =
|
||||||
|
scoreboard_short.total_neg_profit_usdt + scoreboard_long.total_neg_profit_usdt;
|
||||||
|
let expect_realtime_expected_total_usdt =
|
||||||
|
available_usdt + total_used_usdt + current_pos_profit_usdt + current_neg_profit_usdt;
|
||||||
|
|
||||||
|
// calculate realtime_profit
|
||||||
|
let mut realtime_profit = 0.0;
|
||||||
|
let current_total_usdt = asset_info.current_total_usdt;
|
||||||
|
if current_total_usdt.is_zero() {
|
||||||
|
realtime_profit = 0.0;
|
||||||
|
} else {
|
||||||
|
realtime_profit = ((expect_realtime_expected_total_usdt
|
||||||
|
/ current_total_usdt.to_f64().unwrap())
|
||||||
|
- 1.0)
|
||||||
|
* 100.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update record
|
||||||
|
let update_table_name = String::from("asset_manage_announcement");
|
||||||
|
let update_values = vec![
|
||||||
|
(
|
||||||
|
String::from("realtime_expected_total_usdt"),
|
||||||
|
expect_realtime_expected_total_usdt.to_string(),
|
||||||
|
),
|
||||||
|
(String::from("realtime_profit"), realtime_profit.to_string()),
|
||||||
|
];
|
||||||
|
let update_condition = vec![(String::from("id"), String::from("1"))];
|
||||||
|
update_record3(&update_table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set available_usdt
|
||||||
|
pub async fn set_available_usdt() {
|
||||||
|
let update_table_name = String::from("asset_manage_announcement");
|
||||||
|
// fetch current_total_usdt
|
||||||
|
let mut asset_info = select_asset_manage_announcement().await;
|
||||||
|
|
||||||
|
let available_usdt = asset_info.current_total_usdt;
|
||||||
|
let unit_trade_usdt = asset_info.unit_trade_usdt;
|
||||||
|
|
||||||
|
// set available_usdt with current_total_usdt and update is_tradable
|
||||||
|
let update_value = {
|
||||||
|
if available_usdt > unit_trade_usdt {
|
||||||
|
vec![
|
||||||
|
(String::from("available_usdt"), available_usdt.to_string()),
|
||||||
|
(String::from("is_tradable"), (String::from("1"))),
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
vec![
|
||||||
|
(String::from("available_usdt"), available_usdt.to_string()),
|
||||||
|
(String::from("is_tradable"), (String::from("0"))),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let update_condition = vec![(String::from("id"), String::from("1"))];
|
||||||
|
update_record3(&update_table_name, &update_value, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch available_usdt
|
||||||
|
pub async fn fetch_available_usdt() -> Decimal {
|
||||||
|
let update_table_name = String::from("asset_manage_announcement");
|
||||||
|
let mut asset_info = select_asset_manage_announcement().await;
|
||||||
|
asset_info.available_usdt
|
||||||
|
}
|
||||||
|
|
||||||
|
// add available_usdt
|
||||||
|
pub async fn add_available_usdt(add_usdt: Decimal) {
|
||||||
|
let update_table_name = String::from("asset_manage_announcement");
|
||||||
|
let mut value_build = String::from("available_usdt + ");
|
||||||
|
value_build.push_str(add_usdt.to_string().as_str());
|
||||||
|
|
||||||
|
let update_values = vec![(String::from("available_usdt"), value_build)];
|
||||||
|
let update_condition = vec![(String::from("id"), String::from("1"))];
|
||||||
|
update_record3(&update_table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// subtract available_usdt
|
||||||
|
pub async fn sub_available_usdt(sub_usdt: Decimal) {
|
||||||
|
let update_table_name = String::from("asset_manage_announcement");
|
||||||
|
let mut value_build = String::from("available_usdt - ");
|
||||||
|
value_build.push_str(sub_usdt.to_string().as_str());
|
||||||
|
|
||||||
|
let update_values = vec![(String::from("available_usdt"), value_build)];
|
||||||
|
let update_condition = vec![(String::from("id"), String::from("1"))];
|
||||||
|
update_record3(&update_table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// set is_tradable as available_usdt
|
||||||
|
pub async fn set_is_tradable() {
|
||||||
|
let update_table_name = String::from("asset_manage_announcement");
|
||||||
|
let mut asset_info = select_asset_manage_announcement().await;
|
||||||
|
let unit_trade_usdt = asset_info.unit_trade_usdt;
|
||||||
|
let available_usdt = asset_info.available_usdt;
|
||||||
|
|
||||||
|
let update_value = {
|
||||||
|
if available_usdt > unit_trade_usdt {
|
||||||
|
vec![(String::from("is_tradable"), (String::from("1")))]
|
||||||
|
} else {
|
||||||
|
vec![(String::from("is_tradable"), (String::from("0")))]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let update_condition = vec![(String::from("id"), String::from("1"))];
|
||||||
|
update_record3(&update_table_name, &update_value, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// check it's tradable or not
|
||||||
|
pub async fn check_is_tradable() -> bool {
|
||||||
|
let asset_info = select_asset_manage_announcement().await;
|
||||||
|
let is_tradable = asset_info.is_tradable;
|
||||||
|
|
||||||
|
if asset_info.is_tradable == 1 {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn fetch_unit_trade_usdt() -> Decimal {
|
||||||
|
let asset_info = select_asset_manage_announcement().await;
|
||||||
|
asset_info.unit_trade_usdt
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn select_asset_manage_announcement() -> AssetInfo {
|
||||||
|
let mut asset_info = AssetInfo {
|
||||||
|
initial_usdt: Decimal::new(0, 8),
|
||||||
|
current_total_usdt: Decimal::new(0, 8),
|
||||||
|
profit: Decimal::new(0, 8),
|
||||||
|
realtime_expected_total_usdt: 0.0,
|
||||||
|
realtime_profit: 0.0,
|
||||||
|
available_usdt: Decimal::new(0, 8),
|
||||||
|
is_tradable: 0,
|
||||||
|
unit_trade_usdt: Decimal::new(0, 8),
|
||||||
|
};
|
||||||
|
|
||||||
|
let table_name = String::from("asset_manage_announcement");
|
||||||
|
let columns = String::from("*");
|
||||||
|
let condition = None;
|
||||||
|
|
||||||
|
let select_result = select_record(&table_name, &columns, &condition, &asset_info)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let result_vec = select_result.first().unwrap();
|
||||||
|
|
||||||
|
asset_info.initial_usdt = result_vec.initial_usdt;
|
||||||
|
asset_info.current_total_usdt = result_vec.current_total_usdt;
|
||||||
|
asset_info.profit = result_vec.profit;
|
||||||
|
asset_info.realtime_expected_total_usdt = result_vec.realtime_expected_total_usdt;
|
||||||
|
asset_info.realtime_profit = result_vec.realtime_profit;
|
||||||
|
asset_info.available_usdt = result_vec.available_usdt;
|
||||||
|
asset_info.is_tradable = result_vec.is_tradable;
|
||||||
|
asset_info.unit_trade_usdt = result_vec.unit_trade_usdt;
|
||||||
|
|
||||||
|
asset_info
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch free usdt from wallet
|
||||||
|
pub async fn fetch_free_usdt() -> Decimal {
|
||||||
|
let wallet_info = WalletInfo {
|
||||||
|
asset: String::new(),
|
||||||
|
free: Decimal::new(0, 8),
|
||||||
|
locked: Decimal::new(0, 8),
|
||||||
|
};
|
||||||
|
let select_table_name = {
|
||||||
|
if RUNNING_MODE == SIMUL || RUNNING_MODE == REAL {
|
||||||
|
String::from("wallet")
|
||||||
|
} else {
|
||||||
|
String::from("wallet_testnet")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let select_columns_name = String::from("*");
|
||||||
|
let condition = Some(String::from("WHERE asset = 'USDT'"));
|
||||||
|
|
||||||
|
let mut select_result = select_record(
|
||||||
|
&select_table_name,
|
||||||
|
&select_columns_name,
|
||||||
|
&condition,
|
||||||
|
&wallet_info,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
select_result.first().unwrap().free
|
||||||
|
}
|
||||||
1173
src/coex/exchange_team.rs
Normal file
1173
src/coex/exchange_team.rs
Normal file
File diff suppressed because it is too large
Load Diff
2689
src/coex/order_team.rs
Normal file
2689
src/coex/order_team.rs
Normal file
File diff suppressed because it is too large
Load Diff
6841
src/coex/strategy_team.rs
Normal file
6841
src/coex/strategy_team.rs
Normal file
File diff suppressed because it is too large
Load Diff
3
src/coin_health_check_team.rs
Normal file
3
src/coin_health_check_team.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod monitors;
|
||||||
|
pub mod request_candles;
|
||||||
|
pub mod request_others;
|
||||||
598
src/coin_health_check_team/monitors.rs
Normal file
598
src/coin_health_check_team/monitors.rs
Normal file
|
|
@ -0,0 +1,598 @@
|
||||||
|
use crate::coin_health_check_team::request_candles::*;
|
||||||
|
use crate::RunningMode::*;
|
||||||
|
use crate::{database_control::*, RUNNING_MODE};
|
||||||
|
use hex::ToHex;
|
||||||
|
use hmac_sha256::HMAC;
|
||||||
|
use reqwest::{Client, ClientBuilder, Response};
|
||||||
|
use rust_decimal::{prelude::ToPrimitive, Decimal};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_json::Value;
|
||||||
|
use sqlx::{Error, FromRow};
|
||||||
|
use std::borrow::{Borrow, BorrowMut};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::{join, sync::Mutex, time::*};
|
||||||
|
|
||||||
|
#[derive(Debug, FromRow)]
|
||||||
|
struct AllCoinProfitChangeAvgList {
|
||||||
|
server_epoch: u64,
|
||||||
|
cnt: u32,
|
||||||
|
avg_profit: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter valid USDT trades from all24hstatistics table in database
|
||||||
|
pub async fn collect_valid_usde_trade(
|
||||||
|
valid_usdt_trade_vec: &mut Vec<String>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
#[derive(Debug, FromRow)]
|
||||||
|
struct UsdtTrades {
|
||||||
|
symbol: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
// if RUNNING_MODE == SIMUL { // || RUNNING_MODE == REAL {
|
||||||
|
// let mut usdt_trades = UsdtTrades { symbol: String::new() };
|
||||||
|
|
||||||
|
// let fetch_table_name = String::from("all_24h_change");
|
||||||
|
// let column_name = String::from("symbol");
|
||||||
|
// let mut condition_build = String::from("WHERE symbol LIKE '%BUSD' AND symbol NOT LIKE '%DOWNUSDT'");
|
||||||
|
// condition_build.push_str(" AND symbol NOT LIKE '%UPUSDT' AND firstId >= 0 AND lastId >= 0");
|
||||||
|
// condition_build.push_str(" AND symbol NOT IN (SELECT symbol FROM stop_usdt_trades)");
|
||||||
|
// // add unnessesary coins
|
||||||
|
// condition_build.push_str(" AND symbol NOT LIKE 'BUSDUSDT'");
|
||||||
|
// condition_build.push_str(" AND symbol NOT LIKE 'TUSDUSDT'");
|
||||||
|
// condition_build.push_str(" AND symbol NOT LIKE 'USDPUSDT'");
|
||||||
|
// condition_build.push_str(" AND symbol NOT LIKE 'SUSDUSDT'");
|
||||||
|
// condition_build.push_str(" AND symbol NOT LIKE 'AUDUSDT'");
|
||||||
|
// condition_build.push_str(" AND symbol NOT LIKE 'EURUSDT'");
|
||||||
|
// condition_build.push_str(" AND symbol NOT LIKE 'GBPUSDT'");
|
||||||
|
// condition_build.push_str(" AND symbol NOT LIKE 'USDCUSDT'");
|
||||||
|
// condition_build.push_str(" AND symbol NOT LIKE 'BZRXUSDT'");
|
||||||
|
// condition_build.push_str(" AND symbol NOT LIKE 'USTUSDT'");
|
||||||
|
// condition_build.push_str(" AND symbol NOT LIKE 'NBTUSDT'");
|
||||||
|
// condition_build.push_str(" AND symbol NOT LIKE 'VGXUSDT'");
|
||||||
|
// condition_build.push_str(" AND symbol NOT LIKE 'RAMPUSDT'");
|
||||||
|
// condition_build.push_str(" AND symbol NOT LIKE 'TORNUSDT'");
|
||||||
|
// condition_build.push_str(" AND symbol NOT LIKE 'BTTCUSDT'");
|
||||||
|
// condition_build.push_str(" AND symbol NOT LIKE 'BTCSTUSDT'");
|
||||||
|
// condition_build.push_str(" AND symbol NOT LIKE 'ACAUSDT'");
|
||||||
|
// condition_build.push_str(" AND symbol NOT LIKE 'ANCUSDT'");
|
||||||
|
// let condition = Some(condition_build);
|
||||||
|
|
||||||
|
// let select_result = select_record(&fetch_table_name, &column_name, &condition, &usdt_trades).await?;
|
||||||
|
|
||||||
|
// let table_name = String::from("valid_usdt_trades");
|
||||||
|
// let columns = vec!["symbol"];
|
||||||
|
// let mut symbol_vec: Vec<String> = Vec::new();
|
||||||
|
// let mut value_wrapper: Vec<Vec<String>> = Vec::new();
|
||||||
|
// for element in select_result {
|
||||||
|
// let mut inner_vec: Vec<String> = Vec::new();
|
||||||
|
// inner_vec.push(element.symbol.clone());
|
||||||
|
// value_wrapper.push(inner_vec);
|
||||||
|
// symbol_vec.push(element.symbol);
|
||||||
|
// }
|
||||||
|
// *valid_usdt_trade_vec = symbol_vec;
|
||||||
|
// delete_all_rows(&table_name).await?;
|
||||||
|
// insert_records(&table_name, &columns, &value_wrapper).await?;
|
||||||
|
// } else
|
||||||
|
if RUNNING_MODE == TEST || RUNNING_MODE == REAL || RUNNING_MODE == SIMUL {
|
||||||
|
let table_name = String::from("valid_usdt_trades");
|
||||||
|
let columns = vec!["symbol"];
|
||||||
|
let mut symbol_vec: Vec<String> = Vec::new();
|
||||||
|
let mut value_wrapper: Vec<Vec<String>> = Vec::new();
|
||||||
|
|
||||||
|
value_wrapper.push(vec![String::from("BTCUSDT")]);
|
||||||
|
value_wrapper.push(vec![String::from("ETHUSDT")]);
|
||||||
|
value_wrapper.push(vec![String::from("XRPUSDT")]);
|
||||||
|
value_wrapper.push(vec![String::from("DOGEUSDT")]);
|
||||||
|
value_wrapper.push(vec![String::from("LTCUSDT")]);
|
||||||
|
value_wrapper.push(vec![String::from("TRXUSDT")]);
|
||||||
|
value_wrapper.push(vec![String::from("DOTUSDT")]);
|
||||||
|
value_wrapper.push(vec![String::from("LINKUSDT")]);
|
||||||
|
value_wrapper.push(vec![String::from("AVAXUSDT")]);
|
||||||
|
value_wrapper.push(vec![String::from("BCHUSDT")]);
|
||||||
|
value_wrapper.push(vec![String::from("SHIBUSDT")]);
|
||||||
|
value_wrapper.push(vec![String::from("APTUSDT")]);
|
||||||
|
value_wrapper.push(vec![String::from("ARBUSDT")]);
|
||||||
|
value_wrapper.push(vec![String::from("ETCUSDT")]);
|
||||||
|
value_wrapper.push(vec![String::from("XMRUSDT")]);
|
||||||
|
value_wrapper.push(vec![String::from("HBARUSDT")]);
|
||||||
|
value_wrapper.push(vec![String::from("UNIUSDT")]);
|
||||||
|
|
||||||
|
symbol_vec.push(String::from("BTCUSDT"));
|
||||||
|
symbol_vec.push(String::from("ETHUSDT"));
|
||||||
|
symbol_vec.push(String::from("XRPUSDT"));
|
||||||
|
symbol_vec.push(String::from("DOGEUSDT"));
|
||||||
|
symbol_vec.push(String::from("LTCUSDT"));
|
||||||
|
symbol_vec.push(String::from("TRXUSDT"));
|
||||||
|
symbol_vec.push(String::from("DOTUSDT"));
|
||||||
|
symbol_vec.push(String::from("LINKUSDT"));
|
||||||
|
symbol_vec.push(String::from("AVAXUSDT"));
|
||||||
|
symbol_vec.push(String::from("BCHUSDT"));
|
||||||
|
symbol_vec.push(String::from("SHIBUSDT"));
|
||||||
|
symbol_vec.push(String::from("APTUSDT"));
|
||||||
|
symbol_vec.push(String::from("ARBUSDT"));
|
||||||
|
symbol_vec.push(String::from("ETCUSDT"));
|
||||||
|
symbol_vec.push(String::from("XMRUSDT"));
|
||||||
|
symbol_vec.push(String::from("HBARUSDT"));
|
||||||
|
symbol_vec.push(String::from("UNIUSDT"));
|
||||||
|
|
||||||
|
*valid_usdt_trade_vec = symbol_vec;
|
||||||
|
delete_all_rows(&table_name).await?;
|
||||||
|
insert_records(&table_name, &columns, &value_wrapper).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// println!("valid USDT trades 완료");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// async fn detect_new_valid_usdt_trade() -> Result<(), Box<dyn std::error::Error + Send + Sync>>{
|
||||||
|
// #[derive(Debug, FromRow)]
|
||||||
|
// struct UsdtTrades {
|
||||||
|
// symbol: String,
|
||||||
|
// }
|
||||||
|
// let mut usdt_trades = UsdtTrades { symbol: String::new() };
|
||||||
|
|
||||||
|
// let table_name = String::from("valid_usdt_trades");
|
||||||
|
// let column_name = String::from("valid_usdt_trades.symbol");
|
||||||
|
// let condition = Some(String::from("LEFT OUTER JOIN prev_valid_usdt_trades ON valid_usdt_trades.symbol = prev_valid_usdt_trades.symbol WHERE prev_valid_usdt_trades.symbol is null"));
|
||||||
|
|
||||||
|
// let select_result = try_select_record(&table_name, &column_name, &condition, &usdt_trades).await.unwrap();
|
||||||
|
|
||||||
|
// if !select_result.is_empty() {
|
||||||
|
// let intervals = vec![String::from("1m"), String::from("30m"), String::from("1d"), String::from("1w"), String::from("1mon")];
|
||||||
|
// let symbol_vec: Vec<String> = select_result.into_iter().map(|element| element.symbol).collect();
|
||||||
|
// for interval in intervals{
|
||||||
|
// for element in &symbol_vec {
|
||||||
|
// create_candle_table(element, &interval).await.unwrap();
|
||||||
|
// }
|
||||||
|
// request_candlestick_initial(&symbol_vec, &interval).await.unwrap();
|
||||||
|
// }
|
||||||
|
// println!("detect_new_valid_usde_trade 완료");
|
||||||
|
// }
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
|
||||||
|
async fn detect_new_valid_usdt_trade() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
#[derive(Debug, FromRow)]
|
||||||
|
struct UsdtTrades {
|
||||||
|
symbol: String,
|
||||||
|
}
|
||||||
|
let mut usdt_trades = UsdtTrades {
|
||||||
|
symbol: String::new(),
|
||||||
|
};
|
||||||
|
let table_name = String::from("valid_usdt_trades");
|
||||||
|
let column_name = String::from("valid_usdt_trades.symbol");
|
||||||
|
let condition = Some(String::from("LEFT OUTER JOIN prev_valid_usdt_trades ON valid_usdt_trades.symbol = prev_valid_usdt_trades.symbol WHERE prev_valid_usdt_trades.symbol is null"));
|
||||||
|
let select_result =
|
||||||
|
try_select_record(&table_name, &column_name, &condition, &usdt_trades).await?;
|
||||||
|
if !select_result.is_empty() {
|
||||||
|
let intervals = vec![
|
||||||
|
String::from("1m"),
|
||||||
|
String::from("30m"),
|
||||||
|
String::from("1d"),
|
||||||
|
String::from("1w"),
|
||||||
|
String::from("1mon"),
|
||||||
|
];
|
||||||
|
for interval in intervals {
|
||||||
|
for element in &select_result {
|
||||||
|
create_candle_table(&element.symbol, &interval).await?;
|
||||||
|
request_candlestick_initial(element.symbol.clone(), &interval).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// println!("detect_new_valid_usde_trade 완료");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn deceased_usdt_trade() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
#[derive(Debug, FromRow)]
|
||||||
|
struct UsdtTrades {
|
||||||
|
symbol: String,
|
||||||
|
}
|
||||||
|
let mut usdt_trades = UsdtTrades {
|
||||||
|
symbol: String::new(),
|
||||||
|
};
|
||||||
|
let insert_table_name = String::from("deceased_usdt_trades");
|
||||||
|
let table_name = String::from("valid_usdt_trades");
|
||||||
|
let column_name = String::from("prev_valid_usdt_trades.symbol");
|
||||||
|
let condition = Some(String::from("RIGHT OUTER JOIN prev_valid_usdt_trades ON valid_usdt_trades.symbol = prev_valid_usdt_trades.symbol WHERE valid_usdt_trades.symbol is null"));
|
||||||
|
|
||||||
|
let select_result =
|
||||||
|
try_select_record(&table_name, &column_name, &condition, &usdt_trades).await?;
|
||||||
|
|
||||||
|
if !select_result.is_empty() {
|
||||||
|
let columns = vec!["symbol"];
|
||||||
|
let mut value_wrapper: Vec<Vec<String>> = Vec::new();
|
||||||
|
for element in select_result {
|
||||||
|
let mut inner_vec: Vec<String> = Vec::new();
|
||||||
|
inner_vec.push(element.symbol);
|
||||||
|
value_wrapper.push(inner_vec);
|
||||||
|
}
|
||||||
|
insert_records(&insert_table_name, &columns, &value_wrapper).await?;
|
||||||
|
// println!("deceased_usde_trade 완료");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn total_24h_change_profit_index() -> Result<(), Box<dyn std::error::Error + Send + Sync>>
|
||||||
|
{
|
||||||
|
#[derive(Debug, FromRow)]
|
||||||
|
struct TradesProfit {
|
||||||
|
pricechangepercent: f64,
|
||||||
|
}
|
||||||
|
let mut trades_profit = TradesProfit {
|
||||||
|
pricechangepercent: 0.0,
|
||||||
|
};
|
||||||
|
let table_name = String::from("all_24h_change");
|
||||||
|
let column_name = String::from("priceChangePercent");
|
||||||
|
let condition = Some(String::from("WHERE symbol NOT LIKE '%DOWNUSDT' AND symbol NOT LIKE '%UPUSDT' AND firstId >= 0 AND lastId >= 0"));
|
||||||
|
|
||||||
|
let select_result =
|
||||||
|
try_select_record(&table_name, &column_name, &condition, &trades_profit).await?;
|
||||||
|
|
||||||
|
let mut nwa = 0.0; // Normalized Weighted Average
|
||||||
|
let mut price_down_count = 0;
|
||||||
|
let mut pdr = 0.0; // Price Down Ratio
|
||||||
|
let mut index = 0.0;
|
||||||
|
|
||||||
|
for element in &select_result {
|
||||||
|
if element.pricechangepercent > 10.0 {
|
||||||
|
nwa += 0.0;
|
||||||
|
} else if element.pricechangepercent <= 10.0 && element.pricechangepercent > 7.0 {
|
||||||
|
nwa += 1.0;
|
||||||
|
} else if element.pricechangepercent <= 7.0 && element.pricechangepercent > 5.0 {
|
||||||
|
nwa += 2.0;
|
||||||
|
} else if element.pricechangepercent <= 5.0 && element.pricechangepercent > 3.0 {
|
||||||
|
nwa += 3.0;
|
||||||
|
} else if element.pricechangepercent <= 3.0 && element.pricechangepercent > 0.00005 {
|
||||||
|
nwa += 4.0;
|
||||||
|
} else if element.pricechangepercent <= 0.00005 && element.pricechangepercent > -0.00005 {
|
||||||
|
nwa += 5.0;
|
||||||
|
} else if element.pricechangepercent <= -0.00005 && element.pricechangepercent > -3.0 {
|
||||||
|
nwa += 6.0;
|
||||||
|
price_down_count += 1;
|
||||||
|
} else if element.pricechangepercent <= -3.0 && element.pricechangepercent > -5.0 {
|
||||||
|
nwa += 7.0;
|
||||||
|
price_down_count += 1;
|
||||||
|
} else if element.pricechangepercent <= -5.0 && element.pricechangepercent > -7.0 {
|
||||||
|
nwa += 8.0;
|
||||||
|
price_down_count += 1;
|
||||||
|
} else if element.pricechangepercent <= -7.0 && element.pricechangepercent >= -10.0 {
|
||||||
|
nwa += 9.0;
|
||||||
|
price_down_count += 1;
|
||||||
|
} else {
|
||||||
|
nwa += 10.0;
|
||||||
|
price_down_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nwa /= select_result.len() as f64;
|
||||||
|
pdr = price_down_count as f64 / select_result.len() as f64;
|
||||||
|
index = ((nwa * 100.0) + (pdr * 4000.0)) / 50.0;
|
||||||
|
|
||||||
|
let update_table_name = String::from("indices");
|
||||||
|
let server_epoch = server_epoch().await;
|
||||||
|
let update_values = vec![
|
||||||
|
(
|
||||||
|
String::from("total_24h_change_profit_index"),
|
||||||
|
index.to_string(),
|
||||||
|
),
|
||||||
|
(String::from("server_epoch"), server_epoch.to_string()),
|
||||||
|
];
|
||||||
|
let update_condition = vec![(String::from("id"), String::from("1"))];
|
||||||
|
|
||||||
|
update_record2(&update_table_name, &update_values, &update_condition).await?;
|
||||||
|
// println!("total_24h_change_profit_index 완료");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn usdt_24h_change_profit_index() -> Result<(), Box<dyn std::error::Error + Send + Sync>>
|
||||||
|
{
|
||||||
|
#[derive(Debug, FromRow)]
|
||||||
|
struct TradesProfit {
|
||||||
|
pricechangepercent: f64,
|
||||||
|
}
|
||||||
|
let mut trades_profit = TradesProfit {
|
||||||
|
pricechangepercent: 0.0,
|
||||||
|
};
|
||||||
|
let table_name = String::from("all_24h_change");
|
||||||
|
let column_name = String::from("priceChangePercent");
|
||||||
|
let condition = Some(String::from("WHERE symbol LIKE '%USDT' AND symbol NOT LIKE '%DOWNUSDT' AND symbol NOT LIKE '%UPUSDT' AND firstId >= 0 AND lastId >= 0"));
|
||||||
|
|
||||||
|
let select_result =
|
||||||
|
try_select_record(&table_name, &column_name, &condition, &trades_profit).await?;
|
||||||
|
|
||||||
|
let mut nwa = 0.0; // Normalized Weighted Average
|
||||||
|
let mut price_down_count = 0;
|
||||||
|
let mut pdr = 0.0; // Price Down Ratio
|
||||||
|
let mut index = 0.0;
|
||||||
|
|
||||||
|
for element in &select_result {
|
||||||
|
if element.pricechangepercent > 10.0 {
|
||||||
|
nwa += 0.0;
|
||||||
|
} else if element.pricechangepercent <= 10.0 && element.pricechangepercent > 7.0 {
|
||||||
|
nwa += 1.0;
|
||||||
|
} else if element.pricechangepercent <= 7.0 && element.pricechangepercent > 5.0 {
|
||||||
|
nwa += 2.0;
|
||||||
|
} else if element.pricechangepercent <= 5.0 && element.pricechangepercent > 3.0 {
|
||||||
|
nwa += 3.0;
|
||||||
|
} else if element.pricechangepercent <= 3.0 && element.pricechangepercent > 0.00005 {
|
||||||
|
nwa += 4.0;
|
||||||
|
} else if element.pricechangepercent <= 0.00005 && element.pricechangepercent > -0.00005 {
|
||||||
|
nwa += 5.0;
|
||||||
|
} else if element.pricechangepercent <= -0.00005 && element.pricechangepercent > -3.0 {
|
||||||
|
nwa += 6.0;
|
||||||
|
price_down_count += 1;
|
||||||
|
} else if element.pricechangepercent <= -3.0 && element.pricechangepercent > -5.0 {
|
||||||
|
nwa += 7.0;
|
||||||
|
price_down_count += 1;
|
||||||
|
} else if element.pricechangepercent <= -5.0 && element.pricechangepercent > -7.0 {
|
||||||
|
nwa += 8.0;
|
||||||
|
price_down_count += 1;
|
||||||
|
} else if element.pricechangepercent <= -7.0 && element.pricechangepercent >= -10.0 {
|
||||||
|
nwa += 9.0;
|
||||||
|
price_down_count += 1;
|
||||||
|
} else {
|
||||||
|
nwa += 10.0;
|
||||||
|
price_down_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nwa /= select_result.len() as f64;
|
||||||
|
pdr = price_down_count as f64 / select_result.len() as f64;
|
||||||
|
index = ((nwa * 100.0) + (pdr * 4000.0)) / 50.0;
|
||||||
|
|
||||||
|
let update_table_name = String::from("indices");
|
||||||
|
let server_epoch = server_epoch().await;
|
||||||
|
let update_values = vec![
|
||||||
|
(
|
||||||
|
String::from("usdt_24h_change_profit_index"),
|
||||||
|
index.to_string(),
|
||||||
|
),
|
||||||
|
(String::from("server_epoch"), server_epoch.to_string()),
|
||||||
|
];
|
||||||
|
let update_condition = vec![(String::from("id"), String::from("1"))];
|
||||||
|
|
||||||
|
update_record2(&update_table_name, &update_values, &update_condition).await?;
|
||||||
|
// println!("usdt_24h_change_profit_index 완료");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn total_price_down_dist_index() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
#[derive(Debug, FromRow)]
|
||||||
|
struct TradesProfit {
|
||||||
|
pricechangepercent: f64,
|
||||||
|
}
|
||||||
|
let mut trades_profit = TradesProfit {
|
||||||
|
pricechangepercent: 0.0,
|
||||||
|
};
|
||||||
|
let table_name = String::from("all_24h_change");
|
||||||
|
let column_name = String::from("priceChangePercent");
|
||||||
|
let condition = Some(String::from("WHERE symbol NOT LIKE '%DOWNUSDT' AND symbol NOT LIKE '%UPUSDT' AND firstId >= 0 AND lastId >= 0"));
|
||||||
|
|
||||||
|
let select_result =
|
||||||
|
try_select_record(&table_name, &column_name, &condition, &trades_profit).await?;
|
||||||
|
|
||||||
|
let mut wa = 0.0; // Weighted Average
|
||||||
|
let mut total_coin_amount = 0;
|
||||||
|
let mut index = 0.0;
|
||||||
|
|
||||||
|
for element in &select_result {
|
||||||
|
if element.pricechangepercent <= 0.00005 && element.pricechangepercent > -0.00005 {
|
||||||
|
wa += 0.0;
|
||||||
|
total_coin_amount += 1;
|
||||||
|
} else if element.pricechangepercent <= -0.00005 && element.pricechangepercent > -3.0 {
|
||||||
|
wa += 2.0;
|
||||||
|
total_coin_amount += 1;
|
||||||
|
} else if element.pricechangepercent <= -3.0 && element.pricechangepercent > -5.0 {
|
||||||
|
wa += 4.0;
|
||||||
|
total_coin_amount += 1;
|
||||||
|
} else if element.pricechangepercent <= -5.0 && element.pricechangepercent > -7.0 {
|
||||||
|
wa += 6.0;
|
||||||
|
total_coin_amount += 1;
|
||||||
|
} else if element.pricechangepercent <= -7.0 && element.pricechangepercent > -10.0 {
|
||||||
|
wa += 8.0;
|
||||||
|
total_coin_amount += 1;
|
||||||
|
} else if element.pricechangepercent <= -10.0 {
|
||||||
|
wa += 10.0;
|
||||||
|
total_coin_amount += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wa /= total_coin_amount as f64;
|
||||||
|
|
||||||
|
index = wa * 10.0;
|
||||||
|
|
||||||
|
let update_table_name = String::from("indices");
|
||||||
|
let server_epoch = server_epoch().await;
|
||||||
|
let update_values = vec![
|
||||||
|
(
|
||||||
|
String::from("total_price_down_dist_index"),
|
||||||
|
index.to_string(),
|
||||||
|
),
|
||||||
|
(String::from("server_epoch"), server_epoch.to_string()),
|
||||||
|
];
|
||||||
|
let update_condition = vec![(String::from("id"), String::from("1"))];
|
||||||
|
|
||||||
|
update_record2(&update_table_name, &update_values, &update_condition).await?;
|
||||||
|
|
||||||
|
// println!("total_price_down_dist_index 완료");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn monitoring_all_coin_profit_change() {
|
||||||
|
// 초기화 확인
|
||||||
|
let select_table_name = String::from("all_coin_profit_change_avg");
|
||||||
|
let select_columns = String::from("*");
|
||||||
|
let select_condition = None;
|
||||||
|
let all_coin_profit_change_avg_stuct = AllCoinProfitChangeAvgList {
|
||||||
|
server_epoch: 0,
|
||||||
|
cnt: 0,
|
||||||
|
avg_profit: 0.0,
|
||||||
|
};
|
||||||
|
let all_coin_profit_change_avg_list = select_record(
|
||||||
|
&select_table_name,
|
||||||
|
&select_columns,
|
||||||
|
&select_condition,
|
||||||
|
&all_coin_profit_change_avg_stuct,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let cnt = all_coin_profit_change_avg_list.first().unwrap().cnt;
|
||||||
|
let mut avg_profit = all_coin_profit_change_avg_list.first().unwrap().avg_profit;
|
||||||
|
let count_row = count_rows(&String::from("all_coin_start_price"))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let server_epoch = server_epoch().await;
|
||||||
|
let timer: u64 = 20_000; // 20_000: 20secs
|
||||||
|
|
||||||
|
// check timer on
|
||||||
|
if server_epoch
|
||||||
|
- all_coin_profit_change_avg_list
|
||||||
|
.first()
|
||||||
|
.unwrap()
|
||||||
|
.server_epoch
|
||||||
|
> timer
|
||||||
|
{
|
||||||
|
// initialize [all_coin_start_price] table
|
||||||
|
delete_all_rows(&String::from("all_coin_start_price"))
|
||||||
|
.await
|
||||||
|
.expect("Failed to delete rows!");
|
||||||
|
initial_all_coin_start_price().await;
|
||||||
|
|
||||||
|
// initialize all values in [all_coin_profit_change_avg]
|
||||||
|
let update_table_name = String::from("all_coin_profit_change_avg");
|
||||||
|
let update_values = vec![
|
||||||
|
(String::from("server_epoch"), server_epoch.to_string()),
|
||||||
|
(String::from("cnt"), 1.to_string()),
|
||||||
|
(String::from("avg_profit"), 0.0.to_string()),
|
||||||
|
];
|
||||||
|
let update_condition = vec![(String::from("id"), String::from("1"))];
|
||||||
|
update_record2(&update_table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// delete all rows when the number of rows is 200
|
||||||
|
let history_rows_num = count_rows(&String::from("all_coin_profit_change_history"))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
if history_rows_num == 200 {
|
||||||
|
delete_all_rows(&String::from("all_coin_profit_change_history"))
|
||||||
|
.await
|
||||||
|
.expect("Failed to delete rows!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert record into [all_coin_profit_change_history]
|
||||||
|
if all_coin_profit_change_avg_list
|
||||||
|
.first()
|
||||||
|
.unwrap()
|
||||||
|
.server_epoch
|
||||||
|
!= 0
|
||||||
|
{
|
||||||
|
let insert_table_name = String::from("all_coin_profit_change_history");
|
||||||
|
let insert_columns = vec!["server_epoch", "avg_profit"];
|
||||||
|
let insert_values = vec![
|
||||||
|
server_epoch.to_string(),
|
||||||
|
all_coin_profit_change_avg_list
|
||||||
|
.first()
|
||||||
|
.unwrap()
|
||||||
|
.avg_profit
|
||||||
|
.to_string(),
|
||||||
|
];
|
||||||
|
insert_one_record(&insert_table_name, &insert_columns, &insert_values).await;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#[derive(FromRow)]
|
||||||
|
struct Avg {
|
||||||
|
avg: f64,
|
||||||
|
}
|
||||||
|
let select_data_structure = Avg { avg: 0.0 };
|
||||||
|
let select_table_name = String::from("(SELECT sum(total) AS result FROM (SELECT (start_price-price)/start_price AS total FROM all_coin_start_price JOIN coinprices ON all_coin_start_price.symbol = coinprices.symbol) total)");
|
||||||
|
let mut select_column = String::from("result/");
|
||||||
|
select_column.push_str(count_row.to_string().as_str());
|
||||||
|
select_column.push_str(" AS avg");
|
||||||
|
let select_condition = Some(String::from("result"));
|
||||||
|
|
||||||
|
let new_avg = select_record(
|
||||||
|
&select_table_name,
|
||||||
|
&select_column,
|
||||||
|
&select_condition,
|
||||||
|
&select_data_structure,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.first()
|
||||||
|
.unwrap()
|
||||||
|
.avg;
|
||||||
|
|
||||||
|
avg_profit = ((cnt as f64 - 1.0) / (cnt as f64)) * avg_profit + (new_avg / (cnt as f64));
|
||||||
|
|
||||||
|
let update_table_name = String::from("all_coin_profit_change_avg");
|
||||||
|
let update_values = vec![
|
||||||
|
(String::from("cnt"), (cnt + 1).to_string()),
|
||||||
|
(String::from("avg_profit"), avg_profit.to_string()),
|
||||||
|
];
|
||||||
|
let update_condition = vec![(String::from("id"), String::from("1"))];
|
||||||
|
update_record2(&update_table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// println!("monitoring all_coin_profit_change 완료");
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn server_epoch() -> u64 {
|
||||||
|
#[derive(Debug, FromRow)]
|
||||||
|
struct ServerEpoch {
|
||||||
|
server_epoch: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
let table_name = String::from("time");
|
||||||
|
let columns = String::from("*");
|
||||||
|
let condition = None;
|
||||||
|
let mut time_info = ServerEpoch { server_epoch: 0 };
|
||||||
|
|
||||||
|
let select_result = select_record(&table_name, &columns, &condition, &time_info)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
select_result.first().unwrap().server_epoch
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn initial_all_coin_start_price() {
|
||||||
|
let table_name = String::from("all_coin_start_price");
|
||||||
|
#[derive(Debug, FromRow)]
|
||||||
|
struct SymbolPrice {
|
||||||
|
symbol: String,
|
||||||
|
price: Decimal,
|
||||||
|
}
|
||||||
|
let fetch_table_name = String::from("valid_usdt_trades");
|
||||||
|
let select_columns = String::from("valid_usdt_trades.symbol, coinprices.price");
|
||||||
|
let condition = Some(String::from(
|
||||||
|
"JOIN coinprices ON valid_usdt_trades.symbol = coinprices.symbol",
|
||||||
|
));
|
||||||
|
let data_struct = SymbolPrice {
|
||||||
|
symbol: String::new(),
|
||||||
|
price: Decimal::new(0, 8),
|
||||||
|
};
|
||||||
|
let symbol_price_vec =
|
||||||
|
select_record(&fetch_table_name, &select_columns, &condition, &data_struct)
|
||||||
|
.await
|
||||||
|
.expect("Failed to fetch records!");
|
||||||
|
|
||||||
|
let insert_columns = vec!["symbol", "start_price"];
|
||||||
|
let mut insert_values: Vec<Vec<String>> = Vec::new();
|
||||||
|
let mut insert_inner_values: Vec<String> = Vec::new();
|
||||||
|
let mut value_string_build = String::new();
|
||||||
|
for element in symbol_price_vec {
|
||||||
|
value_string_build.clear();
|
||||||
|
value_string_build.push_str(element.symbol.as_str());
|
||||||
|
value_string_build.push_str("\', \'");
|
||||||
|
value_string_build.push_str(element.price.to_string().as_str());
|
||||||
|
insert_inner_values.push(value_string_build.clone());
|
||||||
|
insert_values.push(insert_inner_values.clone());
|
||||||
|
insert_inner_values.clear();
|
||||||
|
}
|
||||||
|
insert_records(&table_name, &insert_columns, &insert_values).await;
|
||||||
|
}
|
||||||
788
src/coin_health_check_team/request_candles.rs
Normal file
788
src/coin_health_check_team/request_candles.rs
Normal file
|
|
@ -0,0 +1,788 @@
|
||||||
|
use crate::database_control::*;
|
||||||
|
use futures::future::try_join_all;
|
||||||
|
use futures::{stream, StreamExt};
|
||||||
|
use reqwest::{Client, ClientBuilder, Response};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_json::Value;
|
||||||
|
use sqlx::{Error, FromRow};
|
||||||
|
use std::borrow::{Borrow, BorrowMut};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::{join, sync::Mutex, time::*};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct CandleData {
|
||||||
|
pub open_time: i64,
|
||||||
|
pub open_price: f64,
|
||||||
|
pub high_price: f64,
|
||||||
|
pub low_price: f64,
|
||||||
|
pub close_price: f64,
|
||||||
|
pub volume: f64,
|
||||||
|
pub close_time: i64,
|
||||||
|
pub quote_asset_volume: f64,
|
||||||
|
pub number_of_trades: i64,
|
||||||
|
pub taker_buy_base_asset_volume: f64,
|
||||||
|
pub taker_buy_quote_asset_volume: f64,
|
||||||
|
pub ignore_this: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch the list of valid usdt trades
|
||||||
|
#[derive(Debug, FromRow, Clone)]
|
||||||
|
struct Symbols {
|
||||||
|
symbol: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
// request candlestick data from endpoint for each symbol (Weight(IP) 1)
|
||||||
|
// use the following intervals: 1m, 30m, 1d, 1w, 1mon (1M)
|
||||||
|
async fn request_candlestick_data(
|
||||||
|
symbol: String,
|
||||||
|
interval: &String,
|
||||||
|
client: &Client,
|
||||||
|
candle_set: &mut Vec<(String, Vec<CandleData>)>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let mut query = String::from("https://api.binance.com/api/v3/klines?");
|
||||||
|
query.push_str("&symbol=");
|
||||||
|
query.push_str(symbol.as_str());
|
||||||
|
query.push_str("&interval=");
|
||||||
|
if interval == "1mon" {
|
||||||
|
query.push_str("1M");
|
||||||
|
} else {
|
||||||
|
query.push_str(interval.as_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = client.get(query).send().await?;
|
||||||
|
|
||||||
|
let mut body = String::new();
|
||||||
|
body = response.text_with_charset("utf-8").await?;
|
||||||
|
|
||||||
|
if interval == "1m" || interval == "30m" {
|
||||||
|
de_candle_json(symbol, &interval, &body, candle_set).await?;
|
||||||
|
} else if interval == "1d" || interval == "1w" || interval == "1mon" {
|
||||||
|
de_candle_json2(symbol, &interval, &body, candle_set).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn de_candle_json(
|
||||||
|
symbol: String,
|
||||||
|
interval: &String,
|
||||||
|
body: &String,
|
||||||
|
candle_set: &mut Vec<(String, Vec<CandleData>)>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let v: Value = serde_json::from_str(body.as_str())?;
|
||||||
|
let mut into_vec = v.as_array();
|
||||||
|
if into_vec == None {
|
||||||
|
return Err("Err")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut candle_data = CandleData {
|
||||||
|
open_time: 0,
|
||||||
|
open_price: 0.0,
|
||||||
|
high_price: 0.0,
|
||||||
|
low_price: 0.0,
|
||||||
|
close_price: 0.0,
|
||||||
|
volume: 0.0,
|
||||||
|
close_time: 0,
|
||||||
|
quote_asset_volume: 0.0,
|
||||||
|
number_of_trades: 0,
|
||||||
|
taker_buy_base_asset_volume: 0.0,
|
||||||
|
taker_buy_quote_asset_volume: 0.0,
|
||||||
|
ignore_this: 0.0,
|
||||||
|
};
|
||||||
|
let mut candle_vec: Vec<CandleData> = Vec::new();
|
||||||
|
for element in into_vec.unwrap() {
|
||||||
|
let inner_into_vec = element.as_array().unwrap();
|
||||||
|
candle_data.open_time = element[0].as_i64().unwrap();
|
||||||
|
candle_data.open_price = element[1].as_str().unwrap().parse::<f64>().unwrap();
|
||||||
|
candle_data.high_price = element[2].as_str().unwrap().parse::<f64>().unwrap();
|
||||||
|
candle_data.low_price = element[3].as_str().unwrap().parse::<f64>().unwrap();
|
||||||
|
candle_data.close_price = element[4].as_str().unwrap().parse::<f64>().unwrap();
|
||||||
|
candle_data.volume = element[5].as_str().unwrap().parse::<f64>().unwrap();
|
||||||
|
candle_data.close_time = element[6].as_i64().unwrap();
|
||||||
|
candle_data.quote_asset_volume = element[7].as_str().unwrap().parse::<f64>().unwrap();
|
||||||
|
candle_data.number_of_trades = element[8].as_i64().unwrap();
|
||||||
|
candle_data.taker_buy_base_asset_volume =
|
||||||
|
element[9].as_str().unwrap().parse::<f64>().unwrap();
|
||||||
|
candle_data.taker_buy_quote_asset_volume =
|
||||||
|
element[10].as_str().unwrap().parse::<f64>().unwrap();
|
||||||
|
candle_data.ignore_this = element[11].as_str().unwrap().parse::<f64>().unwrap();
|
||||||
|
|
||||||
|
candle_vec.push(candle_data.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
candle_set.push((symbol, candle_vec));
|
||||||
|
// let search_result = candle_set.iter().position(|x| x.0 == symbol);
|
||||||
|
// match search_result {
|
||||||
|
// Some(T) => {
|
||||||
|
// candle_set[T].1 = candle_vec;
|
||||||
|
// },
|
||||||
|
// None => {
|
||||||
|
// candle_set.push((symbol, candle_vec));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn de_candle_json2(
|
||||||
|
symbol: String,
|
||||||
|
interval: &String,
|
||||||
|
body: &String,
|
||||||
|
candle_set: &mut Vec<(String, Vec<CandleData>)>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let v: Value = serde_json::from_str(body.as_str())?;
|
||||||
|
let mut into_vec = v.as_array();
|
||||||
|
if into_vec == None {
|
||||||
|
return Err("Err")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut candle_data = CandleData {
|
||||||
|
open_time: 0,
|
||||||
|
open_price: 0.0,
|
||||||
|
high_price: 0.0,
|
||||||
|
low_price: 0.0,
|
||||||
|
close_price: 0.0,
|
||||||
|
volume: 0.0,
|
||||||
|
close_time: 0,
|
||||||
|
quote_asset_volume: 0.0,
|
||||||
|
number_of_trades: 0,
|
||||||
|
taker_buy_base_asset_volume: 0.0,
|
||||||
|
taker_buy_quote_asset_volume: 0.0,
|
||||||
|
ignore_this: 0.0,
|
||||||
|
};
|
||||||
|
let mut candle_vec: Vec<CandleData> = Vec::new();
|
||||||
|
for element in into_vec.unwrap() {
|
||||||
|
let inner_into_vec = element.as_array().unwrap();
|
||||||
|
candle_data.open_time = element[0].as_i64().unwrap();
|
||||||
|
candle_data.open_price = element[1].as_str().unwrap().parse::<f64>().unwrap();
|
||||||
|
candle_data.high_price = element[2].as_str().unwrap().parse::<f64>().unwrap();
|
||||||
|
candle_data.low_price = element[3].as_str().unwrap().parse::<f64>().unwrap();
|
||||||
|
candle_data.close_price = element[4].as_str().unwrap().parse::<f64>().unwrap();
|
||||||
|
candle_data.volume = element[5].as_str().unwrap().parse::<f64>().unwrap();
|
||||||
|
candle_data.close_time = element[6].as_i64().unwrap();
|
||||||
|
candle_data.quote_asset_volume = element[7].as_str().unwrap().parse::<f64>().unwrap();
|
||||||
|
candle_data.number_of_trades = element[8].as_i64().unwrap();
|
||||||
|
candle_data.taker_buy_base_asset_volume =
|
||||||
|
element[9].as_str().unwrap().parse::<f64>().unwrap();
|
||||||
|
candle_data.taker_buy_quote_asset_volume =
|
||||||
|
element[10].as_str().unwrap().parse::<f64>().unwrap();
|
||||||
|
candle_data.ignore_this = element[11].as_str().unwrap().parse::<f64>().unwrap();
|
||||||
|
|
||||||
|
candle_vec.push(candle_data.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let search_result = candle_set.iter().position(|x| x.0 == symbol);
|
||||||
|
match search_result {
|
||||||
|
Some(T) => {
|
||||||
|
candle_set[T].1 = candle_vec;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
candle_set.push((symbol, candle_vec));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// // request candlestick data from endpoint for each symbol (Weight(IP) 1)
|
||||||
|
// // use the following intervals: 1m, 30m, 1d, 1w, 1mon(1M)
|
||||||
|
// async fn request_candlestick_data(symbol: &String, interval: &String, client: &Client) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
// let mut query = String::from("https://api.binance.com/api/v3/klines?");
|
||||||
|
// query.push_str("&symbol=");
|
||||||
|
// query.push_str(symbol.as_str());
|
||||||
|
// query.push_str("&interval=");
|
||||||
|
// if interval == "1mon" {
|
||||||
|
// query.push_str("1M");
|
||||||
|
// } else {
|
||||||
|
// query.push_str(interval.as_str());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let response = client.get(query).send().await?;
|
||||||
|
|
||||||
|
// let mut body = String::new();
|
||||||
|
// body = response.text_with_charset("utf-8").await?;
|
||||||
|
|
||||||
|
// de_candle_json(&symbol, &interval, &body).await?;
|
||||||
|
// // println!(" candle {} {} 완료", symbol, interval);
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // for initialization
|
||||||
|
// pub async fn request_candlestick_initial (symbol_vec: &Vec<String>, interval: &String) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
// const CONCURRENT_REQUESTS: usize = 50;
|
||||||
|
// #[derive(Clone, Debug)]
|
||||||
|
// struct QuerySet {
|
||||||
|
// symbol: String,
|
||||||
|
// query_url: String,
|
||||||
|
// interval: String,
|
||||||
|
// body: String,
|
||||||
|
// }
|
||||||
|
// let client = ClientBuilder::new().connect_timeout(tokio::time::Duration::from_millis(10000)).build().unwrap();
|
||||||
|
// let mut query_set = QuerySet{ symbol: String::new(), query_url: String::new(), interval: String::new(), body: String::new() };
|
||||||
|
// let mut query_set_vec: Vec<QuerySet> = Vec::new();
|
||||||
|
// for symbol in symbol_vec {
|
||||||
|
|
||||||
|
// let mut query_url = String::from("https://api.binance.com/api/v3/klines?");
|
||||||
|
// query_url.push_str("&interval=");
|
||||||
|
// if interval == "1mon" {
|
||||||
|
// query_url.push_str("1M");
|
||||||
|
// } else {
|
||||||
|
// query_url.push_str(interval.as_str());
|
||||||
|
// }
|
||||||
|
// query_url.push_str("&symbol=");
|
||||||
|
// query_url.push_str(symbol.as_str());
|
||||||
|
// query_set.query_url = query_url;
|
||||||
|
// query_set.symbol = symbol.clone();
|
||||||
|
// query_set.interval = interval.clone();
|
||||||
|
// query_set_vec.push(query_set.clone());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let bodies = stream::iter(query_set_vec)
|
||||||
|
// .map(|mut query_set| {
|
||||||
|
// let client = &client;
|
||||||
|
// async move {
|
||||||
|
// let mut response = client.get(query_set.query_url.clone()).send().await.unwrap();
|
||||||
|
// let mut body = response.text_with_charset("utf-8").await;
|
||||||
|
// while let Err(e) = body {
|
||||||
|
// response = client.get(query_set.query_url.clone()).send().await.unwrap();
|
||||||
|
// body = response.text_with_charset("utf-8").await;
|
||||||
|
// sleep(Duration::from_secs(1)).await;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// query_set.body = body.unwrap();
|
||||||
|
// query_set
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// .buffer_unordered(CONCURRENT_REQUESTS);
|
||||||
|
// bodies.for_each(|query_set| async move {de_candle_json(&query_set.symbol.to_string(), &query_set.interval.to_string(), &query_set.body.to_string()).await;}).await;
|
||||||
|
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for initialization
|
||||||
|
pub async fn request_candlestick_initial(
|
||||||
|
symbol: String,
|
||||||
|
interval: &String,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let mut candle_set: Vec<(String, Vec<CandleData>)> = Vec::new();
|
||||||
|
let client = ClientBuilder::new()
|
||||||
|
.timeout(tokio::time::Duration::from_millis(20000))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
let mut query = String::from("https://api.binance.com/api/v3/klines?");
|
||||||
|
// query.push_str("&limit=");
|
||||||
|
// query.push_str("200");
|
||||||
|
query.push_str("&symbol=");
|
||||||
|
query.push_str(symbol.as_str());
|
||||||
|
query.push_str("&interval=");
|
||||||
|
if interval == "1mon" {
|
||||||
|
query.push_str("1M");
|
||||||
|
} else {
|
||||||
|
query.push_str(interval.as_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut response = client.get(&query).send().await;
|
||||||
|
while let Err(e) = response {
|
||||||
|
response = client.get(&query).send().await;
|
||||||
|
sleep(Duration::from_secs(1)).await;
|
||||||
|
}
|
||||||
|
let mut body = response.unwrap().text_with_charset("utf-8").await;
|
||||||
|
while let Err(e) = body {
|
||||||
|
response = client.get(&query).send().await;
|
||||||
|
while let Err(e) = response {
|
||||||
|
response = client.get(&query).send().await;
|
||||||
|
sleep(Duration::from_secs(1)).await;
|
||||||
|
}
|
||||||
|
body = response.unwrap().text_with_charset("utf-8").await;
|
||||||
|
}
|
||||||
|
|
||||||
|
de_candle_json(symbol, &interval, &body.unwrap(), &mut candle_set).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub async fn request_candlestick_initial (symbol: &String, interval: &String) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
// let client = ClientBuilder::new().timeout(tokio::time::Duration::from_millis(10000)).build().unwrap();
|
||||||
|
// let mut query = String::from("https://api.binance.com/api/v3/klines?");
|
||||||
|
// query.push_str("&symbol=");
|
||||||
|
// query.push_str(symbol.as_str());
|
||||||
|
// query.push_str("&interval=");
|
||||||
|
// if interval == "1mon" {
|
||||||
|
// query.push_str("1M");
|
||||||
|
// } else {
|
||||||
|
// query.push_str(interval.as_str());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// let mut response = client.get(&query).send().await;
|
||||||
|
// let temp = reqwest::get("http://site.with.redirect.loop").await;
|
||||||
|
// if let Err(e) = temp{
|
||||||
|
// e.
|
||||||
|
// }
|
||||||
|
// if let Err(e) = response {
|
||||||
|
// if e.is_timeout() {
|
||||||
|
// loop{
|
||||||
|
// println!(">>> [timeout] retry fetching candle data: {} {}", symbol, interval);
|
||||||
|
// tokio::time::sleep(Duration::from_secs(1)).await;
|
||||||
|
// response = client.get(&query).send().await?;
|
||||||
|
// if response.is_ok() { break; }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// let mut body = String::new();
|
||||||
|
// body = response.unwrap().text_with_charset("utf-8").await.unwrap();
|
||||||
|
// de_candle_json(&symbol, &interval, &body).await.unwrap();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
|
||||||
|
// async fn de_candle_json(symbol: &String, interval: &String, body: &String) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
// let v: Value = serde_json::from_str(body.as_str())?;
|
||||||
|
// let mut into_vec = v.as_array();
|
||||||
|
// if into_vec == None {
|
||||||
|
// return Err("Err")?
|
||||||
|
// }
|
||||||
|
// // let mut one_candle_data: &Vec<Value> = Vec::new();
|
||||||
|
|
||||||
|
// let columns = vec!["openTime", "openPrice", "highPrice","lowPrice", "closePrice",
|
||||||
|
// "volume", "closeTime", "quoteAssetVolume", "numberOfTrades", "takerBuyBaseAssetVolume",
|
||||||
|
// "takerBuyQuoteAssetVolume", "ignoreThis"];
|
||||||
|
// let mut value_wrapper: Vec<Vec<String>> = Vec::new();
|
||||||
|
|
||||||
|
// for element in into_vec.unwrap() {
|
||||||
|
// let inner_into_vec = element.as_array().unwrap();
|
||||||
|
|
||||||
|
// let mut value_vec = Vec::new();
|
||||||
|
// for element in inner_into_vec {
|
||||||
|
// if element.is_number() {
|
||||||
|
// value_vec.push(element.as_i64().unwrap().to_string());
|
||||||
|
// } else if element.is_string() {
|
||||||
|
// value_vec.push(element.as_str().unwrap().to_string());
|
||||||
|
// } else {
|
||||||
|
// println!("Elements in body msg are changed. Please update parsing in de_candle_json.");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// value_wrapper.push(value_vec);
|
||||||
|
// }
|
||||||
|
// store_candle_db(&symbol, &interval, &columns, value_wrapper).await;
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
|
||||||
|
async fn store_candle_db(
|
||||||
|
symbol: &String,
|
||||||
|
interval: &String,
|
||||||
|
columns: &Vec<&str>,
|
||||||
|
value_wrapper: Vec<Vec<String>>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let mut table_name = String::from("candle_");
|
||||||
|
table_name.push_str(symbol.as_str());
|
||||||
|
table_name.push('_');
|
||||||
|
table_name.push_str(interval.as_str());
|
||||||
|
|
||||||
|
delete_all_rows(&table_name).await.unwrap();
|
||||||
|
insert_records(&table_name, columns, &value_wrapper)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_candle_table(
|
||||||
|
symbol: &String,
|
||||||
|
interval: &String,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let mut table_name = String::from("candle_");
|
||||||
|
table_name.push_str(symbol.as_str());
|
||||||
|
table_name.push('_');
|
||||||
|
table_name.push_str(interval.as_str());
|
||||||
|
let initial_table = vec![
|
||||||
|
("id", "integer", Some("PK, AI")),
|
||||||
|
("openTime", "bigint", None),
|
||||||
|
("openPrice", "double", None),
|
||||||
|
("highPrice", "double", None),
|
||||||
|
("lowPrice", "double", None),
|
||||||
|
("closePrice", "double", None),
|
||||||
|
("volume", "double", None),
|
||||||
|
("closeTime", "bigint", None),
|
||||||
|
("quoteAssetVolume", "double", None),
|
||||||
|
("numberOfTrades", "int", None),
|
||||||
|
("takerBuyBaseAssetVolume", "double", None),
|
||||||
|
("takerBuyQuoteAssetVolume", "double", None),
|
||||||
|
("ignoreThis", "double", None),
|
||||||
|
];
|
||||||
|
let initial_columns = vec![
|
||||||
|
"openTime",
|
||||||
|
"openPrice",
|
||||||
|
"highPrice",
|
||||||
|
"lowPrice",
|
||||||
|
"closePrice",
|
||||||
|
"volume",
|
||||||
|
"closeTime",
|
||||||
|
"quoteAssetVolume",
|
||||||
|
"numberOfTrades",
|
||||||
|
"takerBuyBaseAssetVolume",
|
||||||
|
"takerBuyQuoteAssetVolume",
|
||||||
|
"ignoreThis",
|
||||||
|
];
|
||||||
|
let initial_values = vec![
|
||||||
|
String::from("0"),
|
||||||
|
String::from("0.0"),
|
||||||
|
String::from("0.0"),
|
||||||
|
String::from("0.0"),
|
||||||
|
String::from("0.0"),
|
||||||
|
String::from("0.0"),
|
||||||
|
String::from("0"),
|
||||||
|
String::from("0.0"),
|
||||||
|
String::from("0"),
|
||||||
|
String::from("0.0"),
|
||||||
|
String::from("0.0"),
|
||||||
|
String::from("0.0"),
|
||||||
|
];
|
||||||
|
let table_condition = Some("ENGINE = MEMORY");
|
||||||
|
|
||||||
|
new_table(&table_name, &initial_table, &table_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
insert_one_record(&table_name, &initial_columns, &initial_values)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// for fetching 1m and 30m candle
|
||||||
|
pub async fn fetch_candle_parallel(
|
||||||
|
interval: &String,
|
||||||
|
candle_vec: &mut Vec<(String, Vec<CandleData>)>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let instant = Instant::now();
|
||||||
|
let fetch_table_name = String::from("valid_usdt_trades");
|
||||||
|
let column_name = String::from("symbol");
|
||||||
|
let condition = None;
|
||||||
|
let mut symbols = Symbols {
|
||||||
|
symbol: String::new(),
|
||||||
|
};
|
||||||
|
let mut select_result = select_record(&fetch_table_name, &column_name, &condition, &symbols)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// 심볼들을 20개씩 청스로 나누어 테스크를 생성하고 각 태스크 별로 병렬로 처리한다.
|
||||||
|
let chunks = select_result.chunks(20);
|
||||||
|
let nbr_chunks = chunks.len();
|
||||||
|
|
||||||
|
let mut candle_vec_arc_wrapper: Vec<Arc<Mutex<Vec<(String, Vec<CandleData>)>>>> = Vec::new();
|
||||||
|
for _ in 0..nbr_chunks {
|
||||||
|
let mut candle_vec_temp: Vec<(String, Vec<CandleData>)> = Vec::new();
|
||||||
|
let mut candle_vec_arc = Arc::new(Mutex::new(candle_vec_temp));
|
||||||
|
candle_vec_arc_wrapper.push(candle_vec_arc);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut task_vec = Vec::new();
|
||||||
|
|
||||||
|
let mut index = 0;
|
||||||
|
for chunk in chunks {
|
||||||
|
let candle_arc = Arc::clone(&candle_vec_arc_wrapper[index]);
|
||||||
|
let chunk_vec = chunk.to_vec();
|
||||||
|
let interval_clone = interval.clone();
|
||||||
|
task_vec.push(tokio::spawn(async move {
|
||||||
|
repeat_task(interval_clone, chunk_vec, candle_arc).await;
|
||||||
|
}));
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = try_join_all(task_vec).await;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(T) => {
|
||||||
|
let mut candle_buffer: Vec<(String, Vec<CandleData>)> = Vec::new();
|
||||||
|
for element in candle_vec_arc_wrapper {
|
||||||
|
let a = element.lock().await.clone();
|
||||||
|
for element in a {
|
||||||
|
candle_buffer.push(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*candle_vec = candle_buffer;
|
||||||
|
// println!(" candle {} 완료 elapsed:{:.2}s", interval.as_str(), instant.elapsed().as_secs_f32());
|
||||||
|
}
|
||||||
|
Err(E) => {
|
||||||
|
panic!("Failed to fetch candle data!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn repeat_task(
|
||||||
|
interval: String,
|
||||||
|
symbol_vec: Vec<Symbols>,
|
||||||
|
my_count: Arc<Mutex<Vec<(String, Vec<CandleData>)>>>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let client = ClientBuilder::new()
|
||||||
|
.timeout(tokio::time::Duration::from_millis(1200))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
for element in symbol_vec {
|
||||||
|
let mut candle_set_lock = my_count.lock().await;
|
||||||
|
request_candlestick_data(element.symbol, &interval, &client, &mut candle_set_lock).await;
|
||||||
|
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// for fetching 1d, 1w, and 1mon candle
|
||||||
|
pub async fn fetch_candle_delay(
|
||||||
|
interval: &String,
|
||||||
|
candle_vec: &mut Vec<(String, Vec<CandleData>)>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let instant_func = Instant::now();
|
||||||
|
let server_epoch = server_epoch().await;
|
||||||
|
let client = ClientBuilder::new()
|
||||||
|
.timeout(tokio::time::Duration::from_millis(1200))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// to decide keeping retry or not as the current server status from database
|
||||||
|
#[derive(Debug, FromRow, Clone)]
|
||||||
|
struct ServerStatus {
|
||||||
|
server_on: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
let serverhealth_table_name = String::from("serverhealth");
|
||||||
|
let serverhealth_column_name = String::from("server_on");
|
||||||
|
let serverhealth_condition = None;
|
||||||
|
let mut serverhealth = ServerStatus { server_on: true };
|
||||||
|
|
||||||
|
// fetch the list of valid usdt trades
|
||||||
|
#[derive(Debug, FromRow)]
|
||||||
|
struct Symbols {
|
||||||
|
symbol: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
let fetch_table_name = String::from("valid_usdt_trades");
|
||||||
|
let column_name = String::from("symbol");
|
||||||
|
let condition = None;
|
||||||
|
let mut symbols = Symbols {
|
||||||
|
symbol: String::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut select_result = select_record(&fetch_table_name, &column_name, &condition, &symbols)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut time_wait = 0;
|
||||||
|
if interval == "1d" {
|
||||||
|
time_wait = 10_000;
|
||||||
|
} else if interval == "1w" {
|
||||||
|
time_wait = 20_000;
|
||||||
|
} else {
|
||||||
|
time_wait = 30_000;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut server_on_result = select_record(
|
||||||
|
&serverhealth_table_name,
|
||||||
|
&serverhealth_column_name,
|
||||||
|
&serverhealth_condition,
|
||||||
|
&serverhealth,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
serverhealth.server_on = server_on_result[0].server_on;
|
||||||
|
|
||||||
|
for element in select_result {
|
||||||
|
let instant = Instant::now();
|
||||||
|
server_on_result = select_record(
|
||||||
|
&serverhealth_table_name,
|
||||||
|
&serverhealth_column_name,
|
||||||
|
&serverhealth_condition,
|
||||||
|
&serverhealth,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
serverhealth.server_on = server_on_result[0].server_on;
|
||||||
|
|
||||||
|
{
|
||||||
|
request_candlestick_data(element.symbol, &interval, &client, candle_vec).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sleep for 10secs for 1d, 20secs for 1w, 30secs for 1mon
|
||||||
|
if time_wait > instant.elapsed().as_millis() {
|
||||||
|
sleep(Duration::from_millis(
|
||||||
|
(time_wait - instant.elapsed().as_millis()) as u64,
|
||||||
|
))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// println!(" candle {} 완료 elapsed:{:.2}s", interval, instant_func.elapsed().as_secs());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub async fn fetch_candle_1w() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
// let server_epoch = server_epoch().await;
|
||||||
|
// let client = ClientBuilder::new().timeout(tokio::time::Duration::from_millis(1200)).build().unwrap();
|
||||||
|
|
||||||
|
// // to decide keeping retry or not as the current server status from database
|
||||||
|
// #[derive(Debug, FromRow, Clone)]
|
||||||
|
// struct ServerStatus {
|
||||||
|
// server_on: bool,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let serverhealth_table_name = String::from("serverhealth");
|
||||||
|
// let serverhealth_column_name = String::from("server_on");
|
||||||
|
// let serverhealth_condition = None;
|
||||||
|
// let mut serverhealth = ServerStatus { server_on: true };
|
||||||
|
|
||||||
|
// // fetch the list of valid usdt trades
|
||||||
|
// #[derive(Debug, FromRow)]
|
||||||
|
// struct Symbols {
|
||||||
|
// symbol: String,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let fetch_table_name = String::from("valid_usdt_trades");
|
||||||
|
// let column_name = String::from("symbol");
|
||||||
|
// let condition = None;
|
||||||
|
// let mut symbols = Symbols { symbol: String::new() };
|
||||||
|
|
||||||
|
// let mut select_result = select_record(&fetch_table_name, &column_name, &condition, &symbols).await.unwrap();
|
||||||
|
|
||||||
|
// let interval = String::from("1w");
|
||||||
|
// let column_name = String::from("closeTime");
|
||||||
|
// let condition = Some(String::from("ORDER BY id DESC LIMIT 1"));
|
||||||
|
|
||||||
|
// #[derive(Debug, FromRow)]
|
||||||
|
// struct CloseTime {
|
||||||
|
// closetime: i64,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let mut server_on_result = select_record(&serverhealth_table_name, &serverhealth_column_name, &serverhealth_condition, &serverhealth).await.unwrap();
|
||||||
|
// serverhealth.server_on = server_on_result[0].server_on;
|
||||||
|
// let instant_1w = Instant::now();
|
||||||
|
|
||||||
|
// for element in select_result {
|
||||||
|
// let instant = Instant::now();
|
||||||
|
// server_on_result = select_record(&serverhealth_table_name, &serverhealth_column_name, &serverhealth_condition, &serverhealth).await.unwrap();
|
||||||
|
// serverhealth.server_on = server_on_result[0].server_on;
|
||||||
|
|
||||||
|
// if serverhealth.server_on == true {
|
||||||
|
// let mut closetime = CloseTime { closetime: 0 };
|
||||||
|
// let mut table_name = String::from("candle_");
|
||||||
|
// table_name.push_str(element.symbol.as_str());
|
||||||
|
// table_name.push_str("_1w");
|
||||||
|
|
||||||
|
// let mut exists_result = exists_table(&table_name).await;
|
||||||
|
// if exists_result == false { create_candle_table(&element.symbol, &interval).await.unwrap(); }
|
||||||
|
|
||||||
|
// let mut select_result = select_record(&table_name, &column_name, &condition, &closetime).await.unwrap();
|
||||||
|
|
||||||
|
// if (select_result[0].closetime as u64) < server_epoch {
|
||||||
|
// while let Err(e) = request_candlestick_data(&element.symbol, &interval, &client).await
|
||||||
|
// {
|
||||||
|
// server_on_result = select_record(&serverhealth_table_name, &serverhealth_column_name, &serverhealth_condition, &serverhealth).await.unwrap();
|
||||||
|
// serverhealth.server_on = server_on_result[0].server_on;
|
||||||
|
// if serverhealth.server_on == false { break; }
|
||||||
|
// println!(">>> retry to fetch candlestick {} {} data from endpoint.", &element.symbol, &interval);
|
||||||
|
// sleep(Duration::from_millis(500)).await;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // sleep as much as the loop recurs per 20 seconds if all operation finished within 20 seconds.
|
||||||
|
// if 20_000_000_000 > instant.elapsed().as_nanos() {
|
||||||
|
// sleep(Duration::from_nanos((20_000_000_000 - instant.elapsed().as_nanos()) as u64)).await;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// println!(" candle 1w 완료 elapsed:{:.2}s", instant_1w.elapsed().as_secs());
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub async fn fetch_candle_1mon() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
// let server_epoch = server_epoch().await;
|
||||||
|
// let client = ClientBuilder::new().timeout(tokio::time::Duration::from_millis(1200)).build().unwrap();
|
||||||
|
|
||||||
|
// // to decide keeping retry or not as the current server status from database
|
||||||
|
// #[derive(Debug, FromRow, Clone)]
|
||||||
|
// struct ServerStatus {
|
||||||
|
// server_on: bool,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let serverhealth_table_name = String::from("serverhealth");
|
||||||
|
// let serverhealth_column_name = String::from("server_on");
|
||||||
|
// let serverhealth_condition = None;
|
||||||
|
// let mut serverhealth = ServerStatus { server_on: true };
|
||||||
|
|
||||||
|
// // fetch the list of valid usdt trades
|
||||||
|
// #[derive(Debug, FromRow)]
|
||||||
|
// struct Symbols {
|
||||||
|
// symbol: String,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let fetch_table_name = String::from("valid_usdt_trades");
|
||||||
|
// let column_name = String::from("symbol");
|
||||||
|
// let condition = None;
|
||||||
|
// let mut symbols = Symbols { symbol: String::new() };
|
||||||
|
|
||||||
|
// let mut select_result = select_record(&fetch_table_name, &column_name, &condition, &symbols).await.unwrap();
|
||||||
|
|
||||||
|
// let interval = String::from("1mon");
|
||||||
|
// let column_name = String::from("closeTime");
|
||||||
|
// let condition = Some(String::from("ORDER BY id DESC LIMIT 1"));
|
||||||
|
|
||||||
|
// #[derive(Debug, FromRow)]
|
||||||
|
// struct CloseTime {
|
||||||
|
// closetime: i64,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let mut server_on_result = select_record(&serverhealth_table_name, &serverhealth_column_name, &serverhealth_condition, &serverhealth).await.unwrap();
|
||||||
|
// serverhealth.server_on = server_on_result[0].server_on;
|
||||||
|
// let instant_1mon = Instant::now();
|
||||||
|
|
||||||
|
// for element in select_result {
|
||||||
|
// let instant = Instant::now();
|
||||||
|
// server_on_result = select_record(&serverhealth_table_name, &serverhealth_column_name, &serverhealth_condition, &serverhealth).await.unwrap();
|
||||||
|
// serverhealth.server_on = server_on_result[0].server_on;
|
||||||
|
|
||||||
|
// if serverhealth.server_on == true {
|
||||||
|
// let mut closetime = CloseTime { closetime: 0 };
|
||||||
|
// let mut table_name = String::from("candle_");
|
||||||
|
// table_name.push_str(element.symbol.as_str());
|
||||||
|
// table_name.push_str("_1mon");
|
||||||
|
|
||||||
|
// let mut exists_result = exists_table(&table_name).await;
|
||||||
|
// if exists_result == false { create_candle_table(&element.symbol, &interval).await.unwrap(); }
|
||||||
|
|
||||||
|
// let mut select_result = select_record(&table_name, &column_name, &condition, &closetime).await.unwrap();
|
||||||
|
|
||||||
|
// if (select_result[0].closetime as u64) < server_epoch {
|
||||||
|
// while let Err(e) = request_candlestick_data(&element.symbol, &interval, &client).await
|
||||||
|
// {
|
||||||
|
// server_on_result = select_record(&serverhealth_table_name, &serverhealth_column_name, &serverhealth_condition, &serverhealth).await.unwrap();
|
||||||
|
// serverhealth.server_on = server_on_result[0].server_on;
|
||||||
|
// if serverhealth.server_on == false { break; }
|
||||||
|
// println!(">>> retry to fetch candlestick {} {} data from endpoint.", &element.symbol, &interval);
|
||||||
|
// sleep(Duration::from_millis(500)).await;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // sleep as much as the loop recurs per 30 seconds if all operation finished within 30 seconds.
|
||||||
|
// if 30_000_000_000 > instant.elapsed().as_nanos() {
|
||||||
|
// sleep(Duration::from_nanos((30_000_000_000 - instant.elapsed().as_nanos()) as u64)).await;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// println!(" candle 1mon 완료 elapsed:{:.2}s", instant_1mon.elapsed().as_secs());
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
|
||||||
|
async fn server_epoch() -> u64 {
|
||||||
|
#[derive(Debug, FromRow)]
|
||||||
|
struct ServerEpoch {
|
||||||
|
server_epoch: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
let table_name = String::from("time");
|
||||||
|
let columns = String::from("*");
|
||||||
|
let condition = None;
|
||||||
|
let mut time_info = ServerEpoch { server_epoch: 0 };
|
||||||
|
|
||||||
|
let select_result = select_record(&table_name, &columns, &condition, &time_info)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
select_result.first().unwrap().server_epoch
|
||||||
|
}
|
||||||
458
src/coin_health_check_team/request_others.rs
Normal file
458
src/coin_health_check_team/request_others.rs
Normal file
|
|
@ -0,0 +1,458 @@
|
||||||
|
use crate::database_control::*;
|
||||||
|
use hex::ToHex;
|
||||||
|
use hmac_sha256::HMAC;
|
||||||
|
use reqwest::{Client, ClientBuilder, Response};
|
||||||
|
use rust_decimal::{prelude::FromPrimitive, prelude::ToPrimitive, Decimal, RoundingStrategy};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_json::Value;
|
||||||
|
use sqlx::{Error, FromRow};
|
||||||
|
use std::borrow::{Borrow, BorrowMut};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::{join, sync::Mutex, time::*};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TradeFee {
|
||||||
|
pub symbol: String,
|
||||||
|
pub makercommission: Decimal,
|
||||||
|
pub takercommission: Decimal,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TradeFee {
|
||||||
|
fn new() -> TradeFee {
|
||||||
|
let a = TradeFee {
|
||||||
|
symbol: String::new(),
|
||||||
|
makercommission: Decimal::new(0, 8),
|
||||||
|
takercommission: Decimal::new(0, 8),
|
||||||
|
};
|
||||||
|
a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, FromRow, Clone)]
|
||||||
|
pub struct ExchangeInfo {
|
||||||
|
pub symbol: String,
|
||||||
|
pub stepsize: Decimal,
|
||||||
|
pub ticksize: Decimal,
|
||||||
|
pub base_asset_precision: u32,
|
||||||
|
pub base_commission_precision: u32,
|
||||||
|
pub quote_asset_precision: u32,
|
||||||
|
pub quote_commission_precision: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExchangeInfo {
|
||||||
|
fn new() -> ExchangeInfo {
|
||||||
|
let a = ExchangeInfo {
|
||||||
|
symbol: String::new(),
|
||||||
|
stepsize: Decimal::new(0, 8),
|
||||||
|
ticksize: Decimal::new(0, 8),
|
||||||
|
base_asset_precision: 0,
|
||||||
|
base_commission_precision: 0,
|
||||||
|
quote_asset_precision: 0,
|
||||||
|
quote_commission_precision: 0,
|
||||||
|
};
|
||||||
|
a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct CoinPriceData {
|
||||||
|
pub symbol: String,
|
||||||
|
pub current_price: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CoinPriceData {
|
||||||
|
fn new() -> CoinPriceData {
|
||||||
|
let a = CoinPriceData {
|
||||||
|
symbol: String::new(),
|
||||||
|
current_price: 0.0,
|
||||||
|
};
|
||||||
|
a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// request all coin price (/api, Weight(IP) 2)
|
||||||
|
// request_all_coin_price -> de_all_coin_price_json -> store_coin_price_db
|
||||||
|
// pub async fn request_all_coin_price(client: &Client, price_vec: &Arc<Mutex<Vec<(String, f64)>>>) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
pub async fn request_all_coin_price(
|
||||||
|
client: &Client,
|
||||||
|
price_vec: &mut Vec<CoinPriceData>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let url = "https://api.binance.com/api/v3/ticker/price";
|
||||||
|
let mut response = client.get(url).send().await?;
|
||||||
|
|
||||||
|
let mut body = response.text_with_charset("utf-8").await?;
|
||||||
|
|
||||||
|
de_all_coin_price_json(&body, price_vec).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn de_all_coin_price_json(
|
||||||
|
body: &String,
|
||||||
|
price_vec: &mut Vec<CoinPriceData>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let v: Value = serde_json::from_str(body.as_str())?;
|
||||||
|
let into_vec = v.as_array();
|
||||||
|
if into_vec == None {
|
||||||
|
return Err("Err")?;
|
||||||
|
}
|
||||||
|
let mut object_map = &serde_json::map::Map::new();
|
||||||
|
|
||||||
|
let mut de_price_vec: Vec<CoinPriceData> = Vec::new();
|
||||||
|
let mut data_temp = CoinPriceData::new();
|
||||||
|
|
||||||
|
for element in into_vec.unwrap() {
|
||||||
|
object_map = element.as_object().unwrap();
|
||||||
|
let mut object_map_iter = object_map.iter();
|
||||||
|
|
||||||
|
for element in object_map_iter {
|
||||||
|
match element.0.as_str() {
|
||||||
|
"symbol" => data_temp.symbol = element.1.as_str().unwrap().to_string(),
|
||||||
|
"price" => {
|
||||||
|
data_temp.current_price = element.1.as_str().unwrap().parse::<f64>().unwrap()
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!("Elements in body msg are changed. Please update both your coinprices table and vectors.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
de_price_vec.push(data_temp.clone());
|
||||||
|
}
|
||||||
|
*price_vec = de_price_vec;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// request trade fee (/sapi, Weight(IP) 1)
|
||||||
|
// request_trade_fee -> deserialization_trade_fee_json -> save_db_trade_fee
|
||||||
|
pub async fn request_trade_fee(
|
||||||
|
api_key: &str,
|
||||||
|
secret_key: &str,
|
||||||
|
local_epoch: u128,
|
||||||
|
difference_epoch: i64,
|
||||||
|
client: &Client,
|
||||||
|
tradefee_vec: &mut Vec<TradeFee>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let mut base_url = String::from("https://api.binance.com/sapi/v1/asset/tradeFee?");
|
||||||
|
|
||||||
|
// local 시간이 server 시간보다 너무 앞서거나 뒤쳐지면 USER_DATA를 요청할 수 없으므로 local과 server 의 시간 차이만큼을 local에서 빼어 보정한 뒤
|
||||||
|
// 이를 timestamp로 사용한다.
|
||||||
|
let mut timestamp;
|
||||||
|
if difference_epoch >= 0 {
|
||||||
|
timestamp = (local_epoch - difference_epoch as u128).to_string();
|
||||||
|
} else if difference_epoch < 0 {
|
||||||
|
timestamp = (local_epoch - (difference_epoch * -1) as u128).to_string();
|
||||||
|
} else {
|
||||||
|
timestamp = local_epoch.to_string();
|
||||||
|
}
|
||||||
|
let recv_window_size = "20000".to_string(); // default: 5,000ms, Max: 60,000ms
|
||||||
|
|
||||||
|
let mut query_string = String::from("×tamp=");
|
||||||
|
query_string.push_str(×tamp);
|
||||||
|
query_string.push_str("&recvWindow=");
|
||||||
|
query_string.push_str(&recv_window_size);
|
||||||
|
|
||||||
|
let signature = HMAC::mac(&query_string.as_bytes(), secret_key.as_bytes());
|
||||||
|
|
||||||
|
query_string.push_str("&signature=");
|
||||||
|
base_url.push_str(&query_string);
|
||||||
|
base_url.push_str(signature.encode_hex::<String>().as_str());
|
||||||
|
|
||||||
|
let mut response = client
|
||||||
|
.get(&base_url)
|
||||||
|
.header("X-MBX-APIKEY", api_key)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
while !response.status().is_success() {
|
||||||
|
sleep(Duration::from_secs(5)).await;
|
||||||
|
response = client
|
||||||
|
.get(&base_url)
|
||||||
|
.header("X-MBX-APIKEY", api_key)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut body = response.text_with_charset("utf-8").await;
|
||||||
|
|
||||||
|
match body {
|
||||||
|
Ok(T) => {
|
||||||
|
de_trade_fee_json(&T, tradefee_vec).await?;
|
||||||
|
// println!("tradefee 완료");
|
||||||
|
}
|
||||||
|
Err(E) => {
|
||||||
|
println!("request_trade_fee body failed!: {:?}", E);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn de_trade_fee_json(
|
||||||
|
body: &String,
|
||||||
|
tradefee_vec: &mut Vec<TradeFee>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let v: Value = serde_json::from_str(body.as_str())?;
|
||||||
|
|
||||||
|
let into_vec = v.as_array();
|
||||||
|
if into_vec == None {
|
||||||
|
return Err("Err")?;
|
||||||
|
}
|
||||||
|
let mut object_map = &serde_json::map::Map::new();
|
||||||
|
let mut de_tradefee_vec: Vec<TradeFee> = Vec::new();
|
||||||
|
let mut tradefee_data = TradeFee::new();
|
||||||
|
|
||||||
|
for element in into_vec.unwrap() {
|
||||||
|
object_map = element.as_object().unwrap();
|
||||||
|
|
||||||
|
let mut object_map_iter = object_map.iter();
|
||||||
|
|
||||||
|
for element in object_map_iter {
|
||||||
|
match element.0.as_str() {
|
||||||
|
"symbol" => tradefee_data.symbol = element.1.as_str().unwrap().to_string(),
|
||||||
|
"makerCommission" => {
|
||||||
|
tradefee_data.makercommission =
|
||||||
|
rust_decimal::prelude::FromStr::from_str(element.1.as_str().unwrap())
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
"takerCommission" => {
|
||||||
|
tradefee_data.takercommission =
|
||||||
|
rust_decimal::prelude::FromStr::from_str(element.1.as_str().unwrap())
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!("Elements in body msg are changed. Please update both your tradefees table and vectors.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
de_tradefee_vec.push(tradefee_data.clone());
|
||||||
|
}
|
||||||
|
tradefee_vec.clear();
|
||||||
|
*tradefee_vec = de_tradefee_vec;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// request 24hr Ticker Price Change Statistics (Weight(IP) 1 for a single symbol, 40 when the symbol parameter is omitted)
|
||||||
|
// request_24hr_ticker_price_change_statistics -> deserialization_24hr_ticker_price_change_statistics_json ->
|
||||||
|
pub async fn request_24hr_ticker_price_change_statistics(
|
||||||
|
client: &Client,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let mut response = client
|
||||||
|
.get("https://api.binance.com/api/v3/ticker/24hr")
|
||||||
|
.send()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Ok(T) => {
|
||||||
|
let mut body = T.text_with_charset("utf-8").await;
|
||||||
|
match body {
|
||||||
|
Ok(T) => {
|
||||||
|
de_24h_change_json(&T).await?;
|
||||||
|
// println!("24h change 완료");
|
||||||
|
}
|
||||||
|
Err(E) => {
|
||||||
|
println!(
|
||||||
|
"request_24hr_ticker_price_change_statistics body failed!: {:?}",
|
||||||
|
E
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(E) => {
|
||||||
|
println!(
|
||||||
|
"request_24hr_ticker_price_change_statistics response failed!: {:?}",
|
||||||
|
E
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn de_24h_change_json(body: &String) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let v: Value = serde_json::from_str(body.as_str())?;
|
||||||
|
let mut into_vec = v.as_array();
|
||||||
|
if into_vec == None {
|
||||||
|
return Err("Err")?;
|
||||||
|
}
|
||||||
|
let mut object_map = &serde_json::map::Map::new();
|
||||||
|
|
||||||
|
let columns = vec![
|
||||||
|
"symbol",
|
||||||
|
"priceChange",
|
||||||
|
"priceChangePercent",
|
||||||
|
"weightedAvgPrice",
|
||||||
|
"prevClosePrice",
|
||||||
|
"lastPrice",
|
||||||
|
"lastQty",
|
||||||
|
"bidPrice",
|
||||||
|
"bidQty",
|
||||||
|
"askPrice",
|
||||||
|
"askQty",
|
||||||
|
"openPrice",
|
||||||
|
"highPrice",
|
||||||
|
"lowPrice",
|
||||||
|
"volume",
|
||||||
|
"quoteVolume",
|
||||||
|
"openTime",
|
||||||
|
"closeTime",
|
||||||
|
"firstId",
|
||||||
|
"lastId",
|
||||||
|
"count",
|
||||||
|
];
|
||||||
|
let mut value_wrapper: Vec<Vec<String>> = Vec::new();
|
||||||
|
|
||||||
|
for element in into_vec.unwrap() {
|
||||||
|
object_map = element.as_object().unwrap();
|
||||||
|
|
||||||
|
let mut value_vec = vec![String::new(); 21];
|
||||||
|
|
||||||
|
let mut object_map_iter = object_map.iter();
|
||||||
|
|
||||||
|
for element in object_map_iter {
|
||||||
|
match element.0.as_str() {
|
||||||
|
"symbol" => value_vec[0] = element.1.as_str().unwrap().to_string(),
|
||||||
|
"priceChange" => value_vec[1] = element.1.as_str().unwrap().to_string(),
|
||||||
|
"priceChangePercent" => value_vec[2] = element.1.as_str().unwrap().to_string(),
|
||||||
|
"weightedAvgPrice" => value_vec[3] = element.1.as_str().unwrap().to_string(),
|
||||||
|
"prevClosePrice" => value_vec[4] = element.1.as_str().unwrap().to_string(),
|
||||||
|
"lastPrice" => value_vec[5] = element.1.as_str().unwrap().to_string(),
|
||||||
|
"lastQty" => value_vec[6] = element.1.as_str().unwrap().to_string(),
|
||||||
|
"bidPrice" => value_vec[7] = element.1.as_str().unwrap().to_string(),
|
||||||
|
"bidQty" => value_vec[8] = element.1.as_str().unwrap().to_string(),
|
||||||
|
"askPrice" => value_vec[9] = element.1.as_str().unwrap().to_string(),
|
||||||
|
"askQty" => value_vec[10] = element.1.as_str().unwrap().to_string(),
|
||||||
|
"openPrice" => value_vec[11] = element.1.as_str().unwrap().to_string(),
|
||||||
|
"highPrice" => value_vec[12] = element.1.as_str().unwrap().to_string(),
|
||||||
|
"lowPrice" => value_vec[13] = element.1.as_str().unwrap().to_string(),
|
||||||
|
"volume" => value_vec[14] = element.1.as_str().unwrap().to_string(),
|
||||||
|
"quoteVolume" => value_vec[15] = element.1.as_str().unwrap().to_string(),
|
||||||
|
"openTime" => value_vec[16] = element.1.as_i64().unwrap().to_string(),
|
||||||
|
"closeTime" => value_vec[17] = element.1.as_i64().unwrap().to_string(),
|
||||||
|
"firstId" => value_vec[18] = element.1.as_i64().unwrap().to_string(),
|
||||||
|
"lastId" => value_vec[19] = element.1.as_i64().unwrap().to_string(),
|
||||||
|
"count" => value_vec[20] = element.1.as_i64().unwrap().to_string(),
|
||||||
|
_ => {
|
||||||
|
println!("Elements in body msg are changed. Please update both your 24h_change table and vectors.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value_wrapper.push(value_vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
store_24h_change_db(columns, value_wrapper).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn store_24h_change_db(
|
||||||
|
columns: Vec<&str>,
|
||||||
|
value_wrapper: Vec<Vec<String>>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let table_name = String::from("all_24h_change");
|
||||||
|
delete_all_rows(&table_name).await?;
|
||||||
|
insert_records(&table_name, &columns, &value_wrapper).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// request exchange information. (/api, Weight(IP) 10)
|
||||||
|
pub async fn request_exchange_infomation(
|
||||||
|
client: &Client,
|
||||||
|
exchange_info_data: &mut Vec<ExchangeInfo>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
// building URL and API-keys
|
||||||
|
let mut url = String::new();
|
||||||
|
|
||||||
|
url.push_str("https://api.binance.com");
|
||||||
|
|
||||||
|
let endpoint_url = "/api/v3/exchangeInfo";
|
||||||
|
url.push_str(endpoint_url);
|
||||||
|
|
||||||
|
let mut res = client.get(&url).send().await?;
|
||||||
|
|
||||||
|
while !res.status().is_success() {
|
||||||
|
sleep(Duration::from_secs(5)).await;
|
||||||
|
res = client.get(&url).send().await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = res.text_with_charset("utf-8").await?;
|
||||||
|
|
||||||
|
// deserialize JSON
|
||||||
|
let v: Value = serde_json::from_str(body.as_str())?;
|
||||||
|
let mut into_vec = v.as_object();
|
||||||
|
if into_vec == None {
|
||||||
|
return Err("Err")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut data_temp = ExchangeInfo::new();
|
||||||
|
let mut data_temp_vec: Vec<ExchangeInfo> = Vec::new();
|
||||||
|
|
||||||
|
for element in into_vec.unwrap() {
|
||||||
|
if element.0.contains("symbols") {
|
||||||
|
for element in element.1.as_array().unwrap() {
|
||||||
|
if element.is_object() {
|
||||||
|
if element
|
||||||
|
.get("symbol")
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
.unwrap()
|
||||||
|
.ends_with("USDT")
|
||||||
|
{
|
||||||
|
data_temp.symbol =
|
||||||
|
(element.get("symbol").unwrap().as_str().unwrap().to_string());
|
||||||
|
data_temp.base_asset_precision =
|
||||||
|
(element.get("baseAssetPrecision").unwrap().as_u64().unwrap()) as u32;
|
||||||
|
data_temp.base_commission_precision = (element
|
||||||
|
.get("baseCommissionPrecision")
|
||||||
|
.unwrap()
|
||||||
|
.as_u64()
|
||||||
|
.unwrap())
|
||||||
|
as u32;
|
||||||
|
data_temp.quote_asset_precision = (element
|
||||||
|
.get("quoteAssetPrecision")
|
||||||
|
.unwrap()
|
||||||
|
.as_u64()
|
||||||
|
.unwrap())
|
||||||
|
as u32;
|
||||||
|
data_temp.quote_commission_precision = (element
|
||||||
|
.get("quoteCommissionPrecision")
|
||||||
|
.unwrap()
|
||||||
|
.as_u64()
|
||||||
|
.unwrap())
|
||||||
|
as u32;
|
||||||
|
for element in element.get("filters").unwrap().as_array().unwrap() {
|
||||||
|
if element
|
||||||
|
.as_object()
|
||||||
|
.unwrap()
|
||||||
|
.get("filterType")
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
.unwrap()
|
||||||
|
.starts_with("LOT_SIZE")
|
||||||
|
{
|
||||||
|
data_temp.stepsize = rust_decimal::prelude::FromStr::from_str(
|
||||||
|
element.get("stepSize").unwrap().as_str().unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
} else if element
|
||||||
|
.as_object()
|
||||||
|
.unwrap()
|
||||||
|
.get("filterType")
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
.unwrap()
|
||||||
|
.starts_with("PRICE_FILTER")
|
||||||
|
{
|
||||||
|
data_temp.ticksize = rust_decimal::prelude::FromStr::from_str(
|
||||||
|
element.get("tickSize").unwrap().as_str().unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data_temp_vec.push(data_temp.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*exchange_info_data = data_temp_vec;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
758
src/database_control.rs
Normal file
758
src/database_control.rs
Normal file
|
|
@ -0,0 +1,758 @@
|
||||||
|
#![allow(unused)]
|
||||||
|
#![allow(warnings)]
|
||||||
|
|
||||||
|
use crate::DB_URL;
|
||||||
|
use sqlx::{mysql::*, Connection, Error, Executor, FromRow, Row};
|
||||||
|
use std::any::Any;
|
||||||
|
use tokio::time::{sleep, Duration};
|
||||||
|
|
||||||
|
// check specific table in database. If the table exists it returns true, or false.
|
||||||
|
pub async fn exists_table(table_name: &String) -> bool {
|
||||||
|
let mut query_base = String::from("SELECT 1 FROM information_schema.tables WHERE table_schema = 'tradingbot' AND table_name = '");
|
||||||
|
query_base.push_str(table_name.as_str());
|
||||||
|
query_base.push('\'');
|
||||||
|
|
||||||
|
let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
//retry connection until it will be done.
|
||||||
|
while conn_result.is_err() {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
}
|
||||||
|
let mut conn = conn_result.unwrap();
|
||||||
|
|
||||||
|
let exists_table = sqlx::query(&query_base).fetch_all(&mut conn).await.unwrap();
|
||||||
|
|
||||||
|
!exists_table.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
// check specific record in table. If the record exists it returns true, or false.
|
||||||
|
pub async fn exists_record(table_name: &String, condition: &Option<String>) -> bool {
|
||||||
|
#[derive(Debug, FromRow)]
|
||||||
|
struct Success {
|
||||||
|
success: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut query_base = String::from("SELECT EXISTS (SELECT * FROM ");
|
||||||
|
query_base.push_str(table_name.as_str());
|
||||||
|
match condition {
|
||||||
|
Some(T) => {
|
||||||
|
query_base.push_str(" WHERE ");
|
||||||
|
query_base.push_str(T.as_str());
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
query_base.push_str(" LIMIT 1) as success;");
|
||||||
|
|
||||||
|
let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
//retry connection until it will be done.
|
||||||
|
while conn_result.is_err() {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
}
|
||||||
|
let mut conn = conn_result.unwrap();
|
||||||
|
let mut exists_record = sqlx::query_as::<_, Success>(&query_base)
|
||||||
|
.fetch_one(&mut conn)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
while exists_record.is_err() {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
exists_record = sqlx::query_as::<_, Success>(&query_base)
|
||||||
|
.fetch_one(&mut conn)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = exists_record.unwrap();
|
||||||
|
if result.success == 0 {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// make a new table. If the job succeeds it returns true, or false.
|
||||||
|
// columns_vec: Vec<(column name, type, option)>
|
||||||
|
pub async fn new_table(
|
||||||
|
table_name: &String,
|
||||||
|
columns_vec: &Vec<(&str, &str, Option<&str>)>,
|
||||||
|
table_condition: &Option<&str>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
// query building.
|
||||||
|
let mut query = String::from("CREATE TABLE IF NOT EXISTS ");
|
||||||
|
query.push_str(table_name);
|
||||||
|
query.push('(');
|
||||||
|
|
||||||
|
for element in columns_vec.iter() {
|
||||||
|
query.push_str(element.0);
|
||||||
|
query.push(' ');
|
||||||
|
query.push_str(element.1);
|
||||||
|
query.push(' ');
|
||||||
|
|
||||||
|
match element.2 {
|
||||||
|
None => {}
|
||||||
|
_ => {
|
||||||
|
if (element.2.unwrap().contains("UNSIGNED")) {
|
||||||
|
query.push_str("UNSIGNED ");
|
||||||
|
}
|
||||||
|
if (element.2.unwrap().contains("UN")) {
|
||||||
|
query.push_str("UNSIGNED ");
|
||||||
|
}
|
||||||
|
if (element.2.unwrap().contains("PK")) {
|
||||||
|
query.push_str("PRIMARY KEY ");
|
||||||
|
}
|
||||||
|
if (element.2.unwrap().contains("NOTNULL")) {
|
||||||
|
query.push_str("NOT NULL");
|
||||||
|
}
|
||||||
|
if (element.2.unwrap().contains("AI")) {
|
||||||
|
query.push_str("AUTO_INCREMENT ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
query.push(',');
|
||||||
|
}
|
||||||
|
query.pop();
|
||||||
|
query.push_str(")");
|
||||||
|
|
||||||
|
match table_condition {
|
||||||
|
Some(T) => {
|
||||||
|
query.push(' ');
|
||||||
|
query.push_str(T);
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
query.push(';');
|
||||||
|
|
||||||
|
let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
//retry connection until it will be done.
|
||||||
|
while conn_result.is_err() {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
}
|
||||||
|
let mut conn = conn_result.unwrap();
|
||||||
|
|
||||||
|
let mut query_result = sqlx::query(&query).execute(&mut conn).await;
|
||||||
|
while let Err(e) = query_result {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
query_result = sqlx::query(&query).execute(&mut conn).await;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// drop a table. If the job succeeds it returns true, or false.
|
||||||
|
pub async fn drop_table(table_name: &String) -> Result<MySqlQueryResult, Error> {
|
||||||
|
let mut query = String::from("DROP TABLE `tradingbot`.`");
|
||||||
|
query.push_str(table_name);
|
||||||
|
query.push_str("`;");
|
||||||
|
|
||||||
|
let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
//retry connection until it will be done.
|
||||||
|
while conn_result.is_err() {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
}
|
||||||
|
let mut conn = conn_result.unwrap();
|
||||||
|
|
||||||
|
let query_result = sqlx::query(&query).execute(&mut conn).await;
|
||||||
|
query_result
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy data from source table to destination table
|
||||||
|
// both table structures must be same
|
||||||
|
pub async fn copy_table_data(
|
||||||
|
source_table: &String,
|
||||||
|
destination_table: &String,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
// query building.
|
||||||
|
let mut query = String::from("INSERT INTO ");
|
||||||
|
query.push_str(destination_table.as_str());
|
||||||
|
query.push_str(" (SELECT * FROM ");
|
||||||
|
query.push_str(source_table.as_str());
|
||||||
|
query.push_str(");");
|
||||||
|
|
||||||
|
let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
//retry connection until it will be done.
|
||||||
|
while conn_result.is_err() {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
}
|
||||||
|
let mut conn = conn_result.unwrap();
|
||||||
|
|
||||||
|
let mut query_result = sqlx::query(&query).execute(&mut conn).await;
|
||||||
|
|
||||||
|
while let Err(e) = query_result {
|
||||||
|
query_result = sqlx::query(&query).execute(&mut conn).await;
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert a record into specific table
|
||||||
|
// e.g. column: ["name", "class", "age"],
|
||||||
|
// values: [["Kim", "blue", "7"]
|
||||||
|
pub async fn insert_one_record(
|
||||||
|
table_name: &String,
|
||||||
|
columns: &Vec<&str>,
|
||||||
|
values: &Vec<String>,
|
||||||
|
) -> Result<MySqlQueryResult, Error> {
|
||||||
|
// query building.
|
||||||
|
let mut query = String::from("INSERT INTO ");
|
||||||
|
query.push_str(table_name);
|
||||||
|
query.push('(');
|
||||||
|
|
||||||
|
for element in (*columns).clone() {
|
||||||
|
query.push_str(element);
|
||||||
|
query.push_str(",");
|
||||||
|
}
|
||||||
|
query.pop();
|
||||||
|
query.push_str(") ");
|
||||||
|
|
||||||
|
query.push_str("VALUES(");
|
||||||
|
for element in (*values).clone() {
|
||||||
|
query.push('\"');
|
||||||
|
if element == "true" {
|
||||||
|
query.push('1');
|
||||||
|
} else if element == "false" {
|
||||||
|
query.push('0');
|
||||||
|
} else {
|
||||||
|
query.push_str(element.as_str());
|
||||||
|
}
|
||||||
|
query.push('\"');
|
||||||
|
query.push_str(",");
|
||||||
|
}
|
||||||
|
query.pop();
|
||||||
|
query.push_str(");");
|
||||||
|
|
||||||
|
let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
//retry connection until it will be done.
|
||||||
|
while conn_result.is_err() {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
}
|
||||||
|
let mut conn = conn_result.unwrap();
|
||||||
|
|
||||||
|
let query_result = sqlx::query(&query).execute(&mut conn).await;
|
||||||
|
|
||||||
|
query_result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn copy_record(
|
||||||
|
src_table: &String,
|
||||||
|
dest_table: &String,
|
||||||
|
columns: &str,
|
||||||
|
condition: &Option<String>,
|
||||||
|
) -> Result<MySqlQueryResult, Error> {
|
||||||
|
// query building.
|
||||||
|
let mut query = String::from("INSERT INTO ");
|
||||||
|
query.push_str(dest_table.as_str());
|
||||||
|
query.push_str(" (");
|
||||||
|
query.push_str(columns);
|
||||||
|
query.push_str(") SELECT ");
|
||||||
|
query.push_str(columns);
|
||||||
|
query.push_str(" FROM ");
|
||||||
|
query.push_str(src_table.as_str());
|
||||||
|
|
||||||
|
match condition {
|
||||||
|
None => {}
|
||||||
|
Some(condition) => {
|
||||||
|
query.push(' ');
|
||||||
|
query.push_str(condition.as_str());
|
||||||
|
query.push(';');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
//retry connection until it will be done.
|
||||||
|
while conn_result.is_err() {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
}
|
||||||
|
let mut conn = conn_result.unwrap();
|
||||||
|
|
||||||
|
let query_result = sqlx::query(&query).execute(&mut conn).await;
|
||||||
|
|
||||||
|
query_result
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert several records into specific table at once
|
||||||
|
// the length of columns and each inner vector in values should be same.
|
||||||
|
// e.g. column: ["name", "class", "age"],
|
||||||
|
// values: [["Kim", "blue", "7"], ["Lee", "red", "9"], ...]
|
||||||
|
pub async fn insert_records(
|
||||||
|
table_name: &String,
|
||||||
|
columns: &Vec<&str>,
|
||||||
|
values: &Vec<Vec<String>>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
// query building.
|
||||||
|
let mut query = String::from("INSERT INTO ");
|
||||||
|
query.push_str(table_name);
|
||||||
|
query.push('(');
|
||||||
|
|
||||||
|
for element in (*columns).clone() {
|
||||||
|
query.push_str(element);
|
||||||
|
query.push_str(",");
|
||||||
|
}
|
||||||
|
query.pop();
|
||||||
|
query.push_str(") ");
|
||||||
|
|
||||||
|
query.push_str("VALUES ");
|
||||||
|
for element in (*values).clone() {
|
||||||
|
query.push('(');
|
||||||
|
for element in element.into_iter() {
|
||||||
|
query.push('\'');
|
||||||
|
if element == "true" {
|
||||||
|
query.push('1');
|
||||||
|
} else if element == "false" {
|
||||||
|
query.push('0');
|
||||||
|
} else {
|
||||||
|
query.push_str(element.as_str());
|
||||||
|
}
|
||||||
|
query.push_str("\',");
|
||||||
|
}
|
||||||
|
query.pop();
|
||||||
|
query.push_str("),");
|
||||||
|
}
|
||||||
|
query.pop();
|
||||||
|
query.push_str(";");
|
||||||
|
|
||||||
|
let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
//retry connection until it will be done.
|
||||||
|
while conn_result.is_err() {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
}
|
||||||
|
let mut conn = conn_result.unwrap();
|
||||||
|
|
||||||
|
let mut query_result = sqlx::query(&query).execute(&mut conn).await;
|
||||||
|
|
||||||
|
while let Err(e) = query_result {
|
||||||
|
query_result = sqlx::query(&query).execute(&mut conn).await;
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// update a record in table
|
||||||
|
// record: [column name, value], condition: (column name, value)
|
||||||
|
pub async fn update_record(
|
||||||
|
table_name: &String,
|
||||||
|
record: &Vec<(&str, &str)>,
|
||||||
|
condition: &Vec<(&str, &str)>,
|
||||||
|
) -> Result<MySqlQueryResult, Error> {
|
||||||
|
// query building.
|
||||||
|
let mut query = String::from("UPDATE ");
|
||||||
|
query.push_str(table_name);
|
||||||
|
query.push_str(" SET ");
|
||||||
|
|
||||||
|
for element in record.iter() {
|
||||||
|
query.push_str(element.0);
|
||||||
|
query.push('=');
|
||||||
|
if element.1 == "true" {
|
||||||
|
query.push('1');
|
||||||
|
} else if element.1 == "false" {
|
||||||
|
query.push('0');
|
||||||
|
} else {
|
||||||
|
query.push('\"');
|
||||||
|
query.push_str(element.1);
|
||||||
|
query.push('\"');
|
||||||
|
}
|
||||||
|
query.push_str(",");
|
||||||
|
}
|
||||||
|
query.pop();
|
||||||
|
query.push_str(" WHERE ");
|
||||||
|
for element in condition {
|
||||||
|
query.push_str(element.0);
|
||||||
|
query.push_str("=\"");
|
||||||
|
query.push_str(element.1);
|
||||||
|
query.push_str("\" AND ");
|
||||||
|
}
|
||||||
|
query.pop();
|
||||||
|
query.pop();
|
||||||
|
query.pop();
|
||||||
|
query.pop();
|
||||||
|
query.push(';');
|
||||||
|
|
||||||
|
let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
//retry connection until it will be done.
|
||||||
|
while conn_result.is_err() {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
}
|
||||||
|
let mut conn = conn_result.unwrap();
|
||||||
|
|
||||||
|
let query_result = sqlx::query(&query).execute(&mut conn).await;
|
||||||
|
|
||||||
|
query_result
|
||||||
|
}
|
||||||
|
|
||||||
|
// update a record in table
|
||||||
|
// record: [column name, value], condition: (column name, value)
|
||||||
|
pub async fn update_record2(
|
||||||
|
table_name: &String,
|
||||||
|
record: &Vec<(String, String)>,
|
||||||
|
condition: &Vec<(String, String)>,
|
||||||
|
) -> Result<MySqlQueryResult, Error> {
|
||||||
|
// query building.
|
||||||
|
let mut query = String::from("UPDATE ");
|
||||||
|
query.push_str(table_name);
|
||||||
|
query.push_str(" SET ");
|
||||||
|
|
||||||
|
for element in record.iter() {
|
||||||
|
query.push_str(element.0.as_str());
|
||||||
|
query.push('=');
|
||||||
|
if element.1.as_str() == "true" {
|
||||||
|
query.push('1');
|
||||||
|
} else if element.1.as_str() == "false" {
|
||||||
|
query.push('0');
|
||||||
|
} else {
|
||||||
|
query.push('\"');
|
||||||
|
query.push_str(element.1.as_str());
|
||||||
|
query.push('\"');
|
||||||
|
}
|
||||||
|
query.push_str(",");
|
||||||
|
}
|
||||||
|
query.pop();
|
||||||
|
query.push_str(" WHERE ");
|
||||||
|
for element in condition {
|
||||||
|
query.push_str(element.0.as_str());
|
||||||
|
query.push_str("=\"");
|
||||||
|
query.push_str(element.1.as_str());
|
||||||
|
query.push_str("\" AND ");
|
||||||
|
}
|
||||||
|
query.pop();
|
||||||
|
query.pop();
|
||||||
|
query.pop();
|
||||||
|
query.pop();
|
||||||
|
query.push(';');
|
||||||
|
|
||||||
|
let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
//retry connection until it will be done.
|
||||||
|
while conn_result.is_err() {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
}
|
||||||
|
let mut conn = conn_result.unwrap();
|
||||||
|
|
||||||
|
let query_result = sqlx::query(&query).execute(&mut conn).await;
|
||||||
|
|
||||||
|
query_result
|
||||||
|
}
|
||||||
|
|
||||||
|
// update a record in table
|
||||||
|
// record: (column name, value), condition: (column name, value)
|
||||||
|
pub async fn update_record3(
|
||||||
|
table_name: &String,
|
||||||
|
record: &Vec<(String, String)>,
|
||||||
|
condition: &Vec<(String, String)>,
|
||||||
|
) -> Result<MySqlQueryResult, Error> {
|
||||||
|
// query building.
|
||||||
|
let mut query = String::from("UPDATE ");
|
||||||
|
query.push_str(table_name);
|
||||||
|
query.push_str(" SET ");
|
||||||
|
|
||||||
|
for element in record.iter() {
|
||||||
|
query.push_str(element.0.as_str());
|
||||||
|
query.push('=');
|
||||||
|
if element.1.as_str() == "true" {
|
||||||
|
query.push('1');
|
||||||
|
} else if element.1.as_str() == "false" {
|
||||||
|
query.push('0');
|
||||||
|
} else {
|
||||||
|
query.push_str(element.1.as_str());
|
||||||
|
}
|
||||||
|
query.push_str(",");
|
||||||
|
}
|
||||||
|
query.pop();
|
||||||
|
query.push_str(" WHERE ");
|
||||||
|
for element in condition {
|
||||||
|
query.push_str(element.0.as_str());
|
||||||
|
query.push_str("=\"");
|
||||||
|
query.push_str(element.1.as_str());
|
||||||
|
query.push_str("\" AND ");
|
||||||
|
}
|
||||||
|
query.pop();
|
||||||
|
query.pop();
|
||||||
|
query.pop();
|
||||||
|
query.pop();
|
||||||
|
query.push(';');
|
||||||
|
|
||||||
|
let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
//retry connection until it will be done.
|
||||||
|
while conn_result.is_err() {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
}
|
||||||
|
let mut conn = conn_result.unwrap();
|
||||||
|
|
||||||
|
let query_result = sqlx::query(&query).execute(&mut conn).await;
|
||||||
|
|
||||||
|
query_result
|
||||||
|
}
|
||||||
|
|
||||||
|
// update many records in a row
|
||||||
|
// record: [[id1, val1_1, val2_1], [id2, val1_2, val2_1], ...] (the first element in each record must be id)
|
||||||
|
// columns: ["col_name1", "col_name2"] (columns except id)
|
||||||
|
pub async fn update_records(
|
||||||
|
table_name: &String,
|
||||||
|
records: &Vec<Vec<String>>,
|
||||||
|
columns: &Vec<&str>,
|
||||||
|
) -> Result<MySqlQueryResult, Error> {
|
||||||
|
// query building.
|
||||||
|
let mut query = String::from("UPDATE ");
|
||||||
|
query.push_str(table_name);
|
||||||
|
query.push_str(" s JOIN(");
|
||||||
|
|
||||||
|
let mut first_flag: bool = true;
|
||||||
|
let col_len = columns.len();
|
||||||
|
let mut record_count: usize = 0;
|
||||||
|
let mut col_count: usize = 0;
|
||||||
|
let mut new_scores_vec: Vec<String> = Vec::new();
|
||||||
|
let mut new_score_name = String::from("new_score");
|
||||||
|
|
||||||
|
for _ in 0..col_len {
|
||||||
|
new_scores_vec.push(new_score_name.clone());
|
||||||
|
new_scores_vec[record_count].push_str(record_count.to_string().as_str());
|
||||||
|
record_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
record_count = 0;
|
||||||
|
for record in records {
|
||||||
|
query.push_str("SELECT ");
|
||||||
|
if first_flag == true {
|
||||||
|
for element in record {
|
||||||
|
if record_count == 0 {
|
||||||
|
query.push_str(&element);
|
||||||
|
query.push_str(" as id, ");
|
||||||
|
record_count += 1;
|
||||||
|
} else {
|
||||||
|
query.push_str(&element);
|
||||||
|
query.push_str(" as ");
|
||||||
|
query.push_str(new_scores_vec[col_count].clone().as_str());
|
||||||
|
query.push_str(", ");
|
||||||
|
record_count += 1;
|
||||||
|
col_count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
query.pop();
|
||||||
|
query.pop();
|
||||||
|
query.push(' ');
|
||||||
|
first_flag = false;
|
||||||
|
} else {
|
||||||
|
for _ in 0..7 {
|
||||||
|
query.pop();
|
||||||
|
}
|
||||||
|
query.push_str("UNION ALL SELECT ");
|
||||||
|
for element in record {
|
||||||
|
if element.as_str() == "true" {
|
||||||
|
query.push('1');
|
||||||
|
} else if element.as_str() == "false" {
|
||||||
|
query.push('0');
|
||||||
|
} else {
|
||||||
|
query.push_str(element.as_str());
|
||||||
|
}
|
||||||
|
query.push_str(",");
|
||||||
|
}
|
||||||
|
query.pop();
|
||||||
|
query.push(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
query.push_str(") vals ON s.id = vals.id SET ");
|
||||||
|
|
||||||
|
record_count = 0;
|
||||||
|
for element in new_scores_vec {
|
||||||
|
query.push_str(columns[record_count]);
|
||||||
|
query.push_str(" = ");
|
||||||
|
query.push_str(&element);
|
||||||
|
query.push_str(", ");
|
||||||
|
record_count += 1;
|
||||||
|
}
|
||||||
|
query.pop();
|
||||||
|
query.pop();
|
||||||
|
query.push(';');
|
||||||
|
|
||||||
|
let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
//retry connection until it will be done.
|
||||||
|
while conn_result.is_err() {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
}
|
||||||
|
let mut conn = conn_result.unwrap();
|
||||||
|
|
||||||
|
let query_result = sqlx::query(&query).execute(&mut conn).await;
|
||||||
|
|
||||||
|
query_result
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete a record in table
|
||||||
|
// condition: (column name, value)
|
||||||
|
pub async fn delete_record(
|
||||||
|
table_name: &String,
|
||||||
|
condition: &String,
|
||||||
|
) -> Result<MySqlQueryResult, Error> {
|
||||||
|
// query building.
|
||||||
|
let mut query = String::from("DELETE FROM ");
|
||||||
|
query.push_str(table_name);
|
||||||
|
query.push(' ');
|
||||||
|
query.push_str(condition.as_str());
|
||||||
|
query.push(';');
|
||||||
|
|
||||||
|
let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
//retry connection until it will be done.
|
||||||
|
while conn_result.is_err() {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
}
|
||||||
|
let mut conn = conn_result.unwrap();
|
||||||
|
|
||||||
|
let query_result = sqlx::query(&query).execute(&mut conn).await;
|
||||||
|
|
||||||
|
query_result
|
||||||
|
}
|
||||||
|
|
||||||
|
// count total row in table
|
||||||
|
pub async fn count_rows(table_name: &String) -> Result<i32, Error> {
|
||||||
|
#[derive(FromRow)]
|
||||||
|
struct Count {
|
||||||
|
cnt: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// query building.
|
||||||
|
let mut query = String::from("SELECT COUNT(*) as cnt FROM ");
|
||||||
|
query.push_str(table_name.as_str());
|
||||||
|
query.push(';');
|
||||||
|
|
||||||
|
let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
//retry connection until it will be done.
|
||||||
|
while conn_result.is_err() {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
}
|
||||||
|
let mut conn = conn_result.unwrap();
|
||||||
|
|
||||||
|
let query_result = sqlx::query_as::<_, Count>(&query)
|
||||||
|
.fetch_one(&mut conn)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let result = match query_result {
|
||||||
|
Ok(T) => Ok(T.cnt),
|
||||||
|
Err(E) => return Err(E),
|
||||||
|
};
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete all rows in table
|
||||||
|
pub async fn delete_all_rows(
|
||||||
|
table_name: &String,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let mut query = String::from("TRUNCATE TABLE ");
|
||||||
|
query.push_str(table_name);
|
||||||
|
query.push_str(";");
|
||||||
|
|
||||||
|
let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
//retry connection until it will be done.
|
||||||
|
while conn_result.is_err() {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
}
|
||||||
|
let mut conn = conn_result.unwrap();
|
||||||
|
|
||||||
|
let mut query_result = sqlx::query(&query).execute(&mut conn).await;
|
||||||
|
|
||||||
|
while let Err(e) = query_result {
|
||||||
|
query_result = sqlx::query(&query).execute(&mut conn).await;
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// select record from table
|
||||||
|
pub async fn select_record<T>(
|
||||||
|
table_name: &String,
|
||||||
|
column: &String,
|
||||||
|
condition: &Option<String>,
|
||||||
|
data_struct: &T,
|
||||||
|
) -> Result<Vec<T>, Box<dyn std::error::Error + Send + Sync>>
|
||||||
|
where
|
||||||
|
T: for<'r> FromRow<'r, MySqlRow> + Send + Unpin,
|
||||||
|
{
|
||||||
|
let mut query = String::from("SELECT ");
|
||||||
|
query.push_str(column.to_lowercase().as_str());
|
||||||
|
query.push_str(" FROM ");
|
||||||
|
query.push_str(table_name.to_lowercase().as_str());
|
||||||
|
|
||||||
|
match condition {
|
||||||
|
None => {}
|
||||||
|
Some(condition) => {
|
||||||
|
query.push(' ');
|
||||||
|
query.push_str(condition.as_str());
|
||||||
|
query.push(';');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
//retry connection until it will be done.
|
||||||
|
while conn_result.is_err() {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
}
|
||||||
|
let mut conn = conn_result.unwrap();
|
||||||
|
|
||||||
|
// let mut query_result: Vec<T> = sqlx::query_as::<_, T>(&query).fetch_all(&mut conn).await?;
|
||||||
|
let mut query_result = sqlx::query_as::<_, T>(&query).fetch_all(&mut conn).await;
|
||||||
|
let mut query_result_vec: Vec<T> = Vec::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match query_result {
|
||||||
|
Ok(T) => {
|
||||||
|
if !T.is_empty() {
|
||||||
|
query_result_vec = T;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
query_result = sqlx::query_as::<_, T>(&query).fetch_all(&mut conn).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
query_result = sqlx::query_as::<_, T>(&query).fetch_all(&mut conn).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(query_result_vec)
|
||||||
|
}
|
||||||
|
|
||||||
|
// select record from table. No loop
|
||||||
|
pub async fn try_select_record<T>(
|
||||||
|
table_name: &String,
|
||||||
|
column: &String,
|
||||||
|
condition: &Option<String>,
|
||||||
|
data_struct: &T,
|
||||||
|
) -> Result<Vec<T>, Box<dyn std::error::Error + Send + Sync>>
|
||||||
|
where
|
||||||
|
T: for<'r> FromRow<'r, MySqlRow> + Send + Unpin,
|
||||||
|
{
|
||||||
|
let mut query = String::from("SELECT ");
|
||||||
|
query.push_str(column.to_lowercase().as_str());
|
||||||
|
query.push_str(" FROM ");
|
||||||
|
query.push_str(table_name.to_lowercase().as_str());
|
||||||
|
|
||||||
|
match condition {
|
||||||
|
None => {}
|
||||||
|
Some(condition) => {
|
||||||
|
query.push(' ');
|
||||||
|
query.push_str(condition.as_str());
|
||||||
|
query.push(';');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
//retry connection until it will be done.
|
||||||
|
while conn_result.is_err() {
|
||||||
|
sleep(Duration::from_millis(200)).await;
|
||||||
|
conn_result = sqlx::mysql::MySqlConnection::connect(DB_URL).await;
|
||||||
|
}
|
||||||
|
let mut conn = conn_result.unwrap();
|
||||||
|
|
||||||
|
let mut query_result: Vec<T> = sqlx::query_as::<_, T>(&query).fetch_all(&mut conn).await?;
|
||||||
|
|
||||||
|
Ok(query_result)
|
||||||
|
}
|
||||||
38
src/decimal_funcs.rs
Normal file
38
src/decimal_funcs.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
use rust_decimal::{Decimal, RoundingStrategy};
|
||||||
|
|
||||||
|
pub fn decimal(string_num: &String) -> Decimal {
|
||||||
|
let mut temp_num = string_num.clone();
|
||||||
|
if temp_num.contains('.') {
|
||||||
|
temp_num.remove(temp_num.find('.').unwrap());
|
||||||
|
}
|
||||||
|
let decimal_num = temp_num.parse::<i128>().unwrap();
|
||||||
|
Decimal::from_i128_with_scale(decimal_num, floating_scale(string_num))
|
||||||
|
.round_dp_with_strategy(8, RoundingStrategy::ToZero)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn floating_scale(string_num: &String) -> u32 {
|
||||||
|
let temp_num = string_num.clone();
|
||||||
|
if temp_num.contains('.') {
|
||||||
|
let len = temp_num.to_string().len();
|
||||||
|
let point_index = temp_num.find('.').unwrap();
|
||||||
|
(len - (point_index + 1)) as u32
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decimal_mul(num1: Decimal, num2: Decimal) -> Decimal {
|
||||||
|
(num1 * num2).round_dp_with_strategy(8, RoundingStrategy::ToZero)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decimal_add(num1: Decimal, num2: Decimal) -> Decimal {
|
||||||
|
(num1 + num2).round_dp_with_strategy(8, RoundingStrategy::ToZero)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decimal_sub(num1: Decimal, num2: Decimal) -> Decimal {
|
||||||
|
(num1 - num2).round_dp_with_strategy(8, RoundingStrategy::ToZero)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decimal_div(num1: Decimal, num2: Decimal) -> Decimal {
|
||||||
|
(num1 / num2).round_dp_with_strategy(8, RoundingStrategy::ToZero)
|
||||||
|
}
|
||||||
1442
src/initialization.rs
Normal file
1442
src/initialization.rs
Normal file
File diff suppressed because it is too large
Load Diff
34
src/lib.rs
Normal file
34
src/lib.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#![allow(unused)]
|
||||||
|
#![allow(warnings)]
|
||||||
|
|
||||||
|
pub const DB_URL: &str = "mysql://root:Durtkarovh23!@localhost:3306/tradingbot";
|
||||||
|
|
||||||
|
// put your API Key and Signature in below variables respectively.
|
||||||
|
pub const API_KEY: &str = "h7HPxyfUpROa7GINE3Ktx5z5p59jESWyFRKhk2D7rhd6dgcLroyNW4urnujjGCmO"; // real account
|
||||||
|
pub const SECRET_KEY: &str = "TkhHmNUG6fCMZcCSjfYn8ZY5UfDbD9O11g9scp2P6fgdyH0eDzcCS9lNpSCLSR6Y"; // real account
|
||||||
|
pub const API_KEY_TESTNET: &str =
|
||||||
|
"NO6QbO5FvBQtYkdl2MCz819hBxbY83gpoSijCgOQbEgNyLWMny2KJOV5LwPsRGsS"; // for spot trading testnet
|
||||||
|
pub const SECRET_KEY_TESTNET: &str =
|
||||||
|
"9cQCq6ufVvxrDaOnnVDszy8ZzSYnuTTNk0WEbo3lNkFCIFM9OQ1l3IDPVz5NiTxA"; // for spot trading testnet
|
||||||
|
|
||||||
|
// URL
|
||||||
|
pub const URL_TEST: &str = "https://testnet.binance.vision";
|
||||||
|
pub const URL: &str = "https://api1.binance.com";
|
||||||
|
|
||||||
|
// Select program mode
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum RunningMode {
|
||||||
|
SIMUL,
|
||||||
|
TEST,
|
||||||
|
REAL,
|
||||||
|
}
|
||||||
|
pub const RUNNING_MODE: RunningMode = RunningMode::REAL;
|
||||||
|
|
||||||
|
mod coex;
|
||||||
|
mod coin_health_check_team;
|
||||||
|
mod database_control;
|
||||||
|
mod decimal_funcs;
|
||||||
|
mod server_health_check_team;
|
||||||
|
mod signal_association;
|
||||||
|
mod time_checking_team;
|
||||||
|
mod value_estimation_team;
|
||||||
2363
src/main.rs
Normal file
2363
src/main.rs
Normal file
File diff suppressed because it is too large
Load Diff
146
src/server_health_check_team.rs
Normal file
146
src/server_health_check_team.rs
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
#![allow(unused)]
|
||||||
|
#![allow(warnings)]
|
||||||
|
|
||||||
|
use crate::database_control::*;
|
||||||
|
use crate::time_checking_team::UserTime;
|
||||||
|
use rand::*;
|
||||||
|
use reqwest::{Client, ClientBuilder, Response};
|
||||||
|
use tokio::join;
|
||||||
|
use tokio::time::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ServerHealth {
|
||||||
|
pub server_on: bool, // the total status of server. Its value is decided by AND operation of ping_on and wallet_system_on
|
||||||
|
// through is_server_on method.
|
||||||
|
pub ping_on: bool, // ping test to server.
|
||||||
|
pub wallet_system_on: bool, // check wallet system of server.
|
||||||
|
pub waiting_maximum: u32, // maximum time for timer to wait until server works on again. the value will be null when the server works on.
|
||||||
|
}
|
||||||
|
impl ServerHealth {
|
||||||
|
pub fn new() -> ServerHealth {
|
||||||
|
// make an instance and initialize fields
|
||||||
|
ServerHealth {
|
||||||
|
server_on: false,
|
||||||
|
ping_on: false,
|
||||||
|
wallet_system_on: false,
|
||||||
|
waiting_maximum: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// to check the current server health and store data into database.
|
||||||
|
pub async fn execute_server_health_check(
|
||||||
|
usertime: &mut UserTime,
|
||||||
|
serverhealth: &mut ServerHealth,
|
||||||
|
client: &Client,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let mut rng = rand::rngs::OsRng;
|
||||||
|
let mut waiting_time: u32;
|
||||||
|
loop {
|
||||||
|
update(serverhealth, usertime, &client).await?;
|
||||||
|
|
||||||
|
if serverhealth.server_on == true {
|
||||||
|
serverhealth.waiting_maximum = 0;
|
||||||
|
// update_database(&serverhealth).await;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
usertime.update_last_server().await;
|
||||||
|
// whenever server is off, waiting_maximum increases a second
|
||||||
|
// and thread sleeps for uniform distribution random number between 1sec and waiting_maximum.
|
||||||
|
serverhealth.waiting_maximum += 1;
|
||||||
|
waiting_time = rng.gen_range(1..=serverhealth.waiting_maximum);
|
||||||
|
|
||||||
|
// update_database(&serverhealth).await;
|
||||||
|
println!(">>> retry connection after {} second(s).", waiting_time);
|
||||||
|
sleep(Duration::from_secs(waiting_time as u64)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// println!("server health 완료");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the server on or off
|
||||||
|
async fn update(
|
||||||
|
serverHealth: &mut ServerHealth,
|
||||||
|
usertime: &mut UserTime,
|
||||||
|
client: &Client,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let (r1, r2) = join!(check_ping(usertime, &client), check_wallet_system(&client));
|
||||||
|
serverHealth.ping_on = r1?;
|
||||||
|
serverHealth.wallet_system_on = r2?;
|
||||||
|
serverHealth.server_on = serverHealth.ping_on && serverHealth.wallet_system_on;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// test ping to server and record the last running time of server (/api, Weight 1)
|
||||||
|
async fn check_ping(
|
||||||
|
usertime: &mut UserTime,
|
||||||
|
client: &Client,
|
||||||
|
) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let response = client
|
||||||
|
.get("https://api.binance.com/api/v3/ping")
|
||||||
|
.send()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Ok(T) => {
|
||||||
|
let body = T.text_with_charset("utf-8").await?;
|
||||||
|
if body.contains("{}") {
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => Ok(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check wallet system status (/sapi, Weight 1)
|
||||||
|
async fn check_wallet_system(
|
||||||
|
client: &Client,
|
||||||
|
) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let response = client
|
||||||
|
.get("https://api.binance.com/sapi/v1/system/status")
|
||||||
|
.send()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Ok(T) => {
|
||||||
|
let body = T.text_with_charset("utf-8").await?;
|
||||||
|
if body.contains("0") {
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(E) => Ok(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_database(serverhealth: &ServerHealth) {
|
||||||
|
let table_name = String::from("serverhealth");
|
||||||
|
let mut record: Vec<(&str, &str)> = Vec::new();
|
||||||
|
let condition = vec![("id", "1")];
|
||||||
|
|
||||||
|
let mut server_on = serverhealth.server_on.to_string();
|
||||||
|
let mut ping_on = serverhealth.ping_on.to_string();
|
||||||
|
let mut wallet_system_on = serverhealth.wallet_system_on.to_string();
|
||||||
|
let mut waiting_maximum = serverhealth.waiting_maximum.to_string();
|
||||||
|
|
||||||
|
record.push(("server_on", server_on.as_str()));
|
||||||
|
record.push(("ping_on", ping_on.as_str()));
|
||||||
|
record.push(("wallet_system_on", wallet_system_on.as_str()));
|
||||||
|
record.push(("waiting_maximum", waiting_maximum.as_str()));
|
||||||
|
|
||||||
|
let mut delete_query_result = update_record(&table_name, &record, &condition).await;
|
||||||
|
if delete_query_result.is_err() {
|
||||||
|
loop {
|
||||||
|
delete_query_result = update_record(&table_name, &record, &condition).await;
|
||||||
|
if delete_query_result.is_ok() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sleep(Duration::from_millis(10)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/signal_association.rs
Normal file
5
src/signal_association.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
pub mod coinmarketcap;
|
||||||
|
pub mod dollar_index;
|
||||||
|
pub mod exchange_rate;
|
||||||
|
pub mod future_ratio;
|
||||||
|
pub mod signal_decision;
|
||||||
609
src/signal_association/coinmarketcap.rs
Normal file
609
src/signal_association/coinmarketcap.rs
Normal file
|
|
@ -0,0 +1,609 @@
|
||||||
|
use crate::database_control::*;
|
||||||
|
use serde_json::Value;
|
||||||
|
use sqlx::{Error, FromRow};
|
||||||
|
use thirtyfour::{prelude::*, OptionRect};
|
||||||
|
use tokio::{join, time::*};
|
||||||
|
|
||||||
|
pub async fn initialize_association_record() {
|
||||||
|
let table_name = String::from("signal_association_opinion");
|
||||||
|
let initial_columns = vec!["name", "is_working", "opinion", "weight"];
|
||||||
|
let initial_values = vec![
|
||||||
|
String::from("coinmarketcap"),
|
||||||
|
String::from("0"),
|
||||||
|
String::from("-"),
|
||||||
|
String::from("0.0"),
|
||||||
|
];
|
||||||
|
insert_one_record(&table_name, &initial_columns, &initial_values).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn market_cap_index() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let association_update_table_name = String::from("signal_association_opinion");
|
||||||
|
let association_update_condition = vec![(String::from("name"), String::from("coinmarketcap"))];
|
||||||
|
|
||||||
|
let caps = DesiredCapabilities::chrome();
|
||||||
|
let driver = WebDriver::new("http://localhost:9515/", &caps).await?;
|
||||||
|
|
||||||
|
// get the current window handle
|
||||||
|
let handle = driver.current_window_handle().await?;
|
||||||
|
|
||||||
|
// open browser and url
|
||||||
|
driver.get("https://web-api.coinmarketcap.com/v1.1/global-metrics/quotes/historical?format=chart&interval=5m&count=60").await?;
|
||||||
|
driver.execute_script(r#"window.open("https://web-api.coinmarketcap.com/v1.1/global-metrics/quotes/historical?format=chart&interval=5m&count=1440");"#).await?;
|
||||||
|
// driver.get("https://web-api.coinmarketcap.com/v1.1/global-metrics/quotes/historical?format=chart&interval=30m").await?;
|
||||||
|
// driver.execute_script(r#"window.open("https://web-api.coinmarketcap.com/v1.1/global-metrics/quotes/historical?format=chart&interval=12h");"#).await?;
|
||||||
|
|
||||||
|
// get window handles and switch to the new tab
|
||||||
|
let handles = driver.window_handles().await?;
|
||||||
|
// switch the first tab
|
||||||
|
let elem = driver.find_element(By::Tag("pre")).await?;
|
||||||
|
let text = elem.text().await?;
|
||||||
|
|
||||||
|
// JSON Parsing
|
||||||
|
let mut v: Value = serde_json::from_str(text.as_str()).unwrap();
|
||||||
|
let mut into_vec = v
|
||||||
|
.as_object()
|
||||||
|
.unwrap()
|
||||||
|
.get("data")
|
||||||
|
.unwrap()
|
||||||
|
.as_object()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// initialize variables
|
||||||
|
let mut market_caps: Vec<f64> = Vec::new();
|
||||||
|
let mut last_updated_time = String::new();
|
||||||
|
let mut normalized_values: Vec<f64> = Vec::new();
|
||||||
|
let mut degree_values: Vec<f64> = Vec::new();
|
||||||
|
let mut weighted_average_degree: f64 = 0.0;
|
||||||
|
|
||||||
|
last_updated_time = into_vec.keys().last().unwrap().clone();
|
||||||
|
for element in into_vec {
|
||||||
|
market_caps.push(
|
||||||
|
element
|
||||||
|
.1
|
||||||
|
.as_array()
|
||||||
|
.unwrap()
|
||||||
|
.first()
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut market_cap_min = market_caps
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.min_by(|a, b| a.partial_cmp(b).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
let mut market_cap_max = market_caps
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.max_by(|a, b| a.partial_cmp(b).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
for element in &market_caps {
|
||||||
|
normalized_values.push((element - market_cap_min) / (market_cap_max - market_cap_min));
|
||||||
|
}
|
||||||
|
for element in &normalized_values {
|
||||||
|
degree_values.push(
|
||||||
|
(normalized_values.last().unwrap() - element)
|
||||||
|
.atan()
|
||||||
|
.to_degrees(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
degree_values.pop();
|
||||||
|
|
||||||
|
for element in degree_values.iter().copied().enumerate() {
|
||||||
|
weighted_average_degree += element.1 * (element.0 + 1) as f64;
|
||||||
|
}
|
||||||
|
// weighted_average_degree /= 45.0;
|
||||||
|
weighted_average_degree /= 1770.0; // summation from 1 to 59
|
||||||
|
|
||||||
|
// initialize variables for Kalman Filtering
|
||||||
|
let h = 1.0;
|
||||||
|
let a = 1.0;
|
||||||
|
let mut q = 0.001;
|
||||||
|
let r = 1.0;
|
||||||
|
let mut x = weighted_average_degree;
|
||||||
|
let mut p = 5000.0;
|
||||||
|
let mut xp = 0.0;
|
||||||
|
let mut pp = 0.0;
|
||||||
|
let mut k = 0.0; // Kalman gain
|
||||||
|
let mut z = 0.0; // measurement
|
||||||
|
|
||||||
|
// variables for using database
|
||||||
|
#[derive(FromRow)]
|
||||||
|
struct SelectData {
|
||||||
|
market_cap_index: f64,
|
||||||
|
minimum: f64,
|
||||||
|
maximum: f64,
|
||||||
|
transition_point: f64,
|
||||||
|
liquidation_signal: i8,
|
||||||
|
negative_buy_signal: i8,
|
||||||
|
transition_buy_signal: i8,
|
||||||
|
};
|
||||||
|
let table_name = String::from("market_cap_index");
|
||||||
|
let update_condition = vec![(String::from("id"), String::from("1"))];
|
||||||
|
let update_condition2 = vec![(String::from("id"), String::from("2"))];
|
||||||
|
let select_columns = String::from("market_cap_index, minimum, maximum, transition_point, liquidation_signal, negative_buy_signal, transition_buy_signal");
|
||||||
|
let select_data_structure = SelectData {
|
||||||
|
market_cap_index: 0.0,
|
||||||
|
minimum: 0.0,
|
||||||
|
maximum: 0.0,
|
||||||
|
transition_point: 0.0,
|
||||||
|
liquidation_signal: 0,
|
||||||
|
negative_buy_signal: 0,
|
||||||
|
transition_buy_signal: 0,
|
||||||
|
};
|
||||||
|
let select_condition = None;
|
||||||
|
|
||||||
|
// variables for EMA calculation of 5hr angle
|
||||||
|
let mut prev_angle: Option<f64> = None;
|
||||||
|
let mut current_angle: Option<f64> = None;
|
||||||
|
|
||||||
|
driver
|
||||||
|
.set_window_rect(OptionRect::new().with_size(1, 1))
|
||||||
|
.await?;
|
||||||
|
// refresh page and save JSON repeatedly
|
||||||
|
loop {
|
||||||
|
// refresh page in the first tab
|
||||||
|
driver.refresh().await?;
|
||||||
|
// switch to the second tab and refresh page
|
||||||
|
driver.switch_to().window(&handles[1]).await?;
|
||||||
|
driver.refresh().await?;
|
||||||
|
// switch to the first tab
|
||||||
|
driver.switch_to().window(&handle).await?;
|
||||||
|
driver.minimize_window().await?;
|
||||||
|
|
||||||
|
// task in the first tab starts
|
||||||
|
// save JSON
|
||||||
|
let elem = driver.find_element(By::Tag("pre")).await;
|
||||||
|
let mut text;
|
||||||
|
match elem {
|
||||||
|
Ok(T) => {
|
||||||
|
text = T.text().await?;
|
||||||
|
v = serde_json::from_str(text.as_str()).unwrap();
|
||||||
|
into_vec = v
|
||||||
|
.as_object()
|
||||||
|
.unwrap()
|
||||||
|
.get("data")
|
||||||
|
.unwrap()
|
||||||
|
.as_object()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if last_updated_time.as_str() != into_vec.keys().last().unwrap().as_str() {
|
||||||
|
// when working
|
||||||
|
let mut association_update_values =
|
||||||
|
vec![(String::from("is_working"), String::from("1"))];
|
||||||
|
let mut update_value_add: (String, String) =
|
||||||
|
(String::from("opinion"), String::from("KEEP"));
|
||||||
|
|
||||||
|
last_updated_time = into_vec.keys().last().unwrap().clone();
|
||||||
|
|
||||||
|
market_caps.clear();
|
||||||
|
normalized_values.clear();
|
||||||
|
degree_values.clear();
|
||||||
|
weighted_average_degree = 0.0;
|
||||||
|
|
||||||
|
for element in into_vec {
|
||||||
|
market_caps.push(
|
||||||
|
element
|
||||||
|
.1
|
||||||
|
.as_array()
|
||||||
|
.unwrap()
|
||||||
|
.first()
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
market_cap_min = market_caps
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.min_by(|a, b| a.partial_cmp(b).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
market_cap_max = market_caps
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.max_by(|a, b| a.partial_cmp(b).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
for element in &market_caps {
|
||||||
|
normalized_values
|
||||||
|
.push((element - market_cap_min) / (market_cap_max - market_cap_min));
|
||||||
|
}
|
||||||
|
|
||||||
|
for element in &normalized_values {
|
||||||
|
degree_values.push(
|
||||||
|
(normalized_values.last().unwrap() - element)
|
||||||
|
.atan()
|
||||||
|
.to_degrees(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
degree_values.pop();
|
||||||
|
|
||||||
|
for element in degree_values.iter().copied().enumerate() {
|
||||||
|
weighted_average_degree += element.1 * (element.0 + 1) as f64;
|
||||||
|
}
|
||||||
|
// weighted_average_degree /= 45.0;
|
||||||
|
weighted_average_degree /= 1770.0; // summation from 1 to 59
|
||||||
|
|
||||||
|
// using database
|
||||||
|
let mut update_values = vec![(String::from("market_cap_index"), x.to_string())];
|
||||||
|
update_record2(&table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let select_result = select_record(
|
||||||
|
&table_name,
|
||||||
|
&select_columns,
|
||||||
|
&select_condition,
|
||||||
|
&select_data_structure,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let record = select_result.first().unwrap();
|
||||||
|
let record2 = select_result.last().unwrap();
|
||||||
|
|
||||||
|
// if record2.market_cap_index > 0.0 { q=0.01; } // lower q than r means filtering is insensitive to turbulance(input noise) -> meaning more delayed filtering
|
||||||
|
// else { q=0.004; }
|
||||||
|
|
||||||
|
// 1st order Kalman Filtering for weighted_average_degree
|
||||||
|
z = weighted_average_degree;
|
||||||
|
xp = x;
|
||||||
|
pp = p + q;
|
||||||
|
k = pp / (pp + r);
|
||||||
|
x = xp + k * (z - xp);
|
||||||
|
p = pp - k * pp;
|
||||||
|
|
||||||
|
if record.market_cap_index > 0.0 {
|
||||||
|
if record.minimum != 0.0 {
|
||||||
|
update_values = vec![
|
||||||
|
(String::from("minimum"), 0.0.to_string()),
|
||||||
|
(String::from("negative_buy_signal"), 0.to_string()),
|
||||||
|
(String::from("transition_point"), 0.0.to_string()),
|
||||||
|
];
|
||||||
|
update_record2(&table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if record.maximum < record.market_cap_index
|
||||||
|
&& record.liquidation_signal == 0
|
||||||
|
{
|
||||||
|
if record.transition_point == 0.0 || record.minimum < 0.0 {
|
||||||
|
update_values = vec![(
|
||||||
|
String::from("maximum"),
|
||||||
|
record.market_cap_index.to_string(),
|
||||||
|
)];
|
||||||
|
update_record2(&table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
} else {
|
||||||
|
if record.market_cap_index >= 5.0
|
||||||
|
&& (record.market_cap_index - record.maximum) / record.maximum
|
||||||
|
> 0.2
|
||||||
|
{
|
||||||
|
// 현재 5 이상 지수가 이전 대비 20% 증가하면 추가 매수하기.
|
||||||
|
update_values = vec![
|
||||||
|
(
|
||||||
|
String::from("maximum"),
|
||||||
|
record.market_cap_index.to_string(),
|
||||||
|
),
|
||||||
|
(String::from("transition_buy_signal"), 1.to_string()),
|
||||||
|
];
|
||||||
|
update_record2(&table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
} else {
|
||||||
|
update_values = vec![
|
||||||
|
(
|
||||||
|
String::from("maximum"),
|
||||||
|
record.market_cap_index.to_string(),
|
||||||
|
),
|
||||||
|
(String::from("transition_buy_signal"), 0.to_string()),
|
||||||
|
];
|
||||||
|
update_record2(&table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if record.maximum > record.market_cap_index
|
||||||
|
&& record.liquidation_signal == 0
|
||||||
|
{
|
||||||
|
update_values = vec![
|
||||||
|
(String::from("liquidation_signal"), 1.to_string()),
|
||||||
|
(
|
||||||
|
String::from("transition_point"),
|
||||||
|
record.market_cap_index.to_string(),
|
||||||
|
),
|
||||||
|
(String::from("transition_buy_signal"), 0.to_string()),
|
||||||
|
];
|
||||||
|
update_record2(&table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if record.liquidation_signal == 1
|
||||||
|
&& record.market_cap_index < record.transition_point
|
||||||
|
{
|
||||||
|
update_values = vec![(
|
||||||
|
String::from("transition_point"),
|
||||||
|
record.market_cap_index.to_string(),
|
||||||
|
)];
|
||||||
|
update_record2(&table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if record.liquidation_signal == 1
|
||||||
|
&& record.market_cap_index > record.transition_point
|
||||||
|
{
|
||||||
|
update_values = vec![
|
||||||
|
(String::from("maximum"), record.market_cap_index.to_string()),
|
||||||
|
(String::from("liquidation_signal"), 0.to_string()),
|
||||||
|
];
|
||||||
|
update_record2(&table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if record.maximum != 0.0 {
|
||||||
|
update_values = vec![
|
||||||
|
(String::from("maximum"), 0.0.to_string()),
|
||||||
|
(String::from("transition_point"), 0.0.to_string()),
|
||||||
|
];
|
||||||
|
update_record2(&table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if record.market_cap_index < record.minimum
|
||||||
|
&& record.negative_buy_signal == 0
|
||||||
|
{
|
||||||
|
update_values = vec![
|
||||||
|
(String::from("minimum"), record.market_cap_index.to_string()),
|
||||||
|
(String::from("liquidation_signal"), 1.to_string()),
|
||||||
|
];
|
||||||
|
update_record2(&table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
if record.market_cap_index > record.minimum
|
||||||
|
&& record.liquidation_signal == 1
|
||||||
|
&& record.negative_buy_signal == 0
|
||||||
|
{
|
||||||
|
if record2.market_cap_index > 0.0 {
|
||||||
|
// considering the market is going up
|
||||||
|
update_values = vec![
|
||||||
|
(String::from("negative_buy_signal"), 1.to_string()),
|
||||||
|
(
|
||||||
|
String::from("transition_point"),
|
||||||
|
record.market_cap_index.to_string(),
|
||||||
|
),
|
||||||
|
(String::from("liquidation_signal"), 0.to_string()),
|
||||||
|
];
|
||||||
|
update_record2(&table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
} else {
|
||||||
|
// considering the market is doing down
|
||||||
|
if record.market_cap_index < -20.0 {
|
||||||
|
update_values = vec![
|
||||||
|
(String::from("negative_buy_signal"), 1.to_string()),
|
||||||
|
(
|
||||||
|
String::from("transition_point"),
|
||||||
|
record.market_cap_index.to_string(),
|
||||||
|
),
|
||||||
|
(String::from("liquidation_signal"), 0.to_string()),
|
||||||
|
];
|
||||||
|
update_record2(&table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
} else {
|
||||||
|
update_values = vec![
|
||||||
|
(
|
||||||
|
String::from("transition_point"),
|
||||||
|
record.market_cap_index.to_string(),
|
||||||
|
),
|
||||||
|
(String::from("liquidation_signal"), 0.to_string()),
|
||||||
|
];
|
||||||
|
update_record2(&table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if record.negative_buy_signal == 1
|
||||||
|
&& record.market_cap_index > record.transition_point
|
||||||
|
{
|
||||||
|
if record.market_cap_index > -5.0 {
|
||||||
|
update_values = vec![
|
||||||
|
(String::from("minimum"), record.market_cap_index.to_string()),
|
||||||
|
(String::from("negative_buy_signal"), 0.to_string()),
|
||||||
|
(String::from("transition_point"), 0.0.to_string()),
|
||||||
|
];
|
||||||
|
update_record2(&table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
} else {
|
||||||
|
update_values = vec![(
|
||||||
|
String::from("transition_point"),
|
||||||
|
record.market_cap_index.to_string(),
|
||||||
|
)];
|
||||||
|
update_record2(&table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if record.negative_buy_signal == 1
|
||||||
|
&& record.market_cap_index < record.transition_point
|
||||||
|
{
|
||||||
|
update_values = vec![
|
||||||
|
(String::from("negative_buy_signal"), 0.to_string()),
|
||||||
|
(String::from("liquidation_signal"), 1.to_string()),
|
||||||
|
(String::from("minimum"), record.market_cap_index.to_string()),
|
||||||
|
];
|
||||||
|
update_record2(&table_name, &update_values, &update_condition)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
} // task in the first tab ends
|
||||||
|
|
||||||
|
// task in the second tab starts
|
||||||
|
driver.switch_to().window(&handles[1]).await?;
|
||||||
|
// save JSON
|
||||||
|
let elem = driver.find_element(By::Tag("pre")).await?;
|
||||||
|
let text = elem.text().await?;
|
||||||
|
v = serde_json::from_str(text.as_str()).unwrap();
|
||||||
|
into_vec = v
|
||||||
|
.as_object()
|
||||||
|
.unwrap()
|
||||||
|
.get("data")
|
||||||
|
.unwrap()
|
||||||
|
.as_object()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
market_caps.clear();
|
||||||
|
normalized_values.clear();
|
||||||
|
degree_values.clear();
|
||||||
|
weighted_average_degree = 0.0;
|
||||||
|
|
||||||
|
for element in into_vec {
|
||||||
|
market_caps.push(
|
||||||
|
element
|
||||||
|
.1
|
||||||
|
.as_array()
|
||||||
|
.unwrap()
|
||||||
|
.first()
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
market_cap_min = market_caps
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.min_by(|a, b| a.partial_cmp(b).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
market_cap_max = market_caps
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.max_by(|a, b| a.partial_cmp(b).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
for element in &market_caps {
|
||||||
|
normalized_values
|
||||||
|
.push((element - market_cap_min) / (market_cap_max - market_cap_min));
|
||||||
|
}
|
||||||
|
|
||||||
|
for element in &normalized_values {
|
||||||
|
degree_values.push(
|
||||||
|
(normalized_values.last().unwrap() - element)
|
||||||
|
.atan()
|
||||||
|
.to_degrees(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
degree_values.pop();
|
||||||
|
|
||||||
|
for element in degree_values.iter().copied().enumerate() {
|
||||||
|
// weighted_average_degree += element.1 * (element.0 + 1) as f64;
|
||||||
|
weighted_average_degree += element.1; // normal average
|
||||||
|
}
|
||||||
|
// weighted_average_degree /= 45.0;
|
||||||
|
// weighted_average_degree /= 1036080.0; // summation from 1 to 1439
|
||||||
|
weighted_average_degree /= degree_values.len() as f64; // normal average
|
||||||
|
|
||||||
|
if prev_angle.is_some() && current_angle.is_some() {
|
||||||
|
current_angle = Some(
|
||||||
|
((1.0 / 4.0) * weighted_average_degree)
|
||||||
|
+ ((3.0 / 4.0) * prev_angle.unwrap()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// if current_angle.unwrap() >= 3.0 {
|
||||||
|
// update_value_add = ((String::from("opinion"), String::from("UP")));
|
||||||
|
// q=0.01;
|
||||||
|
// } else if current_angle.unwrap() <= -3.0 {
|
||||||
|
// update_value_add = ((String::from("opinion"), String::from("DOWN")));
|
||||||
|
// q=0.004;
|
||||||
|
// } else {
|
||||||
|
// update_value_add = ((String::from("opinion"), String::from("KEEP")));
|
||||||
|
// q=0.01;
|
||||||
|
// }
|
||||||
|
|
||||||
|
if current_angle.unwrap() > 3.0 {
|
||||||
|
if current_angle.unwrap() - prev_angle.unwrap() <= -0.15 {
|
||||||
|
update_value_add =
|
||||||
|
((String::from("opinion"), String::from("DOWN")));
|
||||||
|
q = 0.004;
|
||||||
|
} else {
|
||||||
|
update_value_add = ((String::from("opinion"), String::from("UP")));
|
||||||
|
q = 0.01;
|
||||||
|
}
|
||||||
|
} else if current_angle.unwrap() < -3.0 {
|
||||||
|
if current_angle.unwrap() - prev_angle.unwrap() >= 0.15 {
|
||||||
|
update_value_add = ((String::from("opinion"), String::from("UP")));
|
||||||
|
q = 0.01;
|
||||||
|
} else {
|
||||||
|
update_value_add =
|
||||||
|
((String::from("opinion"), String::from("DOWN")));
|
||||||
|
q = 0.004;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
update_value_add = ((String::from("opinion"), String::from("KEEP")));
|
||||||
|
q = 0.01;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_angle = current_angle;
|
||||||
|
} else {
|
||||||
|
current_angle = Some(weighted_average_degree);
|
||||||
|
prev_angle = Some(weighted_average_degree);
|
||||||
|
}
|
||||||
|
|
||||||
|
// using database
|
||||||
|
let update_values = vec![(
|
||||||
|
String::from("market_cap_index"),
|
||||||
|
current_angle.unwrap().to_string(),
|
||||||
|
)];
|
||||||
|
update_record2(&table_name, &update_values, &update_condition2)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
// return to the first tab
|
||||||
|
driver.switch_to().window(&handle).await?;
|
||||||
|
driver.minimize_window().await?;
|
||||||
|
|
||||||
|
// update record in [signal_association_opinion]
|
||||||
|
association_update_values.push(update_value_add);
|
||||||
|
update_record2(
|
||||||
|
&association_update_table_name,
|
||||||
|
&association_update_values,
|
||||||
|
&association_update_condition,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
// println!("5hr_angle:{},5day_angle:{}", x, current_angle.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(E) => {
|
||||||
|
// when not working
|
||||||
|
let association_update_values = vec![
|
||||||
|
(String::from("is_working"), String::from("0")),
|
||||||
|
(String::from("opinion"), String::from("-")),
|
||||||
|
(String::from("weight"), String::from("0.0")),
|
||||||
|
];
|
||||||
|
update_record2(
|
||||||
|
&association_update_table_name,
|
||||||
|
&association_update_values,
|
||||||
|
&association_update_condition,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sleep for 60 seconds
|
||||||
|
sleep(Duration::from_nanos(60_000_000_000)).await;
|
||||||
|
|
||||||
|
// println!("market_cap_index 완료");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
208
src/signal_association/dollar_index.rs
Normal file
208
src/signal_association/dollar_index.rs
Normal file
|
|
@ -0,0 +1,208 @@
|
||||||
|
use crate::database_control::*;
|
||||||
|
use chrono::naive::NaiveDate;
|
||||||
|
use reqwest::{Client, ClientBuilder};
|
||||||
|
use serde_json::Value;
|
||||||
|
use sqlx::{Error, FromRow};
|
||||||
|
use tokio::{join, time::Duration, time::*};
|
||||||
|
|
||||||
|
#[derive(Debug, FromRow, Clone)]
|
||||||
|
pub struct DollarIndex {
|
||||||
|
pub date: NaiveDate,
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, FromRow)]
|
||||||
|
pub struct SignalAssociaionOpinion {
|
||||||
|
pub id: i64,
|
||||||
|
pub name: String,
|
||||||
|
pub is_working: bool,
|
||||||
|
pub opinion: String,
|
||||||
|
pub weight: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn initialize_association_record() {
|
||||||
|
let table_name = String::from("signal_association_opinion");
|
||||||
|
let initial_columns = vec!["name", "is_working", "opinion", "weight"];
|
||||||
|
let initial_values = vec![
|
||||||
|
String::from("dollar_index"),
|
||||||
|
String::from("0"),
|
||||||
|
String::from("-"),
|
||||||
|
String::from("0.0"),
|
||||||
|
];
|
||||||
|
insert_one_record(&table_name, &initial_columns, &initial_values).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn initialize_record() {
|
||||||
|
let mut dollar_index_vec: Vec<DollarIndex> = Vec::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let result = reqeust_dollar_index().await;
|
||||||
|
match result {
|
||||||
|
Ok(T) => {
|
||||||
|
dollar_index_vec = T;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(E) => {
|
||||||
|
println!("{}", E);
|
||||||
|
let association_update_table_name = String::from("signal_association_opinion");
|
||||||
|
let association_update_condition =
|
||||||
|
vec![(String::from("name"), String::from("dollar_index"))];
|
||||||
|
let association_update_values: Vec<(String, String)> = vec![
|
||||||
|
(String::from("is_working"), 0.to_string()),
|
||||||
|
(String::from("opinion"), String::from("-")),
|
||||||
|
];
|
||||||
|
|
||||||
|
update_record2(
|
||||||
|
&association_update_table_name,
|
||||||
|
&association_update_values,
|
||||||
|
&association_update_condition,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
sleep(Duration::from_secs(5)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let insert_table_name = String::from("dollar_index");
|
||||||
|
let insert_columns = vec!["date", "value"];
|
||||||
|
let mut initial_values: Vec<String> = Vec::new();
|
||||||
|
let mut insert_value_container: Vec<Vec<String>> = Vec::new();
|
||||||
|
for element in dollar_index_vec {
|
||||||
|
let mut insert_value_build: Vec<String> = Vec::new();
|
||||||
|
|
||||||
|
insert_value_build.push(element.date.to_string());
|
||||||
|
insert_value_build.push(element.value);
|
||||||
|
insert_value_container.push(insert_value_build.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
insert_records(&insert_table_name, &insert_columns, &insert_value_container).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn reqeust_dollar_index(
|
||||||
|
) -> Result<Vec<DollarIndex>, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let client = ClientBuilder::new()
|
||||||
|
.timeout(tokio::time::Duration::from_secs(30))
|
||||||
|
.build()?;
|
||||||
|
let mut url = String::from("https://api.stlouisfed.org/fred/series/observations?series_id=DTWEXAFEGS&api_key=d4af0a1b811bd0324c4210ca59db99fb&file_type=json&limit=90&sort_order=desc");
|
||||||
|
|
||||||
|
let res = client.get(&url).send().await?;
|
||||||
|
|
||||||
|
let body = res.text_with_charset("utf-8").await?;
|
||||||
|
|
||||||
|
let v = serde_json::from_str::<Value>(body.as_str())?;
|
||||||
|
|
||||||
|
let mut a = v.get("observations").unwrap().as_array().unwrap();
|
||||||
|
|
||||||
|
let mut vec: Vec<DollarIndex> = Vec::new();
|
||||||
|
let mut dollar_index_info = DollarIndex {
|
||||||
|
date: chrono::naive::MAX_DATE,
|
||||||
|
value: String::new(),
|
||||||
|
};
|
||||||
|
let parse_from_str = NaiveDate::parse_from_str;
|
||||||
|
|
||||||
|
for element in a {
|
||||||
|
dollar_index_info.date =
|
||||||
|
parse_from_str(element.get("date").unwrap().as_str().unwrap(), "%Y-%m-%d").unwrap();
|
||||||
|
dollar_index_info.value = element.get("value").unwrap().as_str().unwrap().to_string();
|
||||||
|
vec.push(dollar_index_info.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(vec)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nominal Advanced Foreign Economies U.S. Dollar Index
|
||||||
|
// dollar index from: https://fred.stlouisfed.org/series/DTWEXAFEGS
|
||||||
|
pub async fn monitoring_dollar_index() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let table_name = String::from("dollar_index");
|
||||||
|
delete_all_rows(&table_name)
|
||||||
|
.await
|
||||||
|
.expect("Failed to delete rows!");
|
||||||
|
let association_update_table_name = String::from("signal_association_opinion");
|
||||||
|
let mut avg = 0.0;
|
||||||
|
let mut cnt = 0;
|
||||||
|
let mut opinion = String::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
initialize_record().await;
|
||||||
|
let mut dollar_index_vec = select_dollar_index().await;
|
||||||
|
|
||||||
|
for element in &dollar_index_vec {
|
||||||
|
if element.value.as_str() != "." {
|
||||||
|
avg += element.value.parse::<f64>().unwrap();
|
||||||
|
cnt += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cnt != 0 {
|
||||||
|
avg /= cnt as f64;
|
||||||
|
|
||||||
|
for element in dollar_index_vec {
|
||||||
|
if element.value.as_str() != "." {
|
||||||
|
if avg * 1.005 <= element.value.parse::<f64>().unwrap() {
|
||||||
|
opinion = String::from("UP");
|
||||||
|
} else if avg * 1.005 > element.value.parse::<f64>().unwrap()
|
||||||
|
&& avg * 0.997 < element.value.parse::<f64>().unwrap()
|
||||||
|
{
|
||||||
|
opinion = String::from("KEEP");
|
||||||
|
} else {
|
||||||
|
opinion = String::from("DOWN");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
println!("cnt is 0!");
|
||||||
|
let association_update_condition =
|
||||||
|
vec![(String::from("name"), String::from("dollar_index"))];
|
||||||
|
let association_update_values: Vec<(String, String)> = vec![
|
||||||
|
(String::from("is_working"), 0.to_string()),
|
||||||
|
(String::from("opinion"), String::from("-")),
|
||||||
|
];
|
||||||
|
|
||||||
|
update_record2(
|
||||||
|
&association_update_table_name,
|
||||||
|
&association_update_values,
|
||||||
|
&association_update_condition,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
sleep(Duration::from_secs(5)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update signal_association_opinion
|
||||||
|
let association_update_condition = vec![(String::from("name"), String::from("dollar_index"))];
|
||||||
|
let association_update_values: Vec<(String, String)> = vec![
|
||||||
|
(String::from("is_working"), 1.to_string()),
|
||||||
|
(String::from("opinion"), opinion),
|
||||||
|
];
|
||||||
|
|
||||||
|
update_record2(
|
||||||
|
&association_update_table_name,
|
||||||
|
&association_update_values,
|
||||||
|
&association_update_condition,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn select_dollar_index() -> Vec<DollarIndex> {
|
||||||
|
let select_table_name = String::from("dollar_index");
|
||||||
|
let select_columns = String::from("*");
|
||||||
|
let select_condition = None;
|
||||||
|
let select_struct = DollarIndex {
|
||||||
|
date: chrono::naive::MAX_DATE,
|
||||||
|
value: String::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let dollar_index_vec = select_record(
|
||||||
|
&select_table_name,
|
||||||
|
&select_columns,
|
||||||
|
&select_condition,
|
||||||
|
&select_struct,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
dollar_index_vec
|
||||||
|
}
|
||||||
607
src/signal_association/exchange_rate.rs
Normal file
607
src/signal_association/exchange_rate.rs
Normal file
|
|
@ -0,0 +1,607 @@
|
||||||
|
use crate::database_control::*;
|
||||||
|
use chrono::*;
|
||||||
|
use reqwest::{Client, ClientBuilder};
|
||||||
|
use serde_json::Value;
|
||||||
|
use sqlx::{Error, FromRow};
|
||||||
|
use tokio::{join, time::Duration, time::*};
|
||||||
|
|
||||||
|
const APP_ID_ACTIVE: &str = "7674e75976bd4f878a67f19926704433";
|
||||||
|
const APP_ID_TEMP: &str = "6bd6804c165d46dda5dd65500b17f3e8";
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ExchangeRateInfo {
|
||||||
|
currency: String,
|
||||||
|
rate: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, FromRow)]
|
||||||
|
struct ExchangeRate {
|
||||||
|
id: i64,
|
||||||
|
date: NaiveDate,
|
||||||
|
RUB: f64,
|
||||||
|
CAD: f64,
|
||||||
|
IQD: f64,
|
||||||
|
CNY: f64,
|
||||||
|
BRL: f64,
|
||||||
|
IRR: f64,
|
||||||
|
MXN: f64,
|
||||||
|
NOK: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn initialize_association_record() {
|
||||||
|
let table_name = String::from("signal_association_opinion");
|
||||||
|
let initial_columns = vec!["name", "is_working", "opinion", "weight"];
|
||||||
|
let initial_values = vec![
|
||||||
|
String::from("exchange_rate"),
|
||||||
|
String::from("0"),
|
||||||
|
String::from("-"),
|
||||||
|
String::from("0.0"),
|
||||||
|
];
|
||||||
|
insert_one_record(&table_name, &initial_columns, &initial_values).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn initialize_record() {
|
||||||
|
// make date str
|
||||||
|
let today = Utc::now().date();
|
||||||
|
let mut prior_dates: Vec<Date<Utc>> = Vec::new();
|
||||||
|
let mut prior_date = today.clone();
|
||||||
|
|
||||||
|
for _ in 0..30 {
|
||||||
|
prior_date = prior_date.pred();
|
||||||
|
prior_dates.push(prior_date);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut today_str = String::new();
|
||||||
|
today_str = today.to_string();
|
||||||
|
today_str.pop();
|
||||||
|
today_str.pop();
|
||||||
|
today_str.pop();
|
||||||
|
|
||||||
|
let mut prior_dates_str: Vec<String> = Vec::new();
|
||||||
|
let mut temp_str = String::new();
|
||||||
|
for element in prior_dates {
|
||||||
|
temp_str = element.to_string();
|
||||||
|
temp_str.pop();
|
||||||
|
temp_str.pop();
|
||||||
|
temp_str.pop();
|
||||||
|
prior_dates_str.push(temp_str.clone());
|
||||||
|
}
|
||||||
|
prior_dates_str.reverse();
|
||||||
|
|
||||||
|
let insert_table_name = String::from("foreign_exchange_rates");
|
||||||
|
let insert_columns = vec![
|
||||||
|
"date", "RUB", "CAD", "IQD", "CNY", "BRL", "IRR", "MXN", "NOK",
|
||||||
|
];
|
||||||
|
let mut initial_values: Vec<String> = Vec::new();
|
||||||
|
|
||||||
|
for element in prior_dates_str {
|
||||||
|
let mut exchange_rates: Vec<ExchangeRateInfo> = Vec::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let result = reqeust_exchange_rate_historical(&element).await;
|
||||||
|
match result {
|
||||||
|
Ok(T) => {
|
||||||
|
exchange_rates = T;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(E) => {
|
||||||
|
println!("{}", E);
|
||||||
|
sleep(Duration::from_secs(5)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initial_values.clear();
|
||||||
|
initial_values.push(element.clone());
|
||||||
|
for element in exchange_rates {
|
||||||
|
initial_values.push(element.rate.to_string());
|
||||||
|
}
|
||||||
|
insert_one_record(&insert_table_name, &insert_columns, &initial_values).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn reqeust_exchange_rate_historical(
|
||||||
|
date: &String,
|
||||||
|
) -> Result<Vec<ExchangeRateInfo>, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let client = ClientBuilder::new()
|
||||||
|
.timeout(tokio::time::Duration::from_secs(30))
|
||||||
|
.build()?;
|
||||||
|
let mut url = String::from("https://openexchangerates.org/api/historical/");
|
||||||
|
url.push_str(&date);
|
||||||
|
url.push_str(".json?app_id=");
|
||||||
|
url.push_str(APP_ID_TEMP);
|
||||||
|
|
||||||
|
let res = client.get(&url).send().await?;
|
||||||
|
|
||||||
|
let body = res.text_with_charset("utf-8").await?;
|
||||||
|
|
||||||
|
let v = serde_json::from_str::<Value>(body.as_str())?;
|
||||||
|
let mut exchange_rates: Vec<ExchangeRateInfo> = Vec::new();
|
||||||
|
exchange_rates.push(ExchangeRateInfo {
|
||||||
|
currency: String::from("RUB"),
|
||||||
|
rate: 0.0,
|
||||||
|
});
|
||||||
|
exchange_rates.push(ExchangeRateInfo {
|
||||||
|
currency: String::from("CAD"),
|
||||||
|
rate: 0.0,
|
||||||
|
});
|
||||||
|
exchange_rates.push(ExchangeRateInfo {
|
||||||
|
currency: String::from("IQD"),
|
||||||
|
rate: 0.0,
|
||||||
|
});
|
||||||
|
exchange_rates.push(ExchangeRateInfo {
|
||||||
|
currency: String::from("CNY"),
|
||||||
|
rate: 0.0,
|
||||||
|
});
|
||||||
|
exchange_rates.push(ExchangeRateInfo {
|
||||||
|
currency: String::from("BRL"),
|
||||||
|
rate: 0.0,
|
||||||
|
});
|
||||||
|
exchange_rates.push(ExchangeRateInfo {
|
||||||
|
currency: String::from("IRR"),
|
||||||
|
rate: 0.0,
|
||||||
|
});
|
||||||
|
exchange_rates.push(ExchangeRateInfo {
|
||||||
|
currency: String::from("MXN"),
|
||||||
|
rate: 0.0,
|
||||||
|
});
|
||||||
|
exchange_rates.push(ExchangeRateInfo {
|
||||||
|
currency: String::from("NOK"),
|
||||||
|
rate: 0.0,
|
||||||
|
});
|
||||||
|
|
||||||
|
for element in &mut exchange_rates {
|
||||||
|
match element.currency.as_str() {
|
||||||
|
"RUB" => {
|
||||||
|
element.rate = v
|
||||||
|
.get("rates")
|
||||||
|
.unwrap()
|
||||||
|
.get("RUB")
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
"CAD" => {
|
||||||
|
element.rate = v
|
||||||
|
.get("rates")
|
||||||
|
.unwrap()
|
||||||
|
.get("CAD")
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
"IQD" => {
|
||||||
|
element.rate = v
|
||||||
|
.get("rates")
|
||||||
|
.unwrap()
|
||||||
|
.get("IQD")
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
"CNY" => {
|
||||||
|
element.rate = v
|
||||||
|
.get("rates")
|
||||||
|
.unwrap()
|
||||||
|
.get("CNY")
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
"BRL" => {
|
||||||
|
element.rate = v
|
||||||
|
.get("rates")
|
||||||
|
.unwrap()
|
||||||
|
.get("BRL")
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
"IRR" => {
|
||||||
|
element.rate = v
|
||||||
|
.get("rates")
|
||||||
|
.unwrap()
|
||||||
|
.get("IRR")
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
"MXN" => {
|
||||||
|
element.rate = v
|
||||||
|
.get("rates")
|
||||||
|
.unwrap()
|
||||||
|
.get("MXN")
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
"NOK" => {
|
||||||
|
element.rate = v
|
||||||
|
.get("rates")
|
||||||
|
.unwrap()
|
||||||
|
.get("NOK")
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(exchange_rates)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn reqeust_exchange_rate_latest(
|
||||||
|
) -> Result<Vec<ExchangeRateInfo>, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let client = ClientBuilder::new()
|
||||||
|
.timeout(tokio::time::Duration::from_secs(30))
|
||||||
|
.build()?;
|
||||||
|
let mut url = String::from("https://openexchangerates.org/api/latest.json?app_id=");
|
||||||
|
url.push_str(APP_ID_ACTIVE);
|
||||||
|
|
||||||
|
let res = client.get(&url).send().await.unwrap();
|
||||||
|
|
||||||
|
let body = res.text_with_charset("utf-8").await?;
|
||||||
|
|
||||||
|
let v = serde_json::from_str::<Value>(body.as_str())?;
|
||||||
|
let mut exchange_rates: Vec<ExchangeRateInfo> = Vec::new();
|
||||||
|
exchange_rates.push(ExchangeRateInfo {
|
||||||
|
currency: String::from("RUB"),
|
||||||
|
rate: 0.0,
|
||||||
|
});
|
||||||
|
exchange_rates.push(ExchangeRateInfo {
|
||||||
|
currency: String::from("CAD"),
|
||||||
|
rate: 0.0,
|
||||||
|
});
|
||||||
|
exchange_rates.push(ExchangeRateInfo {
|
||||||
|
currency: String::from("IQD"),
|
||||||
|
rate: 0.0,
|
||||||
|
});
|
||||||
|
exchange_rates.push(ExchangeRateInfo {
|
||||||
|
currency: String::from("CNY"),
|
||||||
|
rate: 0.0,
|
||||||
|
});
|
||||||
|
exchange_rates.push(ExchangeRateInfo {
|
||||||
|
currency: String::from("BRL"),
|
||||||
|
rate: 0.0,
|
||||||
|
});
|
||||||
|
exchange_rates.push(ExchangeRateInfo {
|
||||||
|
currency: String::from("IRR"),
|
||||||
|
rate: 0.0,
|
||||||
|
});
|
||||||
|
exchange_rates.push(ExchangeRateInfo {
|
||||||
|
currency: String::from("MXN"),
|
||||||
|
rate: 0.0,
|
||||||
|
});
|
||||||
|
exchange_rates.push(ExchangeRateInfo {
|
||||||
|
currency: String::from("NOK"),
|
||||||
|
rate: 0.0,
|
||||||
|
});
|
||||||
|
|
||||||
|
for element in &mut exchange_rates {
|
||||||
|
match element.currency.as_str() {
|
||||||
|
"RUB" => {
|
||||||
|
element.rate = v
|
||||||
|
.get("rates")
|
||||||
|
.unwrap()
|
||||||
|
.get("RUB")
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
"CAD" => {
|
||||||
|
element.rate = v
|
||||||
|
.get("rates")
|
||||||
|
.unwrap()
|
||||||
|
.get("CAD")
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
"IQD" => {
|
||||||
|
element.rate = v
|
||||||
|
.get("rates")
|
||||||
|
.unwrap()
|
||||||
|
.get("IQD")
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
"CNY" => {
|
||||||
|
element.rate = v
|
||||||
|
.get("rates")
|
||||||
|
.unwrap()
|
||||||
|
.get("CNY")
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
"BRL" => {
|
||||||
|
element.rate = v
|
||||||
|
.get("rates")
|
||||||
|
.unwrap()
|
||||||
|
.get("BRL")
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
"IRR" => {
|
||||||
|
element.rate = v
|
||||||
|
.get("rates")
|
||||||
|
.unwrap()
|
||||||
|
.get("IRR")
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
"MXN" => {
|
||||||
|
element.rate = v
|
||||||
|
.get("rates")
|
||||||
|
.unwrap()
|
||||||
|
.get("MXN")
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
"NOK" => {
|
||||||
|
element.rate = v
|
||||||
|
.get("rates")
|
||||||
|
.unwrap()
|
||||||
|
.get("NOK")
|
||||||
|
.unwrap()
|
||||||
|
.as_f64()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(exchange_rates)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Weight to average is from amount of crude oil for each country: https://yearbook.enerdata.co.kr/crude-oil/world-production-statistics.html
|
||||||
|
// based on 2020 data
|
||||||
|
// Weight: USD(1) , RUB(0.709141), CAD(0.353186), IQD(0.285319), CNY(0.278393), BRL(0.216066), IRR(0.184211), MXN(0.131579), NOK(0.131579)
|
||||||
|
// FX rate from: https://openexchangerates.org
|
||||||
|
pub async fn monitoring_fx_rate_index() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let mut exchange_rate_vec = select_fx_rate().await;
|
||||||
|
let mut today = Utc::now().date().to_string();
|
||||||
|
let mut yesterday = Utc::now().date().pred().to_string();
|
||||||
|
today.pop();
|
||||||
|
today.pop();
|
||||||
|
today.pop();
|
||||||
|
yesterday.pop();
|
||||||
|
yesterday.pop();
|
||||||
|
yesterday.pop();
|
||||||
|
|
||||||
|
exchange_rate_vec.sort_by_key(|a| a.id);
|
||||||
|
let mut weight_avg_vec: Vec<f64> = Vec::new();
|
||||||
|
if exchange_rate_vec.last().unwrap().date.to_string() == yesterday {
|
||||||
|
let rub_weight = 0.709141;
|
||||||
|
let cad_weight = 0.353186;
|
||||||
|
let iqd_weight = 0.285319;
|
||||||
|
let cny_weight = 0.278393;
|
||||||
|
let brl_weight = 0.216066;
|
||||||
|
let irr_weight = 0.184211;
|
||||||
|
let mxn_weight = 0.131579;
|
||||||
|
let nok_weight = 0.131579;
|
||||||
|
|
||||||
|
// calculate each average for 30 days
|
||||||
|
let mut rub_avg = 0.0;
|
||||||
|
let mut cad_avg = 0.0;
|
||||||
|
let mut iqd_avg = 0.0;
|
||||||
|
let mut cny_avg = 0.0;
|
||||||
|
let mut brl_avg = 0.0;
|
||||||
|
let mut irr_avg = 0.0;
|
||||||
|
let mut mxn_avg = 0.0;
|
||||||
|
let mut nok_avg = 0.0;
|
||||||
|
|
||||||
|
for element in &exchange_rate_vec {
|
||||||
|
rub_avg += element.RUB;
|
||||||
|
cad_avg += element.CAD;
|
||||||
|
iqd_avg += element.IQD;
|
||||||
|
cny_avg += element.CNY;
|
||||||
|
brl_avg += element.BRL;
|
||||||
|
irr_avg += element.IRR;
|
||||||
|
mxn_avg += element.MXN;
|
||||||
|
nok_avg += element.NOK;
|
||||||
|
}
|
||||||
|
rub_avg /= exchange_rate_vec.len() as f64;
|
||||||
|
cad_avg /= exchange_rate_vec.len() as f64;
|
||||||
|
iqd_avg /= exchange_rate_vec.len() as f64;
|
||||||
|
cny_avg /= exchange_rate_vec.len() as f64;
|
||||||
|
brl_avg /= exchange_rate_vec.len() as f64;
|
||||||
|
irr_avg /= exchange_rate_vec.len() as f64;
|
||||||
|
mxn_avg /= exchange_rate_vec.len() as f64;
|
||||||
|
nok_avg /= exchange_rate_vec.len() as f64;
|
||||||
|
|
||||||
|
// calculate weight average for each day
|
||||||
|
for element in &exchange_rate_vec {
|
||||||
|
let weight_avg = ((element.RUB / rub_avg * rub_weight)
|
||||||
|
+ (element.CAD / cad_avg * cad_weight)
|
||||||
|
+ (element.IQD / iqd_avg * iqd_weight)
|
||||||
|
+ (element.CNY / cny_avg * cny_weight)
|
||||||
|
+ (element.BRL / brl_avg * brl_weight)
|
||||||
|
+ (element.IRR / irr_avg * irr_weight)
|
||||||
|
+ (element.MXN / mxn_avg * mxn_weight)
|
||||||
|
+ (element.NOK / nok_avg * nok_weight))
|
||||||
|
/ (rub_weight
|
||||||
|
+ cad_weight
|
||||||
|
+ iqd_weight
|
||||||
|
+ cny_weight
|
||||||
|
+ brl_weight
|
||||||
|
+ irr_weight
|
||||||
|
+ mxn_weight
|
||||||
|
+ nok_weight);
|
||||||
|
weight_avg_vec.push(weight_avg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut current_exchange_rates: Vec<ExchangeRateInfo> = Vec::new();
|
||||||
|
loop {
|
||||||
|
let result = reqeust_exchange_rate_latest().await;
|
||||||
|
match result {
|
||||||
|
Ok(T) => {
|
||||||
|
current_exchange_rates = T;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(E) => {
|
||||||
|
println!("{}", E);
|
||||||
|
let association_update_table_name = String::from("signal_association_opinion");
|
||||||
|
let association_update_condition =
|
||||||
|
vec![(String::from("name"), String::from("exchange_rate"))];
|
||||||
|
let association_update_values: Vec<(String, String)> = vec![
|
||||||
|
(String::from("is_working"), 0.to_string()),
|
||||||
|
(String::from("opinion"), String::from("-")),
|
||||||
|
];
|
||||||
|
update_record2(
|
||||||
|
&association_update_table_name,
|
||||||
|
&association_update_values,
|
||||||
|
&association_update_condition,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
sleep(Duration::from_secs(60)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut current_avg = 0.0;
|
||||||
|
|
||||||
|
for element in ¤t_exchange_rates {
|
||||||
|
match element.currency.as_str() {
|
||||||
|
"RUB" => {
|
||||||
|
current_avg += element.rate / rub_avg * rub_weight;
|
||||||
|
}
|
||||||
|
"CAD" => {
|
||||||
|
current_avg += element.rate / cad_avg * cad_weight;
|
||||||
|
}
|
||||||
|
"IQD" => {
|
||||||
|
current_avg += element.rate / iqd_avg * iqd_weight;
|
||||||
|
}
|
||||||
|
"CNY" => {
|
||||||
|
current_avg += element.rate / cny_avg * cny_weight;
|
||||||
|
}
|
||||||
|
"BRL" => {
|
||||||
|
current_avg += element.rate / brl_avg * brl_weight;
|
||||||
|
}
|
||||||
|
"IRR" => {
|
||||||
|
current_avg += element.rate / irr_avg * irr_weight;
|
||||||
|
}
|
||||||
|
"MXN" => {
|
||||||
|
current_avg += element.rate / mxn_avg * mxn_weight;
|
||||||
|
}
|
||||||
|
"NOK" => {
|
||||||
|
current_avg += element.rate / nok_avg * nok_weight;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current_avg /= (rub_weight
|
||||||
|
+ cad_weight
|
||||||
|
+ iqd_weight
|
||||||
|
+ cny_weight
|
||||||
|
+ brl_weight
|
||||||
|
+ irr_weight
|
||||||
|
+ mxn_weight
|
||||||
|
+ nok_weight);
|
||||||
|
|
||||||
|
// update signal_association_opinion
|
||||||
|
let association_update_table_name = String::from("signal_association_opinion");
|
||||||
|
let association_update_condition =
|
||||||
|
vec![(String::from("name"), String::from("exchange_rate"))];
|
||||||
|
let mut association_update_values: Vec<(String, String)> = Vec::new();
|
||||||
|
if current_avg > 1.003 {
|
||||||
|
// UP Trend
|
||||||
|
association_update_values = vec![
|
||||||
|
(String::from("is_working"), 1.to_string()),
|
||||||
|
(String::from("opinion"), String::from("UP")),
|
||||||
|
];
|
||||||
|
} else if current_avg < 0.997 {
|
||||||
|
// DOWN Trend
|
||||||
|
association_update_values = vec![
|
||||||
|
(String::from("is_working"), 1.to_string()),
|
||||||
|
(String::from("opinion"), String::from("DOWN")),
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
// KEEP Trend
|
||||||
|
association_update_values = vec![
|
||||||
|
(String::from("is_working"), 1.to_string()),
|
||||||
|
(String::from("opinion"), String::from("KEEP")),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
update_record2(
|
||||||
|
&association_update_table_name,
|
||||||
|
&association_update_values,
|
||||||
|
&association_update_condition,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
} else {
|
||||||
|
// delete the first record in [foreign_exchange_rates] and
|
||||||
|
let table_name = String::from("foreign_exchange_rates");
|
||||||
|
let mut delete_condition = String::from("WHERE id = ");
|
||||||
|
delete_condition.push_str(exchange_rate_vec.first().unwrap().id.to_string().as_str());
|
||||||
|
delete_record(&table_name, &delete_condition).await;
|
||||||
|
|
||||||
|
// add yesterday's record
|
||||||
|
let mut exchange_rates: Vec<ExchangeRateInfo> = Vec::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let result = reqeust_exchange_rate_historical(&yesterday).await;
|
||||||
|
match result {
|
||||||
|
Ok(T) => {
|
||||||
|
exchange_rates = T;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(E) => {
|
||||||
|
println!("{}", E);
|
||||||
|
let association_update_table_name = String::from("signal_association_opinion");
|
||||||
|
let association_update_condition =
|
||||||
|
vec![(String::from("name"), String::from("exchange_rate"))];
|
||||||
|
let association_update_values: Vec<(String, String)> = vec![
|
||||||
|
(String::from("is_working"), 0.to_string()),
|
||||||
|
(String::from("opinion"), String::from("-")),
|
||||||
|
];
|
||||||
|
sleep(Duration::from_secs(60)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let insert_columns = vec![
|
||||||
|
"date", "RUB", "CAD", "IQD", "CNY", "BRL", "IRR", "MXN", "NOK",
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut initial_values: Vec<String> = Vec::new();
|
||||||
|
initial_values.push(yesterday.clone());
|
||||||
|
for element in exchange_rates {
|
||||||
|
initial_values.push(element.rate.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
insert_one_record(&table_name, &insert_columns, &initial_values).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn select_fx_rate() -> Vec<ExchangeRate> {
|
||||||
|
let select_table_name = String::from("foreign_exchange_rates");
|
||||||
|
let select_columns = String::from("*");
|
||||||
|
let select_condition = None;
|
||||||
|
let select_struct = ExchangeRate {
|
||||||
|
id: 0,
|
||||||
|
date: chrono::naive::MAX_DATE,
|
||||||
|
RUB: 0.0,
|
||||||
|
CAD: 0.0,
|
||||||
|
IQD: 0.0,
|
||||||
|
CNY: 0.0,
|
||||||
|
BRL: 0.0,
|
||||||
|
IRR: 0.0,
|
||||||
|
MXN: 0.0,
|
||||||
|
NOK: 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let exchange_rate_vec = select_record(
|
||||||
|
&select_table_name,
|
||||||
|
&select_columns,
|
||||||
|
&select_condition,
|
||||||
|
&select_struct,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
exchange_rate_vec
|
||||||
|
}
|
||||||
208
src/signal_association/future_ratio.rs
Normal file
208
src/signal_association/future_ratio.rs
Normal file
|
|
@ -0,0 +1,208 @@
|
||||||
|
use crate::database_control::*;
|
||||||
|
use reqwest::{Client, ClientBuilder, Response};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
pub async fn initialize_association_record() {
|
||||||
|
let table_name = String::from("signal_association_opinion");
|
||||||
|
let initial_columns = vec!["name", "is_working", "opinion", "weight"];
|
||||||
|
let initial_values = vec![
|
||||||
|
String::from("future_ratio"),
|
||||||
|
String::from("0"),
|
||||||
|
String::from("-"),
|
||||||
|
String::from("0.0"),
|
||||||
|
];
|
||||||
|
insert_one_record(&table_name, &initial_columns, &initial_values).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn monitoring_future_ratio() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let client = ClientBuilder::new()
|
||||||
|
.timeout(tokio::time::Duration::from_secs(30))
|
||||||
|
.build()?;
|
||||||
|
let url = "https://fapi.binance.com/futures/data/globalLongShortAccountRatio?&period=5m&symbol=BTCUSDT";
|
||||||
|
let mut response = client.get(url).send().await;
|
||||||
|
|
||||||
|
let mut ratio_vec: Vec<f64> = Vec::new();
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Ok(T) => {
|
||||||
|
let mut body = T.text_with_charset("utf-8").await;
|
||||||
|
match body {
|
||||||
|
Ok(T) => {
|
||||||
|
ratio_vec = de_json_ratio(&T).await?;
|
||||||
|
}
|
||||||
|
Err(E) => {
|
||||||
|
println!("future_ratio body failed! : {:?}", E);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(E) => {
|
||||||
|
println!("future_ratio response failed!: {:?}", E);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let url = "https://fapi.binance.com/futures/data/takerlongshortRatio?&symbol=BTCUSDT&period=5m";
|
||||||
|
let mut response = client.get(url).send().await;
|
||||||
|
|
||||||
|
let mut wrapper_vec: Vec<Vec<f64>> = Vec::new();
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Ok(T) => {
|
||||||
|
let mut body = T.text_with_charset("utf-8").await;
|
||||||
|
match body {
|
||||||
|
Ok(T) => {
|
||||||
|
wrapper_vec = de_json_vol(&T).await?;
|
||||||
|
}
|
||||||
|
Err(E) => {
|
||||||
|
println!("request_all_coin_price body failed! : {:?}", E);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(E) => {
|
||||||
|
println!("request_all_coin_price response failed!: {:?}", E);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let association_update_table_name = String::from("signal_association_opinion");
|
||||||
|
let association_update_condition = vec![(String::from("name"), String::from("future_ratio"))];
|
||||||
|
let mut association_update_values: Vec<(String, String)> = Vec::new();
|
||||||
|
|
||||||
|
if ratio_vec.len() != 0 && wrapper_vec.len() != 0 {
|
||||||
|
let mut avg_account_ratio = 0.0;
|
||||||
|
let mut avg_vol_ratio = 0.0;
|
||||||
|
let mut sum_sell_buy_vol = 0.0;
|
||||||
|
|
||||||
|
ratio_vec.reverse();
|
||||||
|
ratio_vec.truncate(6);
|
||||||
|
ratio_vec.reverse();
|
||||||
|
|
||||||
|
for element in &ratio_vec {
|
||||||
|
avg_account_ratio += element;
|
||||||
|
}
|
||||||
|
avg_account_ratio /= ratio_vec.len() as f64;
|
||||||
|
|
||||||
|
let mut vol_ratio_vec = wrapper_vec[0].to_vec();
|
||||||
|
let mut buy_vol_vec = wrapper_vec[1].to_vec();
|
||||||
|
let mut sell_vol_vec = wrapper_vec[2].to_vec();
|
||||||
|
|
||||||
|
vol_ratio_vec.reverse();
|
||||||
|
vol_ratio_vec.truncate(6);
|
||||||
|
vol_ratio_vec.reverse();
|
||||||
|
buy_vol_vec.reverse();
|
||||||
|
buy_vol_vec.truncate(6);
|
||||||
|
sell_vol_vec.reverse();
|
||||||
|
sell_vol_vec.truncate(6);
|
||||||
|
|
||||||
|
for element in &vol_ratio_vec {
|
||||||
|
avg_vol_ratio += element;
|
||||||
|
}
|
||||||
|
|
||||||
|
avg_vol_ratio /= vol_ratio_vec.len() as f64;
|
||||||
|
|
||||||
|
for element in buy_vol_vec {
|
||||||
|
sum_sell_buy_vol += element;
|
||||||
|
}
|
||||||
|
for element in sell_vol_vec {
|
||||||
|
sum_sell_buy_vol -= element;
|
||||||
|
}
|
||||||
|
|
||||||
|
// println!("{} {} {}", avg_account_ratio, avg_vol_ratio, sum_sell_buy_vol);
|
||||||
|
if avg_account_ratio > 1.1 && sum_sell_buy_vol > 0.0 {
|
||||||
|
// expect UP Trend
|
||||||
|
association_update_values = vec![
|
||||||
|
(String::from("is_working"), 1.to_string()),
|
||||||
|
(String::from("opinion"), String::from("UP")),
|
||||||
|
];
|
||||||
|
// println!("UP Trend");
|
||||||
|
} else if avg_account_ratio < 1.0 {
|
||||||
|
// expect DOWN Trend
|
||||||
|
association_update_values = vec![
|
||||||
|
(String::from("is_working"), 1.to_string()),
|
||||||
|
(String::from("opinion"), String::from("DOWN")),
|
||||||
|
];
|
||||||
|
// println!("Down Trend");
|
||||||
|
} else {
|
||||||
|
association_update_values = vec![
|
||||||
|
(String::from("is_working"), 1.to_string()),
|
||||||
|
(String::from("opinion"), String::from("KEEP")),
|
||||||
|
];
|
||||||
|
// println!("Keep Trend");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
association_update_values = vec![
|
||||||
|
(String::from("is_working"), 0.to_string()),
|
||||||
|
(String::from("opinion"), String::from("-")),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
update_record2(
|
||||||
|
&association_update_table_name,
|
||||||
|
&association_update_values,
|
||||||
|
&association_update_condition,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn de_json_ratio(
|
||||||
|
body: &String,
|
||||||
|
) -> Result<Vec<f64>, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let v: Value = serde_json::from_str(body.as_str())?;
|
||||||
|
let mut into_vec = v.as_array();
|
||||||
|
if into_vec == None {
|
||||||
|
return Err("Err")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut object_map = &serde_json::map::Map::new();
|
||||||
|
let mut ratio_vec: Vec<f64> = Vec::new();
|
||||||
|
|
||||||
|
for element in into_vec.unwrap() {
|
||||||
|
object_map = element.as_object().unwrap();
|
||||||
|
let mut object_map_iter = object_map.iter();
|
||||||
|
let result = element.get("longShortRatio");
|
||||||
|
if result.is_some() {
|
||||||
|
ratio_vec.push(result.unwrap().as_str().unwrap().parse::<f64>().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(ratio_vec)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn de_json_vol(
|
||||||
|
body: &String,
|
||||||
|
) -> Result<Vec<Vec<f64>>, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let v: Value = serde_json::from_str(body.as_str())?;
|
||||||
|
let mut into_vec = v.as_array();
|
||||||
|
if into_vec == None {
|
||||||
|
return Err("Err")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut object_map = &serde_json::map::Map::new();
|
||||||
|
let mut buy_vol_vec: Vec<f64> = Vec::new();
|
||||||
|
let mut sell_vol_vec: Vec<f64> = Vec::new();
|
||||||
|
let mut ratio_vec: Vec<f64> = Vec::new();
|
||||||
|
let mut wrapper_vec: Vec<Vec<f64>> = Vec::new();
|
||||||
|
|
||||||
|
for element in into_vec.unwrap() {
|
||||||
|
object_map = element.as_object().unwrap();
|
||||||
|
let mut object_map_iter = object_map.iter();
|
||||||
|
let result = element.get("buySellRatio");
|
||||||
|
if result.is_some() {
|
||||||
|
ratio_vec.push(result.unwrap().as_str().unwrap().parse::<f64>().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = element.get("buyVol");
|
||||||
|
if result.is_some() {
|
||||||
|
buy_vol_vec.push(result.unwrap().as_str().unwrap().parse::<f64>().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = element.get("sellVol");
|
||||||
|
if result.is_some() {
|
||||||
|
sell_vol_vec.push(result.unwrap().as_str().unwrap().parse::<f64>().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wrapper_vec.push(ratio_vec);
|
||||||
|
wrapper_vec.push(buy_vol_vec);
|
||||||
|
wrapper_vec.push(sell_vol_vec);
|
||||||
|
|
||||||
|
Ok(wrapper_vec)
|
||||||
|
}
|
||||||
103
src/signal_association/signal_decision.rs
Normal file
103
src/signal_association/signal_decision.rs
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
use crate::database_control::*;
|
||||||
|
use sqlx::{Error, FromRow};
|
||||||
|
|
||||||
|
#[derive(Debug, FromRow)]
|
||||||
|
pub struct SignalDecisionInfo {
|
||||||
|
pub decision: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, FromRow)]
|
||||||
|
pub struct SignalAssociaionOpinion {
|
||||||
|
pub id: i64,
|
||||||
|
pub name: String,
|
||||||
|
pub is_working: bool,
|
||||||
|
pub opinion: String,
|
||||||
|
pub weight: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn monitoring_signal_decision() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let signal_association_opinions = select_signal_association_opinion().await;
|
||||||
|
let mut up_cnt = 0;
|
||||||
|
let mut down_cnt = 0;
|
||||||
|
let mut keep_cnt = 0;
|
||||||
|
|
||||||
|
for element in signal_association_opinions {
|
||||||
|
if element.is_working == true {
|
||||||
|
match element.opinion.as_str() {
|
||||||
|
"UP" => {
|
||||||
|
up_cnt += 1;
|
||||||
|
}
|
||||||
|
"DOWN" => {
|
||||||
|
down_cnt += 1;
|
||||||
|
}
|
||||||
|
"KEEP" => {
|
||||||
|
keep_cnt += 1;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let update_table_name = String::from("signal_decision");
|
||||||
|
let mut association_update_values: Vec<(String, String)> = Vec::new();
|
||||||
|
let association_update_condition = vec![(String::from("id"), String::from("1"))];
|
||||||
|
if up_cnt > down_cnt && up_cnt >= keep_cnt {
|
||||||
|
association_update_values = vec![(String::from("decision"), String::from("UP"))];
|
||||||
|
} else if down_cnt > up_cnt && down_cnt >= keep_cnt {
|
||||||
|
association_update_values = vec![(String::from("decision"), String::from("DOWN"))];
|
||||||
|
} else {
|
||||||
|
association_update_values = vec![(String::from("decision"), String::from("KEEP"))];
|
||||||
|
}
|
||||||
|
update_record2(
|
||||||
|
&update_table_name,
|
||||||
|
&association_update_values,
|
||||||
|
&association_update_condition,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn select_signal_association_opinion() -> Vec<SignalAssociaionOpinion> {
|
||||||
|
let select_table_name = String::from("signal_association_opinion");
|
||||||
|
let select_columns = String::from("*");
|
||||||
|
let select_condition = None;
|
||||||
|
let select_struct = SignalAssociaionOpinion {
|
||||||
|
id: 0,
|
||||||
|
name: String::new(),
|
||||||
|
is_working: false,
|
||||||
|
opinion: String::new(),
|
||||||
|
weight: 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let select_result = select_record(
|
||||||
|
&select_table_name,
|
||||||
|
&select_columns,
|
||||||
|
&select_condition,
|
||||||
|
&select_struct,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
select_result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn select_signal_decision() -> Vec<SignalDecisionInfo> {
|
||||||
|
let select_table_name = String::from("signal_decision");
|
||||||
|
let select_columns = String::from("*");
|
||||||
|
let select_condition = None;
|
||||||
|
let select_struct = SignalDecisionInfo {
|
||||||
|
decision: String::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let select_result = select_record(
|
||||||
|
&select_table_name,
|
||||||
|
&select_columns,
|
||||||
|
&select_condition,
|
||||||
|
&select_struct,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
select_result
|
||||||
|
}
|
||||||
144
src/time_checking_team.rs
Normal file
144
src/time_checking_team.rs
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
use crate::database_control::*;
|
||||||
|
use chrono::TimeZone;
|
||||||
|
use reqwest::{Client, ClientBuilder};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
use tokio::{join, time::*};
|
||||||
|
|
||||||
|
// structure for checking server time and local time
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct UserTime {
|
||||||
|
pub server_epoch: u128, // server time in Unix Epoch Time
|
||||||
|
pub local_epoch: u128, // local time where the tradingbot is running in Unix Epoch Time.
|
||||||
|
pub epoch_difference: i64, // epoch difference from server time to local time in Unix Epoch Time. (can be used as RTT)
|
||||||
|
// positive value means server is leading to local, negative value means server is lagging to local.
|
||||||
|
server_ymdhs: String, // human-readable date form of server time in UTC (y: year, m: month, d: day, h: hour, s: second)
|
||||||
|
local_ymdhs: String, // human-readable date form of local time in UTC (y: year, m: month, d: day, h: hour, s: second)
|
||||||
|
last_server_epoch: u128, // the last server epoch until the server is turned off.
|
||||||
|
last_server_ymdhs: String, // the last server date until the server is turned off.
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserTime {
|
||||||
|
// make an instance and initialize fields
|
||||||
|
pub fn new() -> UserTime {
|
||||||
|
UserTime {
|
||||||
|
server_epoch: 0,
|
||||||
|
local_epoch: 0,
|
||||||
|
epoch_difference: 0,
|
||||||
|
server_ymdhs: String::new(),
|
||||||
|
local_ymdhs: String::new(),
|
||||||
|
last_server_epoch: 0,
|
||||||
|
last_server_ymdhs: String::from("----"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update all field in Time struct.
|
||||||
|
async fn update(
|
||||||
|
&mut self,
|
||||||
|
client: &Client,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let (servertime, localtime) = join!(Self::get_server_time(&client), Self::get_local_time());
|
||||||
|
self.server_epoch = servertime?;
|
||||||
|
self.local_epoch = localtime?;
|
||||||
|
self.epoch_difference =
|
||||||
|
Self::get_difference_time(self.server_epoch, self.local_epoch).await;
|
||||||
|
self.server_ymdhs = Self::convert_epoch_to_ymdhs(self.server_epoch);
|
||||||
|
self.local_ymdhs = Self::convert_epoch_to_ymdhs(self.local_epoch);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// to record the last server-on time when request doesn't work.
|
||||||
|
pub async fn update_last_server(&mut self) {
|
||||||
|
self.last_server_epoch = self.server_epoch;
|
||||||
|
if self.server_epoch == 0 {
|
||||||
|
self.last_server_ymdhs =
|
||||||
|
String::from("server has been shutdown before the bot was executed.");
|
||||||
|
} else {
|
||||||
|
self.last_server_ymdhs = self.server_ymdhs.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_server_time(
|
||||||
|
client: &Client,
|
||||||
|
) -> Result<u128, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct ServerTime {
|
||||||
|
serverTime: u128,
|
||||||
|
}
|
||||||
|
let mut servertime = ServerTime { serverTime: 0 };
|
||||||
|
|
||||||
|
let response = client
|
||||||
|
.get("https://api.binance.com/api/v3/time")
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
servertime = response.json::<ServerTime>().await?;
|
||||||
|
Ok(servertime.serverTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to get the current local time
|
||||||
|
async fn get_local_time() -> Result<u128, std::time::SystemTimeError> {
|
||||||
|
match SystemTime::now().duration_since(UNIX_EPOCH) {
|
||||||
|
Ok(T) => Ok(T.as_millis()),
|
||||||
|
Err(E) => Err(E),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to get the difference time from the local to the server
|
||||||
|
async fn get_difference_time(servertime: u128, localtime: u128) -> i64 {
|
||||||
|
if servertime >= localtime {
|
||||||
|
(servertime - localtime) as i64
|
||||||
|
} else {
|
||||||
|
0 - ((localtime - servertime) as i64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to convert Unix Epoch time of server to human-readable date in UTC
|
||||||
|
fn convert_epoch_to_ymdhs(epoch_time: u128) -> String {
|
||||||
|
let time_i64 = (epoch_time / 1000) as i64;
|
||||||
|
chrono::offset::Utc.timestamp(time_i64, 0).to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// run time checking and store data into database.
|
||||||
|
pub async fn execute_time_check(
|
||||||
|
usertime: &mut UserTime,
|
||||||
|
client: &Client,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
usertime.update(&client).await?;
|
||||||
|
|
||||||
|
let table_name = String::from("time");
|
||||||
|
let mut record: Vec<(&str, &str)> = Vec::new();
|
||||||
|
let condition = vec![("id", "1")];
|
||||||
|
|
||||||
|
let server_epoch = usertime.server_epoch.to_string();
|
||||||
|
let local_epoch = usertime.local_epoch.to_string();
|
||||||
|
let epoch_difference = usertime.epoch_difference.to_string();
|
||||||
|
let last_server_epoch = usertime.last_server_epoch.to_string();
|
||||||
|
|
||||||
|
record.push(("server_epoch", server_epoch.as_str()));
|
||||||
|
record.push(("local_epoch", local_epoch.as_str()));
|
||||||
|
record.push(("epoch_difference", epoch_difference.as_str()));
|
||||||
|
record.push(("server_ymdhs", usertime.server_ymdhs.as_str()));
|
||||||
|
record.push(("local_ymdhs", usertime.local_ymdhs.as_str()));
|
||||||
|
record.push(("last_server_epoch", last_server_epoch.as_str()));
|
||||||
|
record.push(("last_server_ymdhs", usertime.last_server_ymdhs.as_str()));
|
||||||
|
|
||||||
|
update_record(&table_name, &record, &condition).await?;
|
||||||
|
// println!("time check 완료");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for UserTime {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
UserTime {
|
||||||
|
server_epoch: self.server_epoch,
|
||||||
|
local_epoch: self.local_epoch,
|
||||||
|
epoch_difference: self.epoch_difference,
|
||||||
|
server_ymdhs: self.last_server_ymdhs.clone(),
|
||||||
|
local_ymdhs: self.local_ymdhs.clone(),
|
||||||
|
last_server_epoch: self.last_server_epoch,
|
||||||
|
last_server_ymdhs: self.last_server_ymdhs.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
src/value_estimation_team.rs
Normal file
2
src/value_estimation_team.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod datapoints;
|
||||||
|
pub mod indicators;
|
||||||
1
src/value_estimation_team/datapoints.rs
Normal file
1
src/value_estimation_team/datapoints.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod price_data;
|
||||||
477
src/value_estimation_team/datapoints/price_data.rs
Normal file
477
src/value_estimation_team/datapoints/price_data.rs
Normal file
|
|
@ -0,0 +1,477 @@
|
||||||
|
#![allow(unused)]
|
||||||
|
#![allow(warnings)]
|
||||||
|
|
||||||
|
use crate::coin_health_check_team::request_candles::CandleData;
|
||||||
|
use crate::coin_health_check_team::request_others::CoinPriceData;
|
||||||
|
use crate::database_control::*;
|
||||||
|
use csv::{DeserializeRecordsIter, StringRecord};
|
||||||
|
use rust_decimal::{prelude::ToPrimitive, Decimal};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use sqlx::FromRow;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::{fs::*, sync::Mutex, time::*};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct RealtimePriceData {
|
||||||
|
pub opclo_price: f64,
|
||||||
|
pub open_price: f64,
|
||||||
|
pub close_price: f64,
|
||||||
|
pub high_price: f64,
|
||||||
|
pub low_price: f64,
|
||||||
|
pub close_time: i64,
|
||||||
|
pub quote_asset_volume: f64,
|
||||||
|
pub candle_type: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RealtimePriceData {
|
||||||
|
fn new() -> RealtimePriceData {
|
||||||
|
let data = RealtimePriceData {
|
||||||
|
opclo_price: 0.0,
|
||||||
|
open_price: 0.0,
|
||||||
|
close_price: 0.0,
|
||||||
|
high_price: 0.0,
|
||||||
|
low_price: 0.0,
|
||||||
|
close_time: 0,
|
||||||
|
quote_asset_volume: 0.0,
|
||||||
|
candle_type: String::new(),
|
||||||
|
};
|
||||||
|
data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_realtime_price_data(
|
||||||
|
interval: &String,
|
||||||
|
read_candle_for_opclo: &Vec<(String, Vec<CandleData>)>,
|
||||||
|
read_candle_for_rt: &Vec<(String, Vec<RealtimePriceData>)>,
|
||||||
|
write_rt_data: &mut Vec<(String, Vec<RealtimePriceData>)>,
|
||||||
|
read_price: &Vec<CoinPriceData>,
|
||||||
|
read_symbol: &Vec<String>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let instant = Instant::now();
|
||||||
|
|
||||||
|
let mut rt_price_vec: Vec<RealtimePriceData> = Vec::new();
|
||||||
|
let mut rt_data_vec: Vec<(String, Vec<RealtimePriceData>)> = Vec::new();
|
||||||
|
|
||||||
|
for element in read_symbol {
|
||||||
|
let candle_search_result = read_candle_for_opclo.iter().position(|x| x.0 == *element);
|
||||||
|
|
||||||
|
match candle_search_result {
|
||||||
|
Some(T) => {
|
||||||
|
for element in &read_candle_for_opclo[T].1 {
|
||||||
|
let mut realtime_price_data_builder = RealtimePriceData::new();
|
||||||
|
realtime_price_data_builder.opclo_price =
|
||||||
|
(element.open_price + element.close_price) / 2.0;
|
||||||
|
realtime_price_data_builder.open_price = element.open_price;
|
||||||
|
realtime_price_data_builder.close_price = element.close_price;
|
||||||
|
realtime_price_data_builder.high_price = element.high_price;
|
||||||
|
realtime_price_data_builder.low_price = element.low_price;
|
||||||
|
realtime_price_data_builder.close_time = element.close_time;
|
||||||
|
realtime_price_data_builder.quote_asset_volume = element.quote_asset_volume;
|
||||||
|
if element.open_price < element.close_price {
|
||||||
|
realtime_price_data_builder.candle_type = String::from("UP");
|
||||||
|
} else {
|
||||||
|
realtime_price_data_builder.candle_type = String::from("DOWN");
|
||||||
|
}
|
||||||
|
rt_price_vec.push(realtime_price_data_builder);
|
||||||
|
}
|
||||||
|
// reflect realtime data to the last element in rt_price_vec
|
||||||
|
if interval.contains("1m") {
|
||||||
|
let price_search_result = read_price.iter().position(|x| x.symbol == *element);
|
||||||
|
if price_search_result.is_some() {
|
||||||
|
// update close_price
|
||||||
|
rt_price_vec.last_mut().unwrap().close_price =
|
||||||
|
read_price[price_search_result.unwrap()].current_price;
|
||||||
|
// update opclo_price
|
||||||
|
rt_price_vec.last_mut().unwrap().opclo_price =
|
||||||
|
(rt_price_vec.last_mut().unwrap().open_price
|
||||||
|
+ rt_price_vec.last_mut().unwrap().close_price)
|
||||||
|
/ 2.0;
|
||||||
|
// update candle_type
|
||||||
|
if rt_price_vec.last_mut().unwrap().close_price
|
||||||
|
>= rt_price_vec.last_mut().unwrap().open_price
|
||||||
|
{
|
||||||
|
rt_price_vec.last_mut().unwrap().candle_type = String::from("UP");
|
||||||
|
} else {
|
||||||
|
rt_price_vec.last_mut().unwrap().candle_type = String::from("DOWN");
|
||||||
|
}
|
||||||
|
// update high_price
|
||||||
|
if rt_price_vec.last_mut().unwrap().high_price
|
||||||
|
< read_price[price_search_result.unwrap()].current_price
|
||||||
|
{
|
||||||
|
rt_price_vec.last_mut().unwrap().high_price =
|
||||||
|
read_price[price_search_result.unwrap()].current_price;
|
||||||
|
}
|
||||||
|
// update low_price
|
||||||
|
if rt_price_vec.last_mut().unwrap().low_price
|
||||||
|
> read_price[price_search_result.unwrap()].current_price
|
||||||
|
{
|
||||||
|
rt_price_vec.last_mut().unwrap().low_price =
|
||||||
|
read_price[price_search_result.unwrap()].current_price;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// for 30m, uses 1m candle
|
||||||
|
// for 1d, uses 30m candle
|
||||||
|
// for 1w, uses 1d candle
|
||||||
|
// for 1mon, uses 1w candle
|
||||||
|
|
||||||
|
// search symbol
|
||||||
|
let candle_search_result =
|
||||||
|
read_candle_for_rt.iter().position(|x| x.0 == *element);
|
||||||
|
match candle_search_result {
|
||||||
|
Some(T) => {
|
||||||
|
if read_candle_for_rt[T].1.len() >= 2 {
|
||||||
|
let mut candle_vec_clone = read_candle_for_rt[T].1.clone();
|
||||||
|
let mut rt_price_vec_clone = rt_price_vec.clone();
|
||||||
|
rt_price_vec_clone.reverse();
|
||||||
|
rt_price_vec_clone.truncate(2);
|
||||||
|
rt_price_vec_clone.reverse();
|
||||||
|
|
||||||
|
// update realtime information for the latest candle
|
||||||
|
let mut update_closeprice = 0.0;
|
||||||
|
let mut update_highprice = 0.0;
|
||||||
|
let mut update_lowprice = 0.0;
|
||||||
|
let mut update_quote_asset_volume = 0.0;
|
||||||
|
|
||||||
|
// search close time
|
||||||
|
let prev_closetime_result = candle_vec_clone.binary_search_by_key(
|
||||||
|
&rt_price_vec_clone.first().unwrap().close_time,
|
||||||
|
|RealtimePriceData {
|
||||||
|
opclo_price,
|
||||||
|
open_price,
|
||||||
|
close_price,
|
||||||
|
high_price,
|
||||||
|
low_price,
|
||||||
|
close_time,
|
||||||
|
quote_asset_volume,
|
||||||
|
candle_type,
|
||||||
|
}| *close_time,
|
||||||
|
);
|
||||||
|
if prev_closetime_result.is_ok() {
|
||||||
|
let result =
|
||||||
|
candle_vec_clone.get(prev_closetime_result.unwrap() + 1..);
|
||||||
|
if result.is_some() {
|
||||||
|
let update_highprice_result =
|
||||||
|
result.unwrap().iter().max_by(|x, y| {
|
||||||
|
x.high_price.partial_cmp(&y.high_price).unwrap()
|
||||||
|
});
|
||||||
|
if update_highprice_result.is_some() {
|
||||||
|
update_highprice =
|
||||||
|
update_highprice_result.unwrap().high_price;
|
||||||
|
}
|
||||||
|
|
||||||
|
let update_lowprice_result =
|
||||||
|
result.unwrap().iter().min_by(|x, y| {
|
||||||
|
x.low_price.partial_cmp(&y.low_price).unwrap()
|
||||||
|
});
|
||||||
|
if update_lowprice_result.is_some() {
|
||||||
|
update_lowprice =
|
||||||
|
update_lowprice_result.unwrap().low_price;
|
||||||
|
}
|
||||||
|
|
||||||
|
for element in result.unwrap() {
|
||||||
|
update_quote_asset_volume += element.quote_asset_volume;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let price_search_result =
|
||||||
|
read_price.iter().position(|x| x.symbol == *element);
|
||||||
|
if price_search_result.is_some() {
|
||||||
|
update_closeprice =
|
||||||
|
read_price[price_search_result.unwrap()].current_price;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the latest candle with values
|
||||||
|
if update_highprice != 0.0
|
||||||
|
&& !update_highprice.is_nan()
|
||||||
|
&& update_highprice.is_finite()
|
||||||
|
{
|
||||||
|
rt_price_vec.last_mut().unwrap().high_price = update_highprice;
|
||||||
|
}
|
||||||
|
if update_lowprice != 0.0
|
||||||
|
&& !update_lowprice.is_nan()
|
||||||
|
&& update_lowprice.is_finite()
|
||||||
|
{
|
||||||
|
rt_price_vec.last_mut().unwrap().low_price = update_lowprice;
|
||||||
|
}
|
||||||
|
if update_quote_asset_volume != 0.0
|
||||||
|
&& !update_quote_asset_volume.is_nan()
|
||||||
|
&& update_quote_asset_volume.is_finite()
|
||||||
|
{
|
||||||
|
rt_price_vec.last_mut().unwrap().quote_asset_volume =
|
||||||
|
update_quote_asset_volume;
|
||||||
|
}
|
||||||
|
if update_closeprice != 0.0
|
||||||
|
&& !update_closeprice.is_nan()
|
||||||
|
&& update_closeprice.is_finite()
|
||||||
|
{
|
||||||
|
rt_price_vec.last_mut().unwrap().close_price =
|
||||||
|
update_closeprice;
|
||||||
|
rt_price_vec.last_mut().unwrap().opclo_price =
|
||||||
|
(update_closeprice
|
||||||
|
+ rt_price_vec.last_mut().unwrap().open_price)
|
||||||
|
/ 2.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rt_data_vec.push((element.clone(), rt_price_vec.clone()));
|
||||||
|
rt_price_vec.clear();
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*write_rt_data = rt_data_vec;
|
||||||
|
|
||||||
|
// println!(" datapoints/price_{} 완료 elapsed:{:.2}s", interval, instant.elapsed().as_secs_f32());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// [price_data]
|
||||||
|
// pub async fn price_data(candle_period: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
// let instant = Instant::now();
|
||||||
|
|
||||||
|
// // select the whole symbol
|
||||||
|
// let mut usdt_trades = UsdtTrades { symbol: String::new() };
|
||||||
|
// let valid_usdt_table_name = String::from("valid_usdt_trades");
|
||||||
|
// let valid_usdt_column_name = String::from("symbol");
|
||||||
|
// let table_condition = None;
|
||||||
|
// let mut select_result = select_record(&valid_usdt_table_name, &valid_usdt_column_name, &table_condition, &usdt_trades).await?;
|
||||||
|
|
||||||
|
// let mut candle_table_name = String::from("candle_");
|
||||||
|
// let mut table_name_build = String::new();
|
||||||
|
// let column_name = String::from("openprice, highprice, lowprice, closeprice, closetime, quoteassetvolume");
|
||||||
|
// let table_condition = None;
|
||||||
|
// let mut candle_data_struct = CandleData { openprice: 0.0, highprice: 0.0, lowprice: 0.0, closeprice: 0.0, closetime: 0, quoteassetvolume: 0.0 };
|
||||||
|
|
||||||
|
// let mut output_path = String::from("datapoints/price/price_");
|
||||||
|
// output_path.push_str(candle_period);
|
||||||
|
// output_path.push('_');
|
||||||
|
// let mut output_path_build = String::new();
|
||||||
|
// let mut content_build = String::new();
|
||||||
|
|
||||||
|
// match candle_period {
|
||||||
|
// "1m" => {
|
||||||
|
// for usdttrade in select_result {
|
||||||
|
// content_build.clear();
|
||||||
|
// table_name_build.clear();
|
||||||
|
// table_name_build.push_str(candle_table_name.as_str());
|
||||||
|
// table_name_build.push_str(usdttrade.symbol.to_lowercase().as_str());
|
||||||
|
// table_name_build.push('_');
|
||||||
|
// table_name_build.push_str(candle_period);
|
||||||
|
|
||||||
|
// output_path_build.clear();
|
||||||
|
// output_path_build.push_str(output_path.as_str());
|
||||||
|
// output_path_build.push_str(usdttrade.symbol.as_str());
|
||||||
|
// output_path_build.push_str(".csv");
|
||||||
|
|
||||||
|
// let mut query_result = select_record(&table_name_build, &column_name, &table_condition, &candle_data_struct).await?;
|
||||||
|
|
||||||
|
// let mut file = tokio::fs::OpenOptions::new().create(true).write(true).truncate(true).open(output_path_build.as_str()).await;
|
||||||
|
// while let Err(e) = file {
|
||||||
|
// file = tokio::fs::OpenOptions::new().create(true).write(true).truncate(true).open(output_path_build.as_str()).await;
|
||||||
|
// sleep(Duration::from_millis(1000));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if file.is_ok() {
|
||||||
|
// for element2 in query_result {
|
||||||
|
// let meanprice = (element2.openprice + element2.closeprice)/2.0;
|
||||||
|
// content_build.push_str(meanprice.to_string().as_str()); // opclo price
|
||||||
|
// content_build.push(',');
|
||||||
|
// content_build.push_str(element2.openprice.to_string().as_str()); // openprice
|
||||||
|
// content_build.push(',');
|
||||||
|
// content_build.push_str(element2.closeprice.to_string().as_str()); // closeprice
|
||||||
|
// content_build.push(',');
|
||||||
|
// content_build.push_str(element2.highprice.to_string().as_str()); // highprice
|
||||||
|
// content_build.push(',');
|
||||||
|
// content_build.push_str(element2.lowprice.to_string().as_str()); // lowprice
|
||||||
|
// content_build.push(',');
|
||||||
|
// content_build.push_str(element2.closetime.to_string().as_str()); // closetime
|
||||||
|
// content_build.push(',');
|
||||||
|
// content_build.push_str(element2.quoteassetvolume.to_string().as_str()); // quote asset volume (USDT)
|
||||||
|
// content_build.push(',');
|
||||||
|
// if element2.closeprice >= element2.openprice {
|
||||||
|
// content_build.push_str("UP"); // UP candle
|
||||||
|
// } else {
|
||||||
|
// content_build.push_str("DOWN"); // DOWN candle
|
||||||
|
// }
|
||||||
|
// content_build.push('\n');
|
||||||
|
// }
|
||||||
|
// content_build.pop();
|
||||||
|
// file?.write_all(content_build.as_bytes()).await;
|
||||||
|
// } else if file.is_err() {
|
||||||
|
// println!(">>> File error occurred {:?} (datapoints/price_{})", file.unwrap_err(), candle_period);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// _ => {
|
||||||
|
// // read price
|
||||||
|
// let mut read_fixed_path = String::from("datapoints/price/price_");
|
||||||
|
|
||||||
|
// match candle_period {
|
||||||
|
// "30m" => {read_fixed_path.push_str("1m");},
|
||||||
|
// "1d" => {read_fixed_path.push_str("30m");},
|
||||||
|
// "1w" => {read_fixed_path.push_str("1d");},
|
||||||
|
// "1mon" => {read_fixed_path.push_str("1d");},
|
||||||
|
// _ => { println!(">>> Wrong candle_period. Check the parameter available (datapoints/price_{})", candle_period); },
|
||||||
|
// }
|
||||||
|
|
||||||
|
// read_fixed_path.push('_');
|
||||||
|
// let mut read_path_build = String::new();
|
||||||
|
// let mut read_data_vec: Vec<PriceData> = Vec::new();
|
||||||
|
|
||||||
|
// // select current coin prices
|
||||||
|
// let coinprice_table_name = String::from("coinprices");
|
||||||
|
// let coinprice_column_name = String::from("symbol, price");
|
||||||
|
// let mut condition = None;
|
||||||
|
// let mut select_data_structure = CoinPriceData { symbol: String::new(), price: Decimal::new(0,8) };
|
||||||
|
// let mut coinprice_select_result = select_record(&coinprice_table_name, &coinprice_column_name, &condition, &select_data_structure).await.unwrap();
|
||||||
|
|
||||||
|
// for usdttrade in select_result {
|
||||||
|
// read_path_build.clear();
|
||||||
|
// read_path_build.push_str(read_fixed_path.as_str());
|
||||||
|
// read_path_build.push_str(usdttrade.symbol.as_str());
|
||||||
|
// read_path_build.push_str(".csv");
|
||||||
|
// let mut price_record = StringRecord::new();
|
||||||
|
// let mut rdr = csv::ReaderBuilder::new()
|
||||||
|
// .has_headers(false)
|
||||||
|
// .from_path(&read_path_build)?;
|
||||||
|
|
||||||
|
// while let false = rdr.read_record(&mut price_record)? {
|
||||||
|
// rdr = csv::ReaderBuilder::new()
|
||||||
|
// .has_headers(false)
|
||||||
|
// .from_path(&read_path_build)?;
|
||||||
|
// sleep(Duration::from_millis(1000));
|
||||||
|
// }
|
||||||
|
// read_data_vec.clear();
|
||||||
|
|
||||||
|
// if price_record[5].parse::<u64>()? != 0 { // 유효한 데이터가 파일에 들어있는 경우
|
||||||
|
// read_data_vec.push(PriceData {
|
||||||
|
// opclo_price: price_record[0].parse::<f64>()?,
|
||||||
|
// open_price: price_record[1].parse::<f64>()?,
|
||||||
|
// close_price: price_record[2].parse::<f64>()?,
|
||||||
|
// high_price: price_record[3].parse::<f64>()?,
|
||||||
|
// low_price: price_record[4].parse::<f64>()?,
|
||||||
|
// close_time: price_record[5].parse::<u64>()?,
|
||||||
|
// quote_asset_volume: price_record[6].parse::<f64>()?,
|
||||||
|
// candle_type: price_record[7].parse::<String>()?,
|
||||||
|
// });
|
||||||
|
// for element in rdr.deserialize() {
|
||||||
|
// read_data_vec.push(element?);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// content_build.clear();
|
||||||
|
// table_name_build.clear();
|
||||||
|
// table_name_build.push_str(candle_table_name.as_str());
|
||||||
|
// table_name_build.push_str(usdttrade.symbol.to_lowercase().as_str());
|
||||||
|
// table_name_build.push('_');
|
||||||
|
// table_name_build.push_str(candle_period);
|
||||||
|
|
||||||
|
// output_path_build.clear();
|
||||||
|
// output_path_build.push_str(output_path.as_str());
|
||||||
|
// output_path_build.push_str(usdttrade.symbol.as_str());
|
||||||
|
// output_path_build.push_str(".csv");
|
||||||
|
// let mut query_result = select_record(&table_name_build, &column_name, &table_condition, &candle_data_struct).await?;
|
||||||
|
|
||||||
|
// if query_result.len() >= 2 {
|
||||||
|
// // update realtime information for the latest candle
|
||||||
|
// let mut update_closeprice = 0.0;
|
||||||
|
// let mut update_highprice = 0.0;
|
||||||
|
// let mut update_lowprice = 0.0;
|
||||||
|
// let mut update_quote_asset_volume = 0.0;
|
||||||
|
|
||||||
|
// let mut query_result_copy = query_result.clone();
|
||||||
|
// query_result_copy.reverse();
|
||||||
|
// query_result_copy.truncate(2);
|
||||||
|
// query_result_copy.reverse();
|
||||||
|
|
||||||
|
// let prev_closetime_result = read_data_vec.binary_search_by_key(&query_result_copy.first().unwrap().closetime, |PriceData {opclo_price, open_price, close_price, high_price, low_price, close_time, quote_asset_volume, candle_type}|*close_time as i64);
|
||||||
|
// if prev_closetime_result.is_ok() {
|
||||||
|
// let result = read_data_vec.get(prev_closetime_result.unwrap()+1..);
|
||||||
|
// if result.is_some() {
|
||||||
|
// let update_highprice_result = result.unwrap().iter().max_by(|x, y| x.high_price.partial_cmp(&y.high_price).unwrap());
|
||||||
|
// if update_highprice_result.is_some() {
|
||||||
|
// update_highprice = update_highprice_result.unwrap().high_price;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let update_lowprice_result = result.unwrap().iter().min_by(|x, y| x.low_price.partial_cmp(&y.low_price).unwrap());
|
||||||
|
// if update_lowprice_result.is_some() {
|
||||||
|
// update_lowprice = update_lowprice_result.unwrap().low_price;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// for element in result.unwrap() {
|
||||||
|
// update_quote_asset_volume += element.quote_asset_volume;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
// let coinprice_result = coinprice_select_result.iter().find(|x| x.symbol==usdttrade.symbol);
|
||||||
|
// if coinprice_result.is_some() {
|
||||||
|
// update_closeprice = coinprice_result.unwrap().price.to_f64().unwrap();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // update the latest candle with values
|
||||||
|
// if let Some(last) = query_result.last_mut() {
|
||||||
|
// if update_highprice != 0.0 && !update_highprice.is_nan() && update_highprice.is_finite() {
|
||||||
|
// last.highprice = update_highprice;
|
||||||
|
// }
|
||||||
|
// if update_lowprice != 0.0 && !update_lowprice.is_nan() && update_lowprice.is_finite() {
|
||||||
|
// last.lowprice = update_lowprice;
|
||||||
|
// }
|
||||||
|
// if update_quote_asset_volume != 0.0 && !update_quote_asset_volume.is_nan() && update_quote_asset_volume.is_finite() {
|
||||||
|
// last.quoteassetvolume = update_quote_asset_volume;
|
||||||
|
// }
|
||||||
|
// if update_closeprice != 0.0 && !update_closeprice.is_nan() && update_closeprice.is_finite() {
|
||||||
|
// last.closeprice = update_closeprice;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let mut file = tokio::fs::OpenOptions::new().create(true).write(true).truncate(true).open(output_path_build.as_str()).await;
|
||||||
|
// while let Err(e) = file {
|
||||||
|
// file = tokio::fs::OpenOptions::new().create(true).write(true).truncate(true).open(output_path_build.as_str()).await;
|
||||||
|
// sleep(Duration::from_millis(1000));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if file.is_ok() {
|
||||||
|
// for element2 in query_result {
|
||||||
|
// let meanprice = (element2.openprice + element2.closeprice)/2.0;
|
||||||
|
// content_build.push_str(meanprice.to_string().as_str()); // opclo price
|
||||||
|
// content_build.push(',');
|
||||||
|
// content_build.push_str(element2.openprice.to_string().as_str()); // openprice
|
||||||
|
// content_build.push(',');
|
||||||
|
// content_build.push_str(element2.closeprice.to_string().as_str()); // closeprice
|
||||||
|
// content_build.push(',');
|
||||||
|
// content_build.push_str(element2.highprice.to_string().as_str()); // highprice
|
||||||
|
// content_build.push(',');
|
||||||
|
// content_build.push_str(element2.lowprice.to_string().as_str()); // lowprice
|
||||||
|
// content_build.push(',');
|
||||||
|
// content_build.push_str(element2.closetime.to_string().as_str()); // closetime
|
||||||
|
// content_build.push(',');
|
||||||
|
// content_build.push_str(element2.quoteassetvolume.to_string().as_str()); // quote asset volume (USDT)
|
||||||
|
// content_build.push(',');
|
||||||
|
// if element2.closeprice >= element2.openprice {
|
||||||
|
// content_build.push_str("UP"); // UP candle
|
||||||
|
// } else {
|
||||||
|
// content_build.push_str("DOWN"); // DOWN candle
|
||||||
|
// }
|
||||||
|
// content_build.push('\n');
|
||||||
|
// }
|
||||||
|
// content_build.pop();
|
||||||
|
// file?.write_all(content_build.as_bytes()).await;
|
||||||
|
// } else if file.is_err() {
|
||||||
|
// println!(">>> File error occurred {:?} (datapoints/price_{})", file.unwrap_err(), candle_period);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// println!(" datapoints/price_{} 완료 elapsed:{:.2}s", candle_period, instant.elapsed().as_secs_f32());
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
9
src/value_estimation_team/indicators.rs
Normal file
9
src/value_estimation_team/indicators.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
pub mod bollingerband;
|
||||||
|
pub mod ema;
|
||||||
|
pub mod heatmap_volume;
|
||||||
|
pub mod macd;
|
||||||
|
pub mod rsi;
|
||||||
|
pub mod sma;
|
||||||
|
pub mod stoch_rsi;
|
||||||
|
pub mod supertrend;
|
||||||
|
pub mod tema;
|
||||||
139
src/value_estimation_team/indicators/bollingerband.rs
Normal file
139
src/value_estimation_team/indicators/bollingerband.rs
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
#![allow(unused)]
|
||||||
|
#![allow(warnings)]
|
||||||
|
|
||||||
|
use crate::database_control::*;
|
||||||
|
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
|
||||||
|
use crate::value_estimation_team::indicators::sma::SmaData;
|
||||||
|
use csv::{DeserializeRecordsIter, StringRecord};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use sqlx::FromRow;
|
||||||
|
use tokio::{fs::*, io::AsyncWriteExt, time::*};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct BollingerBandData {
|
||||||
|
pub sma: f64,
|
||||||
|
pub upperband: f64,
|
||||||
|
pub lowerband: f64,
|
||||||
|
pub close_time: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BollingerBandData {
|
||||||
|
fn new() -> BollingerBandData {
|
||||||
|
let a = BollingerBandData {
|
||||||
|
sma: 0.0,
|
||||||
|
upperband: 0.0,
|
||||||
|
lowerband: 0.0,
|
||||||
|
close_time: 0,
|
||||||
|
};
|
||||||
|
a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Binance Bollingerband (SMA)
|
||||||
|
pub async fn bollingerband(
|
||||||
|
ma_number: usize,
|
||||||
|
sd_factor: f64,
|
||||||
|
input_sma_data: &Vec<(String, Vec<SmaData>)>,
|
||||||
|
rt_input_data: &Vec<(String, Vec<RealtimePriceData>)>,
|
||||||
|
output_bb_data: &mut Vec<(String, Vec<BollingerBandData>)>,
|
||||||
|
valid_usdt_trades: &Vec<String>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let instant = Instant::now();
|
||||||
|
|
||||||
|
let mut read_rt_data_vec: Vec<(String, Vec<RealtimePriceData>)> = rt_input_data.clone();
|
||||||
|
let mut read_sma_data_vec: Vec<(String, Vec<SmaData>)> = input_sma_data.clone();
|
||||||
|
|
||||||
|
let mut standard_deviation: f64 = 0.0;
|
||||||
|
let mut sd_mean: f64 = 0.0;
|
||||||
|
let mut read_data_buffer: Option<&SmaData>;
|
||||||
|
|
||||||
|
let mut bb_data_wrapper: Vec<(String, Vec<BollingerBandData>)> = Vec::new();
|
||||||
|
let mut bb_data_vec: Vec<BollingerBandData> = Vec::new();
|
||||||
|
|
||||||
|
for symbol in valid_usdt_trades {
|
||||||
|
bb_data_vec.clear();
|
||||||
|
let mut bb_data = BollingerBandData::new();
|
||||||
|
|
||||||
|
let symbol_search_result1 = read_rt_data_vec.iter().position(|x| x.0 == *symbol);
|
||||||
|
let symbol_search_result2 = read_sma_data_vec.iter().position(|x| x.0 == *symbol);
|
||||||
|
|
||||||
|
match symbol_search_result1 {
|
||||||
|
Some(rt_index) => {
|
||||||
|
match symbol_search_result2 {
|
||||||
|
Some(sma_index) => {
|
||||||
|
// if the data has shorter than buffer
|
||||||
|
if read_sma_data_vec[sma_index].1.len() < ma_number {
|
||||||
|
bb_data.sma = 0.0;
|
||||||
|
bb_data.upperband = 0.0;
|
||||||
|
bb_data.lowerband = 0.0;
|
||||||
|
bb_data.close_time = 0;
|
||||||
|
bb_data_vec.push(bb_data.clone());
|
||||||
|
} else {
|
||||||
|
let result = read_rt_data_vec[rt_index].1.binary_search_by_key(
|
||||||
|
&read_sma_data_vec[sma_index].1.first().unwrap().close_time,
|
||||||
|
|RealtimePriceData {
|
||||||
|
opclo_price,
|
||||||
|
open_price,
|
||||||
|
close_price,
|
||||||
|
high_price,
|
||||||
|
low_price,
|
||||||
|
close_time,
|
||||||
|
quote_asset_volume,
|
||||||
|
candle_type,
|
||||||
|
}| *close_time,
|
||||||
|
);
|
||||||
|
match result {
|
||||||
|
Ok(T) => {
|
||||||
|
if T >= 0 && T <= ma_number - 1 {
|
||||||
|
let mut read_data_iter =
|
||||||
|
read_sma_data_vec[sma_index].1.iter();
|
||||||
|
for _ in T..ma_number - 1 {
|
||||||
|
read_data_iter.next();
|
||||||
|
}
|
||||||
|
let window_iter =
|
||||||
|
read_rt_data_vec[rt_index].1.windows(ma_number);
|
||||||
|
for buffer in window_iter {
|
||||||
|
sd_mean = 0.0;
|
||||||
|
standard_deviation = 0.0;
|
||||||
|
for element in buffer {
|
||||||
|
sd_mean += element.close_price;
|
||||||
|
}
|
||||||
|
sd_mean /= (ma_number as f64);
|
||||||
|
for element in buffer {
|
||||||
|
standard_deviation +=
|
||||||
|
(element.close_price - sd_mean).powi(2);
|
||||||
|
}
|
||||||
|
standard_deviation = sd_factor
|
||||||
|
* ((standard_deviation / ma_number as f64).sqrt());
|
||||||
|
|
||||||
|
read_data_buffer = read_data_iter.next();
|
||||||
|
|
||||||
|
match read_data_buffer {
|
||||||
|
Some(T) => {
|
||||||
|
bb_data.sma = T.sma_value;
|
||||||
|
bb_data.upperband =
|
||||||
|
T.sma_value + standard_deviation;
|
||||||
|
bb_data.lowerband =
|
||||||
|
T.sma_value - standard_deviation;
|
||||||
|
bb_data.close_time = T.close_time;
|
||||||
|
bb_data_vec.push(bb_data.clone());
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(E) => {}
|
||||||
|
}
|
||||||
|
bb_data_wrapper.push((symbol.clone(), bb_data_vec.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*output_bb_data = bb_data_wrapper;
|
||||||
|
// println!(" indicators/bb{} 완료 elapsed:{:.2}s", ma_number, instant.elapsed().as_secs_f32());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
84
src/value_estimation_team/indicators/ema.rs
Normal file
84
src/value_estimation_team/indicators/ema.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
#![allow(unused)]
|
||||||
|
#![allow(warnings)]
|
||||||
|
use crate::database_control::*;
|
||||||
|
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
|
||||||
|
use csv::{DeserializeRecordsIter, StringRecord};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use sqlx::FromRow;
|
||||||
|
use tokio::{fs::*, io::AsyncWriteExt, time::*};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct EmaData {
|
||||||
|
pub ema_value: f64,
|
||||||
|
pub close_time: i64,
|
||||||
|
}
|
||||||
|
impl EmaData {
|
||||||
|
fn new() -> EmaData {
|
||||||
|
let a = EmaData {
|
||||||
|
ema_value: 0.0,
|
||||||
|
close_time: 0,
|
||||||
|
};
|
||||||
|
a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binance EMA (closeprice)
|
||||||
|
pub async fn ema(
|
||||||
|
ema_number: usize,
|
||||||
|
input_rt_data: &Vec<(String, Vec<RealtimePriceData>)>,
|
||||||
|
output_ema_data: &mut Vec<(String, Vec<EmaData>)>,
|
||||||
|
valid_usdt_trades: &Vec<String>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let instant = Instant::now();
|
||||||
|
let alpha: f64 = 2.0 / (ema_number as f64 + 1.0);
|
||||||
|
let mut ema_t: f64 = 0.0;
|
||||||
|
let mut ema_prev: f64 = 0.0;
|
||||||
|
|
||||||
|
let mut ema_data = EmaData::new();
|
||||||
|
let mut ema_data_wrapper: Vec<(String, Vec<EmaData>)> = Vec::new();
|
||||||
|
let mut ema_data_vec: Vec<EmaData> = Vec::new();
|
||||||
|
for symbol in valid_usdt_trades {
|
||||||
|
let symbol_search_result = input_rt_data.iter().position(|x| x.0 == *symbol);
|
||||||
|
ema_data_vec.clear();
|
||||||
|
|
||||||
|
match symbol_search_result {
|
||||||
|
Some(T) => {
|
||||||
|
if input_rt_data[T].1.len() < ema_number {
|
||||||
|
ema_data.ema_value = 0.0;
|
||||||
|
ema_data.close_time = 0;
|
||||||
|
ema_data_vec.push(ema_data.clone());
|
||||||
|
} else {
|
||||||
|
let partial_vec1 = input_rt_data[T].1.get(..ema_number).unwrap();
|
||||||
|
let partial_vec2 = input_rt_data[T].1.get(ema_number..).unwrap();
|
||||||
|
|
||||||
|
let mut sma_for_initial_value = 0.0;
|
||||||
|
for element in partial_vec1 {
|
||||||
|
sma_for_initial_value += element.close_price;
|
||||||
|
}
|
||||||
|
sma_for_initial_value /= ema_number as f64;
|
||||||
|
|
||||||
|
ema_data.ema_value = sma_for_initial_value;
|
||||||
|
ema_data.close_time = partial_vec1.last().unwrap().close_time;
|
||||||
|
ema_data_vec.push(ema_data.clone());
|
||||||
|
|
||||||
|
ema_prev = sma_for_initial_value;
|
||||||
|
|
||||||
|
for element in partial_vec2 {
|
||||||
|
ema_t = (1.0 - alpha) * ema_prev + alpha * element.close_price;
|
||||||
|
|
||||||
|
ema_data.ema_value = ema_t;
|
||||||
|
ema_data.close_time = element.close_time;
|
||||||
|
ema_data_vec.push(ema_data.clone());
|
||||||
|
|
||||||
|
ema_prev = ema_t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ema_data_wrapper.push((symbol.clone(), ema_data_vec.clone()));
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*output_ema_data = ema_data_wrapper;
|
||||||
|
// println!(" indicators/ema{} 완료 elapsed:{:.2}s", ema_number, instant.elapsed().as_secs_f32());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
166
src/value_estimation_team/indicators/heatmap_volume.rs
Normal file
166
src/value_estimation_team/indicators/heatmap_volume.rs
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum HeatMapLevel {
|
||||||
|
ExtraHigh,
|
||||||
|
High,
|
||||||
|
Medium,
|
||||||
|
Normal,
|
||||||
|
Low,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct HeatmapVolumeData {
|
||||||
|
pub heatmap_value: f64,
|
||||||
|
pub heatmap_level: HeatMapLevel,
|
||||||
|
pub close_time: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation from TradingView Script (HeatMap Volume by xdecow)
|
||||||
|
pub async fn heatmap_volume(
|
||||||
|
symbol: &String,
|
||||||
|
input_rt_data: &Vec<(String, Vec<RealtimePriceData>)>,
|
||||||
|
ma_len: usize,
|
||||||
|
std_len: usize,
|
||||||
|
extra_high_thold: f64,
|
||||||
|
high_thold: f64,
|
||||||
|
medium_thold: f64,
|
||||||
|
normal_thold: f64,
|
||||||
|
) -> Option<Vec<HeatmapVolumeData>> {
|
||||||
|
let symbol_search_result = input_rt_data.iter().position(|x| x.0 == *symbol);
|
||||||
|
|
||||||
|
match symbol_search_result {
|
||||||
|
Some(T) => {
|
||||||
|
if input_rt_data[T].1.len() >= ma_len && input_rt_data[T].1.len() >= std_len {
|
||||||
|
// calc mean
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct MeanData {
|
||||||
|
mean_value: f64,
|
||||||
|
close_time: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut mean_vec: Vec<MeanData> = Vec::new();
|
||||||
|
let mut mean_data = MeanData {
|
||||||
|
mean_value: 0.0,
|
||||||
|
close_time: 0,
|
||||||
|
};
|
||||||
|
let window = input_rt_data[T].1.windows(ma_len);
|
||||||
|
for buffer in window {
|
||||||
|
// calculate SMA of volume
|
||||||
|
mean_data.mean_value = 0.0;
|
||||||
|
mean_data.close_time = 0;
|
||||||
|
for element in buffer {
|
||||||
|
mean_data.mean_value += element.quote_asset_volume;
|
||||||
|
}
|
||||||
|
mean_data.mean_value /= ma_len as f64;
|
||||||
|
mean_data.close_time = buffer.last().unwrap().close_time;
|
||||||
|
|
||||||
|
mean_vec.push(mean_data.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// calc pstdev
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct PstdevData {
|
||||||
|
pstdev_value: f64, // population standard deviation value
|
||||||
|
close_time: i64,
|
||||||
|
}
|
||||||
|
let mut pstdev_vec: Vec<PstdevData> = Vec::new();
|
||||||
|
let mut pstdev_data = PstdevData {
|
||||||
|
pstdev_value: 0.0,
|
||||||
|
close_time: 0,
|
||||||
|
};
|
||||||
|
let mut mean = 0.0;
|
||||||
|
let mut summation = 0.0;
|
||||||
|
let mut sample_minus_mean = 0.0;
|
||||||
|
let window = input_rt_data[T].1.windows(std_len);
|
||||||
|
for buffer in window {
|
||||||
|
pstdev_data.pstdev_value = 0.0;
|
||||||
|
pstdev_data.close_time = 0;
|
||||||
|
mean = 0.0;
|
||||||
|
summation = 0.0;
|
||||||
|
sample_minus_mean = 0.0;
|
||||||
|
for element in buffer {
|
||||||
|
mean += element.quote_asset_volume;
|
||||||
|
}
|
||||||
|
mean /= std_len as f64;
|
||||||
|
|
||||||
|
for element in buffer {
|
||||||
|
sample_minus_mean = element.quote_asset_volume - mean;
|
||||||
|
summation += sample_minus_mean.powi(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
pstdev_data.pstdev_value = (summation / std_len as f64).sqrt();
|
||||||
|
pstdev_data.close_time = buffer.last().unwrap().close_time;
|
||||||
|
|
||||||
|
pstdev_vec.push(pstdev_data.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// calc stdbar and heatmap volume
|
||||||
|
let mut heatmap_vol_vec: Vec<HeatmapVolumeData> = Vec::new();
|
||||||
|
let mut heatmap_vol_data = HeatmapVolumeData {
|
||||||
|
heatmap_value: 0.0,
|
||||||
|
heatmap_level: HeatMapLevel::Normal,
|
||||||
|
close_time: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if ma_len == std_len {
|
||||||
|
let mut rt_data_trunc_vec =
|
||||||
|
input_rt_data[T].1.get(ma_len - 1..).unwrap().iter();
|
||||||
|
|
||||||
|
let zipped = mean_vec.iter().zip(pstdev_vec.iter());
|
||||||
|
for element in zipped {
|
||||||
|
heatmap_vol_data.heatmap_value =
|
||||||
|
(rt_data_trunc_vec.next().unwrap().quote_asset_volume
|
||||||
|
- element.0.mean_value)
|
||||||
|
/ element.1.pstdev_value;
|
||||||
|
heatmap_vol_data.close_time = element.0.close_time;
|
||||||
|
if heatmap_vol_data.heatmap_value >= extra_high_thold {
|
||||||
|
heatmap_vol_data.heatmap_level = HeatMapLevel::ExtraHigh;
|
||||||
|
} else if heatmap_vol_data.heatmap_value > high_thold {
|
||||||
|
heatmap_vol_data.heatmap_level = HeatMapLevel::High;
|
||||||
|
} else if heatmap_vol_data.heatmap_value > medium_thold {
|
||||||
|
heatmap_vol_data.heatmap_level = HeatMapLevel::Medium;
|
||||||
|
} else if heatmap_vol_data.heatmap_value > normal_thold {
|
||||||
|
heatmap_vol_data.heatmap_level = HeatMapLevel::Normal;
|
||||||
|
} else {
|
||||||
|
heatmap_vol_data.heatmap_level = HeatMapLevel::Low;
|
||||||
|
}
|
||||||
|
heatmap_vol_vec.push(heatmap_vol_data.clone());
|
||||||
|
}
|
||||||
|
} else if ma_len > std_len {
|
||||||
|
let mut rt_data_trunc_vec =
|
||||||
|
input_rt_data[T].1.get(std_len - 1..).unwrap().iter();
|
||||||
|
let mut mean_trunc_vec =
|
||||||
|
mean_vec.get(mean_vec.len() - std_len..).unwrap().iter();
|
||||||
|
let zipped = mean_trunc_vec.zip(pstdev_vec.iter());
|
||||||
|
for element in zipped {
|
||||||
|
heatmap_vol_data.heatmap_value =
|
||||||
|
(rt_data_trunc_vec.next().unwrap().quote_asset_volume
|
||||||
|
- element.0.mean_value)
|
||||||
|
/ element.1.pstdev_value;
|
||||||
|
heatmap_vol_data.close_time = element.0.close_time;
|
||||||
|
heatmap_vol_vec.push(heatmap_vol_data.clone());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut rt_data_trunc_vec =
|
||||||
|
input_rt_data[T].1.get(ma_len - 1..).unwrap().iter();
|
||||||
|
let mut pstdev_trunc_vec =
|
||||||
|
pstdev_vec.get(pstdev_vec.len() - ma_len..).unwrap().iter();
|
||||||
|
let zipped = mean_vec.iter().zip(pstdev_trunc_vec);
|
||||||
|
for element in zipped {
|
||||||
|
heatmap_vol_data.heatmap_value =
|
||||||
|
(rt_data_trunc_vec.next().unwrap().quote_asset_volume
|
||||||
|
- element.0.mean_value)
|
||||||
|
/ element.1.pstdev_value;
|
||||||
|
heatmap_vol_data.close_time = element.0.close_time;
|
||||||
|
heatmap_vol_vec.push(heatmap_vol_data.clone());
|
||||||
|
} // level 구현
|
||||||
|
}
|
||||||
|
Some(heatmap_vol_vec)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
83
src/value_estimation_team/indicators/macd.rs
Normal file
83
src/value_estimation_team/indicators/macd.rs
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
use crate::value_estimation_team::indicators::ema::EmaData;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct EmaMacd {
|
||||||
|
pub macd_value: f64,
|
||||||
|
pub close_time: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EmaMacd {
|
||||||
|
fn new() -> EmaMacd {
|
||||||
|
let a = EmaMacd {
|
||||||
|
macd_value: 0.0,
|
||||||
|
close_time: 0,
|
||||||
|
};
|
||||||
|
a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn ema_macd(
|
||||||
|
fast_ema: &Vec<EmaData>,
|
||||||
|
slow_ema: &Vec<EmaData>,
|
||||||
|
signal_length: usize,
|
||||||
|
) -> Result<Vec<EmaMacd>, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let mut macd = EmaMacd::new();
|
||||||
|
let mut macd_vec: Vec<EmaMacd> = Vec::new();
|
||||||
|
let mut macd_signal_vec: Vec<EmaMacd> = Vec::new();
|
||||||
|
let mut macd_oscil_vec: Vec<EmaMacd> = Vec::new();
|
||||||
|
|
||||||
|
if fast_ema.len() >= signal_length && slow_ema.len() >= signal_length {
|
||||||
|
let result = fast_ema.binary_search_by_key(
|
||||||
|
&slow_ema.first().unwrap().close_time,
|
||||||
|
|&EmaData {
|
||||||
|
ema_value,
|
||||||
|
close_time,
|
||||||
|
}| close_time,
|
||||||
|
);
|
||||||
|
if result.is_ok() {
|
||||||
|
let temp_vec = fast_ema.get(result.unwrap()..).unwrap();
|
||||||
|
let zipped = temp_vec.iter().zip(slow_ema);
|
||||||
|
for element in zipped {
|
||||||
|
macd.macd_value = element.0.ema_value - element.1.ema_value;
|
||||||
|
macd.close_time = element.0.close_time;
|
||||||
|
macd_vec.push(macd.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// making signal
|
||||||
|
let macd_vec_window = macd_vec.windows(signal_length);
|
||||||
|
for window in macd_vec_window {
|
||||||
|
let mut sum_value = 0.0;
|
||||||
|
for element in window {
|
||||||
|
sum_value += element.macd_value;
|
||||||
|
}
|
||||||
|
macd.macd_value = sum_value / signal_length as f64;
|
||||||
|
macd.close_time = window.last().unwrap().close_time;
|
||||||
|
macd_signal_vec.push(macd.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = macd_vec.binary_search_by_key(
|
||||||
|
&macd_signal_vec.first().unwrap().close_time,
|
||||||
|
|&EmaMacd {
|
||||||
|
macd_value,
|
||||||
|
close_time,
|
||||||
|
}| close_time,
|
||||||
|
);
|
||||||
|
if result.is_ok() {
|
||||||
|
let result = macd_vec.get(result.unwrap()..);
|
||||||
|
if result.is_some() {
|
||||||
|
if result.unwrap().len() == macd_signal_vec.len() {
|
||||||
|
let zipped = result.unwrap().iter().zip(macd_signal_vec);
|
||||||
|
|
||||||
|
for element in zipped {
|
||||||
|
macd.macd_value = element.0.macd_value - element.1.macd_value;
|
||||||
|
macd.close_time = element.0.close_time;
|
||||||
|
macd_oscil_vec.push(macd.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(macd_oscil_vec)
|
||||||
|
}
|
||||||
151
src/value_estimation_team/indicators/rsi.rs
Normal file
151
src/value_estimation_team/indicators/rsi.rs
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
#![allow(unused)]
|
||||||
|
#![allow(warnings)]
|
||||||
|
|
||||||
|
use crate::database_control::*;
|
||||||
|
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
|
||||||
|
use csv::{DeserializeRecordsIter, StringRecord};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use sqlx::FromRow;
|
||||||
|
use std::f64::NAN;
|
||||||
|
use tokio::{fs::*, io::AsyncWriteExt, time::*};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct RsiData {
|
||||||
|
pub rsi_value: f64,
|
||||||
|
pub close_time: i64,
|
||||||
|
}
|
||||||
|
impl RsiData {
|
||||||
|
pub fn new() -> RsiData {
|
||||||
|
let a = RsiData {
|
||||||
|
rsi_value: 0.0,
|
||||||
|
close_time: 0,
|
||||||
|
};
|
||||||
|
a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binance RSI (EMA, Closeprice, Wilder's weight, 150 candles)
|
||||||
|
pub async fn rsi(
|
||||||
|
rsi_number: usize,
|
||||||
|
input_rt_data: &Vec<(String, Vec<RealtimePriceData>)>,
|
||||||
|
output_rsi_data: &mut Vec<(String, Vec<RsiData>)>,
|
||||||
|
valid_usdt_trades: &Vec<String>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let instant = Instant::now();
|
||||||
|
|
||||||
|
let mut read_data_vec: Vec<RealtimePriceData> = Vec::new();
|
||||||
|
let mut read_price_buffer: Vec<RealtimePriceData> = Vec::new();
|
||||||
|
let mut prev_price: f64 = 0.0;
|
||||||
|
let mut current_price: f64 = 0.0;
|
||||||
|
let mut sum_increase: f64 = 0.0;
|
||||||
|
let mut sum_decrease: f64 = 0.0;
|
||||||
|
let mut rsi: f64 = 0.0;
|
||||||
|
let mut last_close_time = 0;
|
||||||
|
|
||||||
|
let mut rsi_data_wrapper: Vec<(String, Vec<RsiData>)> = Vec::new();
|
||||||
|
let mut rsi_data_vec: Vec<RsiData> = Vec::new();
|
||||||
|
let mut rsi_data = RsiData::new();
|
||||||
|
for symbol in valid_usdt_trades {
|
||||||
|
read_data_vec.clear();
|
||||||
|
read_price_buffer.clear();
|
||||||
|
rsi_data_vec.clear();
|
||||||
|
let symbol_search_result = input_rt_data.iter().position(|x| x.0 == *symbol);
|
||||||
|
match symbol_search_result {
|
||||||
|
Some(T) => {
|
||||||
|
if input_rt_data[T].1.len() < rsi_number + 1 {
|
||||||
|
rsi_data.rsi_value = 0.0;
|
||||||
|
rsi_data.close_time = 0;
|
||||||
|
rsi_data_vec.push(rsi_data.clone());
|
||||||
|
} else {
|
||||||
|
read_data_vec = input_rt_data[T].1.clone();
|
||||||
|
if read_data_vec.len() >= (150 + rsi_number) as usize {
|
||||||
|
read_data_vec.reverse();
|
||||||
|
read_data_vec.truncate((150 + rsi_number) as usize);
|
||||||
|
read_data_vec.reverse();
|
||||||
|
}
|
||||||
|
let window_iter = read_data_vec.windows(rsi_number + 1);
|
||||||
|
|
||||||
|
let mut prev_avg_ups: Option<f64> = None;
|
||||||
|
let mut prev_avg_downs: Option<f64> = None;
|
||||||
|
let mut current_avg_ups: Option<f64> = None;
|
||||||
|
let mut current_avg_downs: Option<f64> = None;
|
||||||
|
|
||||||
|
for buffer in window_iter {
|
||||||
|
let mut up_vec: Vec<f64> = Vec::new();
|
||||||
|
let mut down_vec: Vec<f64> = Vec::new();
|
||||||
|
|
||||||
|
let buffer_window = buffer.windows(2);
|
||||||
|
for element in buffer_window {
|
||||||
|
if element.last().unwrap().close_price
|
||||||
|
- element.first().unwrap().close_price
|
||||||
|
> 0.0
|
||||||
|
{
|
||||||
|
up_vec.push(
|
||||||
|
element.last().unwrap().close_price
|
||||||
|
- element.first().unwrap().close_price,
|
||||||
|
);
|
||||||
|
down_vec.push(0.0);
|
||||||
|
} else if element.last().unwrap().close_price
|
||||||
|
- element.first().unwrap().close_price
|
||||||
|
< 0.0
|
||||||
|
{
|
||||||
|
up_vec.push(0.0);
|
||||||
|
down_vec.push(
|
||||||
|
(element.last().unwrap().close_price
|
||||||
|
- element.first().unwrap().close_price)
|
||||||
|
.abs(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
up_vec.push(0.0);
|
||||||
|
down_vec.push(0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if current_avg_ups == None
|
||||||
|
&& current_avg_downs == None
|
||||||
|
&& prev_avg_ups == None
|
||||||
|
&& prev_avg_downs == None
|
||||||
|
{
|
||||||
|
// initial averages based on SMA
|
||||||
|
let mut avg_ups: f64 = up_vec.iter().sum();
|
||||||
|
avg_ups /= rsi_number as f64;
|
||||||
|
let mut avg_downs: f64 = down_vec.iter().sum();
|
||||||
|
avg_downs /= rsi_number as f64;
|
||||||
|
|
||||||
|
current_avg_ups = Some(avg_ups);
|
||||||
|
current_avg_downs = Some(avg_downs);
|
||||||
|
} else {
|
||||||
|
// [EMA]
|
||||||
|
let alpha = 1.0 / (rsi_number as f64); // Wilder's weight
|
||||||
|
current_avg_ups = Some(
|
||||||
|
alpha * up_vec.last().unwrap()
|
||||||
|
+ ((1.0 - alpha) * prev_avg_ups.unwrap()),
|
||||||
|
);
|
||||||
|
current_avg_downs = Some(
|
||||||
|
alpha * down_vec.last().unwrap()
|
||||||
|
+ ((1.0 - alpha) * prev_avg_downs.unwrap()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
prev_avg_ups = current_avg_ups;
|
||||||
|
prev_avg_downs = current_avg_downs;
|
||||||
|
|
||||||
|
let rs =
|
||||||
|
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));
|
||||||
|
|
||||||
|
rsi_data.rsi_value = rsi;
|
||||||
|
rsi_data.close_time = buffer.last().unwrap().close_time;
|
||||||
|
rsi_data_vec.push(rsi_data.clone());
|
||||||
|
}
|
||||||
|
rsi_data_wrapper.push((symbol.clone(), rsi_data_vec.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*output_rsi_data = rsi_data_wrapper;
|
||||||
|
// println!(" indicators/rsi{} 완료 elapsed:{:.2}s", rsi_number, instant.elapsed().as_secs_f32());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
71
src/value_estimation_team/indicators/sma.rs
Normal file
71
src/value_estimation_team/indicators/sma.rs
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
#![allow(unused)]
|
||||||
|
#![allow(warnings)]
|
||||||
|
|
||||||
|
use crate::database_control::*;
|
||||||
|
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
|
||||||
|
use csv::{DeserializeRecordsIter, StringRecord};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use sqlx::FromRow;
|
||||||
|
use tokio::{fs::*, io::AsyncWriteExt, time::*};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SmaData {
|
||||||
|
pub sma_value: f64,
|
||||||
|
pub close_time: i64,
|
||||||
|
}
|
||||||
|
impl SmaData {
|
||||||
|
fn new() -> SmaData {
|
||||||
|
let a = SmaData {
|
||||||
|
sma_value: 0.0,
|
||||||
|
close_time: 0,
|
||||||
|
};
|
||||||
|
a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binance MA (closeprice)
|
||||||
|
pub async fn sma(
|
||||||
|
moving_number: usize,
|
||||||
|
input_rt_data: &Vec<(String, Vec<RealtimePriceData>)>,
|
||||||
|
output_sma_data: &mut Vec<(String, Vec<SmaData>)>,
|
||||||
|
valid_usdt_trades: &Vec<String>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let instant = Instant::now();
|
||||||
|
|
||||||
|
let mut sma_data_wrapper: Vec<(String, Vec<SmaData>)> = Vec::new();
|
||||||
|
let mut sma_data_vec: Vec<SmaData> = Vec::new();
|
||||||
|
let mut sma_data = SmaData::new();
|
||||||
|
|
||||||
|
for symbol in valid_usdt_trades {
|
||||||
|
let symbol_search_result = input_rt_data.iter().position(|x| x.0 == *symbol);
|
||||||
|
sma_data_vec.clear();
|
||||||
|
|
||||||
|
match symbol_search_result {
|
||||||
|
Some(T) => {
|
||||||
|
if input_rt_data[T].1.len() < moving_number {
|
||||||
|
sma_data.sma_value = 0.0;
|
||||||
|
sma_data.close_time = 0;
|
||||||
|
sma_data_vec.push(sma_data.clone());
|
||||||
|
} else {
|
||||||
|
let mut iter = input_rt_data[T].1.windows(moving_number);
|
||||||
|
for buffer in iter {
|
||||||
|
let mut avg = 0.0;
|
||||||
|
for element in buffer {
|
||||||
|
avg += element.close_price;
|
||||||
|
}
|
||||||
|
avg /= (moving_number as f64);
|
||||||
|
|
||||||
|
sma_data.sma_value = avg;
|
||||||
|
sma_data.close_time = buffer.last().unwrap().close_time;
|
||||||
|
sma_data_vec.push(sma_data.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sma_data_wrapper.push((symbol.clone(), sma_data_vec.clone()));
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*output_sma_data = sma_data_wrapper;
|
||||||
|
// println!(" indicators/sma{} 완료 elapsed:{:.2}s", moving_number, instant.elapsed().as_secs_f32());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
131
src/value_estimation_team/indicators/stoch_rsi.rs
Normal file
131
src/value_estimation_team/indicators/stoch_rsi.rs
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
#![allow(unused)]
|
||||||
|
#![allow(warnings)]
|
||||||
|
|
||||||
|
use crate::database_control::*;
|
||||||
|
use crate::value_estimation_team::indicators::rsi::RsiData;
|
||||||
|
use csv::{DeserializeRecordsIter, StringRecord};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use sqlx::FromRow;
|
||||||
|
use std::f64::NAN;
|
||||||
|
use tokio::{fs::*, io::AsyncWriteExt, time::*};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct StochRsiKData {
|
||||||
|
pub stoch_rsi_k_value: f64,
|
||||||
|
pub close_time: i64,
|
||||||
|
}
|
||||||
|
impl StochRsiKData {
|
||||||
|
fn new() -> StochRsiKData {
|
||||||
|
let a = StochRsiKData {
|
||||||
|
stoch_rsi_k_value: 0.0,
|
||||||
|
close_time: 0,
|
||||||
|
};
|
||||||
|
a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct StochRsiDData {
|
||||||
|
pub stoch_rsi_d_value: f64,
|
||||||
|
pub close_time: i64,
|
||||||
|
}
|
||||||
|
impl StochRsiDData {
|
||||||
|
fn new() -> StochRsiDData {
|
||||||
|
let a = StochRsiDData {
|
||||||
|
stoch_rsi_d_value: 0.0,
|
||||||
|
close_time: 0,
|
||||||
|
};
|
||||||
|
a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binance Stoch RSI (RSI10, length 10, K: 3, D: 3)
|
||||||
|
pub async fn stoch_rsi(
|
||||||
|
input_rsi_data: &Vec<(String, Vec<RsiData>)>,
|
||||||
|
stoch_rsi_length: usize,
|
||||||
|
output_stoch_rsi_k_data: &mut Vec<(String, Vec<StochRsiKData>)>,
|
||||||
|
output_stoch_rsi_d_data: &mut Vec<(String, Vec<StochRsiDData>)>,
|
||||||
|
valid_usdt_trades: &Vec<String>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let mut read_data_vec: Vec<RsiData> = Vec::new();
|
||||||
|
let mut stoch_rsi_k_data_wrapper: Vec<(String, Vec<StochRsiKData>)> = Vec::new();
|
||||||
|
let mut stoch_rsi_k_data_vec: Vec<StochRsiKData> = Vec::new();
|
||||||
|
let mut stoch_rsi_k_data = StochRsiKData::new();
|
||||||
|
let mut stoch_rsi_d_data_wrapper: Vec<(String, Vec<StochRsiDData>)> = Vec::new();
|
||||||
|
let mut stoch_rsi_d_data_vec: Vec<StochRsiDData> = Vec::new();
|
||||||
|
let mut stoch_rsi_d_data = StochRsiDData::new();
|
||||||
|
|
||||||
|
let mut stoch_rsi_vec: Vec<RsiData> = Vec::new();
|
||||||
|
let mut stoch_rsi = RsiData::new();
|
||||||
|
let k_length = 2;
|
||||||
|
let d_length = 2;
|
||||||
|
|
||||||
|
for symbol in valid_usdt_trades {
|
||||||
|
stoch_rsi_k_data_vec.clear();
|
||||||
|
stoch_rsi_d_data_vec.clear();
|
||||||
|
stoch_rsi_vec.clear();
|
||||||
|
let symbol_search_result = input_rsi_data.iter().position(|x| x.0 == *symbol);
|
||||||
|
match symbol_search_result {
|
||||||
|
Some(T) => {
|
||||||
|
if input_rsi_data[T].1.len() >= stoch_rsi_length {
|
||||||
|
read_data_vec = input_rsi_data[T].1.clone();
|
||||||
|
let window_iter = read_data_vec.windows(stoch_rsi_length);
|
||||||
|
|
||||||
|
for buffer_window in window_iter {
|
||||||
|
let max_value = buffer_window
|
||||||
|
.iter()
|
||||||
|
.max_by(|x, y| x.rsi_value.partial_cmp(&y.rsi_value).unwrap())
|
||||||
|
.unwrap()
|
||||||
|
.rsi_value;
|
||||||
|
let min_value = buffer_window
|
||||||
|
.iter()
|
||||||
|
.min_by(|x, y| x.rsi_value.partial_cmp(&y.rsi_value).unwrap())
|
||||||
|
.unwrap()
|
||||||
|
.rsi_value;
|
||||||
|
|
||||||
|
let stoch_rsi_value = if max_value == min_value {
|
||||||
|
max_value
|
||||||
|
} else {
|
||||||
|
(buffer_window.last().unwrap().rsi_value - min_value)
|
||||||
|
/ (max_value - min_value)
|
||||||
|
};
|
||||||
|
|
||||||
|
stoch_rsi.rsi_value = stoch_rsi_value;
|
||||||
|
stoch_rsi.close_time = buffer_window.last().unwrap().close_time;
|
||||||
|
|
||||||
|
stoch_rsi_vec.push(stoch_rsi.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// making Stoch RSI K data
|
||||||
|
let window_iter = stoch_rsi_vec.windows(k_length);
|
||||||
|
for buffer_window in window_iter {
|
||||||
|
stoch_rsi_k_data.stoch_rsi_k_value =
|
||||||
|
(buffer_window.iter().fold(0.0, |acc, x| acc + x.rsi_value)
|
||||||
|
/ (k_length as f64))
|
||||||
|
* 100.0;
|
||||||
|
stoch_rsi_k_data.close_time = buffer_window.last().unwrap().close_time;
|
||||||
|
stoch_rsi_k_data_vec.push(stoch_rsi_k_data.clone());
|
||||||
|
}
|
||||||
|
stoch_rsi_k_data_wrapper.push((symbol.clone(), stoch_rsi_k_data_vec.clone()));
|
||||||
|
|
||||||
|
// making Stoch RSI D data
|
||||||
|
let window_iter = stoch_rsi_k_data_vec.windows(d_length);
|
||||||
|
for buffer_window in window_iter {
|
||||||
|
stoch_rsi_d_data.stoch_rsi_d_value = (buffer_window
|
||||||
|
.iter()
|
||||||
|
.fold(0.0, |acc, x| acc + x.stoch_rsi_k_value)
|
||||||
|
/ (k_length as f64));
|
||||||
|
stoch_rsi_d_data.close_time = buffer_window.last().unwrap().close_time;
|
||||||
|
stoch_rsi_d_data_vec.push(stoch_rsi_d_data.clone());
|
||||||
|
}
|
||||||
|
stoch_rsi_d_data_wrapper.push((symbol.clone(), stoch_rsi_d_data_vec.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*output_stoch_rsi_k_data = stoch_rsi_k_data_wrapper;
|
||||||
|
*output_stoch_rsi_d_data = stoch_rsi_d_data_wrapper;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
238
src/value_estimation_team/indicators/supertrend.rs
Normal file
238
src/value_estimation_team/indicators/supertrend.rs
Normal file
|
|
@ -0,0 +1,238 @@
|
||||||
|
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SupertrendData {
|
||||||
|
pub band_value: f64,
|
||||||
|
pub signal: Option<String>, // BUY or SELL
|
||||||
|
pub area: String, // UP or DOWN
|
||||||
|
pub close_time: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SupertrendData {
|
||||||
|
fn new() -> SupertrendData {
|
||||||
|
let a = SupertrendData {
|
||||||
|
band_value: 0.0,
|
||||||
|
signal: None,
|
||||||
|
area: String::new(),
|
||||||
|
close_time: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct TrueRangeData {
|
||||||
|
tr_value: f64,
|
||||||
|
close_time: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TrueRangeData {
|
||||||
|
fn new() -> TrueRangeData {
|
||||||
|
let a = TrueRangeData {
|
||||||
|
tr_value: 0.0,
|
||||||
|
close_time: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct ATRData {
|
||||||
|
atr_value: f64,
|
||||||
|
close_time: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ATRData {
|
||||||
|
fn new() -> ATRData {
|
||||||
|
let a = ATRData {
|
||||||
|
atr_value: 0.0,
|
||||||
|
close_time: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation from TradingView Script (SuperTrend by KivancOzbilgic)
|
||||||
|
pub async fn supertrend(
|
||||||
|
symbol: &String,
|
||||||
|
input_rt_data: &Vec<(String, Vec<RealtimePriceData>)>,
|
||||||
|
atr_period: usize,
|
||||||
|
multiplier: f64,
|
||||||
|
is_original_method: bool,
|
||||||
|
) -> Option<Vec<SupertrendData>> {
|
||||||
|
let symbol_search_result = input_rt_data.iter().position(|x| x.0 == *symbol);
|
||||||
|
|
||||||
|
match symbol_search_result {
|
||||||
|
Some(T) => {
|
||||||
|
if input_rt_data[T].1.len() >= atr_period && atr_period >= 2 {
|
||||||
|
// making True Range
|
||||||
|
let mut true_range_vec: Vec<TrueRangeData> = Vec::new();
|
||||||
|
let mut tr_data = TrueRangeData::new();
|
||||||
|
let mut tr_val_hl = 0.0; // High - Low
|
||||||
|
let mut tr_val_hcp = 0.0; // Abs(High - Close_previous)
|
||||||
|
let mut tr_val_lcp = 0.0; // Abs(Low - Close_previous)
|
||||||
|
|
||||||
|
let window_vec = input_rt_data[T].1.windows(2);
|
||||||
|
|
||||||
|
tr_data.tr_value = input_rt_data[T].1.first().unwrap().high_price
|
||||||
|
- input_rt_data[T].1.first().unwrap().low_price;
|
||||||
|
tr_data.close_time = input_rt_data[T].1.first().unwrap().close_time;
|
||||||
|
true_range_vec.push(tr_data.clone());
|
||||||
|
|
||||||
|
for buffer in window_vec {
|
||||||
|
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_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.close_time = buffer[1].close_time;
|
||||||
|
|
||||||
|
true_range_vec.push(tr_data.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// making Average True Range
|
||||||
|
let mut average_true_range_vec: Vec<ATRData> = Vec::new();
|
||||||
|
let mut atr_data = ATRData::new();
|
||||||
|
|
||||||
|
if is_original_method == true {
|
||||||
|
// original calculation of ATR
|
||||||
|
let mut first_value = 0.0;
|
||||||
|
let mut first_vec = &true_range_vec[0..atr_period];
|
||||||
|
|
||||||
|
for element in first_vec {
|
||||||
|
first_value += element.tr_value;
|
||||||
|
}
|
||||||
|
first_value /= (atr_period) as f64;
|
||||||
|
atr_data.atr_value = first_value;
|
||||||
|
atr_data.close_time = first_vec.last().unwrap().close_time;
|
||||||
|
average_true_range_vec.push(atr_data.clone());
|
||||||
|
|
||||||
|
let mut temp_prev_atr_value = first_value;
|
||||||
|
for element in &true_range_vec[atr_period..] {
|
||||||
|
atr_data.atr_value = ((temp_prev_atr_value * ((atr_period - 1) as f64))
|
||||||
|
+ element.tr_value)
|
||||||
|
/ atr_period as f64;
|
||||||
|
atr_data.close_time = element.close_time;
|
||||||
|
|
||||||
|
average_true_range_vec.push(atr_data.clone());
|
||||||
|
temp_prev_atr_value = atr_data.atr_value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Calculation of ATR from SMA True Range with atr_period
|
||||||
|
let window = true_range_vec.windows(atr_period);
|
||||||
|
|
||||||
|
let mut sum = 0.0;
|
||||||
|
for buffer in window {
|
||||||
|
for element in buffer {
|
||||||
|
sum += element.tr_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
atr_data.atr_value = sum / atr_period as f64;
|
||||||
|
atr_data.close_time = buffer.last().unwrap().close_time;
|
||||||
|
average_true_range_vec.push(atr_data.clone());
|
||||||
|
|
||||||
|
sum = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// making Supertrend
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct BandData {
|
||||||
|
basic_upperband: f64,
|
||||||
|
basic_lowerband: f64,
|
||||||
|
close_time: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut supertrend_vec: Vec<SupertrendData> = Vec::new();
|
||||||
|
let mut supertrend_data = SupertrendData::new();
|
||||||
|
let mut band_vec: Vec<BandData> = Vec::new();
|
||||||
|
let mut band_data = BandData {
|
||||||
|
basic_upperband: 0.0,
|
||||||
|
basic_lowerband: 0.0,
|
||||||
|
close_time: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let closetime_search_result = input_rt_data[T].1.iter().position(|x| {
|
||||||
|
x.close_time == average_true_range_vec.first().unwrap().close_time
|
||||||
|
});
|
||||||
|
let mut rt_data = &input_rt_data[T].1[closetime_search_result.unwrap()..];
|
||||||
|
let zipped = rt_data.iter().zip(average_true_range_vec);
|
||||||
|
|
||||||
|
for element in zipped {
|
||||||
|
band_data.basic_upperband = ((element.0.high_price + element.0.low_price)
|
||||||
|
/ 2.0)
|
||||||
|
- (multiplier * element.1.atr_value);
|
||||||
|
band_data.basic_lowerband = ((element.0.high_price + element.0.low_price)
|
||||||
|
/ 2.0)
|
||||||
|
+ (multiplier * element.1.atr_value);
|
||||||
|
band_data.close_time = element.1.close_time;
|
||||||
|
band_vec.push(band_data.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut zipped = rt_data.iter().zip(band_vec);
|
||||||
|
let first_element = zipped.next().unwrap();
|
||||||
|
let mut prev_close_price = first_element.0.close_price;
|
||||||
|
let mut final_upperband = 0.0;
|
||||||
|
let mut final_lowerband = 0.0;
|
||||||
|
let mut prev_final_upperband = first_element.1.basic_upperband;
|
||||||
|
let mut prev_final_lowerband = first_element.1.basic_lowerband;
|
||||||
|
let mut trend = 1; // 1 means up trend, -1 means down trend
|
||||||
|
let mut prev_trend = 1;
|
||||||
|
for element in zipped {
|
||||||
|
// set final upperband
|
||||||
|
if prev_close_price > prev_final_upperband {
|
||||||
|
final_upperband = prev_final_upperband.max(element.1.basic_upperband);
|
||||||
|
} else {
|
||||||
|
final_upperband = element.1.basic_upperband;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set final lowerband
|
||||||
|
if prev_close_price < prev_final_lowerband {
|
||||||
|
final_lowerband = prev_final_lowerband.min(element.1.basic_lowerband);
|
||||||
|
} else {
|
||||||
|
final_lowerband = element.1.basic_lowerband;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set supertrend
|
||||||
|
if trend == -1 && element.0.close_price > prev_final_lowerband {
|
||||||
|
supertrend_data.area = String::from("UP");
|
||||||
|
trend = 1;
|
||||||
|
} else if trend == 1 && element.0.close_price < prev_final_upperband {
|
||||||
|
supertrend_data.area = String::from("DOWN");
|
||||||
|
trend = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if supertrend_data.area.contains("UP") {
|
||||||
|
supertrend_data.band_value = final_upperband;
|
||||||
|
} else {
|
||||||
|
supertrend_data.band_value = final_lowerband;
|
||||||
|
}
|
||||||
|
|
||||||
|
if trend == 1 && prev_trend == -1 {
|
||||||
|
supertrend_data.signal = Some(String::from("BUY"));
|
||||||
|
} else if trend == -1 && prev_trend == 1 {
|
||||||
|
supertrend_data.signal = Some(String::from("SELL"));
|
||||||
|
} else {
|
||||||
|
supertrend_data.signal = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
supertrend_data.close_time = element.1.close_time;
|
||||||
|
supertrend_vec.push(supertrend_data.clone());
|
||||||
|
|
||||||
|
prev_close_price = element.0.close_price;
|
||||||
|
prev_final_upperband = final_upperband;
|
||||||
|
prev_final_lowerband = final_lowerband;
|
||||||
|
prev_trend = trend;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(supertrend_vec)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
87
src/value_estimation_team/indicators/tema.rs
Normal file
87
src/value_estimation_team/indicators/tema.rs
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
#![allow(unused)]
|
||||||
|
#![allow(warnings)]
|
||||||
|
|
||||||
|
use crate::database_control::*;
|
||||||
|
use csv::{DeserializeRecordsIter, StringRecord};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use sqlx::FromRow;
|
||||||
|
use tokio::{fs::*, io::AsyncWriteExt, time::*};
|
||||||
|
|
||||||
|
pub async fn tema(
|
||||||
|
tema_number: usize,
|
||||||
|
input_ema_data: &Vec<(String, Vec<(f64, i64)>)>,
|
||||||
|
output_tema_data: &mut Vec<(String, Vec<(f64, i64)>)>,
|
||||||
|
valid_usdt_trades: &Vec<String>,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let alpha: f64 = 2.0 / (tema_number as f64 + 1.0);
|
||||||
|
let mut ema_2order: Vec<f64> = Vec::new();
|
||||||
|
let mut ema_3order: Vec<f64> = Vec::new();
|
||||||
|
|
||||||
|
let mut tema_data_wrapper: Vec<(String, Vec<(f64, i64)>)> = Vec::new();
|
||||||
|
let mut tema_data_vec: Vec<(f64, i64)> = Vec::new();
|
||||||
|
for symbol in valid_usdt_trades {
|
||||||
|
let symbol_search_result = input_ema_data.iter().position(|x| x.0 == *symbol);
|
||||||
|
|
||||||
|
tema_data_vec.clear();
|
||||||
|
ema_2order.clear();
|
||||||
|
ema_3order.clear();
|
||||||
|
|
||||||
|
match symbol_search_result {
|
||||||
|
Some(T) => {
|
||||||
|
if input_ema_data[T].1.len() < tema_number {
|
||||||
|
tema_data_vec.push((0.0, 0));
|
||||||
|
} else {
|
||||||
|
// calculate 2order of ema from 1order of ema
|
||||||
|
let mut ema_prev = 0.0;
|
||||||
|
{
|
||||||
|
let partial_vec1 = input_ema_data[T].1.get(..tema_number).unwrap();
|
||||||
|
let partial_vec2 = input_ema_data[T].1.get(tema_number..).unwrap();
|
||||||
|
|
||||||
|
let mut sma_for_initial_value = 0.0;
|
||||||
|
for element in partial_vec1 {
|
||||||
|
sma_for_initial_value += element.0;
|
||||||
|
}
|
||||||
|
sma_for_initial_value /= tema_number as f64;
|
||||||
|
ema_2order.push(sma_for_initial_value);
|
||||||
|
ema_prev = sma_for_initial_value;
|
||||||
|
|
||||||
|
let mut index_2order: usize = 0;
|
||||||
|
for element in partial_vec2 {
|
||||||
|
ema_2order.push(
|
||||||
|
((1.0 - alpha) * ema_2order[index_2order]) + (alpha * element.0),
|
||||||
|
);
|
||||||
|
index_2order += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate 3order of ema from 2order of ema
|
||||||
|
let mut index_3order: usize = 0;
|
||||||
|
for element in &ema_2order {
|
||||||
|
if element == ema_2order.first().unwrap() {
|
||||||
|
ema_3order.push(*element);
|
||||||
|
} else {
|
||||||
|
ema_3order.push(
|
||||||
|
((1.0 - alpha) * ema_3order[index_3order]) + (alpha * (*element)),
|
||||||
|
);
|
||||||
|
index_3order += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write string
|
||||||
|
let mut index: usize = 0;
|
||||||
|
let mut tema_price: f64 = 0.0;
|
||||||
|
for element in &input_ema_data[T].1 {
|
||||||
|
tema_price = ((element.0 - ema_2order[index]) * 3.0) + ema_3order[index];
|
||||||
|
tema_data_vec.push((tema_price, element.1));
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
tema_data_wrapper.push((symbol.clone(), tema_data_vec.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*output_tema_data = tema_data_wrapper;
|
||||||
|
// println!(" indicators/tema{}_sec 완료", tema_number);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user