// use consts
use crate::RunningMode::*;
use crate::API_KEY;
use crate::API_KEY_TESTNET;
use crate::RUNNING_MODE;
use crate::SECRET_KEY;
use crate::SECRET_KEY_TESTNET;
use crate::URL;
use crate::URL_TEST;

// use crates
use crate::coex::assets_managing_team::*;
use crate::coex::exchange_team::*;
use crate::coin_health_check_team::request_others::{ExchangeInfo, TradeFee};
use crate::database_control::*;
use crate::decimal_funcs::*;
use crate::signal_association::signal_decision::*;
use crate::strategy_team::{AllData, TimeData};
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
use crate::value_estimation_team::indicators::ema::EmaData;
use crate::value_estimation_team::indicators::sma::SmaData;
use crate::value_estimation_team::indicators::stoch_rsi::StochRsiData;
use crate::value_estimation_team::indicators::supertrend::{supertrend, SupertrendData};
use futures::future::try_join_all;
use hex::ToHex;
use hmac_sha256::HMAC;
use log;
use reqwest::{Client, ClientBuilder};
use rust_decimal::{prelude::FromPrimitive, prelude::ToPrimitive, Decimal, RoundingStrategy};
use rust_decimal_macros::dec;
use serde_json::Value;
use sqlx::FromRow;
use std::collections::HashMap;
use tokio::time::*;

pub enum OrderSide {
    Buy,
    Sell,
}
pub enum OrderType {
    Limit,
    Market,
    LimitMaker,
}
pub enum TimeInForce {
    Gtc,
    Ioc,
    Fok,
}

#[derive(Debug, FromRow, Clone)]
pub struct BuyOrderedCoinList {
    pub id: u64,
    pub symbol: String,
    pub order_id: u64,
    pub transact_time: i64,
    pub close_time: i64,
    pub status: String,
    pub used_usdt: Decimal,
    pub expected_get_usdt: f64,
    pub expected_usdt_profit: f64,
    pub buy_price: Decimal,
    pub current_price: Decimal,
    pub stoploss: Decimal,
    pub target_price: Decimal,
    pub base_qty_ordered: Decimal,
    pub base_qty_fee_adjusted: Decimal,
    pub pure_profit_percent: f64,
    pub minimum_profit_percent: f64,
    pub maximum_profit_percent: f64,
    pub registerer: u16,
    pub is_long: u8,
}

#[derive(Debug, FromRow)]
pub struct SellOrderedCoinList {
    pub id: u64,
    pub symbol: String,
    pub buy_order_id: u64,
    pub sell_order_id: u64,
    pub transact_time: i64,
    pub close_time: i64,
    pub status: String,
    pub used_usdt: Decimal,
    pub get_usdt: Decimal,
    pub get_usdt_fee_adjusted: Decimal,
    pub buy_price: Decimal,
    pub sell_price: Decimal,
    pub stoploss: Decimal,
    pub target_price: Decimal,
    pub base_qty_ordered: Decimal,
    pub pure_profit_percent: Decimal,
    pub maximum_profit_percent: f64,
    pub registerer: u16,
    pub is_long: u8,
}

#[derive(Debug, FromRow, Clone)]
pub struct SellHistoryList {
    pub id: u64,
    pub symbol: String,
    pub soldtime: i64,
    pub used_usdt: Decimal,
    pub get_usdt: Decimal,
    pub buy_price: Decimal,
    pub sell_price: Decimal,
    pub base_qty: Decimal,
    pub pure_profit_percent: f64,
    pub pure_profit_usdt: Decimal,
    pub registerer: u16,
}

pub trait DBlist {
    fn new() -> Self;
}

impl DBlist for SellOrderedCoinList {
    fn new() -> SellOrderedCoinList {
        let a = SellOrderedCoinList {
            id: 0,
            symbol: String::new(),
            buy_order_id: 0,
            sell_order_id: 0,
            transact_time: 0,
            close_time: 0,
            status: String::new(),
            used_usdt: Decimal::new(0, 8),
            get_usdt: Decimal::new(0, 8),
            get_usdt_fee_adjusted: Decimal::new(0, 8),
            buy_price: Decimal::new(0, 8),
            sell_price: Decimal::new(0, 8),
            stoploss: Decimal::new(0, 8),
            target_price: Decimal::new(0, 8),
            base_qty_ordered: Decimal::new(0, 8),
            pure_profit_percent: Decimal::new(0, 8),
            maximum_profit_percent: 0.0,
            registerer: 0,
            is_long: 0,
        };
        a
    }
}

impl DBlist for BuyOrderedCoinList {
    fn new() -> BuyOrderedCoinList {
        let a = BuyOrderedCoinList {
            id: 0,
            symbol: String::new(),
            order_id: 0,
            transact_time: 0,
            close_time: 0,
            status: String::new(),
            used_usdt: Decimal::new(0, 8),
            expected_get_usdt: 0.0,
            expected_usdt_profit: 0.0,
            buy_price: Decimal::new(0, 8),
            current_price: Decimal::new(0, 8),
            stoploss: Decimal::new(0, 8),
            target_price: Decimal::new(0, 8),
            base_qty_ordered: Decimal::new(0, 8),
            base_qty_fee_adjusted: Decimal::new(0, 8),
            pure_profit_percent: 0.0,
            minimum_profit_percent: 0.0,
            maximum_profit_percent: 0.0,
            registerer: 0,
            is_long: 0,
        };
        a
    }
}

impl DBlist for SellHistoryList {
    fn new() -> SellHistoryList {
        let a = SellHistoryList {
            id: 0,
            symbol: String::new(),
            soldtime: 0,
            used_usdt: Decimal::new(0, 8),
            get_usdt: Decimal::new(0, 8),
            buy_price: Decimal::new(0, 8),
            sell_price: Decimal::new(0, 8),
            base_qty: Decimal::new(0, 8),
            pure_profit_percent: 0.0,
            pure_profit_usdt: Decimal::new(0, 8),
            registerer: 0,
        };
        a
    }
}

#[derive(Debug, FromRow)]
pub struct SignalDecisionInfo {
    pub decision: String,
}

pub async fn limit_order_buy_test(
    client: &Client,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // building URL and API-keys
    let mut url = String::new();
    let mut api_key = String::new();
    unsafe {
        if RUNNING_MODE == TEST {
            url.push_str(URL_TEST);
            api_key = API_KEY_TESTNET.to_string();
        } else {
            url.push_str(URL);
            api_key = API_KEY.to_string();
        }
    }

    let endpoint_url = "/api/v3/order?";
    url.push_str(endpoint_url);

    let mut url_build = String::new();

    // add parameters into URL
    url_build.push_str("&symbol=BTCUSDT");
    url_build.push_str("&side=BUY");
    url_build.push_str("&timeInForce=GTC");

    url_build.push_str("&type=LIMIT");
    url_build.push_str("&quantity=0.3");
    url_build.push_str("&price=20451.37");

    hmac_signature(&mut url_build).await;
    url.push_str(&url_build);
    println!("url: {}", url);
    let res = client
        .post(&url)
        .header("X-MBX-APIKEY", api_key)
        .send()
        .await
        .unwrap();
    println!("response: {:?}", res);
    let body = res.text_with_charset("utf-8").await.unwrap();
    println!("limit_order_buy실행 body: {}", body);
    // deserialize JSON and then insert record into table
    let v = serde_json::from_str::<Value>(body.as_str());

    // match v {
    //     Ok(T) => {
    //         if T.get("code").is_some() {    // when request failed

    //         } else {    // when request succeed
    //             insert_value_container.push(T.get("symbol").unwrap().as_str().unwrap().to_string());        // symbol
    //             insert_value_container.push(T.get("orderId").unwrap().as_u64().unwrap().to_string());       // order_id
    //             insert_value_container.push(server_epoch.to_string());                                             // transact_time
    //             insert_value_container.push(element.close_time.to_string());                                        // close_time
    //             insert_value_container.push(T.get("status").unwrap().as_str().unwrap().to_string());        // status

    //             if T.get("status").unwrap().as_str().unwrap() == "FILLED" {
    //                 let base_qty_ordered = rust_decimal::prelude::FromStr::from_str(T.get("origQty").unwrap().as_str().unwrap()).unwrap();
    //                 let base_qty_fee_adjusted = decimal_mul(base_qty_ordered, decimal_sub(dec!(1), trade_fee));

    //                 let cummulative_quote_qty = rust_decimal::prelude::FromStr::from_str(T.get("cummulativeQuoteQty").unwrap().as_str().unwrap()).unwrap();
    //                 let base_asset_precision = exchange_info_vec.iter().find(|ExchangeInfo| ExchangeInfo.symbol == element.symbol).unwrap().base_asset_precision;
    //                 let buy_price = decimal_div(cummulative_quote_qty, base_qty_ordered).round_dp_with_strategy(base_asset_precision, RoundingStrategy::ToZero);

    //                 insert_value_container.push(cummulative_quote_qty.to_string()); // used_usdt
    //                 insert_value_container.push(0.0.to_string());       // expected_get_usdt
    //                 insert_value_container.push(0.0.to_string());       // expected_usdt_profit
    //                 insert_value_container.push(buy_price.to_string());  // buy_price
    //                 insert_value_container.push(0.0.to_string());       // current_price
    //                 insert_value_container.push(base_qty_ordered.to_string());  // base_qty_ordered
    //                 insert_value_container.push(base_qty_fee_adjusted.to_string());       // base_qty_fee_adjusted

    //                 // reflect available_usdt in [asset_manage_announcement]
    //                 // sub_available_usdt(cummulative_quote_qty).await;
    //                 sub_available_usdt(simul_used_usdt).await;
    //                 println!("                  buy coin 완료");
    //             } else {
    //                 insert_value_container.push(simul_used_usdt.to_string());       // used_usdt
    //                 insert_value_container.push(0.0.to_string());       // expected_get_usdt
    //                 insert_value_container.push(0.0.to_string());       // expected_usdt_profit
    //                 insert_value_container.push(0.0.to_string());       // buy_price
    //                 insert_value_container.push(0.0.to_string());       // current_price
    //                 insert_value_container.push(T.get("origQty").unwrap().as_str().unwrap().to_string());  // base_qty_ordered
    //                 insert_value_container.push(0.0.to_string());       // base_qty_fee_adjusted

    //                 // reflect available_usdt in [asset_manage_announcement]
    //                 sub_available_usdt(simul_used_usdt).await;
    //             }

    //             insert_value_container.push(0.0.to_string());       // pure_profit_percent
    //             insert_value_container.push(0.0.to_string());       // minimum_profit_percent
    //             insert_value_container.push(0.0.to_string());       // maximum_profit_percent
    //             insert_value_container.push(element.registerer.to_string());// registerer
    //             insert_value_container.push(0.to_string());         // is_long

    //             insert_values.push(insert_value_container.clone());
    //             insert_records(&insert_table_name, &insert_columns, &insert_values).await;
    //         }
    //     },
    //     Err(e) => {
    //         println!("order failed!: {}", body);
    //     }
    // }

    Ok(())
}

pub async fn limit_order_buy(
    element: &SuggestedCoin,
    exchange_info_map: &HashMap<String, ExchangeInfo>,
    trade_fee: Decimal,
    tif: TimeInForce,
    order_price: Decimal,
    order_quantity: Decimal,
    used_usdt: Decimal,
    simul_base_qty_fee_adjusted: &String,
    client: &Client,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let insert_table_name = String::from("buy_ordered_coin_list");
    let insert_columns = vec![
        "symbol",
        "order_id",
        "transact_time",
        "close_time",
        "status",
        "used_usdt",
        "expected_get_usdt",
        "expected_usdt_profit",
        "buy_price",
        "current_price",
        "stoploss",
        "target_price",
        "base_qty_ordered",
        "base_qty_fee_adjusted",
        "pure_profit_percent",
        "minimum_profit_percent",
        "maximum_profit_percent",
        "registerer",
        "is_long",
    ];
    let mut insert_values: Vec<Vec<String>> = Vec::new();
    let mut insert_value_container: Vec<String> = Vec::new();
    let server_epoch = get_server_epoch().await;
    unsafe {
        if RUNNING_MODE == SIMUL {
            insert_value_container.push(element.symbol.clone()); // symbol
            insert_value_container.push(0.to_string()); // order_id
            insert_value_container.push(server_epoch.to_string()); // transact_time
            insert_value_container.push(element.close_time.to_string()); // close_time
            insert_value_container.push(String::from("SIMUL")); // status
            insert_value_container.push(used_usdt.to_string()); // used_usdt
            insert_value_container.push(0.0.to_string()); // expected_get_usdt
            insert_value_container.push(0.0.to_string()); // expected_usdt_profit
            insert_value_container.push(order_price.to_string()); // buy_price
            insert_value_container.push(0.0.to_string()); // current_price
            insert_value_container.push(element.stoploss.to_string()); // stoploss
            insert_value_container.push(element.target_price.to_string()); // target_price
            insert_value_container.push(order_quantity.to_string()); // base_qty_ordered
            insert_value_container.push(simul_base_qty_fee_adjusted.to_string()); // base_qty_fee_adjusted
            insert_value_container.push(0.0.to_string()); // pure_profit_percent
            insert_value_container.push(0.0.to_string()); // minimum_profit_percent
            insert_value_container.push(0.0.to_string()); // maximum_profit_percent
            insert_value_container.push(element.registerer.to_string()); // registerer
            insert_value_container.push(0.to_string()); // is_long

            insert_values.push(insert_value_container.clone());
            insert_records(&insert_table_name, &insert_columns, &insert_values).await;

            // reflect available_usdt in [asset_manage_announcement]
            sub_available_usdt(used_usdt).await;

            println!("SIMUL buy coin: {}", element.symbol);
        } else {
            // building URL and API-keys
            let mut url = String::new();
            let mut api_key = String::new();
            if RUNNING_MODE == TEST {
                url.push_str(URL_TEST);
                api_key = API_KEY_TESTNET.to_string();
            } else {
                url.push_str(URL);
                api_key = API_KEY.to_string();
            }

            let endpoint_url = "/api/v3/order?";
            url.push_str(endpoint_url);

            let mut url_build = String::new();

            // add parameters into URL
            url_build.push_str("&symbol=");
            url_build.push_str(&element.symbol);
            url_build.push_str("&side=BUY");

            match tif {
                TimeInForce::Gtc => {
                    url_build.push_str("&timeInForce=GTC");
                }
                TimeInForce::Ioc => {
                    url_build.push_str("&timeInForce=IOC");
                }
                TimeInForce::Fok => {
                    url_build.push_str("&timeInForce=FOK");
                }
            }
            url_build.push_str("&type=LIMIT");
            url_build.push_str("&quantity=");
            url_build.push_str(order_quantity.to_string().as_str());
            url_build.push_str("&price=");
            url_build.push_str(order_price.to_string().as_str());

            hmac_signature(&mut url_build).await;
            url.push_str(&url_build);

            let res = client
                .post(&url)
                .header("X-MBX-APIKEY", api_key)
                .send()
                .await
                .unwrap();

            let body = res.text_with_charset("utf-8").await.unwrap();
            // deserialize JSON and then insert record into table
            let v = serde_json::from_str::<Value>(body.as_str());

            match v {
                Ok(T) => {
                    if T.get("code").is_some() { // when request failed
                    } else {
                        // when request succeed
                        insert_value_container
                            .push(T.get("symbol").unwrap().as_str().unwrap().to_string()); // symbol
                        insert_value_container
                            .push(T.get("orderId").unwrap().as_u64().unwrap().to_string()); // order_id
                        insert_value_container.push(server_epoch.to_string()); // transact_time
                        insert_value_container.push(element.close_time.to_string()); // close_time
                        insert_value_container
                            .push(T.get("status").unwrap().as_str().unwrap().to_string()); // status

                        if T.get("status").unwrap().as_str().unwrap() == "FILLED" {
                            let base_qty_ordered = rust_decimal::prelude::FromStr::from_str(
                                T.get("origQty").unwrap().as_str().unwrap(),
                            )
                            .unwrap();
                            let base_qty_fee_adjusted =
                                decimal_mul(base_qty_ordered, decimal_sub(dec!(1), trade_fee));

                            let cummulative_quote_qty = rust_decimal::prelude::FromStr::from_str(
                                T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(),
                            )
                            .unwrap();
                            // let base_asset_precision = exchange_info_vec
                            //     .iter()
                            //     .find(|exchange_info| exchange_info.symbol == element.symbol)
                            //     .unwrap()
                            //     .base_asset_precision;
                            let buy_price = decimal_div(cummulative_quote_qty, base_qty_ordered)
                                .round_dp_with_strategy(8, RoundingStrategy::ToZero);

                            insert_value_container.push(cummulative_quote_qty.to_string()); // used_usdt
                            insert_value_container.push(0.0.to_string()); // expected_get_usdt
                            insert_value_container.push(0.0.to_string()); // expected_usdt_profit
                            insert_value_container.push(buy_price.to_string()); // buy_price
                            insert_value_container.push(0.0.to_string()); // current_price
                            insert_value_container.push(element.stoploss.to_string()); // stoploss
                            insert_value_container.push(element.target_price.to_string()); // target_price
                            insert_value_container.push(base_qty_ordered.to_string()); // base_qty_ordered
                            insert_value_container.push(base_qty_fee_adjusted.to_string()); // base_qty_fee_adjusted

                            // reflect available_usdt in [asset_manage_announcement]
                            // sub_available_usdt(cummulative_quote_qty).await;
                            sub_available_usdt(used_usdt).await;
                            println!("buy {}", element.symbol);
                        } else {
                            insert_value_container.push(used_usdt.to_string()); // used_usdt
                            insert_value_container.push(0.0.to_string()); // expected_get_usdt
                            insert_value_container.push(0.0.to_string()); // expected_usdt_profit
                            insert_value_container.push(0.0.to_string()); // buy_price
                            insert_value_container.push(0.0.to_string()); // current_price
                            insert_value_container.push(element.stoploss.to_string()); // stoploss
                            insert_value_container.push(element.target_price.to_string()); // target_price
                            insert_value_container
                                .push(T.get("origQty").unwrap().as_str().unwrap().to_string()); // base_qty_ordered
                            insert_value_container.push(0.0.to_string()); // base_qty_fee_adjusted

                            // reflect available_usdt in [asset_manage_announcement]
                            sub_available_usdt(used_usdt).await;
                        }

                        insert_value_container.push(0.0.to_string()); // pure_profit_percent
                        insert_value_container.push(0.0.to_string()); // minimum_profit_percent
                        insert_value_container.push(0.0.to_string()); // maximum_profit_percent
                        insert_value_container.push(element.registerer.to_string()); // registerer
                        insert_value_container.push(0.to_string()); // is_long

                        insert_values.push(insert_value_container.clone());
                        insert_records(&insert_table_name, &insert_columns, &insert_values).await;
                    }
                }
                Err(e) => {
                    log::warn!("order failed!: {}", body);
                }
            }
        }
    }
    Ok(())
}

pub async fn monitoring_open_buy_order(
    client: &Client,
    trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let open_buy_orders = select_open_buy_orders().await;

    if !open_buy_orders.is_empty() {
        let server_epoch = get_server_epoch().await;
        let orders_outdated = open_buy_orders
            .iter()
            .filter(|&element| server_epoch - element.transact_time >= 600_000)
            .collect::<Vec<&BuyOrderedCoinList>>(); // wait up to 600 secs
        let orders_to_be_queried = open_buy_orders
            .iter()
            .filter(|&element| server_epoch - element.transact_time < 600_000)
            .collect::<Vec<&BuyOrderedCoinList>>();

        // cancel orders outdated over 3mins and delete the records in [buy_ordered_coin_list]
        if !orders_outdated.is_empty() {
            for element in orders_outdated {
                cancel_buy_order(element, &client, trade_fee_map).await;
                sleep(Duration::from_millis(500)).await; // Use max 30 LIMIT/min
            }
        }

        if !orders_to_be_queried.is_empty() {
            for element in orders_to_be_queried {
                query_buy_order(element, &client, trade_fee_map).await;
                sleep(Duration::from_millis(500)).await; // Use max 30 LIMIT/min
            }
        }
    }

    Ok(())
}

pub async fn update_price_of_filled_buy_order(
    coin_price_map: &HashMap<String, f64>,
    exchange_info_map: &HashMap<String, ExchangeInfo>,
    trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let filled_buy_orders = select_filled_buy_orders(0).await?;

    if !filled_buy_orders.is_empty() {
        // 심볼들을 2개씩 청스로 나누어 테스크를 생성하고 각 태스크 별로 병렬로 처리한다.
        // update real-time current price to each record through chunks
        let chunks: std::slice::Chunks<'_, BuyOrderedCoinList> = filled_buy_orders.chunks(3);
        let mut task_vec = Vec::new();

        for chunk in chunks {
            let chunk_vec = chunk.to_vec();
            let coin_price_vec_c = coin_price_map.clone();
            let exchange_info_vec_c = exchange_info_map.clone();
            let trade_fee_vec_c = trade_fee_map.clone();
            task_vec.push(tokio::spawn(async move {
                update_repeat_task(
                    chunk_vec,
                    &coin_price_vec_c,
                    &exchange_info_vec_c,
                    &trade_fee_vec_c,
                )
                .await;
            }));
        }
    }
    Ok(())
}

async fn update_repeat_task(
    buy_ordered_coin_vec: Vec<BuyOrderedCoinList>,
    coin_price_map: &HashMap<String, f64>,
    exchange_info_map: &HashMap<String, ExchangeInfo>,
    trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let update_table_name = String::from("buy_ordered_coin_list");
    let mut update_values: Vec<(String, String)> = Vec::new();
    let mut update_condition: Vec<(String, String)> = Vec::new();
    let mut price = Decimal::new(0, 8);
    let mut profit_percent = 0.0;
    let server_epoch = get_server_epoch().await;

    let update_colums = vec![
        "current_price",
        "expected_get_usdt",
        "expected_usdt_profit",
        "pure_profit_percent",
        "minimum_profit_percent",
        "maximum_profit_percent",
        "is_long",
    ];
    let mut update_record_build: Vec<String> = Vec::new();
    let mut update_record: Vec<Vec<String>> = Vec::new();

    // update current_price, expected__get_usdt, expected_usdt_profit, pure_profit_percent, minimum_profit_percent, maximum_profit_percent
    for element in buy_ordered_coin_vec {
        // build update values
        update_record_build.clear();
        if coin_price_map.contains_key(&element.symbol)
            && exchange_info_map.contains_key(&element.symbol)
            && trade_fee_map.contains_key(&element.symbol)
        {
            price = rust_decimal::prelude::FromPrimitive::from_f64(
                *coin_price_map.get(&element.symbol).unwrap(),
            )
            .unwrap();
            if !price.is_zero() {
                // to get quote_commission_precision
                let trade_fee = trade_fee_map.get(&element.symbol).unwrap().takercommission;
                let lot_step_size = exchange_info_map.get(&element.symbol).unwrap().stepsize;
                let quote_commission_precision = exchange_info_map
                    .get(&element.symbol)
                    .unwrap()
                    .quote_commission_precision;
                let base_qty_to_be_ordered = element.base_qty_fee_adjusted.round_dp_with_strategy(
                    lot_step_size.normalize().scale(),
                    RoundingStrategy::ToZero,
                );
                let expected_get_usdt = decimal_mul(
                    decimal_mul(base_qty_to_be_ordered, price).round_dp_with_strategy(
                        quote_commission_precision,
                        RoundingStrategy::ToZero,
                    ),
                    decimal_sub(dec!(1), trade_fee),
                );

                // TODO: sell_count >=1 이면 expected_get_usdt 는 한번만 tradefee만 적용하여 업데이트 할 것. 현재는 수수료를 2번 (매수,매도)를 계산함. 아래 변수에 든 값으로 업데이트 하면 됨
                // let expected_get_usdt =
                //     decimal_mul(base_qty_to_be_ordered, price).round_dp_with_strategy(
                //         quote_commission_precision,
                //         RoundingStrategy::ToZero,
                //     );
                let mut pure_profit_percent = ((expected_get_usdt.to_f64().unwrap()
                    / element.used_usdt.to_f64().unwrap())
                    - 1.0)
                    * 100.0;
                pure_profit_percent = (pure_profit_percent * 100.0).round() / 100.0; // Rounding
                update_record_build.push(element.id.to_string()); // id
                update_record_build.push(price.to_string()); // current_price
                update_record_build.push(expected_get_usdt.to_string()); //expected_get_usdt
                update_record_build
                    .push(decimal_sub(expected_get_usdt, element.used_usdt).to_string()); // expected_usdt_profit
                update_record_build.push(pure_profit_percent.to_string()); // pure_profit_percent

                if element.minimum_profit_percent > pure_profit_percent {
                    update_record_build.push(pure_profit_percent.to_string());
                // minimum_profit_percent
                } else if pure_profit_percent >= 0.0 {
                    update_record_build.push(0.0.to_string()); // minimum_profit_percent
                } else {
                    update_record_build.push(element.minimum_profit_percent.to_string());
                    // minimum_profit_percent
                }

                if element.maximum_profit_percent < pure_profit_percent {
                    update_record_build.push(pure_profit_percent.to_string());
                // maximum_profit_percent
                } else if pure_profit_percent <= 0.0 {
                    update_record_build.push(0.0.to_string()); //  maximum_profit_percent
                } else {
                    update_record_build.push(element.maximum_profit_percent.to_string());
                    // maximum_profit_percent
                }

                if server_epoch - element.transact_time >= 86_400_000 {
                    // turn is_long from 0 to 1 for orders whose transact time is over than a day (86,400,000 millis = a day)
                    update_record_build.push(1.to_string());
                } else {
                    update_record_build.push(0.to_string());
                }
                update_record.push(update_record_build.clone());
            }
        }
    }

    update_records(&update_table_name, &update_record, &update_colums).await;

    Ok(())
}

pub async fn limit_order_sell(
    buy_ordered_coin: &BuyOrderedCoinList,
    sell_base_price: Decimal,
    sell_base_quantity: Decimal,
    client: &Client,
    exchange_info_map: &HashMap<String, ExchangeInfo>,
    trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let insert_table_name = String::from("sell_ordered_coin_list");
    let insert_columns = vec![
        "symbol",
        "buy_order_id",
        "sell_order_id",
        "transact_time",
        "close_time",
        "status",
        "used_usdt",
        "get_usdt",
        "get_usdt_fee_adjusted",
        "buy_price",
        "sell_price",
        "stoploss",
        "target_price",
        "base_qty_ordered",
        "pure_profit_percent",
        "maximum_profit_percent",
        "registerer",
        "is_long",
    ];
    let mut insert_values: Vec<Vec<String>> = Vec::new();
    let mut insert_value_container: Vec<String> = Vec::new();
    unsafe {
        if RUNNING_MODE == SIMUL && buy_ordered_coin.status == "SIMUL" {
            if exchange_info_map.contains_key(&buy_ordered_coin.symbol)
                && trade_fee_map.contains_key(&buy_ordered_coin.symbol)
            {
                let quote_asset_precision = exchange_info_map
                    .get(&buy_ordered_coin.symbol)
                    .unwrap()
                    .quote_asset_precision;
                let trade_fee = trade_fee_map
                    .get(&buy_ordered_coin.symbol)
                    .unwrap()
                    .takercommission;

                let get_usdt = decimal_mul(sell_base_quantity, sell_base_price)
                    .round_dp_with_strategy(quote_asset_precision, RoundingStrategy::ToZero);
                let get_usdt_fee_adjusted = decimal_mul(get_usdt, decimal_sub(dec!(1), trade_fee))
                    .round_dp_with_strategy(quote_asset_precision, RoundingStrategy::ToZero);

                insert_value_container.push(buy_ordered_coin.symbol.to_string()); // symbol
                insert_value_container.push(0.to_string()); // buy_order_id
                insert_value_container.push(0.to_string()); // sell_order_id
                insert_value_container.push(buy_ordered_coin.transact_time.to_string()); // transact_time
                insert_value_container.push(buy_ordered_coin.close_time.to_string()); // close_time
                insert_value_container.push(String::from("SIMUL")); // status
                insert_value_container.push(buy_ordered_coin.used_usdt.to_string()); // used_usdt
                insert_value_container.push(get_usdt.to_string()); // get_usdt
                insert_value_container.push(get_usdt_fee_adjusted.to_string()); // get_usdt_fee_adjusted
                insert_value_container.push(buy_ordered_coin.buy_price.to_string()); // buy_price
                insert_value_container.push(sell_base_price.to_string()); // sell_price
                insert_value_container.push(buy_ordered_coin.stoploss.to_string()); // stoploss
                insert_value_container.push(buy_ordered_coin.target_price.to_string()); // target_price
                insert_value_container.push(sell_base_quantity.to_string()); // base_qty_ordered

                let pure_profit_percent = decimal_mul(
                    decimal_sub(
                        decimal_div(get_usdt_fee_adjusted, buy_ordered_coin.used_usdt),
                        dec!(1),
                    ),
                    dec!(100),
                )
                .round_dp(2);
                insert_value_container.push(pure_profit_percent.to_string()); // pure_profit_percent
                insert_value_container.push(buy_ordered_coin.maximum_profit_percent.to_string()); // maximum_profit_percent
                insert_value_container.push(buy_ordered_coin.registerer.to_string()); // registerer
                insert_value_container.push(buy_ordered_coin.is_long.to_string()); // is_long

                insert_values.push(insert_value_container.clone());
                insert_records(&insert_table_name, &insert_columns, &insert_values).await;

                // delete record in buy_ordered_coin_list
                let delete_table_name = String::from("buy_ordered_coin_list");
                let mut delete_condition = String::from("WHERE id = ");
                delete_condition.push_str(buy_ordered_coin.id.to_string().as_str());
                delete_record(&delete_table_name, &delete_condition).await;
            }
        } else if RUNNING_MODE == REAL || RUNNING_MODE == TEST {
            // building URL and API-keys
            let mut url = String::new();
            let mut api_key = String::new();
            if RUNNING_MODE == TEST {
                url.push_str(URL_TEST);
                api_key = API_KEY_TESTNET.to_string();
            } else {
                url.push_str(URL);
                api_key = API_KEY.to_string();
            }

            let endpoint_url = "/api/v3/order?";
            url.push_str(endpoint_url);

            let mut url_build = String::new();

            // add parameters into URL
            url_build.push_str("&symbol=");
            url_build.push_str(&buy_ordered_coin.symbol);
            url_build.push_str("&side=SELL");
            url_build.push_str("&timeInForce=GTC");

            url_build.push_str("&type=LIMIT");
            url_build.push_str("&quantity=");
            url_build.push_str(sell_base_quantity.to_string().as_str());
            url_build.push_str("&price=");
            url_build.push_str(sell_base_price.to_string().as_str());

            hmac_signature(&mut url_build).await;
            url.push_str(&url_build);

            let res = client
                .post(&url)
                .header("X-MBX-APIKEY", api_key)
                .send()
                .await?;

            let body = res.text_with_charset("utf-8").await.unwrap();
            // println!("limit_order_sell실행 body: {}", body);
            // deserialize JSON and then insert record into table
            let v = serde_json::from_str::<Value>(body.as_str());

            match v {
                Ok(T) => {
                    if T.get("code").is_some() {
                        // when request failed
                        sleep(Duration::from_secs(1)).await;
                    } else {
                        // when request succeed
                        insert_value_container
                            .push(T.get("symbol").unwrap().as_str().unwrap().to_string()); // symbol
                        insert_value_container.push(buy_ordered_coin.order_id.to_string()); // buy_order_id
                        insert_value_container
                            .push(T.get("orderId").unwrap().as_u64().unwrap().to_string()); // sell_order_id
                        insert_value_container.push(buy_ordered_coin.transact_time.to_string()); // transact_time
                        insert_value_container.push(buy_ordered_coin.close_time.to_string()); // close_time
                        insert_value_container
                            .push(T.get("status").unwrap().as_str().unwrap().to_string()); // status
                        insert_value_container.push(buy_ordered_coin.used_usdt.to_string()); // used_usdt

                        if exchange_info_map.contains_key(&buy_ordered_coin.symbol)
                            && trade_fee_map.contains_key(&buy_ordered_coin.symbol)
                        {
                            let quote_asset_precision = exchange_info_map
                                .get(&buy_ordered_coin.symbol)
                                .unwrap()
                                .quote_asset_precision;
                            let trade_fee = trade_fee_map
                                .get(&buy_ordered_coin.symbol)
                                .unwrap()
                                .takercommission;
                            let get_usdt = rust_decimal::prelude::FromStr::from_str(
                                T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(),
                            )
                            .unwrap();
                            let get_usdt_fee_adjusted =
                                decimal_mul(get_usdt, decimal_sub(dec!(1), trade_fee))
                                    .round_dp_with_strategy(
                                        quote_asset_precision,
                                        RoundingStrategy::ToZero,
                                    );
                            let ordered_base_qty = rust_decimal::prelude::FromStr::from_str(
                                T.get("origQty").unwrap().as_str().unwrap(),
                            )
                            .unwrap();

                            if T.get("status").unwrap().as_str().unwrap() == "FILLED" {
                                insert_value_container.push(get_usdt.to_string()); // get_usdt
                                insert_value_container.push(get_usdt_fee_adjusted.to_string());
                            // get_usdt_fee_adjusted
                            } else {
                                insert_value_container.push(0.0.to_string()); // get_usdt
                                insert_value_container.push(0.0.to_string()); // get_usdt_fee_adjusted
                            }

                            insert_value_container.push(buy_ordered_coin.buy_price.to_string()); // buy_price

                            if T.get("status").unwrap().as_str().unwrap() == "FILLED" {
                                let sell_price = decimal_div(get_usdt, ordered_base_qty)
                                    .round_dp_with_strategy(
                                        quote_asset_precision,
                                        RoundingStrategy::ToZero,
                                    );
                                insert_value_container.push(sell_price.to_string());
                            // sell_price
                            } else {
                                insert_value_container.push(0.0.to_string()); // sell_price
                            }

                            insert_value_container.push(buy_ordered_coin.stoploss.to_string()); // stoploss
                            insert_value_container.push(buy_ordered_coin.target_price.to_string()); // target_price
                            insert_value_container.push(ordered_base_qty.to_string()); // base_qty_ordered

                            if T.get("status").unwrap().as_str().unwrap() == "FILLED" {
                                let pure_profit_percent = decimal_mul(
                                    decimal_sub(
                                        decimal_div(
                                            get_usdt_fee_adjusted,
                                            buy_ordered_coin.used_usdt,
                                        ),
                                        dec!(1),
                                    ),
                                    dec!(100),
                                )
                                .round_dp(2);
                                insert_value_container
                                    .push(buy_ordered_coin.pure_profit_percent.to_string());
                            // pure_profit_percent
                            } else {
                                insert_value_container.push(0.0.to_string()); // pure_profit_percent
                            }
                            insert_value_container
                                .push(buy_ordered_coin.maximum_profit_percent.to_string()); // maximum_profit_percent
                            insert_value_container.push(buy_ordered_coin.registerer.to_string()); // registerer
                            insert_value_container.push(buy_ordered_coin.is_long.to_string()); // is_long

                            insert_values.push(insert_value_container.clone());
                            insert_records(&insert_table_name, &insert_columns, &insert_values)
                                .await;

                            // delete record in buy_ordered_coin_list
                            let delete_table_name = String::from("buy_ordered_coin_list");
                            let mut delete_condition = String::from("WHERE id = ");
                            delete_condition.push_str(buy_ordered_coin.id.to_string().as_str());
                            delete_record(&delete_table_name, &delete_condition).await;
                        }
                    }
                }
                Err(e) => {
                    log::warn!("sell order failed!: {}", body);
                }
            }
        }
    }
    Ok(())
}

pub async fn monitoring_open_sell_order(
    client: &Client,
    exchange_info_map: &HashMap<String, ExchangeInfo>,
    trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let open_sell_orders = select_open_sell_orders().await;

    if !open_sell_orders.is_empty() {
        let server_epoch = get_server_epoch().await;
        let orders_outdated = open_sell_orders
            .iter()
            .filter(|&element| server_epoch - element.transact_time >= 5_000)
            .collect::<Vec<&SellOrderedCoinList>>();
        let orders_to_be_queried = open_sell_orders
            .iter()
            .filter(|&element| server_epoch - element.transact_time < 5_000)
            .collect::<Vec<&SellOrderedCoinList>>();

        // cancel orders outdated over 30secs, delete its records in [sell_ordered_coin_list], and move them into [buy_ordered_coin_list]
        if !orders_outdated.is_empty() {
            for element in orders_outdated {
                cancel_sell_order(element, &client, exchange_info_map, trade_fee_map).await;
                sleep(Duration::from_millis(200)).await; // Use max 30 LIMIT/min
            }
        }

        if !orders_to_be_queried.is_empty() {
            for element in orders_to_be_queried {
                query_sell_order(element, &client, exchange_info_map, trade_fee_map).await;
                sleep(Duration::from_millis(300)).await; // Use max 30 LIMIT/min
            }
        }
    }

    Ok(())
}

pub async fn monitoring_filled_sell_order(
    client: &Client,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let filled_sell_orders = select_filled_sell_orders().await;

    // Move record whose status is "FILLED" or "SIMUL" into [sell_history] and [achievement_evaluation]
    if !filled_sell_orders.is_empty() {
        let insert_table_name = String::from("sell_history");
        let insert_columns = vec![
            "symbol",
            "soldtime",
            "used_usdt",
            "get_usdt",
            "buy_price",
            "sell_price",
            "base_qty",
            "pure_profit_percent",
            "pure_profit_usdt",
            "registerer",
        ];
        let mut insert_value_container: Vec<Vec<String>> = Vec::new();
        let mut insert_value_build: Vec<String> = Vec::new();

        let update_table_name = String::from("achievement_evaluation");
        let server_epoch = get_server_epoch().await;
        let mut total_get_usdt = Decimal::new(0, 8);

        for element in filled_sell_orders {
            // build insert value
            let pure_profit_usdt = decimal_sub(element.get_usdt_fee_adjusted, element.used_usdt);
            let pure_profit_percent =
                decimal_mul(decimal_div(pure_profit_usdt, element.used_usdt), dec!(100))
                    .round_dp(2);
            insert_value_build.clear();
            insert_value_build.push(element.symbol.clone()); // symbol
            insert_value_build.push(server_epoch.to_string()); // soldtime
            insert_value_build.push(element.used_usdt.to_string()); // used_usdt
            insert_value_build.push(element.get_usdt_fee_adjusted.to_string()); // get_usdt
            insert_value_build.push(element.buy_price.to_string()); // buy_price
            insert_value_build.push(element.sell_price.to_string()); // sell_price
            insert_value_build.push(element.base_qty_ordered.to_string()); // base_qty
            insert_value_build.push(pure_profit_percent.to_string()); // pure_profit_percent
            insert_value_build.push(pure_profit_usdt.to_string()); // pure_profit_usdt
            insert_value_build.push(element.registerer.to_string()); // registerer
            insert_value_container.push(insert_value_build.clone());

            // update [achievement_evaluation]
            let mut value_build1 = String::from("invested_usdt + ");
            let mut invested_usdt = Decimal::new(0, 8);
            invested_usdt = decimal_add(invested_usdt, element.used_usdt);
            value_build1.push_str(invested_usdt.to_string().as_str());
            let update_value1 = vec![(String::from("invested_usdt"), value_build1)];
            let update_condition =
                vec![(String::from("strategist"), element.registerer.to_string())];
            update_record3(&update_table_name, &update_value1, &update_condition)
                .await
                .unwrap();

            if element.get_usdt_fee_adjusted.is_sign_negative() {
                let mut value_build2 = String::from("usdt_profit - ");
                value_build2.push_str(element.get_usdt_fee_adjusted.abs().to_string().as_str());
                let update_value2 = vec![(String::from("usdt_profit"), value_build2)];
                update_record3(&update_table_name, &update_value2, &update_condition)
                    .await
                    .unwrap();
            } else {
                let mut value_build2 = String::from("usdt_profit + ");
                value_build2.push_str(element.get_usdt_fee_adjusted.to_string().as_str());
                let update_value2 = vec![(String::from("usdt_profit"), value_build2)];
                update_record3(&update_table_name, &update_value2, &update_condition)
                    .await
                    .unwrap();
            }

            // add available_usdt
            add_available_usdt(element.get_usdt_fee_adjusted).await;

            // delete in [sell_ordered_coin_list]
            let delete_table_name = String::from("sell_ordered_coin_list");
            let mut delete_condition = String::from("WHERE id = ");
            delete_condition.push_str(element.id.to_string().as_str());
            delete_record(&delete_table_name, &delete_condition).await;

            // total_get_usdt = decimal_add(total_get_usdt, element.get_usdt_fee_adjusted);
            println!("sell {}: {:.2}%", element.symbol, pure_profit_percent);
        }

        // update profit_percent in [achievement_evaluation]
        update_profit_percent().await;

        // insert in [sell_history]
        insert_records(&insert_table_name, &insert_columns, &insert_value_container).await;

        // add available_usdt
        // if total_get_usdt.is_sign_positive() {
        //     add_available_usdt(total_get_usdt).await;
        // } else {
        //     sub_available_usdt(total_get_usdt.abs()).await;
        // }
    }

    Ok(())
}

pub async fn market_order(
    symbol: &String,
    side: OrderSide,
    quantity: u64,
    client: &Client,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // building URL and API-keys
    let mut url = String::new();
    let mut api_key = String::new();
    unsafe {
        if RUNNING_MODE == TEST {
            url.push_str(URL_TEST);
            api_key = API_KEY_TESTNET.to_string();
        } else {
            url.push_str(URL);
            api_key = API_KEY.to_string();
        }
    }

    let endpoint_url = "/api/v3/order?";
    url.push_str(endpoint_url);
    hmac_signature(&mut url).await;

    // add parameters into URL
    url.push_str("&symbol=");
    url.push_str(symbol.as_str());
    match side {
        OrderSide::Buy => {
            url.push_str("&side=BUY");
        }
        OrderSide::Sell => {
            url.push_str("&side=SELL");
        }
    }

    url.push_str("&type=MARKET");
    url.push_str("&quantity=");
    url.push_str(quantity.to_string().as_str());

    let res = client
        .post(&url)
        .header("X-MBX-APIKEY", api_key)
        .send()
        .await?;

    Ok(())
}

// Cancel an NEW or PARTIALLY FILLED order. (/api, Weight(IP) 1)
pub async fn cancel_buy_order(
    order: &BuyOrderedCoinList,
    client: &Client,
    trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // building URL and API-keys
    let mut url = String::new();
    let mut api_key = String::new();
    unsafe {
        if RUNNING_MODE == TEST {
            url.push_str(URL_TEST);
            api_key = API_KEY_TESTNET.to_string();
        } else if RUNNING_MODE == REAL {
            url.push_str(URL);
            api_key = API_KEY.to_string();
        }
    }

    let endpoint_url = "/api/v3/order?";
    url.push_str(endpoint_url);

    let mut url_build = String::new();
    // add parameters into URL
    url_build.push_str("&symbol=");
    url_build.push_str(&order.symbol);
    url_build.push_str("&orderId=");
    url_build.push_str(order.order_id.to_string().as_str());
    hmac_signature(&mut url_build).await;
    url.push_str(&url_build);

    let res = client
        .delete(&url)
        .header("X-MBX-APIKEY", api_key)
        .send()
        .await?;

    let body = res.text_with_charset("utf-8").await.unwrap();
    // println!("cancel_buy_order실행 body: {}", body);
    // deserialize JSON and then insert record into table
    let v = serde_json::from_str::<Value>(body.as_str());

    match v {
        Ok(T) => {
            if T.get("status").is_some() {
                // case that the order is canceled successfully
                if T.get("status").unwrap().as_str().unwrap() == "CANCELED" {
                    let table_name = String::from("buy_ordered_coin_list");
                    let cummulative_quote_qty: Decimal = rust_decimal::prelude::FromStr::from_str(
                        T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(),
                    )
                    .unwrap();

                    if cummulative_quote_qty.is_zero() {
                        // NOT Filled case
                        // delete record in [buy_ordered_coin_list]
                        let mut condition_build = String::from("WHERE order_id = ");
                        condition_build.push_str(order.order_id.to_string().as_str());
                        condition_build.push_str(" AND symbol = \'");
                        condition_build.push_str(&order.symbol);
                        condition_build.push('\'');
                        delete_record(&table_name, &condition_build).await;

                        add_available_usdt(order.used_usdt).await;

                        let update_table_name = String::from("suggested_coin_list");
                        let update_condition = vec![
                            (String::from("symbol"), order.symbol.clone()),
                            (String::from("close_time"), order.close_time.to_string()),
                        ];
                        let update_values: Vec<(String, String)> =
                            vec![(String::from("already_buy"), 0.to_string())];
                        update_record2(&update_table_name, &update_values, &update_condition).await;
                    } else {
                        // Patially Filled case
                        // update values in [buy_ordered_coin_list]
                        let mut status_value_build = String::from("\'");
                        status_value_build.push_str("FILLED");
                        status_value_build.push('\'');

                        // calculate values to be updated
                        if trade_fee_map.contains_key(&order.symbol) {
                            let trade_fee =
                                trade_fee_map.get(&order.symbol).unwrap().takercommission;
                            let base_qty_ordered = rust_decimal::prelude::FromStr::from_str(
                                T.get("executedQty").unwrap().as_str().unwrap(),
                            )
                            .unwrap();

                            let base_qty_fee_adjusted =
                                decimal_mul(base_qty_ordered, decimal_sub(dec!(1), trade_fee));
                            let buy_price = decimal_div(cummulative_quote_qty, base_qty_ordered)
                                .round_dp_with_strategy(8, RoundingStrategy::ToZero);

                            let update_values = vec![
                                (String::from("status"), status_value_build), // status
                                (String::from("used_usdt"), cummulative_quote_qty.to_string()), // used_usdt
                                (String::from("buy_price"), buy_price.to_string()), // buy_price
                                (
                                    String::from("base_qty_ordered"),
                                    base_qty_ordered.to_string(),
                                ), // base_qty_ordered
                                (
                                    String::from("base_qty_fee_adjusted"),
                                    base_qty_fee_adjusted.to_string(),
                                ), // base_qty_fee_adjusted
                            ];
                            let update_condition = vec![
                                (String::from("order_id"), order.order_id.to_string()),
                                (String::from("symbol"), order.symbol.clone()),
                            ];
                            update_record3(&table_name, &update_values, &update_condition)
                                .await
                                .unwrap();

                            // update available_usdt
                            if order.used_usdt > cummulative_quote_qty {
                                add_available_usdt(decimal_sub(
                                    order.used_usdt,
                                    cummulative_quote_qty,
                                ));
                            } else {
                                sub_available_usdt(decimal_sub(
                                    cummulative_quote_qty,
                                    order.used_usdt,
                                ));
                            }
                            println!("partially buy {}", order.symbol);
                        }
                    }
                }
            } else if T.get("code").is_some() {
                // case that the order isn't canceled because the order completes while canceling
                // update record in ordered_coin_list
                query_buy_order(order, &client, trade_fee_map).await;
            }
        }
        Err(e) => {
            query_buy_order(order, &client, trade_fee_map).await;
            log::warn!("cancel order buy failed!: {}", body);
        }
    }

    Ok(())
}

// Cancel an NEW or PARTIALLY FILLED order. (/api, Weight(IP) 1)
pub async fn cancel_sell_order(
    order: &SellOrderedCoinList,
    client: &Client,
    exchange_info_map: &HashMap<String, ExchangeInfo>,
    trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // building URL and API-keys
    let mut url = String::new();
    let mut api_key = String::new();
    unsafe {
        if RUNNING_MODE == TEST {
            url.push_str(URL_TEST);
            api_key = API_KEY_TESTNET.to_string();
        } else if RUNNING_MODE == REAL {
            url.push_str(URL);
            api_key = API_KEY.to_string();
        }
    }

    let endpoint_url = "/api/v3/order?";
    url.push_str(endpoint_url);

    let mut url_build = String::new();
    // add parameters into URL
    url_build.push_str("&symbol=");
    url_build.push_str(&order.symbol);
    url_build.push_str("&orderId=");
    url_build.push_str(order.sell_order_id.to_string().as_str());
    hmac_signature(&mut url_build).await;
    url.push_str(&url_build);

    let res = client
        .delete(&url)
        .header("X-MBX-APIKEY", api_key)
        .send()
        .await?;

    let body = res.text_with_charset("utf-8").await.unwrap();
    // println!("cancel_sell_order실행 body: {}", body);
    // deserialize JSON and then insert record into table
    let v = serde_json::from_str::<Value>(body.as_str());

    match v {
        Ok(T) => {
            if T.get("status").is_some() {
                // case that the order is canceled successfully
                if T.get("status").unwrap().as_str().unwrap() == "CANCELED" {
                    let insert_table_name = String::from("buy_ordered_coin_list");
                    let insert_columns = vec![
                        "symbol",
                        "order_id",
                        "transact_time",
                        "close_time",
                        "status",
                        "used_usdt",
                        "expected_get_usdt",
                        "expected_usdt_profit",
                        "buy_price",
                        "current_price",
                        "stoploss",
                        "target_price",
                        "base_qty_ordered",
                        "base_qty_fee_adjusted",
                        "pure_profit_percent",
                        "minimum_profit_percent",
                        "maximum_profit_percent",
                        "registerer",
                        "is_long",
                    ];
                    let base_qty_executed: Decimal = rust_decimal::prelude::FromStr::from_str(
                        T.get("executedQty").unwrap().as_str().unwrap(),
                    )
                    .unwrap();
                    let base_qty_ordered: Decimal = rust_decimal::prelude::FromStr::from_str(
                        T.get("origQty").unwrap().as_str().unwrap(),
                    )
                    .unwrap();

                    if base_qty_executed.is_zero() {
                        // Not FILLED
                        // delete record in [sell_ordered_coin_list]
                        let table_name = String::from("sell_ordered_coin_list");
                        let mut condition_build = String::from("WHERE sell_order_id = ");
                        condition_build.push_str(order.sell_order_id.to_string().as_str());
                        condition_build.push_str(" AND symbol = \'");
                        condition_build.push_str(&order.symbol);
                        condition_build.push('\'');
                        delete_record(&table_name, &condition_build).await;

                        // insert record in [buy_ordered_coin_list]
                        let mut insert_values: Vec<Vec<String>> = Vec::new();
                        let mut insert_value_container: Vec<String> = Vec::new();
                        let server_epoch = get_server_epoch().await;
                        insert_value_container.push(order.symbol.clone()); // symbol
                        insert_value_container.push(order.buy_order_id.to_string()); // order_id
                        insert_value_container.push(order.transact_time.to_string()); // transact_time
                        insert_value_container.push(order.close_time.to_string()); // close_time
                        insert_value_container.push(String::from("FILLED")); // status
                        insert_value_container.push(order.used_usdt.to_string()); // used_usdt
                        insert_value_container.push(0.0.to_string()); // expected_get_usdt
                        insert_value_container.push(0.0.to_string()); // expected_usdt_profit
                        insert_value_container.push(order.buy_price.to_string()); // buy_price
                        insert_value_container.push(0.0.to_string()); // current_price
                        insert_value_container.push(order.stoploss.to_string()); // stoploss
                        insert_value_container.push(order.target_price.to_string()); // target_price
                        insert_value_container.push(order.base_qty_ordered.to_string()); // base_qty_ordered
                        insert_value_container.push(order.base_qty_ordered.to_string()); // base_qty_fee_adjusted
                        insert_value_container.push(0.0.to_string()); // pure_profit_percent
                        insert_value_container.push(0.0.to_string()); // minimum_profit_percent
                        insert_value_container.push(order.maximum_profit_percent.to_string()); // maximum_profit_percent
                        insert_value_container.push(order.registerer.to_string()); // registerer
                        insert_value_container.push(order.is_long.to_string()); // is_long

                        insert_values.push(insert_value_container.clone());
                        insert_records(&insert_table_name, &insert_columns, &insert_values).await;
                    } else {
                        if exchange_info_map.contains_key(&order.symbol)
                            && trade_fee_map.contains_key(&order.symbol)
                        {
                            let quote_asset_precision = exchange_info_map
                                .get(&order.symbol)
                                .unwrap()
                                .quote_asset_precision;
                            let trade_fee =
                                trade_fee_map.get(&order.symbol).unwrap().takercommission;
                            if base_qty_executed == base_qty_ordered {
                                // FILLED case
                                // update status FILLED
                                let get_usdt = rust_decimal::prelude::FromStr::from_str(
                                    T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(),
                                )
                                .unwrap();
                                let get_usdt_fee_adjusted =
                                    decimal_mul(get_usdt, decimal_sub(dec!(1), trade_fee))
                                        .round_dp_with_strategy(
                                            quote_asset_precision,
                                            RoundingStrategy::ToZero,
                                        );
                                let sell_price = decimal_div(get_usdt, base_qty_executed)
                                    .round_dp_with_strategy(
                                        quote_asset_precision,
                                        RoundingStrategy::ToZero,
                                    );
                                let pure_profit_percent = decimal_mul(
                                    decimal_sub(
                                        decimal_div(get_usdt_fee_adjusted, order.used_usdt),
                                        dec!(1),
                                    ),
                                    dec!(100),
                                )
                                .round_dp(2);

                                let table_name = String::from("sell_ordered_coin_list");
                                let mut value_build = String::from("\'");
                                value_build.push_str("FILLED");
                                value_build.push('\'');

                                let update_values = vec![
                                    (String::from("status"), value_build),
                                    (String::from("get_usdt"), get_usdt.to_string()),
                                    (
                                        String::from("get_usdt_fee_adjusted"),
                                        get_usdt_fee_adjusted.to_string(),
                                    ),
                                    (String::from("sell_price"), sell_price.to_string()),
                                    (
                                        String::from("pure_profit_percent"),
                                        pure_profit_percent.to_string(),
                                    ),
                                ];
                                let update_condition =
                                    vec![(String::from("id"), order.id.to_string())];
                                update_record3(&table_name, &update_values, &update_condition)
                                    .await
                                    .unwrap();
                            } else {
                                // PARTIALLY FILLED case
                                // reflect partially filled information and update status with FILLED
                                let used_usdt = decimal_mul(base_qty_executed, order.buy_price);
                                let get_usdt = rust_decimal::prelude::FromStr::from_str(
                                    T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(),
                                )
                                .unwrap();
                                let get_usdt_fee_adjusted =
                                    decimal_mul(get_usdt, decimal_sub(dec!(1), trade_fee))
                                        .round_dp_with_strategy(
                                            quote_asset_precision,
                                            RoundingStrategy::ToZero,
                                        );
                                let rest_used_usdt = decimal_sub(order.used_usdt, used_usdt);
                                let sell_price = decimal_div(get_usdt, base_qty_executed)
                                    .round_dp_with_strategy(
                                        quote_asset_precision,
                                        RoundingStrategy::ToZero,
                                    );
                                let pure_profit_percent = decimal_mul(
                                    decimal_sub(
                                        decimal_div(get_usdt_fee_adjusted, get_usdt),
                                        dec!(1),
                                    ),
                                    dec!(100),
                                )
                                .round_dp(2);

                                let table_name = String::from("sell_ordered_coin_list");
                                let mut value_build = String::from("\'");
                                value_build.push_str("FILLED");
                                value_build.push('\'');

                                let update_values = vec![
                                    (String::from("status"), value_build),
                                    (String::from("used_usdt"), used_usdt.to_string()),
                                    (String::from("get_usdt"), get_usdt.to_string()),
                                    (
                                        String::from("get_usdt_fee_adjusted"),
                                        get_usdt_fee_adjusted.to_string(),
                                    ),
                                    (
                                        String::from("base_qty_ordered"),
                                        base_qty_executed.to_string(),
                                    ),
                                    (String::from("sell_price"), sell_price.to_string()),
                                    (
                                        String::from("pure_profit_percent"),
                                        pure_profit_percent.to_string(),
                                    ),
                                ];
                                let update_condition =
                                    vec![(String::from("id"), order.id.to_string())];
                                update_record3(&table_name, &update_values, &update_condition)
                                    .await
                                    .unwrap();

                                // insert record in [buy_ordered_coin_list]
                                let rest_base_qty =
                                    decimal_sub(base_qty_ordered, base_qty_executed);
                                let rest_base_qty_fee_adjusted =
                                    decimal_mul(rest_base_qty, decimal_sub(dec!(1), trade_fee));
                                let mut insert_values: Vec<Vec<String>> = Vec::new();
                                let mut insert_value_container: Vec<String> = Vec::new();
                                insert_value_container.push(order.symbol.clone()); // symbol
                                insert_value_container.push(order.buy_order_id.to_string()); // order_id
                                insert_value_container.push(order.transact_time.to_string()); // transact_time
                                insert_value_container.push(order.close_time.to_string()); // close_time
                                insert_value_container.push(String::from("FILLED")); // status
                                insert_value_container.push(rest_used_usdt.to_string()); // used_usdt
                                insert_value_container.push(0.0.to_string()); // expected_get_usdt
                                insert_value_container.push(0.0.to_string()); // expected_usdt_profit
                                insert_value_container.push(order.buy_price.to_string()); // buy_price
                                insert_value_container.push(0.0.to_string()); // current_price
                                insert_value_container.push(order.stoploss.to_string()); // stoploss
                                insert_value_container.push(order.target_price.to_string()); // target_price
                                insert_value_container.push(rest_base_qty.to_string()); // base_qty_ordered
                                insert_value_container.push(rest_base_qty_fee_adjusted.to_string()); // base_qty_fee_adjusted
                                insert_value_container.push(0.0.to_string()); // pure_profit_percent
                                insert_value_container.push(0.0.to_string()); // minimum_profit_percent
                                insert_value_container.push(0.0.to_string()); // maximum_profit_percent
                                insert_value_container.push(order.registerer.to_string()); // registerer
                                insert_value_container.push(order.is_long.to_string()); // is_long

                                insert_values.push(insert_value_container.clone());
                                insert_records(&insert_table_name, &insert_columns, &insert_values)
                                    .await;
                            }
                        }
                    }
                } else {
                    query_sell_order(&order, &client, exchange_info_map, trade_fee_map).await;
                }
            } else if T.get("code").is_some() {
                // case that the order isn't canceled because the order completes while canceling
                // update record in ordered_coin_list
                query_sell_order(&order, &client, exchange_info_map, trade_fee_map).await;
            }
        }
        Err(e) => {
            log::warn!("cancel sell order failed!: {}", body);
        }
    }
    Ok(())
}

// cancel all open orders on a symbol (/api, Weight(IP) 1)
pub async fn cancel_all_order(
    symbol: &String,
    client: &Client,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // building URL
    let mut url = String::new();
    unsafe {
        if RUNNING_MODE == TEST {
            url.push_str(URL_TEST);
        } else if RUNNING_MODE == REAL {
            url.push_str(URL);
        }
    }

    let endpoint_url = "/api/v3/openOrders?";
    url.push_str(endpoint_url);

    // add parameters into URL
    url.push_str("&symbol=");
    url.push_str(symbol.as_str());
    url.push_str("&timestamp=");
    url.push_str(get_timestamp().await.as_str());

    let res = client.delete(&url).send().await?;

    Ok(())
}

// Get all account orders; active, canceled, or filled. on a symbol (/api, Weight(IP) 10)
pub async fn all_orders(
    symbol: &String,
    client: &Client,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // building URL and API-keys
    let mut url = String::new();
    let mut api_key = String::new();
    unsafe {
        if RUNNING_MODE == TEST {
            url.push_str(URL_TEST);
            api_key = API_KEY_TESTNET.to_string();
        } else if RUNNING_MODE == REAL {
            url.push_str(URL);
            api_key = API_KEY.to_string();
        }
    }

    let endpoint_url = "/api/v3/allOrders?";
    url.push_str(endpoint_url);

    let mut url_build = String::new();

    // add parameters into URL
    url_build.push_str("&symbol=");
    url_build.push_str(&symbol);
    hmac_signature(&mut url_build).await;
    url.push_str(&url_build);

    let res = client
        .get(&url)
        .header("X-MBX-APIKEY", api_key)
        .send()
        .await?;

    let body = res.text_with_charset("utf-8").await.unwrap();
    // println!("{}", body);
    Ok(())
}

// query order and update record in buy_ordered_coin_list (/api, Weight(IP) 2)
pub async fn query_buy_order(
    order: &BuyOrderedCoinList,
    client: &Client,
    trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // building URL and API-keys
    let mut url = String::new();
    let mut api_key = String::new();
    unsafe {
        if RUNNING_MODE == TEST {
            url.push_str(URL_TEST);
            api_key = API_KEY_TESTNET.to_string();
        } else if RUNNING_MODE == REAL {
            url.push_str(URL);
            api_key = API_KEY.to_string();
        }
    }

    let endpoint_url = "/api/v3/order?";
    url.push_str(endpoint_url);

    let mut url_build = String::new();

    // add parameters into URL
    url_build.push_str("&symbol=");
    url_build.push_str(&order.symbol);
    url_build.push_str("&orderId=");
    url_build.push_str(order.order_id.to_string().as_str());

    hmac_signature(&mut url_build).await;
    url.push_str(&url_build);

    let res = client
        .get(&url)
        .header("X-MBX-APIKEY", api_key)
        .send()
        .await?;

    let body = res.text_with_charset("utf-8").await.unwrap();
    // println!("query_buy_order: {}", body);
    // deserialize JSON and then update record in table
    let v = serde_json::from_str::<Value>(body.as_str());

    match v {
        Ok(T) => {
            if T.get("status").is_some() {
                if T.get("status").unwrap().as_str().unwrap() == "FILLED"
                    || T.get("status").unwrap().as_str().unwrap() == "PARTIALLY_FILLED"
                {
                    let update_table_name = String::from("suggested_coin_list");
                    let update_condition = vec![
                        (String::from("symbol"), order.symbol.clone()),
                        (String::from("close_time"), order.close_time.to_string()),
                    ];
                    let update_values: Vec<(String, String)> =
                        vec![(String::from("already_buy"), 1.to_string())];
                    update_record2(&update_table_name, &update_values, &update_condition).await;

                    // update values in [buy_ordered_coin_list]
                    let table_name = String::from("buy_ordered_coin_list");
                    let mut value_build = String::from("\'");
                    value_build.push_str(T.get("status").unwrap().as_str().unwrap());
                    value_build.push('\'');

                    // calculate values to be updated
                    if trade_fee_map.contains_key(&order.symbol) {
                        let trade_fee = trade_fee_map.get(&order.symbol).unwrap().takercommission;
                        let base_qty_ordered = rust_decimal::prelude::FromStr::from_str(
                            T.get("executedQty").unwrap().as_str().unwrap(),
                        )
                        .unwrap();
                        let base_qty_fee_adjusted =
                            decimal_mul(base_qty_ordered, decimal_sub(dec!(1), trade_fee));

                        let cummulative_quote_qty = rust_decimal::prelude::FromStr::from_str(
                            T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(),
                        )
                        .unwrap();

                        let buy_price = decimal_div(cummulative_quote_qty, base_qty_ordered)
                            .round_dp_with_strategy(8, RoundingStrategy::ToZero);

                        let update_values = vec![
                            (String::from("status"), value_build), // status
                            (String::from("used_usdt"), cummulative_quote_qty.to_string()), // used_usdt
                            (String::from("buy_price"), buy_price.to_string()), // buy_price
                            (
                                String::from("base_qty_ordered"),
                                base_qty_ordered.to_string(),
                            ), // base_qty_ordered
                            (
                                String::from("base_qty_fee_adjusted"),
                                base_qty_fee_adjusted.to_string(),
                            ), // base_qty_fee_adjusted
                        ];
                        let update_condition = vec![
                            (String::from("order_id"), order.order_id.to_string()),
                            (String::from("symbol"), order.symbol.clone()),
                        ];
                        update_record3(&table_name, &update_values, &update_condition)
                            .await
                            .unwrap();

                        if T.get("status").unwrap().as_str().unwrap() == "FILLED" {
                            println!("buy {}", order.symbol);
                            // update available_usdt
                            if order.used_usdt > cummulative_quote_qty {
                                add_available_usdt(decimal_sub(
                                    order.used_usdt,
                                    cummulative_quote_qty,
                                ));
                            } else {
                                sub_available_usdt(decimal_sub(
                                    cummulative_quote_qty,
                                    order.used_usdt,
                                ));
                            }
                        }
                    }
                } else if T.get("status").unwrap().as_str().unwrap() == "CANCELED" {
                    let update_table_name = String::from("suggested_coin_list");
                    let update_condition = vec![
                        (String::from("symbol"), order.symbol.clone()),
                        (String::from("close_time"), order.close_time.to_string()),
                    ];
                    let update_values: Vec<(String, String)> =
                        vec![(String::from("already_buy"), 0.to_string())];
                }
            }
        }
        Err(e) => {
            log::warn!("query order failed!: {}", body);
        }
    }
    Ok(())
}

// query order and update record in sell_ordered_coin_list (/api, Weight(IP) 2)
pub async fn query_sell_order(
    order: &SellOrderedCoinList,
    client: &Client,
    exchange_info_map: &HashMap<String, ExchangeInfo>,
    trade_fee_map: &HashMap<String, TradeFee>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // building URL and API-keys
    let mut url = String::new();
    let mut api_key = String::new();
    unsafe {
        if RUNNING_MODE == TEST {
            url.push_str(URL_TEST);
            api_key = API_KEY_TESTNET.to_string();
        } else if RUNNING_MODE == REAL {
            url.push_str(URL);
            api_key = API_KEY.to_string();
        }
    }

    let endpoint_url = "/api/v3/order?";
    url.push_str(endpoint_url);

    let mut url_build = String::new();

    // add parameters into URL
    url_build.push_str("&symbol=");
    url_build.push_str(&order.symbol);
    url_build.push_str("&orderId=");
    url_build.push_str(order.sell_order_id.to_string().as_str());

    hmac_signature(&mut url_build).await;
    url.push_str(&url_build);

    let res = client
        .get(&url)
        .header("X-MBX-APIKEY", api_key)
        .send()
        .await?;

    let body = res.text_with_charset("utf-8").await.unwrap();
    // println!("query_sell_order실행 body: {}", body);
    // deserialize JSON and then update record in table
    let v = serde_json::from_str::<Value>(body.as_str());

    match v {
        Ok(T) => {
            if T.get("status")
                .is_some_and(|a| a.as_str().unwrap() == "FILLED")
                || T.get("status")
                    .is_some_and(|a| a.as_str().unwrap() == "PARTIALLY_FILLED")
            {
                if exchange_info_map.contains_key(&order.symbol)
                    && trade_fee_map.contains_key(&order.symbol)
                {
                    let quote_asset_precision = exchange_info_map
                        .get(&order.symbol)
                        .unwrap()
                        .quote_asset_precision;
                    let trade_fee = trade_fee_map.get(&order.symbol).unwrap().takercommission;
                    let get_usdt = rust_decimal::prelude::FromStr::from_str(
                        T.get("cummulativeQuoteQty").unwrap().as_str().unwrap(),
                    )
                    .unwrap();
                    let get_usdt_fee_adjusted =
                        decimal_mul(get_usdt, decimal_sub(dec!(1), trade_fee))
                            .round_dp_with_strategy(
                                quote_asset_precision,
                                RoundingStrategy::ToZero,
                            );
                    let ordered_base_qty = rust_decimal::prelude::FromStr::from_str(
                        T.get("executedQty").unwrap().as_str().unwrap(),
                    )
                    .unwrap();
                    let sell_price = decimal_div(get_usdt, ordered_base_qty)
                        .round_dp_with_strategy(quote_asset_precision, RoundingStrategy::ToZero);
                    let pure_profit_percent = decimal_mul(
                        decimal_sub(decimal_div(get_usdt_fee_adjusted, order.used_usdt), dec!(1)),
                        dec!(100),
                    )
                    .round_dp(2);

                    let table_name = String::from("sell_ordered_coin_list");
                    let mut value_build = String::from("\'");
                    value_build.push_str(T.get("status").unwrap().as_str().unwrap());
                    value_build.push('\'');

                    let update_values = vec![
                        (String::from("status"), value_build),
                        (String::from("get_usdt"), get_usdt.to_string()),
                        (
                            String::from("get_usdt_fee_adjusted"),
                            get_usdt_fee_adjusted.to_string(),
                        ),
                        (String::from("sell_price"), sell_price.to_string()),
                        (
                            String::from("pure_profit_percent"),
                            pure_profit_percent.to_string(),
                        ),
                    ];
                    let update_condition = vec![(String::from("id"), order.id.to_string())];
                    update_record3(&table_name, &update_values, &update_condition)
                        .await
                        .unwrap();
                }
            }
        }
        Err(e) => {
            log::warn!("query sell order failed!: {}", body);
        }
    }
    Ok(())
}

// request current open orders (/api, Weight(IP) 40)
pub async fn current_open_orders(
    client: &Client,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // building URL and API-keys
    let mut url = String::new();
    let mut api_key = String::new();
    unsafe {
        if RUNNING_MODE == TEST {
            url.push_str(URL_TEST);
            api_key = API_KEY_TESTNET.to_string();
        } else if RUNNING_MODE == REAL {
            url.push_str(URL);
            api_key = API_KEY.to_string();
        }
    }

    let endpoint_url = "/api/v3/openOrders?";
    url.push_str(endpoint_url);

    let mut url_build = String::new();
    hmac_signature(&mut url_build).await;
    url.push_str(&url_build);

    let res = client
        .get(&url)
        .header("X-MBX-APIKEY", api_key)
        .send()
        .await?;

    let body = res.text_with_charset("utf-8").await.unwrap();

    // println!("msg: {}", body);
    Ok(())
}

// request all information and status of current wallet. (/sapi, Weight(IP) 10)
// NOT FOR TESTNET
pub async fn all_coins_information(
    client: &Client,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // building URL and API-keys
    let mut url = String::new();
    let mut api_key = String::new();

    url.push_str(URL);
    api_key = API_KEY.to_string();

    let endpoint_url = "/sapi/v1/capital/config/getall?";
    url.push_str(endpoint_url);

    let mut url_build = String::new();
    hmac_signature(&mut url_build).await;
    url.push_str(&url_build);

    let res = client
        .get(&url)
        .header("X-MBX-APIKEY", api_key)
        .send()
        .await?;

    let body = res.text_with_charset("utf-8").await?;

    // deserialize JSON
    let v = serde_json::from_str::<Value>(body.as_str());

    match v {
        Ok(T) => {
            let mut into_vec = T.as_array();
            if into_vec == None {
                return Err("Err")?;
            }

            let insert_table_name = String::from("wallet");
            let insert_columns = vec!["symbol", "free", "locked"];
            let mut insert_values: Vec<Vec<String>> = Vec::new();
            let mut insert_value_container: Vec<String> = Vec::new();

            for element in into_vec.unwrap() {
                if element.is_object() {
                    insert_value_container.clear();
                    insert_value_container
                        .push(element.get("coin").unwrap().as_str().unwrap().to_string());
                    insert_value_container
                        .push(element.get("free").unwrap().as_str().unwrap().to_string());
                    insert_value_container
                        .push(element.get("locked").unwrap().as_str().unwrap().to_string());
                    insert_values.push(insert_value_container.clone());
                }
            }
            delete_all_rows(&insert_table_name).await;
            insert_records(&insert_table_name, &insert_columns, &insert_values).await;
        }
        Err(e) => {
            log::warn!("all coins information failed!: {}", body);
        }
    }
    Ok(())
}

// request balances of all rest coins in the account. (/api, Weight(IP) 10)
pub async fn account_information(
    client: &Client,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // building URL and API-keys
    let mut url = String::new();
    let mut api_key = String::new();
    unsafe {
        if RUNNING_MODE == TEST {
            url.push_str(URL_TEST);
            api_key = API_KEY_TESTNET.to_string();
        } else if RUNNING_MODE == REAL {
            url.push_str(URL);
            api_key = API_KEY.to_string();
        }
    }

    let endpoint_url = "/api/v3/account?";
    url.push_str(endpoint_url);

    let mut url_build = String::new();
    hmac_signature(&mut url_build).await;
    url.push_str(&url_build);

    let res = client
        .get(&url)
        .header("X-MBX-APIKEY", api_key)
        .send()
        .await?;

    let body = res.text_with_charset("utf-8").await.unwrap();

    // deserialize JSON
    let v = serde_json::from_str::<Value>(body.as_str());

    match v {
        Ok(T) => {
            let option_result = T.get("balances");
            if option_result.is_none() {
                return Err("Err")?;
            }
            let mut into_vec = option_result.unwrap().as_array();
            if into_vec == None {
                return Err("Err")?;
            }

            // insert contents into table
            let mut insert_table_name = String::new();
            unsafe {
                if RUNNING_MODE == TEST {
                    insert_table_name = String::from("wallet_testnet");
                } else if RUNNING_MODE == REAL {
                    insert_table_name = String::from("wallet");
                }
            }
            let insert_columns = vec!["asset", "free", "locked"];
            let mut insert_values: Vec<Vec<String>> = Vec::new();
            let mut insert_value_container: Vec<String> = Vec::new();

            for element in into_vec.unwrap() {
                if element.is_object() {
                    insert_value_container.clear();
                    insert_value_container
                        .push(element.get("asset").unwrap().as_str().unwrap().to_string());
                    insert_value_container
                        .push(element.get("free").unwrap().as_str().unwrap().to_string());
                    insert_value_container
                        .push(element.get("locked").unwrap().as_str().unwrap().to_string());
                    insert_values.push(insert_value_container.clone());
                }
            }

            delete_all_rows(&insert_table_name).await;
            insert_records(&insert_table_name, &insert_columns, &insert_values).await;
        }
        Err(e) => {
            log::warn!("account information failed!: {}", body);
        }
    }
    Ok(())
}

async fn hmac_signature(query: &mut String) {
    // fetch time information from [time] table
    let table_name = String::from("time");
    let columns = String::from("*");
    let condition = None;
    let mut time_info = TimeData {
        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::new(),
    };

    let select_result = select_record(&table_name, &columns, &condition, &time_info)
        .await
        .unwrap();
    let difference_epoch = select_result.first().unwrap().epoch_difference;
    let server_epoch = select_result.first().unwrap().server_epoch;

    // build query message
    // local 시간이 server 시간보다 너무 앞서거나 뒤쳐지면 USER_DATA를 요청할 수 없으므로 local과 server 의 시간 차이만큼을 local에서 빼어 보정한 뒤
    // 이를 timestamp로 사용한다.
    let mut timestamp;
    if difference_epoch >= 0 {
        timestamp = (server_epoch as u128 + difference_epoch as u128).to_string();
    } else if difference_epoch < 0 {
        timestamp = (server_epoch as u128 + (difference_epoch * -1) as u128).to_string();
    } else {
        timestamp = server_epoch.to_string();
    }
    let recv_window_size = "30000".to_string(); // default: 5,000ms, Max: 60,000ms

    let mut query_build = String::from("&timestamp=");
    query_build.push_str(&timestamp);
    query_build.push_str("&recvWindow=");
    query_build.push_str(&recv_window_size);

    let mut secret_key = String::new();
    unsafe {
        if RUNNING_MODE == TEST {
            secret_key.push_str(SECRET_KEY_TESTNET);
        } else if RUNNING_MODE == REAL {
            secret_key.push_str(SECRET_KEY);
        }
    }

    query.push_str(&query_build);
    let signature = HMAC::mac(&query.as_bytes(), secret_key.as_bytes());

    query.push_str("&signature=");
    query.push_str(signature.encode_hex::<String>().as_str());
}

async fn get_timestamp() -> String {
    // fetch time information from [time] table
    let table_name = String::from("time");
    let columns = String::from("*");
    let condition = None;
    let mut time_info = TimeData {
        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::new(),
    };

    let select_result = select_record(&table_name, &columns, &condition, &time_info)
        .await
        .unwrap();
    let difference_epoch = select_result.first().unwrap().epoch_difference;
    let local_epoch = select_result.first().unwrap().local_epoch;

    // build query message
    // local 시간이 server 시간보다 너무 앞서거나 뒤쳐지면 USER_DATA를 요청할 수 없으므로 local과 server 의 시간 차이만큼을 local에서 빼어 보정한 뒤
    // 이를 timestamp로 사용한다.
    let mut timestamp;
    if difference_epoch >= 0 {
        timestamp = (local_epoch as u128 - difference_epoch as u128).to_string();
    } else if difference_epoch < 0 {
        timestamp = (local_epoch as u128 - (difference_epoch * -1) as u128).to_string();
    } else {
        timestamp = local_epoch.to_string();
    }

    timestamp
}

// parameter 0: select all registerers
pub async fn select_filled_buy_orders(
    registerer: u32,
) -> Result<Vec<BuyOrderedCoinList>, Box<dyn std::error::Error + Send + Sync>> {
    let select_table_name = String::from("buy_ordered_coin_list");
    let select_columns = String::from("*");
    let mut select_condition_build = String::from("WHERE (status = 'FILLED' or status = 'SIMUL')");
    if registerer > 0 {
        select_condition_build.push_str(" and registerer = ");
        select_condition_build.push_str(registerer.to_string().as_str());
    }
    let select_condition = Some(select_condition_build);

    let data_struct = BuyOrderedCoinList::new();

    let select_result = try_select_record(
        &select_table_name,
        &select_columns,
        &select_condition,
        &data_struct,
    )
    .await;

    if select_result.is_ok() {
        Ok(select_result.unwrap())
    } else {
        eprint!("select_filled_buy_order() error!");
        Err("error")?
    }
}

// select open buy orders (NEW, Partially Filled)
async fn select_open_buy_orders() -> Vec<BuyOrderedCoinList> {
    let select_table_name = String::from("buy_ordered_coin_list");
    let select_columns = String::from("*");
    let select_condition = Some(String::from(
        "WHERE status = 'NEW' or status = 'PARTIALLY_FILLED'",
    ));
    let data_struct = BuyOrderedCoinList::new();
    let select_result = try_select_record(
        &select_table_name,
        &select_columns,
        &select_condition,
        &data_struct,
    )
    .await
    .unwrap();

    select_result
}

pub async fn select_filled_sell_orders() -> Vec<SellOrderedCoinList> {
    let select_table_name = String::from("sell_ordered_coin_list");
    let select_columns = String::from("*");
    let select_condition = Some(String::from("WHERE status = 'FILLED' or status = 'SIMUL'"));
    let data_struct = SellOrderedCoinList::new();

    let select_result = try_select_record(
        &select_table_name,
        &select_columns,
        &select_condition,
        &data_struct,
    )
    .await
    .unwrap();

    select_result
}

// select open sell orders (NEW, Partially Filled)
async fn select_open_sell_orders() -> Vec<SellOrderedCoinList> {
    let select_table_name = String::from("sell_ordered_coin_list");
    let select_columns = String::from("*");
    let select_condition = Some(String::from(
        "WHERE status = 'NEW' or status = 'PARTIALLY_FILLED'",
    ));
    let data_struct = SellOrderedCoinList::new();

    let select_result = try_select_record(
        &select_table_name,
        &select_columns,
        &select_condition,
        &data_struct,
    )
    .await
    .unwrap();

    select_result
}
