Fork from tradingbot_sandbox

This commit is contained in:
Sik Yoon 2023-07-15 19:41:36 +09:00
commit 0c4e8ada55
38 changed files with 24113 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

2803
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

24
Cargo.toml Normal file
View 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
View File

@ -0,0 +1,4 @@
pub mod assets_managing_team;
pub mod exchange_team;
pub mod order_team;
pub mod strategy_team;

View 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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
pub mod monitors;
pub mod request_candles;
pub mod request_others;

View 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;
}

View 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
}

View 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("&timestamp=");
query_string.push_str(&timestamp);
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
View 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
View 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

File diff suppressed because it is too large Load Diff

34
src/lib.rs Normal file
View 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

File diff suppressed because it is too large Load Diff

View 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;
}
}
}

View File

@ -0,0 +1,5 @@
pub mod coinmarketcap;
pub mod dollar_index;
pub mod exchange_rate;
pub mod future_ratio;
pub mod signal_decision;

View 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(())
}

View 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
}

View 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 &current_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
}

View 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)
}

View 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
View 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(),
}
}
}

View File

@ -0,0 +1,2 @@
pub mod datapoints;
pub mod indicators;

View File

@ -0,0 +1 @@
pub mod price_data;

View 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(())
// }

View 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;

View 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(())
}

View 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(())
}

View 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,
}
}

View 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)
}

View 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(())
}

View 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(())
}

View 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(())
}

View 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,
}
}

View 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(())
}