tradingbot/src/coin_health_check_team/request_others.rs

459 lines
16 KiB
Rust

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