Compare commits
115 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8b8cbb0dce | |||
| 6dfdc719c3 | |||
| 35d008450d | |||
| 7ea865ef54 | |||
| 3d821c7ec3 | |||
| eae8a367dd | |||
| 73e72bdd3c | |||
| 6bb2b2df8e | |||
| c0cef48c50 | |||
| 64f1ef7851 | |||
| 805d8c87e8 | |||
| 98a29873b9 | |||
| 0efc7505cb | |||
| 1e7be2681f | |||
| b4dbf9e1d2 | |||
| 653c3835d4 | |||
| 0c1e8d1949 | |||
| 0ba0d3702d | |||
| 3990205979 | |||
| 54f70b47eb | |||
| bbaf39173d | |||
| 2b08bbfd66 | |||
| 33221f253a | |||
| a3a61f24c6 | |||
| dcfbf64036 | |||
| f3cb66f5f6 | |||
| 9540191955 | |||
| 7fbf68ba11 | |||
| 069f1c85a6 | |||
| 7c0b8960cb | |||
| 80dad1d352 | |||
| fadf1ea26a | |||
| 757400b35e | |||
| e3479e346a | |||
| 83017ea8e3 | |||
| daf1fc1256 | |||
| 7c1e12bcbb | |||
| 7f88bab770 | |||
| 2fa4696e81 | |||
| 4afe543dc4 | |||
| 5123ac7647 | |||
| aac7d00e9f | |||
| 7483258922 | |||
| 65f523eee1 | |||
| 62fad62a4d | |||
| f42570edc0 | |||
| 85935e553b | |||
| 1c8f0d1eec | |||
| 02251bb171 | |||
| 2e2222377f | |||
| 59a5e540ac | |||
| 31f211c3c2 | |||
| 75f4432090 | |||
| a4ecc2df5b | |||
| f438ae502b | |||
| 5dd0873add | |||
| d50ac9b12e | |||
| 64e6e92747 | |||
| 1e64873e4c | |||
| 26bd898d25 | |||
| 143e06b1b1 | |||
| 9c5a3f0a93 | |||
| 12d97c109a | |||
| c691999599 | |||
| c1ed9af720 | |||
| 566ea51ee6 | |||
| e1f50efd79 | |||
| bc8b1f12d5 | |||
| ab6ae5e667 | |||
| 583787432e | |||
| 0425911286 | |||
| 83a20c4393 | |||
| 0778575b74 | |||
| a0a9f254e1 | |||
| ddd690fd66 | |||
| 2f83c28077 | |||
| 2561a1081d | |||
| b867d231fa | |||
| 80741086d9 | |||
| ede6c9e29d | |||
| d61cc14bd5 | |||
| 40193b171a | |||
| 87cde15b37 | |||
| 5937ee9be0 | |||
| 512bc46b39 | |||
| 367809a919 | |||
| 4bce70f7b7 | |||
| 982a679703 | |||
| 80252e235b | |||
| 1e06080ce6 | |||
| a3675db92d | |||
| 5e5f0cbc05 | |||
| 1337a78d73 | |||
| 2d50f9cdbb | |||
| 2fbce18948 | |||
| 2d19927048 | |||
| 6fad7b0ec4 | |||
| 970971e005 | |||
| 26e32279eb | |||
| e68804aaf7 | |||
| 229a4c38ae | |||
| b9621ccc72 | |||
| 3549fbf289 | |||
| 6a1541bc95 | |||
| 6bec61cb2f | |||
| 30bc530f21 | |||
| d2efd67c4f | |||
| f92786fdcf | |||
| 162ff464c3 | |||
| 3152d79e5c | |||
| 5849f924e8 | |||
| 5bc08eaf10 | |||
| 1822e82bb0 | |||
| 0eca6971db | |||
| d8592f65f6 |
|
|
@ -106,7 +106,7 @@ pub async fn set_unit_usdt() {
|
|||
let asset_info = select_asset_manage_announcement().await;
|
||||
let mut set_unit_trade_usdt = Decimal::new(0, 8);
|
||||
|
||||
set_unit_trade_usdt = dec!(50.0); // $50 for each trade
|
||||
set_unit_trade_usdt = dec!(10.0); // $50 for each trade
|
||||
|
||||
// update fields in [asset_manage_announcement] table
|
||||
let update_table_name = String::from("asset_manage_announcement");
|
||||
|
|
|
|||
223
src/future/mod.rs
Normal file
223
src/future/mod.rs
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
pub mod order;
|
||||
pub mod table_mgmt;
|
||||
|
||||
use hmac_sha256::HMAC;
|
||||
use crate::RunningMode::*;
|
||||
use crate::{API_KEY, API_KEY_TESTNET, RUNNING_MODE, SECRET_KEY, SECRET_KEY_TESTNET, FUTURES_URL, FUTURES_URL_TEST};
|
||||
use crate::strategy_team::{AllData, TimeData};
|
||||
use crate::database_control::*;
|
||||
use rust_decimal::Decimal;
|
||||
use sqlx::FromRow;
|
||||
use hex::ToHex;
|
||||
use crate::future::table_mgmt::select_listuped_positions;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, sqlx::Type)]
|
||||
pub enum Position {
|
||||
Long,
|
||||
Short
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, sqlx::Type)]
|
||||
pub enum Status {
|
||||
#[sqlx(rename = "listup")]
|
||||
Listup,
|
||||
#[sqlx(rename = "filled")]
|
||||
Filled,
|
||||
#[sqlx(rename = "partially_filled")]
|
||||
PartiallyFilled
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Position {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EntryCoinInfo {
|
||||
pub symbol: String,
|
||||
pub close_time: i64,
|
||||
pub registered_server_epoch: i64,
|
||||
}
|
||||
|
||||
impl EntryCoinInfo {
|
||||
fn new() -> EntryCoinInfo {
|
||||
let a = EntryCoinInfo {
|
||||
symbol: String::new(),
|
||||
close_time: 0,
|
||||
registered_server_epoch: 0
|
||||
};
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ContractType {
|
||||
Perpetual, // this is the target
|
||||
CurrentMonth,
|
||||
NextMonth,
|
||||
CurrentQuarter,
|
||||
NextQuarter,
|
||||
PerpetualDelivering,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ContractStatus {
|
||||
PendingTrading, // this is the target
|
||||
Trading,
|
||||
PreDelivering,
|
||||
Delivering,
|
||||
Delivered,
|
||||
PreSettle,
|
||||
Settling,
|
||||
Close,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromRow, Clone)]
|
||||
pub struct FuturesExchangeInfo {
|
||||
pub stepsize: Decimal,
|
||||
pub ticksize: Decimal,
|
||||
pub contract_type: ContractType,
|
||||
pub contract_status: ContractStatus,
|
||||
pub base_asset_precision: u32,
|
||||
pub quote_precision: u32,
|
||||
pub notional: Decimal
|
||||
}
|
||||
|
||||
impl FuturesExchangeInfo {
|
||||
fn new() -> FuturesExchangeInfo {
|
||||
let a = FuturesExchangeInfo {
|
||||
stepsize: Decimal::new(0, 8),
|
||||
ticksize: Decimal::new(0, 8),
|
||||
contract_type: ContractType::Perpetual,
|
||||
contract_status: ContractStatus::Trading,
|
||||
base_asset_precision: 0,
|
||||
quote_precision: 0,
|
||||
notional: Decimal::new(0, 8),
|
||||
};
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FuturesTradeFee {
|
||||
user_level: Option<i32>,
|
||||
taker_fee_percent: Decimal,
|
||||
maker_fee_percent: Decimal,
|
||||
}
|
||||
|
||||
impl FuturesTradeFee {
|
||||
pub fn new() -> FuturesTradeFee {
|
||||
let a = FuturesTradeFee {
|
||||
user_level: None,
|
||||
taker_fee_percent: Decimal::new(0, 8),
|
||||
maker_fee_percent: Decimal::new(0, 8),
|
||||
};
|
||||
a
|
||||
}
|
||||
}
|
||||
#[derive(Debug, FromRow, Clone)]
|
||||
pub struct PositionCoinList {
|
||||
pub id: u64,
|
||||
pub order_type: String,
|
||||
pub status: String,
|
||||
pub symbol: String,
|
||||
pub order_id: u64,
|
||||
pub position: String,
|
||||
pub registered_server_epoch: i64,
|
||||
pub transact_time: i64,
|
||||
pub close_time: i64,
|
||||
pub used_usdt: Decimal,
|
||||
pub expected_get_usdt: f64,
|
||||
pub pnl: f64,
|
||||
pub entry_price: Decimal,
|
||||
pub current_price: Decimal,
|
||||
pub target_percent: f64,
|
||||
pub stoploss_percent: f64,
|
||||
pub base_qty_ordered: Decimal,
|
||||
pub pure_profit_percent: f64,
|
||||
pub minimum_profit_percent: f64,
|
||||
pub maximum_profit_percent: f64,
|
||||
}
|
||||
|
||||
impl PositionCoinList {
|
||||
fn new() -> PositionCoinList {
|
||||
let a = PositionCoinList{
|
||||
id: 0,
|
||||
order_type: String::new(),
|
||||
status: String::new(),
|
||||
symbol: String::new(),
|
||||
order_id: 0,
|
||||
position: String::new(),
|
||||
registered_server_epoch: 0,
|
||||
transact_time: 0,
|
||||
close_time: 0,
|
||||
used_usdt: Decimal::new(0, 8),
|
||||
expected_get_usdt: 0.0,
|
||||
pnl: 0.0,
|
||||
entry_price: Decimal::new(0, 8),
|
||||
current_price: Decimal::new(0, 8),
|
||||
target_percent: 0.0,
|
||||
stoploss_percent: 0.0,
|
||||
base_qty_ordered: Decimal::new(0, 8),
|
||||
pure_profit_percent: 0.0,
|
||||
minimum_profit_percent: 0.0,
|
||||
maximum_profit_percent: 0.0,
|
||||
};
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
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("×tamp=");
|
||||
query_build.push_str(×tamp);
|
||||
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 {
|
||||
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());
|
||||
}
|
||||
1194
src/future/order.rs
Normal file
1194
src/future/order.rs
Normal file
File diff suppressed because it is too large
Load Diff
434
src/future/table_mgmt.rs
Normal file
434
src/future/table_mgmt.rs
Normal file
|
|
@ -0,0 +1,434 @@
|
|||
use super::{hmac_signature, REAL, SIMUL, TEST, RUNNING_MODE, FUTURES_URL, FUTURES_URL_TEST, API_KEY, API_KEY_TESTNET, FuturesExchangeInfo, PositionCoinList, FuturesTradeFee};
|
||||
use std::collections::HashMap;
|
||||
use rust_decimal::{Decimal, RoundingStrategy};
|
||||
use crate::database_control::*;
|
||||
use crate::decimal_funcs::*;
|
||||
use rust_decimal_macros::dec;
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
use serde_json::Value;
|
||||
use reqwest::{Client, ClientBuilder};
|
||||
use crate::coex::exchange_team::*;
|
||||
|
||||
pub async fn get_tradefee_balance(future_trade_fee: &mut FuturesTradeFee, 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(FUTURES_URL_TEST);
|
||||
api_key = API_KEY_TESTNET.to_string();
|
||||
} else {
|
||||
url.push_str(FUTURES_URL);
|
||||
api_key = API_KEY.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
let endpoint_url = "/fapi/v2/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?;
|
||||
|
||||
// deserialize JSON
|
||||
let v = serde_json::from_str::<Value>(body.as_str());
|
||||
match v {
|
||||
Ok(T) => {
|
||||
if let (Some(fee_tier), Some(available_balance)) = (T.get("feeTier"), T.get("availableBalance")) {
|
||||
let update_table_name = String::from("future_available_balance");
|
||||
let update_values = vec![
|
||||
(String::from("available_usdt"), available_balance.as_str().unwrap().to_string()),
|
||||
];
|
||||
let update_condition = vec![(String::from("id"), String::from("1"))];
|
||||
update_record2(&update_table_name, &update_values, &update_condition)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
match fee_tier.as_number().unwrap().as_i64() {
|
||||
Some(0) => { // Regular User
|
||||
future_trade_fee.user_level = Some(0);
|
||||
future_trade_fee.maker_fee_percent = dec!(0.0200);
|
||||
future_trade_fee.taker_fee_percent = dec!(0.0500);
|
||||
},
|
||||
Some(1) => { // VIP1
|
||||
future_trade_fee.user_level = Some(1);
|
||||
future_trade_fee.maker_fee_percent = dec!(0.0160);
|
||||
future_trade_fee.taker_fee_percent = dec!(0.0400);
|
||||
},
|
||||
Some(2) => {// VIP 2
|
||||
future_trade_fee.user_level = Some(2);
|
||||
future_trade_fee.maker_fee_percent = dec!(0.0140);
|
||||
future_trade_fee.taker_fee_percent = dec!(0.0350);
|
||||
},
|
||||
Some(3) => {// VIP 3
|
||||
future_trade_fee.user_level = Some(3);
|
||||
future_trade_fee.maker_fee_percent = dec!(0.0120);
|
||||
future_trade_fee.taker_fee_percent = dec!(0.0320);
|
||||
},
|
||||
Some(4) => {// VIP 4
|
||||
future_trade_fee.user_level = Some(4);
|
||||
future_trade_fee.maker_fee_percent = dec!(0.0100);
|
||||
future_trade_fee.taker_fee_percent = dec!(0.0300);
|
||||
},
|
||||
Some(5) => {// VIP 5
|
||||
future_trade_fee.user_level = Some(5);
|
||||
future_trade_fee.maker_fee_percent = dec!(0.0080);
|
||||
future_trade_fee.taker_fee_percent = dec!(0.0270);
|
||||
},
|
||||
Some(6) => {// VIP 6
|
||||
future_trade_fee.user_level = Some(6);
|
||||
future_trade_fee.maker_fee_percent = dec!(0.0060);
|
||||
future_trade_fee.taker_fee_percent = dec!(0.0250);
|
||||
},
|
||||
Some(7) => {// VIP 7
|
||||
future_trade_fee.user_level = Some(7);
|
||||
future_trade_fee.maker_fee_percent = dec!(0.0040);
|
||||
future_trade_fee.taker_fee_percent = dec!(0.0220);
|
||||
},
|
||||
Some(8) => {// VIP 8
|
||||
future_trade_fee.user_level = Some(8);
|
||||
future_trade_fee.maker_fee_percent = dec!(0.0020);
|
||||
future_trade_fee.taker_fee_percent = dec!(0.0200);
|
||||
},
|
||||
Some(9) => {// VIP 9
|
||||
future_trade_fee.user_level = Some(9);
|
||||
future_trade_fee.maker_fee_percent = dec!(0.0000);
|
||||
future_trade_fee.taker_fee_percent = dec!(0.0170);
|
||||
},
|
||||
Some(_) => {},
|
||||
None => {}
|
||||
}
|
||||
log::info!("fee_tier: {:?}. available_balance: {:?}", fee_tier, available_balance);
|
||||
} else {
|
||||
log::error!("Endpoint(/fapi/v1/leverage?) output changed!");
|
||||
return Err("Err")?;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("account information failed!: {}, {}", body, e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_price_of_filled_positions(
|
||||
coin_price_map: &HashMap<String, f64>,
|
||||
exchange_info_map: &HashMap<String, FuturesExchangeInfo>,
|
||||
futures_trade_fee: &FuturesTradeFee,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let filled_buy_orders = select_filled_positions().await?;
|
||||
|
||||
if !filled_buy_orders.is_empty() {
|
||||
// 심볼들을 5개씩 청스로 나누어 테스크를 생성하고 각 태스크 별로 병렬로 처리한다.
|
||||
// update real-time current price to each record through chunks
|
||||
let chunks: std::slice::Chunks<'_, PositionCoinList> = filled_buy_orders.chunks(5);
|
||||
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 futures_trade_fee_map_c = futures_trade_fee.clone();
|
||||
task_vec.push(tokio::spawn(async move {
|
||||
update_repeat_task(
|
||||
chunk_vec,
|
||||
&coin_price_vec_c,
|
||||
&exchange_info_vec_c,
|
||||
&futures_trade_fee_map_c,
|
||||
)
|
||||
.await;
|
||||
}));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_repeat_task(
|
||||
buy_ordered_coin_vec: Vec<PositionCoinList>,
|
||||
coin_price_map: &HashMap<String, f64>,
|
||||
exchange_info_map: &HashMap<String, FuturesExchangeInfo>,
|
||||
futures_trade_fee: &FuturesTradeFee,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let update_table_name = String::from("future_ordered_coin_list");
|
||||
let mut price = Decimal::new(0, 8);
|
||||
let mut profit_percent = 0.0;
|
||||
|
||||
// update current_price, expected__get_usdt, pnl, pure_profit_percent, minimum_profit_percent, maximum_profit_percent
|
||||
for element in buy_ordered_coin_vec {
|
||||
// build update values
|
||||
if coin_price_map.contains_key(&element.symbol)
|
||||
&& exchange_info_map.contains_key(&element.symbol)
|
||||
&& futures_trade_fee.user_level.is_some()
|
||||
{
|
||||
price = rust_decimal::prelude::FromPrimitive::from_f64(
|
||||
*coin_price_map.get(&element.symbol).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
if !price.is_zero() {
|
||||
let mut update_values: Vec<(String, String)> = Vec::new();
|
||||
// to get quote_commission_precision
|
||||
let entry_trade_fee_ratio = decimal_div(futures_trade_fee.maker_fee_percent, dec!(100));
|
||||
let exit_trade_fee_ratio = decimal_div(futures_trade_fee.taker_fee_percent, dec!(100));
|
||||
let trade_fee = decimal_div(futures_trade_fee.taker_fee_percent, dec!(100));
|
||||
let lot_step_size = exchange_info_map.get(&element.symbol).unwrap().stepsize;
|
||||
let quote_precision = exchange_info_map
|
||||
.get(&element.symbol)
|
||||
.unwrap()
|
||||
.quote_precision;
|
||||
let position_size = element.base_qty_ordered.round_dp_with_strategy(
|
||||
lot_step_size.normalize().scale(),
|
||||
RoundingStrategy::ToZero,
|
||||
);
|
||||
|
||||
let entry_trade_fee = decimal_mul(decimal_mul(position_size, element.entry_price), entry_trade_fee_ratio);
|
||||
let exit_trade_fee = decimal_mul(decimal_mul(position_size, price), exit_trade_fee_ratio);
|
||||
let fee_total = decimal_add(entry_trade_fee, exit_trade_fee);
|
||||
let initial_margin = decimal_add(decimal_mul(position_size, element.entry_price), entry_trade_fee);
|
||||
let mut unrealized_pnl = Decimal::new(0, 8);
|
||||
|
||||
if element.position.contains("Long") {
|
||||
unrealized_pnl = decimal_sub(decimal_mul(decimal_sub(price, element.entry_price), position_size), fee_total);
|
||||
} else {
|
||||
unrealized_pnl = decimal_sub(decimal_mul(decimal_sub(element.entry_price, price), position_size), fee_total);
|
||||
}
|
||||
|
||||
let mut pure_profit_percent = (unrealized_pnl.to_f64().unwrap()
|
||||
/ decimal_add(initial_margin, unrealized_pnl).to_f64().unwrap())
|
||||
* 100.0;
|
||||
|
||||
pure_profit_percent = (pure_profit_percent * 100.0).round() / 100.0; // Rounding
|
||||
update_values.push((String::from("current_price"), price.to_string()));
|
||||
update_values.push((String::from("expected_get_usdt"), decimal_add(unrealized_pnl, element.used_usdt).to_string()));
|
||||
update_values.push((String::from("pnl"), unrealized_pnl.to_string()));
|
||||
update_values.push((String::from("pure_profit_percent"), pure_profit_percent.to_string()));
|
||||
|
||||
if element.minimum_profit_percent > pure_profit_percent {
|
||||
update_values.push((String::from("minimum_profit_percent"), pure_profit_percent.to_string()));
|
||||
// minimum_profit_percent
|
||||
} else if pure_profit_percent >= 0.0 {
|
||||
update_values.push((String::from("minimum_profit_percent"), 0.0.to_string()));
|
||||
} else {
|
||||
update_values.push((String::from("minimum_profit_percent"), element.minimum_profit_percent.to_string()));
|
||||
// minimum_profit_percent
|
||||
}
|
||||
|
||||
if element.maximum_profit_percent < pure_profit_percent {
|
||||
update_values.push((String::from("maximum_profit_percent"), pure_profit_percent.to_string()));
|
||||
// maximum_profit_percent
|
||||
} else if pure_profit_percent <= 0.0 {
|
||||
update_values.push((String::from("maximum_profit_percent"), 0.0.to_string()));
|
||||
} else {
|
||||
update_values.push((String::from("maximum_profit_percent"), element.maximum_profit_percent.to_string()));
|
||||
// maximum_profit_percent
|
||||
}
|
||||
let update_condition = vec![(String::from("id"), element.id.to_string())];
|
||||
update_record2(&update_table_name, &update_values, &update_condition)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn select_filled_positions() -> Result<Vec<PositionCoinList>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let select_table_name = String::from("future_ordered_coin_list");
|
||||
let select_columns = String::from("*");
|
||||
let mut select_condition_build = String::from("WHERE order_type = 'POSITIONING' and (status = 'FILLED' or status = 'SIMUL')");
|
||||
|
||||
let select_condition = Some(select_condition_build);
|
||||
|
||||
let data_struct = PositionCoinList::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_positions() error!");
|
||||
Err("error")?
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn select_short_filled_positions() -> Result<Vec<PositionCoinList>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let select_table_name = String::from("future_ordered_coin_list");
|
||||
let select_columns = String::from("*");
|
||||
let mut select_condition_build = String::from("WHERE order_type = 'POSITIONING' and position = 'Short' and (status = 'FILLED' or status = 'SIMUL')");
|
||||
|
||||
let select_condition = Some(select_condition_build);
|
||||
|
||||
let data_struct = PositionCoinList::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_positions() error!");
|
||||
Err("error")?
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn select_long_filled_positions() -> Result<Vec<PositionCoinList>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let select_table_name = String::from("future_ordered_coin_list");
|
||||
let select_columns = String::from("*");
|
||||
let mut select_condition_build = String::from("WHERE order_type = 'POSITIONING' and position = 'Long' and (status = 'FILLED' or status = 'SIMUL')");
|
||||
|
||||
let select_condition = Some(select_condition_build);
|
||||
|
||||
let data_struct = PositionCoinList::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_positions() error!");
|
||||
Err("error")?
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn select_listuped_positions() -> Result<Vec<PositionCoinList>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let select_table_name = String::from("future_ordered_coin_list");
|
||||
let select_columns = String::from("*");
|
||||
let mut select_condition_build = String::from("WHERE status = 'LISTUP'");
|
||||
|
||||
let select_condition = Some(select_condition_build);
|
||||
|
||||
let data_struct = PositionCoinList::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_listup_positions() error!");
|
||||
Err("error")?
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn select_closed_positions() -> Result<Vec<PositionCoinList>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let select_table_name = String::from("future_ordered_coin_list");
|
||||
let select_columns = String::from("*");
|
||||
let mut select_condition_build = String::from("WHERE order_type = 'CLOSING' AND status = 'FILLED'");
|
||||
|
||||
let select_condition = Some(select_condition_build);
|
||||
|
||||
let data_struct = PositionCoinList::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_listup_positions() error!");
|
||||
Err("error")?
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn move_closed_positions() {
|
||||
let closed_positions = select_closed_positions().await.unwrap();
|
||||
|
||||
if closed_positions.len() != 0 {
|
||||
let delete_table_name = String::from("future_ordered_coin_list");
|
||||
let insert_table_name = String::from("future_closed_coin_list");
|
||||
let insert_columns = vec![
|
||||
"order_type",
|
||||
"status",
|
||||
"symbol",
|
||||
"order_id",
|
||||
"position",
|
||||
"registered_server_epoch",
|
||||
"transact_time",
|
||||
"close_time",
|
||||
"used_usdt",
|
||||
"expected_get_usdt",
|
||||
"pnl",
|
||||
"entry_price",
|
||||
"current_price",
|
||||
"target_percent",
|
||||
"stoploss_percent",
|
||||
"base_qty_ordered",
|
||||
"pure_profit_percent",
|
||||
"minimum_profit_percent",
|
||||
"maximum_profit_percent",
|
||||
];
|
||||
|
||||
let server_epoch = get_server_epoch().await;
|
||||
|
||||
for element in closed_positions {
|
||||
if server_epoch - element.registered_server_epoch > 1_800_000 {
|
||||
let mut insert_values = vec![
|
||||
element.order_type.to_string(), // order_type
|
||||
element.status, // status
|
||||
element.symbol, // symbol
|
||||
element.order_id.to_string(), // order_id
|
||||
element.position.to_string(), // position
|
||||
element.registered_server_epoch.to_string(),// registered_server_epoch
|
||||
element.transact_time.to_string(), // transact_time
|
||||
element.close_time.to_string(), // close_time
|
||||
element.used_usdt.to_string(), // used_usdt
|
||||
element.expected_get_usdt.to_string(), // expected_get_usdt
|
||||
element.pnl.to_string(), // pnl
|
||||
element.entry_price.to_string(), // entry_price
|
||||
element.current_price.to_string(), // current_price
|
||||
element.target_percent.to_string(), // target_percent
|
||||
element.stoploss_percent.to_string(), // stoploss_percent
|
||||
element.base_qty_ordered.to_string(), // base_qty_ordered
|
||||
element.pure_profit_percent.to_string(), // pure_profit_percent
|
||||
element.minimum_profit_percent.to_string(), // minimum_profit_percent
|
||||
element.maximum_profit_percent.to_string(), // maximum_profit_percent
|
||||
];
|
||||
|
||||
insert_one_record(&insert_table_name, &insert_columns, &insert_values).await;
|
||||
|
||||
let mut condition_build = String::from("WHERE id = ");
|
||||
condition_build.push_str(element.id.to_string().as_str());
|
||||
// condition_build.push_str(" AND symbol = \'");
|
||||
// condition_build.push_str(element.symbol.as_str());
|
||||
// condition_build.push('\'');
|
||||
delete_record(&delete_table_name, &condition_build).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,9 @@ use rust_decimal_macros::dec;
|
|||
use sqlx::FromRow;
|
||||
use std::{io, io::Write, path::Path, process::Stdio};
|
||||
use tokio::{fs::*, io::ErrorKind, process::Command, task::JoinHandle, time::*};
|
||||
use crate::future::order::*;
|
||||
use crate::future::FuturesExchangeInfo;
|
||||
use std::collections::HashMap;
|
||||
|
||||
const STRATEGIST_NUMBER: u32 = 16;
|
||||
|
||||
|
|
@ -21,6 +24,7 @@ pub async fn initialization() {
|
|||
println!("- - - initialization start - - -");
|
||||
// initialize_webdriver().await;
|
||||
initialize_database().await;
|
||||
future_setup().await;
|
||||
println!("- - - initialization done - - -");
|
||||
}
|
||||
|
||||
|
|
@ -65,6 +69,32 @@ async fn initialize_webdriver() {
|
|||
println!("Ok");
|
||||
}
|
||||
|
||||
async fn future_setup() {
|
||||
print!(">>> Check future setting...");
|
||||
io::stdout().flush();
|
||||
let client = ClientBuilder::new()
|
||||
.timeout(tokio::time::Duration::from_millis(700))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let mut task_vec = Vec::new();
|
||||
let mut future_exchange_info_map: HashMap<String, FuturesExchangeInfo> = HashMap::new();
|
||||
request_future_exchange_infomation(&client, &mut future_exchange_info_map).await;
|
||||
for (symbol, futures_exchange_info) in future_exchange_info_map {
|
||||
let symbol_c = symbol.clone();
|
||||
let client_c = client.clone();
|
||||
task_vec.push(tokio::spawn(async move {
|
||||
set_margin_type(&symbol_c, MarginType::Isolated, &client_c).await; // Default: Isolated
|
||||
set_initial_leverage(&symbol_c, 1, &client_c).await; // Default: x1
|
||||
}));
|
||||
}
|
||||
try_join_all(task_vec).await;
|
||||
|
||||
set_position_mode(&client).await; // Default: One-way Mode
|
||||
set_asset_mode(&client).await; // Default: Single-Asset Mode
|
||||
println!("Ok");
|
||||
}
|
||||
|
||||
async fn initialize_database() {
|
||||
let client = ClientBuilder::new()
|
||||
.timeout(tokio::time::Duration::from_millis(700))
|
||||
|
|
@ -1226,6 +1256,138 @@ async fn initialize_database() {
|
|||
println!("Ok");
|
||||
}
|
||||
|
||||
{
|
||||
// future_available_balance
|
||||
print!("table 'future_available_balance'...");
|
||||
io::stdout().flush();
|
||||
|
||||
let table_name = String::from("future_available_balance");
|
||||
let exists_result = exists_table(&table_name).await;
|
||||
let initial_table = vec![
|
||||
("id", "integer", Some("PK, AI, UN")),
|
||||
("available_usdt", "decimal(16,8)", None),
|
||||
];
|
||||
let table_condition = None;
|
||||
|
||||
if exists_result == false {
|
||||
let mut result = new_table(&table_name, &initial_table, &table_condition).await;
|
||||
if result.is_err() {
|
||||
loop {
|
||||
result = new_table(&table_name, &initial_table, &table_condition).await;
|
||||
if result.is_ok() {
|
||||
break;
|
||||
}
|
||||
sleep(Duration::from_millis(10)).await;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
delete_all_rows(&table_name)
|
||||
.await
|
||||
.expect("Failed to delete rows!");
|
||||
}
|
||||
|
||||
let initial_columns = vec!["available_usdt"];
|
||||
let initial_values = vec![String::from("0.0")];
|
||||
insert_one_record(&table_name, &initial_columns, &initial_values)
|
||||
.await
|
||||
.expect("Failed to insert initial record!");
|
||||
println!("Ok");
|
||||
}
|
||||
|
||||
{
|
||||
// future_ordered_coin_list
|
||||
print!("table 'future_ordered_coin_list'...");
|
||||
io::stdout().flush();
|
||||
|
||||
let table_name = String::from("future_ordered_coin_list");
|
||||
let exists_result = exists_table(&table_name).await;
|
||||
let initial_table = vec![
|
||||
("id", "integer", Some("PK, AI, UN")),
|
||||
("order_type", "char(20)", None), // POSITIONING, CLOSING
|
||||
("status", "char(20)", None), // LISTUP, FILLED, PARTIALLY_FILLED
|
||||
("symbol", "char(20)", None),
|
||||
("order_id", "bigint", Some("UN")),
|
||||
("position", "char(20)", None),
|
||||
("registered_server_epoch", "bigint", None),
|
||||
("transact_time", "bigint", None),
|
||||
("close_time", "bigint", None),
|
||||
("used_usdt", "decimal(16,8)", None),
|
||||
("expected_get_usdt", "double", None),
|
||||
("pnl", "double", None),
|
||||
("entry_price", "decimal(16,8)", None),
|
||||
("current_price", "decimal(16,8)", None),
|
||||
("target_percent", "double", None),
|
||||
("stoploss_percent", "double", None),
|
||||
("base_qty_ordered", "decimal(16,8)", None),
|
||||
("pure_profit_percent", "double", None),
|
||||
("minimum_profit_percent", "double", None),
|
||||
("maximum_profit_percent", "double", None),
|
||||
];
|
||||
let table_condition = None;
|
||||
|
||||
if exists_result == false {
|
||||
let mut result = new_table(&table_name, &initial_table, &table_condition).await;
|
||||
if result.is_err() {
|
||||
loop {
|
||||
result = new_table(&table_name, &initial_table, &table_condition).await;
|
||||
if result.is_ok() {
|
||||
break;
|
||||
}
|
||||
sleep(Duration::from_millis(10)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("Ok");
|
||||
}
|
||||
|
||||
{
|
||||
// future_ordered_coin_list
|
||||
print!("table 'future_closed_coin_list'...");
|
||||
io::stdout().flush();
|
||||
|
||||
let table_name = String::from("future_closed_coin_list");
|
||||
let exists_result = exists_table(&table_name).await;
|
||||
let initial_table = vec![
|
||||
("id", "integer", Some("PK, AI, UN")),
|
||||
("order_type", "char(20)", None), // POSITIONING, CLOSING
|
||||
("status", "char(20)", None), // LISTUP, FILLED, PARTIALLY_FILLED
|
||||
("symbol", "char(20)", None),
|
||||
("order_id", "bigint", Some("UN")),
|
||||
("position", "char(20)", None),
|
||||
("registered_server_epoch", "bigint", None),
|
||||
("transact_time", "bigint", None),
|
||||
("close_time", "bigint", None),
|
||||
("used_usdt", "decimal(16,8)", None),
|
||||
("expected_get_usdt", "double", None),
|
||||
("pnl", "double", None),
|
||||
("entry_price", "decimal(16,8)", None),
|
||||
("current_price", "decimal(16,8)", None),
|
||||
("target_percent", "double", None),
|
||||
("stoploss_percent", "double", None),
|
||||
("base_qty_ordered", "decimal(16,8)", None),
|
||||
("pure_profit_percent", "double", None),
|
||||
("minimum_profit_percent", "double", None),
|
||||
("maximum_profit_percent", "double", None),
|
||||
];
|
||||
let table_condition = None;
|
||||
|
||||
if exists_result == false {
|
||||
let mut result = new_table(&table_name, &initial_table, &table_condition).await;
|
||||
if result.is_err() {
|
||||
loop {
|
||||
result = new_table(&table_name, &initial_table, &table_condition).await;
|
||||
if result.is_ok() {
|
||||
break;
|
||||
}
|
||||
sleep(Duration::from_millis(10)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("Ok");
|
||||
}
|
||||
|
||||
{
|
||||
// buy_ordered_coin_list
|
||||
print!("table 'buy_ordered_coin_list'...");
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ pub const SECRET_KEY_TESTNET: &str =
|
|||
// URL
|
||||
pub const URL_TEST: &str = "https://testnet.binance.vision";
|
||||
pub const URL: &str = "https://api1.binance.com";
|
||||
pub const FUTURES_URL_TEST: &str = "https://testnet.binancefuture.com";
|
||||
pub const FUTURES_URL: &str = "https://fapi.binance.com";
|
||||
|
||||
// Select program mode
|
||||
#[derive(PartialEq)]
|
||||
|
|
@ -35,3 +37,4 @@ pub mod signal_association;
|
|||
pub mod strategy_team;
|
||||
pub mod time_checking_team;
|
||||
pub mod value_estimation_team;
|
||||
pub mod future;
|
||||
|
|
|
|||
246
src/main.rs
246
src/main.rs
|
|
@ -14,6 +14,7 @@ use reqwest::{Client, ClientBuilder};
|
|||
use rust_decimal::Decimal;
|
||||
use simple_logger::set_up_color_terminal;
|
||||
use sqlx::{mysql::*, Connection, Executor, FromRow, Row};
|
||||
use tradingbot::future::{FuturesExchangeInfo, FuturesTradeFee};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::{
|
||||
io::{self, Write},
|
||||
|
|
@ -24,6 +25,24 @@ use tradingbot::{RunningMode::*, *};
|
|||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
// future last price data
|
||||
let mut future_price_map: HashMap<String, f64> = HashMap::new(); // <symbol, price>
|
||||
let (tx_future_price_map, mut rx_future_price_map) = watch::channel(future_price_map);
|
||||
let mut rx2_future_price_map = rx_future_price_map.clone();
|
||||
|
||||
let mut futures_exchange_info_map: HashMap<String, FuturesExchangeInfo> = HashMap::new();
|
||||
let (tx_futures_exchange_info, mut rx_futures_exchange_info) = watch::channel(futures_exchange_info_map);
|
||||
let mut rx2_futures_exchange_info = rx_futures_exchange_info.clone();
|
||||
let mut rx3_futures_exchange_info = rx_futures_exchange_info.clone();
|
||||
let mut rx4_futures_exchange_info = rx_futures_exchange_info.clone();
|
||||
|
||||
let mut futures_trade_fee = FuturesTradeFee::new();
|
||||
let (tx_futures_trade_fee, mut rx_futures_trade_fee) = watch::channel(futures_trade_fee);
|
||||
let mut rx2_futures_trade_fee = rx_futures_trade_fee.clone();
|
||||
let mut rx3_futures_trade_fee = rx_futures_trade_fee.clone();
|
||||
|
||||
|
||||
// parse argument and set program preference
|
||||
program_setting();
|
||||
|
||||
|
|
@ -116,6 +135,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
let mut rx5_price_map = rx_price_map.clone();
|
||||
let mut price_map_capacity = rx_price_map.clone();
|
||||
|
||||
|
||||
|
||||
// candle data from endpoint and channels
|
||||
let mut candle_1m_map: HashMap<String, Vec<CandleData>> = HashMap::new(); // <symbol, Vec<CandleData>>
|
||||
let (tx_candle_1m_map, mut rx_candle_1m_map) = watch::channel(candle_1m_map);
|
||||
|
|
@ -921,8 +942,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
all_data.rt_price_1w_vec = rx3_rt_price_1w_map.borrow().clone();
|
||||
all_data.rt_price_1mon_vec = rx3_rt_price_1mon_map.borrow().clone();
|
||||
|
||||
// future exchange info
|
||||
let futures_exchange_info = rx3_futures_exchange_info.borrow().clone();
|
||||
let result =
|
||||
strategy_team::strategy_manager::execute_list_up_for_buy(&all_data).await;
|
||||
strategy_team::strategy_manager::execute_list_up_for_buy(&all_data, &futures_exchange_info).await;
|
||||
|
||||
match result {
|
||||
Ok(T) => {
|
||||
|
|
@ -999,10 +1022,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
all_data.rt_price_1d_vec = rx4_rt_price_1d_map.borrow().clone();
|
||||
all_data.rt_price_1w_vec = rx4_rt_price_1w_map.borrow().clone();
|
||||
all_data.rt_price_1mon_vec = rx4_rt_price_1mon_map.borrow().clone();
|
||||
|
||||
let futures_exchange_info = rx4_futures_exchange_info.borrow().clone();
|
||||
let result = strategy_team::strategy_manager::execute_list_up_for_sell(
|
||||
&all_data,
|
||||
&exchange_info_map,
|
||||
&futures_exchange_info,
|
||||
&trade_fee_map,
|
||||
)
|
||||
.await;
|
||||
|
|
@ -1354,6 +1378,224 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Futures Section
|
||||
// Task#XX: get future last price
|
||||
tokio::task::spawn(async move {
|
||||
sleep(Duration::from_secs(20)).await;
|
||||
let mut elapsed_time = 0;
|
||||
loop {
|
||||
let instant = Instant::now();
|
||||
let client = ClientBuilder::new()
|
||||
.timeout(tokio::time::Duration::from_millis(1000))
|
||||
.build()
|
||||
.unwrap();
|
||||
let mut future_price_map_temp: HashMap<String, f64> = HashMap::new();
|
||||
future::order::get_last_price(&client, &mut future_price_map_temp).await;
|
||||
|
||||
if future_price_map_temp.len() != 0 {
|
||||
tx_future_price_map.send_modify(|vec| *vec = future_price_map_temp);
|
||||
}
|
||||
}
|
||||
if 333 > elapsed_time {
|
||||
sleep(Duration::from_millis((333 - elapsed_time) as u64)).await;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Task#XX: get future exchange information
|
||||
tokio::task::spawn(async move {
|
||||
sleep(Duration::from_secs(20)).await;
|
||||
let client = ClientBuilder::new()
|
||||
.timeout(Duration::from_millis(1000))
|
||||
.build()
|
||||
.unwrap();
|
||||
let mut elapsed_time = 0;
|
||||
loop {
|
||||
let instant = Instant::now();
|
||||
let mut futures_exchange_info_map_temp: HashMap<String, FuturesExchangeInfo> = HashMap::new();
|
||||
let result = future::order::request_future_exchange_infomation(
|
||||
&client,
|
||||
&mut futures_exchange_info_map_temp,
|
||||
)
|
||||
.await;
|
||||
|
||||
if tx_futures_exchange_info.is_closed() {
|
||||
log::error!("tx_futures_exchange_info has been closed!");
|
||||
} else {
|
||||
if futures_exchange_info_map_temp.len() != 0 {
|
||||
tx_futures_exchange_info.send_modify(|map| *map = futures_exchange_info_map_temp);
|
||||
}
|
||||
}
|
||||
|
||||
// send Task#0 a message to notify running on
|
||||
match result {
|
||||
Ok(T) => {
|
||||
|
||||
}
|
||||
Err(E) => {}
|
||||
}
|
||||
|
||||
// sleep as much as the loop recurs per 10 second if all operation finished within 10 second.
|
||||
elapsed_time = instant.elapsed().as_millis();
|
||||
|
||||
if 10000 > elapsed_time {
|
||||
sleep(Duration::from_millis((10000 - elapsed_time) as u64)).await;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Task#XX: get futures trade fee and available balance(USDT)
|
||||
tokio::task::spawn(async move {
|
||||
sleep(Duration::from_secs(3)).await;
|
||||
let mut elapsed_time = 0;
|
||||
loop {
|
||||
let instant = Instant::now();
|
||||
let client = ClientBuilder::new()
|
||||
.timeout(tokio::time::Duration::from_millis(1000))
|
||||
.build()
|
||||
.unwrap();
|
||||
let mut futures_trade_fee_temp = FuturesTradeFee::new();
|
||||
future::table_mgmt::get_tradefee_balance(&mut futures_trade_fee_temp, &client).await;
|
||||
tx_futures_trade_fee.send_modify(|vec| *vec = futures_trade_fee_temp);
|
||||
|
||||
}
|
||||
|
||||
if 2000 > elapsed_time {
|
||||
sleep(Duration::from_millis((2000 - elapsed_time) as u64)).await;
|
||||
}
|
||||
});
|
||||
|
||||
// Task#XX: update price of filled positions
|
||||
tokio::task::spawn(async move {
|
||||
sleep(Duration::from_secs(20)).await;
|
||||
let mut elapsed_time = 0;
|
||||
loop {
|
||||
let instant = Instant::now();
|
||||
let coin_price_map = rx_future_price_map.borrow().clone();
|
||||
let futures_exchange_info_map = rx_futures_exchange_info.borrow().clone();
|
||||
let future_trade_fee = rx_futures_trade_fee.borrow().clone();
|
||||
|
||||
let result = future::table_mgmt::update_price_of_filled_positions(
|
||||
&coin_price_map,
|
||||
&futures_exchange_info_map,
|
||||
&future_trade_fee,
|
||||
)
|
||||
.await;
|
||||
|
||||
// send Task#0 a message to notify running on
|
||||
match result {
|
||||
Ok(T) => {
|
||||
}
|
||||
Err(E) => {}
|
||||
}
|
||||
|
||||
// sleep as much as the loop recurs per 1 second if all operation finished within 1 second.
|
||||
elapsed_time = instant.elapsed().as_millis();
|
||||
|
||||
if 100 > elapsed_time {
|
||||
sleep(Duration::from_millis((100 - elapsed_time) as u64)).await;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Task#XX: monitoring ordered positions
|
||||
tokio::task::spawn(async move {
|
||||
sleep(Duration::from_secs(20)).await;
|
||||
let mut elapsed_time = 0;
|
||||
loop {
|
||||
let instant = Instant::now();
|
||||
let client = ClientBuilder::new()
|
||||
.timeout(tokio::time::Duration::from_millis(1000))
|
||||
.build()
|
||||
.unwrap();
|
||||
let future_trade_fee = rx2_futures_trade_fee.borrow().clone();
|
||||
|
||||
let result = future::order::monitoring_unfilled_order(
|
||||
&client,
|
||||
&future_trade_fee,
|
||||
)
|
||||
.await;
|
||||
|
||||
// send Task#0 a message to notify running on
|
||||
match result {
|
||||
Ok(T) => {
|
||||
}
|
||||
Err(E) => {}
|
||||
}
|
||||
|
||||
// sleep as much as the loop recurs per 1 second if all operation finished within 1 second.
|
||||
elapsed_time = instant.elapsed().as_millis();
|
||||
|
||||
if 100 > elapsed_time {
|
||||
sleep(Duration::from_millis((100 - elapsed_time) as u64)).await;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Task#XX: monitoring ordered positions
|
||||
tokio::task::spawn(async move {
|
||||
sleep(Duration::from_secs(20)).await;
|
||||
let mut elapsed_time = 0;
|
||||
loop {
|
||||
let instant = Instant::now();
|
||||
let coin_price_map = rx2_future_price_map.borrow().clone();
|
||||
let futures_exchange_info_map = rx2_futures_exchange_info.borrow().clone();
|
||||
let future_trade_fee = rx3_futures_trade_fee.borrow().clone();
|
||||
|
||||
let result = future::order::entry_position(
|
||||
&coin_price_map,
|
||||
&futures_exchange_info_map,
|
||||
&future_trade_fee,
|
||||
)
|
||||
.await;
|
||||
|
||||
// send Task#0 a message to notify running on
|
||||
match result {
|
||||
Ok(T) => {
|
||||
}
|
||||
Err(E) => {}
|
||||
}
|
||||
|
||||
// sleep as much as the loop recurs per 1 second if all operation finished within 1 second.
|
||||
elapsed_time = instant.elapsed().as_millis();
|
||||
|
||||
if 50 > elapsed_time {
|
||||
sleep(Duration::from_millis((50 - elapsed_time) as u64)).await;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Task#XX: move closed positions
|
||||
tokio::task::spawn(async move {
|
||||
sleep(Duration::from_secs(20)).await;
|
||||
let mut elapsed_time = 0;
|
||||
loop {
|
||||
let instant = Instant::now();
|
||||
|
||||
let result = future::table_mgmt::move_closed_positions().await;
|
||||
|
||||
// send Task#0 a message to notify running on
|
||||
// match result {
|
||||
// Ok(T) => {
|
||||
// }
|
||||
// Err(E) => {}
|
||||
// }
|
||||
|
||||
// sleep as much as the loop recurs per 1 second if all operation finished within 1 second.
|
||||
elapsed_time = instant.elapsed().as_millis();
|
||||
|
||||
if 100 > elapsed_time {
|
||||
sleep(Duration::from_millis((100 - elapsed_time) as u64)).await;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
loop {}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -1,505 +0,0 @@
|
|||
use crate::value_estimation_team::indicators::wiliams_percent_r;
|
||||
|
||||
use super::{
|
||||
adx, dec, decimal_add, decimal_div, decimal_mul, decimal_sub, dema, duplicate_filter, ema,
|
||||
ema_macd, exists_record, get_server_epoch, heatmap_volume,
|
||||
insert_pre_suggested_coins, limit_order_sell, remove_keys, rsi, select_filled_buy_orders,
|
||||
stoch_rsi, supertrend, tema, try_join_all, update_record3, wiliams_percent_r, AdxData, AllData,
|
||||
Arc, BollingerBandData, Client, ClientBuilder, Decimal, DemaData, EmaData, ExchangeInfo,
|
||||
FilteredDataValue, HashMap, HashSet, HeatMapLevel, HeatmapVolumeData, MacdData, Mutex,
|
||||
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SuperTrendArea, SuperTrendSignal,
|
||||
SupertrendData, TemaData, ToPrimitive, TradeFee, WiliamsPercentR
|
||||
};
|
||||
|
||||
// BUY conditions
|
||||
pub async fn list_up_for_buy(
|
||||
alldata: &AllData,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
// print rt_price for debugging
|
||||
// let a = alldata.rt_price_30m_vec.iter().position(|a| a.0 == "BTCUSDT");
|
||||
// println!("BTCUSDT: {:?}", alldata.rt_price_30m_vec[a.unwrap()].1.last().unwrap());
|
||||
|
||||
// basic filtering: filtering valid trade pair
|
||||
let mut filtered_data: HashMap<String, FilteredDataValue> = HashMap::new();
|
||||
for symbol in &alldata.valid_symbol_vec {
|
||||
filtered_data.insert(symbol.clone(), FilteredDataValue::new());
|
||||
}
|
||||
|
||||
// current Tema(3) > current Tema(10)
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// let tema_3 = tema(3, &alldata.rt_price_1d_vec, &filtered_data).await?;
|
||||
// let tema_10 = tema(10, &alldata.rt_price_1d_vec, &filtered_data).await?;
|
||||
// let server_epoch = get_server_epoch().await;
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// if let (Some(tema5_vec), Some(tema10_vec)) = (tema_3.get(symbol), tema_10.get(symbol)) {
|
||||
// if tema5_vec.len() > 2
|
||||
// && tema10_vec.len() > 2
|
||||
// && tema5_vec.last().unwrap().close_time == tema10_vec.last().unwrap().close_time
|
||||
// && tema5_vec.last().unwrap().close_time > server_epoch
|
||||
// && tema10_vec.last().unwrap().close_time > server_epoch
|
||||
// {
|
||||
// if tema5_vec.last().unwrap().tema_value > tema10_vec.last().unwrap().tema_value {
|
||||
// } else {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// } else {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// } else {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// Wiliams %R(200) > -20.0
|
||||
// Wiliams %R(30) > -20.0
|
||||
let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
let mut wprs200 = wiliams_percent_r(200, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
let mut wprs30 = wiliams_percent_r(30, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
let server_epoch = get_server_epoch().await;
|
||||
for (symbol, values) in &mut filtered_data {
|
||||
if let (Some(wpr200_vec), Some(wpr30_vec)) = (wprs200.get(symbol), wprs30.get(symbol)) {
|
||||
if wpr200_vec.len() > 15
|
||||
&& wpr30_vec.len() > 15
|
||||
&& wpr200_vec.last().unwrap().close_time > server_epoch
|
||||
&& wpr200_vec.last().unwrap().r_value > -20.0
|
||||
&& wpr30_vec.last().unwrap().close_time > server_epoch
|
||||
&& wpr30_vec.last().unwrap().r_value > -20.0
|
||||
{
|
||||
} else {
|
||||
keys_to_remove.insert(symbol.clone());
|
||||
}
|
||||
} else {
|
||||
keys_to_remove.insert(symbol.clone());
|
||||
}
|
||||
}
|
||||
remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// current Tema(300) > current Tema(200) > current Tema(100)
|
||||
let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
let tema_100 = tema(100, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
let tema_200 = tema(200, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
let tema_300 = tema(300, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
let server_epoch = get_server_epoch().await;
|
||||
for (symbol, values) in &mut filtered_data {
|
||||
if let (Some(tema100_vec), Some(tema200_vec), Some(tema300_vec)) = (
|
||||
tema_100.get(symbol),
|
||||
tema_200.get(symbol),
|
||||
tema_300.get(symbol),
|
||||
) {
|
||||
if (tema100_vec.len() > 15 && tema200_vec.len() > 15 && tema300_vec.len() > 15)
|
||||
&& tema100_vec.last().unwrap().close_time == tema200_vec.last().unwrap().close_time
|
||||
&& tema200_vec.last().unwrap().close_time == tema300_vec.last().unwrap().close_time
|
||||
&& tema100_vec.last().unwrap().close_time > server_epoch
|
||||
{
|
||||
if tema100_vec.last().unwrap().tema_value > tema300_vec.last().unwrap().tema_value
|
||||
&& tema200_vec.last().unwrap().tema_value
|
||||
< tema300_vec.last().unwrap().tema_value
|
||||
&& tema200_vec.last().unwrap().tema_value
|
||||
> tema100_vec.last().unwrap().tema_value
|
||||
{
|
||||
} else {
|
||||
keys_to_remove.insert(symbol.clone());
|
||||
}
|
||||
} else {
|
||||
keys_to_remove.insert(symbol.clone());
|
||||
}
|
||||
} else {
|
||||
keys_to_remove.insert(symbol.clone());
|
||||
}
|
||||
}
|
||||
remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// supertrend(ATR period 10, multiplier: 3.0, 30m close price)
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// let server_epoch = get_server_epoch().await;
|
||||
// let supertrend_30m_map =
|
||||
// supertrend(10, 3.0, true, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// if let (Some(supertrend_vec), Some(rt_price_vec)) = (
|
||||
// supertrend_30m_map.get(symbol),
|
||||
// alldata.rt_price_30m_vec.get(symbol),
|
||||
// ) {
|
||||
// if supertrend_vec.last().unwrap().close_time == rt_price_vec.last().unwrap().close_time
|
||||
// && rt_price_vec.last().unwrap().close_time > server_epoch
|
||||
// {
|
||||
// // input stoploss, target_price
|
||||
// let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(
|
||||
// supertrend_vec.last().unwrap().band_value,
|
||||
// )
|
||||
// .unwrap();
|
||||
// let open_price: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(
|
||||
// rt_price_vec.last().unwrap().open_price,
|
||||
// )
|
||||
// .unwrap();
|
||||
// let current_price: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(
|
||||
// rt_price_vec.last().unwrap().close_price,
|
||||
// )
|
||||
// .unwrap();
|
||||
// if supertrend_vec.last().unwrap().area == SuperTrendArea::UP
|
||||
// && band_value < current_price
|
||||
// && band_value < open_price
|
||||
// {
|
||||
// values.current_price = current_price;
|
||||
// values.closetime = rt_price_vec.last().unwrap().close_time;
|
||||
// values.stoploss = band_value;
|
||||
// values.target_price = decimal_add(
|
||||
// decimal_mul(decimal_sub(current_price, values.stoploss), dec!(10.0)),
|
||||
// current_price,
|
||||
// );
|
||||
// } else if supertrend_vec.last().unwrap().area == SuperTrendArea::DOWN
|
||||
// && band_value > current_price
|
||||
// && band_value > open_price
|
||||
// {
|
||||
// values.current_price = current_price;
|
||||
// values.closetime = rt_price_vec.last().unwrap().close_time;
|
||||
// values.stoploss = decimal_sub(open_price, decimal_sub(band_value, open_price));
|
||||
// values.target_price = decimal_add(
|
||||
// decimal_mul(decimal_sub(open_price, values.stoploss), dec!(10.0)),
|
||||
// current_price,
|
||||
// );
|
||||
// } else {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// } else {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// } else {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// current ADX(15, 15) < 25
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// let adx_vec = adx(15, 15, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// if let Some(adx_vec) = adx_vec.get(symbol) {
|
||||
// if let Some(last_idx) = adx_vec.iter().position(|elem| elem.close_time == values.closetime) {
|
||||
// if adx_vec.len() > 10 &&
|
||||
// adx_vec[last_idx].adx < 25.0 {
|
||||
// } else {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// } else {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// } else {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// StochRSI (RSI_len: 200, StochRSI_len: 200, K: 3, D: 3) K_current < 70, K_prev < 70, K_prev_1 < 70
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// let stoch_rsis = stoch_rsi(200, 200, 3, 3, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// if stoch_rsis.contains_key(symbol) {
|
||||
// let stoch_rsi_vec = stoch_rsis.get(symbol).unwrap();
|
||||
// let search_result = stoch_rsi_vec
|
||||
// .iter()
|
||||
// .position(|x| x.close_time == values.closetime);
|
||||
// if stoch_rsi_vec.len() > 10
|
||||
// && search_result.is_some_and(|a| {
|
||||
// stoch_rsi_vec[a].k < 70.0
|
||||
// && stoch_rsi_vec[a - 1].k < 70.0
|
||||
// && stoch_rsi_vec[a - 2].k < 70.0
|
||||
// })
|
||||
// {
|
||||
// } else {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// } else {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// Heatmap volume: filtering close price with Extra High is over the previous candle from 30 previous candles
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// let heatmap_volumes = heatmap_volume(
|
||||
// 30,
|
||||
// 30,
|
||||
// 4.0,
|
||||
// 2.5,
|
||||
// 1.0,
|
||||
// -0.5,
|
||||
// &filtered_data,
|
||||
// &alldata.rt_price_30m_vec,
|
||||
// )
|
||||
// .await?;
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// if let Some(heatmap_volume_vec) = heatmap_volumes.get(symbol) {
|
||||
// if heatmap_volume_vec.len() > 50 {
|
||||
// let heatmap_volume_trunc = heatmap_volume_vec
|
||||
// .get(heatmap_volume_vec.len() - 31..heatmap_volume_vec.len() - 1)
|
||||
// .unwrap();
|
||||
// let windows = heatmap_volume_trunc.windows(2);
|
||||
// for slice in windows {
|
||||
// if slice[1].heatmap_level == HeatMapLevel::ExtraHigh {
|
||||
// if let (prev_candle_idx, current_candle_idx) = (
|
||||
// (&alldata
|
||||
// .rt_price_30m_vec
|
||||
// .get(symbol)
|
||||
// .unwrap()
|
||||
// .iter()
|
||||
// .position(|x| x.close_time == slice[0].close_time))
|
||||
// .unwrap(),
|
||||
// (&alldata
|
||||
// .rt_price_30m_vec
|
||||
// .get(symbol)
|
||||
// .unwrap()
|
||||
// .iter()
|
||||
// .position(|x| x.close_time == slice[1].close_time))
|
||||
// .unwrap(),
|
||||
// ) {
|
||||
// let prev_candle =
|
||||
// &alldata.rt_price_30m_vec.get(symbol).unwrap()[prev_candle_idx];
|
||||
// let current_candle =
|
||||
// &alldata.rt_price_30m_vec.get(symbol).unwrap()[current_candle_idx];
|
||||
// if current_candle.close_price < prev_candle.close_price
|
||||
// || current_candle.close_price < prev_candle.open_price
|
||||
// || current_candle.close_price < prev_candle.high_price
|
||||
// || current_candle.close_price < prev_candle.low_price
|
||||
// {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// limit buy price: 0.25 * abs(이전 5 개 중 최대값 제거 한 opclo 값 평균 - 현재 open 값) + 현재 open 값 > current_price
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// let server_epoch = get_server_epoch().await;
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// if let Some(rt_price_vec) = alldata.rt_price_30m_vec.get(symbol) {
|
||||
// if rt_price_vec.last().unwrap().close_time > server_epoch && rt_price_vec.len() >= 6 {
|
||||
// let mut opclo_vec: Vec<f64> = Vec::new();
|
||||
// opclo_vec.push(rt_price_vec[rt_price_vec.len() - 2].opclo_price);
|
||||
// opclo_vec.push(rt_price_vec[rt_price_vec.len() - 3].opclo_price);
|
||||
// opclo_vec.push(rt_price_vec[rt_price_vec.len() - 4].opclo_price);
|
||||
// opclo_vec.push(rt_price_vec[rt_price_vec.len() - 5].opclo_price);
|
||||
// opclo_vec.push(rt_price_vec[rt_price_vec.len() - 6].opclo_price);
|
||||
// let max_idx = opclo_vec.iter().position(|&x| {
|
||||
// x == *opclo_vec
|
||||
// .iter()
|
||||
// .max_by(|&a, &b| a.partial_cmp(b).unwrap())
|
||||
// .unwrap()
|
||||
// });
|
||||
// opclo_vec.remove(max_idx.unwrap());
|
||||
|
||||
// let mut mean = 0.0;
|
||||
// for element in &opclo_vec {
|
||||
// mean += element;
|
||||
// }
|
||||
// mean /= opclo_vec.len() as f64;
|
||||
// let current_price = rt_price_vec.last().unwrap().close_price;
|
||||
// let difference = (mean - rt_price_vec.last().unwrap().open_price).abs();
|
||||
|
||||
// if current_price > rt_price_vec.last().unwrap().open_price + (0.5 * difference) {
|
||||
// } else {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// } else {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// } else {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
if filtered_data.keys().len() != 0 {
|
||||
let date_now = chrono::Local::now().to_rfc2822();
|
||||
println!("{} future coins: {:?}", date_now, filtered_data.keys());
|
||||
}
|
||||
|
||||
// let final_filtered_data = duplicate_filter(8, &filtered_data).await?;
|
||||
// insert_pre_suggested_coins(8, false, &final_filtered_data, &alldata).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn list_up_for_sell(
|
||||
all_data: &AllData,
|
||||
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(8).await?;
|
||||
|
||||
if !filled_buy_orders.is_empty() {
|
||||
let client = ClientBuilder::new()
|
||||
.timeout(tokio::time::Duration::from_millis(5000))
|
||||
.build()
|
||||
.unwrap();
|
||||
let mut supertrend_vec: Vec<SupertrendData> = Vec::new();
|
||||
let server_epoch = get_server_epoch().await;
|
||||
let mut filtered_symbols: HashMap<String, FilteredDataValue> = HashMap::new();
|
||||
for element in &filled_buy_orders {
|
||||
filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new());
|
||||
}
|
||||
let supertrend_30m =
|
||||
supertrend(10, 2.0, true, &all_data.rt_price_30m_vec, &filtered_symbols).await?;
|
||||
let tema_300 = tema(300, &all_data.rt_price_30m_vec, &filtered_symbols).await?;
|
||||
let tema_200 = tema(200, &all_data.rt_price_30m_vec, &filtered_symbols).await?;
|
||||
let tema_100 = tema(100, &all_data.rt_price_30m_vec, &filtered_symbols).await?;
|
||||
for element in filled_buy_orders {
|
||||
let mut is_sell = false;
|
||||
let mut is_overturned = false;
|
||||
if element.used_usdt >= dec!(10.0) {
|
||||
if let (Some(tema300_vec), Some(tema200_vec), Some(tema100_vec)) = (
|
||||
tema_300.get(&element.symbol),
|
||||
tema_200.get(&element.symbol),
|
||||
tema_100.get(&element.symbol),
|
||||
) {
|
||||
if tema200_vec.len() > 2
|
||||
&& tema100_vec.len() > 2
|
||||
&& tema300_vec.len() > 2
|
||||
&& tema200_vec.last().unwrap().close_time
|
||||
== tema100_vec.last().unwrap().close_time
|
||||
&& tema300_vec.last().unwrap().close_time
|
||||
== tema100_vec.last().unwrap().close_time
|
||||
&& tema300_vec.last().unwrap().close_time > server_epoch
|
||||
&& tema200_vec.last().unwrap().close_time > server_epoch
|
||||
&& tema100_vec.last().unwrap().close_time > server_epoch
|
||||
&& ((tema200_vec.last().unwrap().tema_value
|
||||
> tema100_vec.last().unwrap().tema_value
|
||||
&& tema200_vec[tema200_vec.len() - 2].tema_value
|
||||
< tema100_vec[tema100_vec.len() - 2].tema_value)
|
||||
|| (tema200_vec.last().unwrap().tema_value
|
||||
> tema300_vec.last().unwrap().tema_value
|
||||
&& tema200_vec[tema200_vec.len() - 2].tema_value
|
||||
< tema300_vec[tema300_vec.len() - 2].tema_value))
|
||||
{
|
||||
is_overturned = true;
|
||||
}
|
||||
}
|
||||
|
||||
if let (Some(exchange_info), Some(tradefee), Some(supertrend_vec)) = (
|
||||
exchange_info_map.get(&element.symbol),
|
||||
trade_fee_map.get(&element.symbol),
|
||||
supertrend_30m.get(&element.symbol),
|
||||
) {
|
||||
// update stoploss
|
||||
let band_value: Decimal = rust_decimal::prelude::FromPrimitive::from_f64(
|
||||
supertrend_vec.last().unwrap().band_value,
|
||||
)
|
||||
.unwrap();
|
||||
if supertrend_vec.last().unwrap().area == SuperTrendArea::UP
|
||||
&& band_value > element.stoploss
|
||||
{
|
||||
let update_table_name = String::from("buy_ordered_coin_list");
|
||||
let update_value = vec![(String::from("stoploss"), band_value.to_string())];
|
||||
let update_condition = vec![(String::from("id"), element.id.to_string())];
|
||||
update_record3(&update_table_name, &update_value, &update_condition)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let lot_step_size = exchange_info.stepsize;
|
||||
let quote_commission_precision = exchange_info.quote_commission_precision;
|
||||
|
||||
// TODO: BNB 코인이 있으면
|
||||
// let base_qty_to_be_ordered =
|
||||
// element.base_qty_ordered.round_dp_with_strategy(
|
||||
// lot_step_size.normalize().scale(),
|
||||
// RoundingStrategy::ToZero,
|
||||
// );
|
||||
// TODO: BNB 코인이 없으면
|
||||
let base_qty_to_be_ordered =
|
||||
element.base_qty_fee_adjusted.round_dp_with_strategy(
|
||||
lot_step_size.normalize().scale(),
|
||||
RoundingStrategy::ToZero,
|
||||
);
|
||||
let target_profit_percent = decimal_mul(
|
||||
decimal_div(
|
||||
decimal_sub(element.target_price, element.buy_price),
|
||||
element.buy_price,
|
||||
),
|
||||
dec!(100),
|
||||
)
|
||||
.to_f64()
|
||||
.unwrap();
|
||||
if !element.current_price.is_zero() {
|
||||
if element.current_price <= element.stoploss {
|
||||
is_sell = true;
|
||||
} else if element.current_price >= element.target_price {
|
||||
is_sell = true;
|
||||
} else if server_epoch - element.transact_time >= (1_800_000) * 5
|
||||
&& is_overturned == true
|
||||
{
|
||||
is_sell = true;
|
||||
}
|
||||
|
||||
let minimum_candles = 5;
|
||||
let maximum_candles = 240;
|
||||
for count_candles in minimum_candles..=maximum_candles {
|
||||
if count_candles < maximum_candles
|
||||
&& server_epoch - element.transact_time
|
||||
> (1_800_000) * count_candles
|
||||
&& (target_profit_percent != 0.0
|
||||
&& target_profit_percent.is_sign_positive()
|
||||
&& target_profit_percent
|
||||
* ((maximum_candles - count_candles) as f64
|
||||
/ (maximum_candles - minimum_candles + 1) as f64)
|
||||
<= element.pure_profit_percent)
|
||||
{
|
||||
is_sell = true;
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if server_epoch - element.transact_time >= (1_800_000) * maximum_candles {
|
||||
// time up selling
|
||||
is_sell = true;
|
||||
}
|
||||
|
||||
// TODO: sell_count가 1일 때 적용하기
|
||||
// else if (supertrend_vec
|
||||
// .last()
|
||||
// .unwrap()
|
||||
// .signal
|
||||
// .as_ref()
|
||||
// .is_some_and(|x| x.contains("SELL"))
|
||||
// || supertrend_vec.last().unwrap().area.contains("DOWN"))
|
||||
// && (supertrend_vec.last().unwrap().close_time > element.close_time)
|
||||
// {
|
||||
// println!(
|
||||
// "SELL signal selling {} {:.2}",
|
||||
// element.symbol, element.pure_profit_percent
|
||||
// );
|
||||
// limit_order_sell(
|
||||
// &element,
|
||||
// element.current_price,
|
||||
// base_qty_to_be_ordered,
|
||||
// &client,
|
||||
// &exchange_info_vec,
|
||||
// &trade_fee_vec,
|
||||
// )
|
||||
// .await;
|
||||
// }
|
||||
if is_sell == true {
|
||||
limit_order_sell(
|
||||
&element,
|
||||
element.current_price,
|
||||
base_qty_to_be_ordered,
|
||||
&client,
|
||||
&exchange_info_map,
|
||||
&trade_fee_map,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
624
src/strategy_team/future_strategy_long.rs
Normal file
624
src/strategy_team/future_strategy_long.rs
Normal file
|
|
@ -0,0 +1,624 @@
|
|||
use rust_decimal::prelude::Signed;
|
||||
|
||||
use crate::value_estimation_team::{datapoints::price_data::CandleType, indicators::wiliams_percent_r};
|
||||
|
||||
use super::{
|
||||
adx, dec, decimal_add, decimal_div, decimal_mul, decimal_sub, dema, duplicate_filter, ema, ema_open,
|
||||
ema_macd, exists_record, get_server_epoch, heatmap_volume,
|
||||
insert_pre_suggested_coins, limit_order_sell, remove_keys, rsi, select_filled_buy_orders,
|
||||
stoch_rsi, supertrend, tema, try_join_all, update_record3, wiliams_percent_r, AdxData, AllData,
|
||||
Arc, BollingerBandData, Client, ClientBuilder, Decimal, DemaData, EmaData, ExchangeInfo,
|
||||
FilteredDataValue, HashMap, HashSet, HeatMapLevel, HeatmapVolumeData, MacdData, Mutex,
|
||||
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SuperTrendArea, SuperTrendSignal,
|
||||
SupertrendData, TemaData, ToPrimitive, TradeFee, WiliamsPercentR, future_duplicate_filter, insert_future_coins, get_current_price_decimal,
|
||||
sma, sma_open, LrData, linear_regression, bollingerband
|
||||
};
|
||||
|
||||
use crate::future::{Position, FuturesExchangeInfo};
|
||||
use crate::future::table_mgmt::select_long_filled_positions;
|
||||
use crate::future::order::{limit_order_close, TimeInForce};
|
||||
|
||||
// BUY conditions
|
||||
pub async fn list_up_for_buy(
|
||||
alldata: &AllData,
|
||||
future_exchange_info_map: &HashMap<String, FuturesExchangeInfo>
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
// print rt_price for debugging
|
||||
// let a = alldata.rt_price_30m_vec.iter().position(|a| a.0 == "BTCUSDT");
|
||||
// println!("BTCUSDT: {:?}", alldata.rt_price_30m_vec[a.unwrap()].1.last().unwrap());
|
||||
|
||||
// basic filtering: filtering valid trade pair
|
||||
let mut filtered_data: HashMap<String, FilteredDataValue> = HashMap::new();
|
||||
for symbol in &alldata.valid_symbol_vec {
|
||||
filtered_data.insert(symbol.clone(), FilteredDataValue::new());
|
||||
}
|
||||
|
||||
let server_epoch = get_server_epoch().await;
|
||||
|
||||
// current lr > prev lr, current r_squared <= 0.01
|
||||
let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
let lr_map = linear_regression(30, 0, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
let lr50_map = linear_regression(50, 0, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
let sma_map = sma(30, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
for (symbol, values) in &mut filtered_data {
|
||||
let mut do_buy = false;
|
||||
if let (Some(lr_vec), Some(lr50_vec), Some(sma_vec), Some(current_info)) = (lr_map.get(symbol), lr50_map.get(symbol), sma_map.get(symbol), get_current_price_decimal(&symbol, &alldata.rt_price_30m_vec).await) {
|
||||
if lr_vec.len() > 10
|
||||
&& lr50_vec.len() > 10
|
||||
&& sma_vec.len() > 10
|
||||
&& lr_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& sma_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& lr_vec[lr_vec.len()-1].lr_value > lr_vec[lr_vec.len()-2].lr_value
|
||||
&& lr_vec[lr_vec.len()-2].lr_value > lr_vec[lr_vec.len()-3].lr_value
|
||||
&& lr_vec.last().unwrap().r_squared <= 0.02
|
||||
&& lr_vec[lr_vec.len()-1].lr_value > sma_vec[sma_vec.len()-1].sma_value
|
||||
&& lr_vec[lr_vec.len()-2].lr_value <= sma_vec[sma_vec.len()-2].sma_value
|
||||
{
|
||||
values.closetime = current_info.1;
|
||||
do_buy = true;
|
||||
}
|
||||
}
|
||||
if do_buy == false {
|
||||
keys_to_remove.insert(symbol.clone());
|
||||
}
|
||||
}
|
||||
remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// sma3_close(current) > sma3_open (current), sma3_close(prev) < sma3_open (prev)
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// let ema10_open = ema_open(5, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
// let ema10_close = ema(5, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
// let ema200_close = ema(200, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// let mut do_buy = false;
|
||||
// let price_and_closetime = get_current_price_decimal(&symbol, &alldata.rt_price_30m_vec).await;
|
||||
// if let (Some(ema10_open_vec), Some(ema10_close_vec), Some(ema200_close_vec), Some(current_info))
|
||||
// = (ema10_open.get(symbol), ema10_close.get(symbol), ema200_close.get(symbol), price_and_closetime) {
|
||||
// if ema10_open_vec.len() > 20
|
||||
// && ema10_close_vec.len() > 20
|
||||
// && ema200_close_vec.len() > 20
|
||||
// && ema10_open_vec.last().unwrap().close_time > server_epoch
|
||||
// && ema10_open_vec.last().unwrap().close_time == ema10_close_vec.last().unwrap().close_time
|
||||
// && ema10_open_vec.last().unwrap().close_time == ema200_close_vec.last().unwrap().close_time
|
||||
// {
|
||||
// if ema10_open_vec.last().unwrap().ema_value < ema10_close_vec.last().unwrap().ema_value
|
||||
// && current_info.0.to_f64().is_some_and(|a| a > ema200_close_vec.last().unwrap().ema_value)
|
||||
// {
|
||||
// values.current_price = current_info.0;
|
||||
// values.closetime = current_info.1;
|
||||
// do_buy = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if do_buy == false {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// current ADX(3, 3), current ADX > prev ADX
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// let adx_vec = adx(3, 3, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// let mut do_buy = false;
|
||||
// if let Some(adx_vec) = adx_vec.get(symbol) {
|
||||
// if let Some(last_idx) = adx_vec.iter().position(|elem| elem.close_time == values.closetime) {
|
||||
// if adx_vec.len() > 10
|
||||
// && adx_vec[last_idx].adx > adx_vec[last_idx-1].adx
|
||||
// {
|
||||
// do_buy = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if do_buy == false {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// supertrend(ATR period 30, multiplier: 2.0, 30m close price)
|
||||
let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
let supertrend_1m_map =
|
||||
supertrend(10, 1.5, true, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
for (symbol, values) in &mut filtered_data {
|
||||
let mut do_buy = false;
|
||||
if let Some(supertrend_vec) = supertrend_1m_map.get(symbol)
|
||||
{
|
||||
if supertrend_vec.len() > 10
|
||||
&& supertrend_vec.last().is_some_and(|a| a.close_time > server_epoch)
|
||||
&& supertrend_vec.last().unwrap().area == SuperTrendArea::UP
|
||||
&& supertrend_vec[supertrend_vec.len()-2].area == SuperTrendArea::UP {
|
||||
do_buy = true;
|
||||
}
|
||||
}
|
||||
|
||||
if do_buy == false {
|
||||
keys_to_remove.insert(symbol.clone());
|
||||
}
|
||||
}
|
||||
remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// BollingerBand (len:30, multiplier 3) 5 previous high price < BB upper
|
||||
let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
let bollingerband_map =
|
||||
bollingerband(30, 3.0, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
for (symbol, filtered_data) in &mut filtered_data {
|
||||
let mut do_buy = false;
|
||||
if let (Some(bb_vec), Some(rt_30m_vec)) = (
|
||||
bollingerband_map.get(symbol),
|
||||
alldata.rt_price_30m_vec.get(symbol),
|
||||
) {
|
||||
if rt_30m_vec.len() >= 10
|
||||
&& bb_vec.len() >= 10
|
||||
&& rt_30m_vec.last().unwrap().close_time > server_epoch
|
||||
{
|
||||
if let Ok(bb_search_result) = bb_vec.binary_search_by_key(
|
||||
&rt_30m_vec.last().unwrap().close_time,
|
||||
|BollingerBandData {
|
||||
sma,
|
||||
upperband,
|
||||
lowerband,
|
||||
close_time,
|
||||
}| *close_time,
|
||||
) {
|
||||
if bb_vec[bb_search_result].upperband > rt_30m_vec[rt_30m_vec.len() - 1].high_price
|
||||
&& bb_vec[bb_search_result-1].upperband > rt_30m_vec[rt_30m_vec.len() - 2].high_price
|
||||
&& bb_vec[bb_search_result-2].upperband > rt_30m_vec[rt_30m_vec.len() - 3].high_price
|
||||
&& bb_vec[bb_search_result-3].upperband > rt_30m_vec[rt_30m_vec.len() - 4].high_price
|
||||
&& bb_vec[bb_search_result-4].upperband > rt_30m_vec[rt_30m_vec.len() - 5].high_price
|
||||
{
|
||||
do_buy = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if do_buy == false {
|
||||
keys_to_remove.insert(symbol.clone());
|
||||
}
|
||||
}
|
||||
remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// set target_price and stop_loss
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// let mut do_buy = false;
|
||||
// if let Some(realtime_price_vec) = alldata.rt_price_30m_vec.get(symbol)
|
||||
// {
|
||||
// let element_number = 5;
|
||||
// if let Some(truncated_vec) = realtime_price_vec.get(realtime_price_vec.len()-element_number..) {
|
||||
// let min_price = truncated_vec
|
||||
// .iter()
|
||||
// .min_by(|x, y| x.low_price.partial_cmp(&y.low_price).unwrap())
|
||||
// .unwrap().low_price;
|
||||
// if values.current_price.to_f64().is_some_and(|a| a < min_price) {
|
||||
// let mut stoploss_percent = ((values.current_price.to_f64().unwrap() - min_price) * 100.0) / values.current_price.to_f64().unwrap();
|
||||
// stoploss_percent = (stoploss_percent * 100.0).floor() / 100.0;
|
||||
// values.stoploss = rust_decimal::prelude::FromPrimitive::from_f64(stoploss_percent).unwrap();
|
||||
// let mut target_percent = stoploss_percent.abs() * 1.5;
|
||||
// target_percent = (target_percent * 100.0).floor() / 100.0;
|
||||
// values.target_price = rust_decimal::prelude::FromPrimitive::from_f64(target_percent).unwrap();
|
||||
|
||||
// do_buy = true;
|
||||
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if do_buy == false {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// Wiliams -60 > %R(100)
|
||||
let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
let mut wpr100_map = wiliams_percent_r(100, &alldata.rt_price_1m_vec, &filtered_data).await?;
|
||||
let server_epoch = get_server_epoch().await;
|
||||
for (symbol, values) in &mut filtered_data {
|
||||
let mut do_buy = false;
|
||||
if let Some(wpr100_vec) = wpr100_map.get(symbol) {
|
||||
if wpr100_vec.len() > 15
|
||||
&& wpr100_vec.last().unwrap().close_time > server_epoch
|
||||
&& wpr100_vec.last().unwrap().r_value < -80.0 {
|
||||
do_buy = true;
|
||||
}
|
||||
}
|
||||
if do_buy == false {
|
||||
keys_to_remove.insert(symbol.clone());
|
||||
}
|
||||
}
|
||||
remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// current Tema(15) > current Tema(30)
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// let tema_10 = tema(10, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
// let tema_30 = tema(30, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
// let server_epoch = get_server_epoch().await;
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// let mut do_buy = false;
|
||||
// let price_and_closetime = get_current_price_decimal(&symbol, &alldata.rt_price_1m_vec).await;
|
||||
// if let (Some(tema10_vec), Some(tema30_vec), Some(current_info)) = (tema_10.get(symbol), tema_30.get(symbol), price_and_closetime) {
|
||||
// if tema10_vec.len() > 10
|
||||
// && tema30_vec.len() > 10
|
||||
// && tema10_vec.last().unwrap().close_time == tema30_vec.last().unwrap().close_time
|
||||
// && tema10_vec.last().unwrap().close_time > server_epoch
|
||||
// && tema30_vec.last().unwrap().close_time > server_epoch
|
||||
// {
|
||||
// if tema10_vec[tema10_vec.len()-1].tema_value > tema30_vec[tema30_vec.len()-1].tema_value
|
||||
// && tema10_vec[tema10_vec.len()-2].tema_value > tema30_vec[tema30_vec.len()-2].tema_value
|
||||
// && tema10_vec[tema10_vec.len()-1].tema_value > tema10_vec[tema10_vec.len()-2].tema_value
|
||||
// {
|
||||
// do_buy = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if do_buy == false {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// supertrend(ATR period 10, multiplier: 3.0, 30m close price)
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// let server_epoch = get_server_epoch().await;
|
||||
// let supertrend_1m_map =
|
||||
// supertrend(60, 1.5, true, &alldata.rt_price_1m_vec, &filtered_data).await?;
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// let mut do_buy = false;
|
||||
// let price_and_closetime = get_current_price_decimal(&symbol, &alldata.rt_price_1m_vec).await;
|
||||
// if let (Some(supertrend_vec), Some(current_info)) = (
|
||||
// supertrend_1m_map.get(symbol),
|
||||
// price_and_closetime,
|
||||
// ) {
|
||||
// if supertrend_vec.last().unwrap().close_time > server_epoch
|
||||
// && supertrend_vec.last().unwrap().signal.as_ref().is_some_and(|a| *a == SuperTrendSignal::BUY)
|
||||
// {
|
||||
// values.current_price = current_info.0;
|
||||
// values.closetime = current_info.1;
|
||||
// do_buy = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if do_buy == false {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// Heatmap volume: filtering close price with Extra High is over the previous candle from 30 previous candles
|
||||
let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
let heatmap_volumes = heatmap_volume(
|
||||
30,
|
||||
30,
|
||||
4.0,
|
||||
1.5,
|
||||
0.5,
|
||||
-0.5,
|
||||
&filtered_data,
|
||||
&alldata.rt_price_30m_vec,
|
||||
)
|
||||
.await?;
|
||||
|
||||
for (symbol, values) in &mut filtered_data {
|
||||
let mut do_buy = true;
|
||||
if let (Some(heatmap_volume_vec), Some(rt_price_vec)) = (heatmap_volumes.get(symbol), alldata.rt_price_30m_vec.get(symbol)) {
|
||||
if heatmap_volume_vec.len() > 100
|
||||
&& heatmap_volume_vec.last().unwrap().close_time > server_epoch
|
||||
&& ((heatmap_volume_vec.last().unwrap().heatmap_level == HeatMapLevel::ExtraHigh && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec.last().unwrap().close_time).is_some_and(|x| x.candle_type == CandleType::UP))
|
||||
|| (heatmap_volume_vec[heatmap_volume_vec.len()-2].heatmap_level == HeatMapLevel::ExtraHigh && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec[heatmap_volume_vec.len()-2].close_time).is_some_and(|x| x.candle_type == CandleType::UP))
|
||||
|| (heatmap_volume_vec[heatmap_volume_vec.len()-3].heatmap_level == HeatMapLevel::ExtraHigh && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec[heatmap_volume_vec.len()-3].close_time).is_some_and(|x| x.candle_type == CandleType::UP))
|
||||
|| (heatmap_volume_vec[heatmap_volume_vec.len()-4].heatmap_level == HeatMapLevel::ExtraHigh && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec[heatmap_volume_vec.len()-4].close_time).is_some_and(|x| x.candle_type == CandleType::UP))
|
||||
|| (heatmap_volume_vec[heatmap_volume_vec.len()-5].heatmap_level == HeatMapLevel::ExtraHigh) && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec[heatmap_volume_vec.len()-5].close_time).is_some_and(|x| x.candle_type == CandleType::UP)
|
||||
|| (heatmap_volume_vec[heatmap_volume_vec.len()-6].heatmap_level == HeatMapLevel::ExtraHigh) && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec[heatmap_volume_vec.len()-6].close_time).is_some_and(|x| x.candle_type == CandleType::UP)
|
||||
|| (heatmap_volume_vec[heatmap_volume_vec.len()-7].heatmap_level == HeatMapLevel::ExtraHigh) && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec[heatmap_volume_vec.len()-7].close_time).is_some_and(|x| x.candle_type == CandleType::UP)
|
||||
|| (heatmap_volume_vec[heatmap_volume_vec.len()-8].heatmap_level == HeatMapLevel::ExtraHigh) && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec[heatmap_volume_vec.len()-8].close_time).is_some_and(|x| x.candle_type == CandleType::UP)
|
||||
|| (heatmap_volume_vec[heatmap_volume_vec.len()-9].heatmap_level == HeatMapLevel::ExtraHigh) && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec[heatmap_volume_vec.len()-9].close_time).is_some_and(|x| x.candle_type == CandleType::UP)
|
||||
|| (heatmap_volume_vec[heatmap_volume_vec.len()-10].heatmap_level == HeatMapLevel::ExtraHigh) && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec[heatmap_volume_vec.len()-10].close_time).is_some_and(|x| x.candle_type == CandleType::UP))
|
||||
{
|
||||
do_buy = false;
|
||||
}
|
||||
}
|
||||
|
||||
if do_buy == false {
|
||||
keys_to_remove.insert(symbol.clone());
|
||||
}
|
||||
}
|
||||
remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// let server_epoch = get_server_epoch().await;
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// let mut do_buy = false;
|
||||
// let opclo_sample_length: usize = 60; // 15 candle samsples
|
||||
// let mut target_profit_percent = 0.0;
|
||||
// if let Some(price_1m_vec) = alldata.rt_price_1m_vec.get(symbol) {
|
||||
// let vec_len = price_1m_vec.len();
|
||||
// if let Some(candles) =
|
||||
// price_1m_vec.get(vec_len - opclo_sample_length - 2..vec_len - 1)
|
||||
// {
|
||||
// let windows = candles.windows(2);
|
||||
// let mut sum_amplitude_candles = 0.0;
|
||||
// let mut sum_ratio_amp_body = 0.0;
|
||||
// let mut average_amplitude = 0.0;
|
||||
|
||||
// for window in windows {
|
||||
// sum_amplitude_candles += ((window.last().unwrap().high_price
|
||||
// - window.last().unwrap().low_price)
|
||||
// * 100.0)
|
||||
// / window.first().unwrap().close_price;
|
||||
// }
|
||||
// let average_amplitude = sum_amplitude_candles / opclo_sample_length as f64; // percent unit
|
||||
// let mut amplitude_variance = 0.0;
|
||||
|
||||
// let windows = candles.windows(2);
|
||||
// for window in windows {
|
||||
// amplitude_variance += ((((window.last().unwrap().high_price
|
||||
// - window.last().unwrap().low_price)
|
||||
// * 100.0)
|
||||
// / window.first().unwrap().close_price)
|
||||
// - average_amplitude)
|
||||
// .powi(2);
|
||||
// }
|
||||
// amplitude_variance = amplitude_variance / (opclo_sample_length - 1) as f64;
|
||||
// let standard_deviation_amplitude = amplitude_variance.sqrt();
|
||||
// target_profit_percent =
|
||||
// average_amplitude + (standard_deviation_amplitude * 2.0);
|
||||
|
||||
// if target_profit_percent * 2.0 > 0.14 {
|
||||
// do_buy = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if do_buy == false {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// StochRSI (RSI_len: 30, StochRSI_len: 30, K: 2, D: 2) K_current < 70, K_prev < 70, K_prev_1 < 70
|
||||
// let server_epoch = get_server_epoch().await;
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// let stoch_rsis = stoch_rsi(30, 30, 2, 2, &alldata.rt_price_1m_vec, &filtered_data).await?;
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// let mut do_buy = false;
|
||||
// let price_and_closetime = get_current_price_decimal(&symbol, &alldata.rt_price_1m_vec).await;
|
||||
// if let (Some(stoch_rsi_vec), Some(current_info)) = (stoch_rsis.get(symbol), price_and_closetime) {
|
||||
// if server_epoch < current_info.1 {
|
||||
// let search_result = stoch_rsi_vec
|
||||
// .iter()
|
||||
// .position(|x| x.close_time == current_info.1);
|
||||
// if stoch_rsi_vec.len() > 10
|
||||
// && search_result.is_some_and(|a| {
|
||||
// stoch_rsi_vec[a].k > 20.0
|
||||
// && stoch_rsi_vec[a].k > stoch_rsi_vec[a].d
|
||||
// && stoch_rsi_vec[a - 1].k < 20.0
|
||||
// && stoch_rsi_vec[a - 2].k < 20.0
|
||||
// && stoch_rsi_vec[a - 3].k < 20.0
|
||||
// && stoch_rsi_vec[a - 4].k < 20.0
|
||||
// })
|
||||
// {
|
||||
// values.closetime = current_info.1;
|
||||
// values.current_price = current_info.0;
|
||||
// do_buy = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if do_buy == false {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
let final_filtered_data = future_duplicate_filter(Position::Long, &filtered_data, &future_exchange_info_map).await?;
|
||||
insert_future_coins(Position::Long, server_epoch, &final_filtered_data).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn list_up_for_sell(all_data: &AllData, futures_exchange_info_map: &HashMap<String, FuturesExchangeInfo>,) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let filled_positions = select_long_filled_positions().await?;
|
||||
|
||||
let client = ClientBuilder::new()
|
||||
.timeout(tokio::time::Duration::from_millis(5000))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let server_epoch = get_server_epoch().await;
|
||||
|
||||
// let mut supertrend_vec: Vec<SupertrendData> = Vec::new();
|
||||
let mut filtered_symbols: HashMap<String, FilteredDataValue> = HashMap::new();
|
||||
for element in &filled_positions {
|
||||
filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new());
|
||||
}
|
||||
let lr_map = linear_regression(30, 0, &all_data.rt_price_30m_vec, &filtered_symbols).await?;
|
||||
let sma_map = sma(30, &all_data.rt_price_30m_vec, &filtered_symbols).await?;
|
||||
let heatmap_30m_map = heatmap_volume(
|
||||
30,
|
||||
30,
|
||||
4.0,
|
||||
1.5,
|
||||
0.5,
|
||||
-0.5,
|
||||
&filtered_symbols,
|
||||
&all_data.rt_price_30m_vec,
|
||||
)
|
||||
.await?;
|
||||
let heatmap_1m_map = heatmap_volume(
|
||||
100,
|
||||
100,
|
||||
5.0,
|
||||
1.5,
|
||||
0.5,
|
||||
-0.5,
|
||||
&filtered_symbols,
|
||||
&all_data.rt_price_1m_vec,
|
||||
)
|
||||
.await?;
|
||||
let bollingerband_30m_map = bollingerband(30, 3.0, &all_data.rt_price_30m_vec, &filtered_symbols).await?;
|
||||
let bollingerband_1m_map = bollingerband(30, 4.5, &all_data.rt_price_1m_vec, &filtered_symbols).await?;
|
||||
let supertrend_30m_map =
|
||||
supertrend(10, 1.5, true, &all_data.rt_price_30m_vec, &filtered_symbols).await?;
|
||||
// let adx_vec = adx(15, 15, &all_data.rt_price_1m_vec, &filtered_symbols).await?;
|
||||
for element in filled_positions {
|
||||
let mut is_sell = false;
|
||||
if let (Some(lr_vec),
|
||||
Some(sma_vec),
|
||||
Some(heatmap_30m_vec),
|
||||
Some(heatmap_1m_vec),
|
||||
Some(bb_30m_vec),
|
||||
Some(bb_1m_vec),
|
||||
Some(rt_price_30m_vec),
|
||||
Some(rt_price_1m_vec),
|
||||
Some(supertrend_vec))
|
||||
= (lr_map.get(&element.symbol),
|
||||
sma_map.get(&element.symbol),
|
||||
heatmap_30m_map.get(&element.symbol),
|
||||
heatmap_1m_map.get(&element.symbol),
|
||||
bollingerband_30m_map.get(&element.symbol),
|
||||
bollingerband_1m_map.get(&element.symbol),
|
||||
all_data.rt_price_30m_vec.get(&element.symbol),
|
||||
all_data.rt_price_1m_vec.get(&element.symbol),
|
||||
supertrend_30m_map.get(&element.symbol)) {
|
||||
if !element.current_price.is_zero()
|
||||
&& lr_vec.len() > 10
|
||||
&& heatmap_30m_vec.len() > 10
|
||||
&& heatmap_1m_vec.len() > 10
|
||||
&& rt_price_30m_vec.len() > 10
|
||||
&& rt_price_1m_vec.len() > 10
|
||||
&& bb_30m_vec.len() > 10
|
||||
&& bb_1m_vec.len() > 10
|
||||
&& sma_vec.len() > 10
|
||||
&& supertrend_vec.len() > 10
|
||||
&& lr_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& sma_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& heatmap_30m_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& heatmap_1m_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& bb_30m_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& bb_1m_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& rt_price_30m_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& rt_price_1m_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& supertrend_vec.last().is_some_and(|x| x.close_time > server_epoch) {
|
||||
|
||||
// if element.pure_profit_percent >= element.target_percent {
|
||||
// is_sell = true;
|
||||
// } else if element.pure_profit_percent <= element.stoploss_percent {
|
||||
// is_sell = true;
|
||||
// } else
|
||||
|
||||
if element.pure_profit_percent.is_sign_positive() && lr_vec.last().is_some_and(|x| x.r_squared >= 0.6) {
|
||||
is_sell = true;
|
||||
} else if server_epoch - element.close_time > 900_000
|
||||
&& lr_vec[lr_vec.len()-1].r_squared < lr_vec[lr_vec.len()-2].r_squared
|
||||
&& lr_vec[lr_vec.len()-2].r_squared > lr_vec[lr_vec.len()-3].r_squared
|
||||
&& lr_vec[lr_vec.len()-3].r_squared > lr_vec[lr_vec.len()-4].r_squared {
|
||||
is_sell = true;
|
||||
} else if server_epoch - element.close_time > 1_800_000
|
||||
&& lr_vec.last().unwrap().close_time == sma_vec.last().unwrap().close_time
|
||||
&& lr_vec[lr_vec.len()-1].lr_value < sma_vec[sma_vec.len()-1].sma_value
|
||||
&& lr_vec[lr_vec.len()-2].lr_value > sma_vec[sma_vec.len()-2].sma_value {
|
||||
is_sell = true;
|
||||
} else if element.pure_profit_percent.is_sign_positive() && heatmap_30m_vec.last().is_some_and(|x| x.heatmap_level == HeatMapLevel::ExtraHigh) {
|
||||
is_sell = true;
|
||||
} else if element.pure_profit_percent.is_sign_positive()
|
||||
&& (heatmap_1m_vec.last().is_some_and(|x| x.heatmap_level == HeatMapLevel::ExtraHigh)
|
||||
|| heatmap_1m_vec[heatmap_1m_vec.len()-2].heatmap_level == HeatMapLevel::ExtraHigh) {
|
||||
is_sell = true;
|
||||
} else if element.pure_profit_percent.is_sign_negative() && element.pure_profit_percent <= -5.0 {
|
||||
is_sell = true;
|
||||
} else if supertrend_vec.last().is_some_and(|x| x.signal.as_ref().is_some_and(|a| *a == SuperTrendSignal::SELL)) {
|
||||
is_sell = true;
|
||||
}
|
||||
|
||||
if is_sell == false {
|
||||
if let Ok(bb_search_result) = bb_30m_vec.binary_search_by_key(
|
||||
&rt_price_30m_vec.last().unwrap().close_time,
|
||||
|BollingerBandData {
|
||||
sma,
|
||||
upperband,
|
||||
lowerband,
|
||||
close_time,
|
||||
}| *close_time,
|
||||
) {
|
||||
if element.pure_profit_percent.is_sign_positive()
|
||||
&& bb_30m_vec[bb_search_result].upperband <= rt_price_30m_vec[rt_price_30m_vec.len() - 1].high_price
|
||||
&& bb_30m_vec[bb_search_result-1].upperband <= rt_price_30m_vec[rt_price_30m_vec.len() - 2].high_price
|
||||
{
|
||||
is_sell = true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(bb_search_result) = bb_1m_vec.binary_search_by_key(
|
||||
&rt_price_1m_vec.last().unwrap().close_time,
|
||||
|BollingerBandData {
|
||||
sma,
|
||||
upperband,
|
||||
lowerband,
|
||||
close_time,
|
||||
}| *close_time,
|
||||
) {
|
||||
if element.pure_profit_percent.is_sign_positive()
|
||||
&& bb_1m_vec[bb_search_result].upperband <= rt_price_1m_vec[rt_price_1m_vec.len() - 1].high_price
|
||||
&& bb_1m_vec[bb_search_result-1].upperband <= rt_price_1m_vec[rt_price_1m_vec.len() - 2].high_price
|
||||
{
|
||||
is_sell = true;
|
||||
}
|
||||
}
|
||||
|
||||
let minimum_candles = 5;
|
||||
let maximum_candles = 30;
|
||||
|
||||
if server_epoch - element.close_time > 1_800_000 * maximum_candles {
|
||||
is_sell = true;
|
||||
}
|
||||
}
|
||||
|
||||
// for count_candles in minimum_candles..=maximum_candles {
|
||||
// if count_candles < maximum_candles
|
||||
// && server_epoch - element.transact_time
|
||||
// > (1_800_000) * count_candles
|
||||
// && ((element.pure_profit_percent > 0.0
|
||||
// && element.target_percent
|
||||
// * ((maximum_candles - count_candles) as f64
|
||||
// / (maximum_candles - minimum_candles + 1) as f64)
|
||||
// <= element.pure_profit_percent)
|
||||
// ||
|
||||
// (element.pure_profit_percent < 0.0
|
||||
// && element.stoploss_percent
|
||||
// * ((maximum_candles - count_candles) as f64
|
||||
// / (maximum_candles - minimum_candles + 1) as f64)
|
||||
// >= element.pure_profit_percent)
|
||||
// )
|
||||
// {
|
||||
// is_sell = true;
|
||||
// break;
|
||||
// } else {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
//} else if server_epoch - element.close_time > 300_000
|
||||
// && adx_vec.get(&element.symbol).is_some_and(|a| a.len() > 10 && a.last().is_some_and(|b| b.close_time > server_epoch && b.adx < 15.0)) {
|
||||
// is_sell = true;
|
||||
// }
|
||||
|
||||
if is_sell == true {
|
||||
limit_order_close(
|
||||
&element,
|
||||
TimeInForce::Gtc,
|
||||
element.current_price,
|
||||
element.base_qty_ordered,
|
||||
&futures_exchange_info_map,
|
||||
&client
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
624
src/strategy_team/future_strategy_short.rs
Normal file
624
src/strategy_team/future_strategy_short.rs
Normal file
|
|
@ -0,0 +1,624 @@
|
|||
use crate::value_estimation_team::{datapoints::price_data::CandleType, indicators::wiliams_percent_r};
|
||||
|
||||
use super::{
|
||||
adx, dec, decimal_add, decimal_div, decimal_mul, decimal_sub, dema, duplicate_filter, ema, ema_open,
|
||||
ema_macd, exists_record, get_server_epoch, heatmap_volume,
|
||||
insert_pre_suggested_coins, limit_order_sell, remove_keys, rsi, select_filled_buy_orders,
|
||||
stoch_rsi, supertrend, tema, try_join_all, update_record3, wiliams_percent_r, AdxData, AllData,
|
||||
Arc, BollingerBandData, Client, ClientBuilder, Decimal, DemaData, EmaData, ExchangeInfo,
|
||||
FilteredDataValue, HashMap, HashSet, HeatMapLevel, HeatmapVolumeData, MacdData, Mutex,
|
||||
RealtimePriceData, RoundingStrategy, RsiData, StochRsiData, SuperTrendArea, SuperTrendSignal,
|
||||
SupertrendData, TemaData, ToPrimitive, TradeFee, WiliamsPercentR, future_duplicate_filter, insert_future_coins, get_current_price_decimal,
|
||||
sma, sma_open, LrData, linear_regression, bollingerband
|
||||
};
|
||||
|
||||
use crate::future::{Position, FuturesExchangeInfo};
|
||||
use crate::future::table_mgmt::select_short_filled_positions;
|
||||
use crate::future::order::{limit_order_close, TimeInForce};
|
||||
|
||||
// BUY conditions
|
||||
pub async fn list_up_for_buy(
|
||||
alldata: &AllData,
|
||||
future_exchange_info_map: &HashMap<String, FuturesExchangeInfo>
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
// print rt_price for debugging
|
||||
// let a = alldata.rt_price_30m_vec.iter().position(|a| a.0 == "BTCUSDT");
|
||||
// println!("BTCUSDT: {:?}", alldata.rt_price_30m_vec[a.unwrap()].1.last().unwrap());
|
||||
|
||||
// basic filtering: filtering valid trade pair
|
||||
let mut filtered_data: HashMap<String, FilteredDataValue> = HashMap::new();
|
||||
for symbol in &alldata.valid_symbol_vec {
|
||||
filtered_data.insert(symbol.clone(), FilteredDataValue::new());
|
||||
}
|
||||
|
||||
let server_epoch = get_server_epoch().await;
|
||||
|
||||
// current lr < prev lr, current r_squared <= 0.01
|
||||
let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
let lr_map = linear_regression(30, 0, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
let lr50_map = linear_regression(50, 0, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
let sma10_map = sma(30, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
for (symbol, values) in &mut filtered_data {
|
||||
let mut do_buy = false;
|
||||
if let (Some(lr_vec), Some(lr50_vec), Some(sma_vec), Some(current_info)) = (lr_map.get(symbol), lr50_map.get(symbol), sma10_map.get(symbol), get_current_price_decimal(&symbol, &alldata.rt_price_30m_vec).await) {
|
||||
if lr_vec.len() > 10
|
||||
&& lr50_vec.len() > 10
|
||||
&& sma_vec.len() > 10
|
||||
&& lr_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& sma_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& lr_vec[lr_vec.len()-1].lr_value < lr_vec[lr_vec.len()-2].lr_value
|
||||
&& lr_vec[lr_vec.len()-2].lr_value < lr_vec[lr_vec.len()-3].lr_value
|
||||
&& lr_vec.last().unwrap().r_squared <= 0.02
|
||||
&& lr_vec[lr_vec.len()-1].lr_value < sma_vec[sma_vec.len()-1].sma_value
|
||||
&& lr_vec[lr_vec.len()-2].lr_value >= sma_vec[sma_vec.len()-2].sma_value
|
||||
{
|
||||
values.closetime = current_info.1;
|
||||
do_buy = true;
|
||||
}
|
||||
}
|
||||
if do_buy == false {
|
||||
keys_to_remove.insert(symbol.clone());
|
||||
}
|
||||
}
|
||||
remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// sma3_close(current) > sma3_open (current), sma3_close(prev) < sma3_open (prev)
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// let ema10_open = ema_open(5, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
// let ema10_close = ema(5, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
// let ema200_close = ema(200, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// let mut do_buy = false;
|
||||
// let price_and_closetime = get_current_price_decimal(&symbol, &alldata.rt_price_30m_vec).await;
|
||||
// if let (Some(ema10_open_vec), Some(ema10_close_vec), Some(ema200_close_vec), Some(current_info))
|
||||
// = (ema10_open.get(symbol), ema10_close.get(symbol), ema200_close.get(symbol), price_and_closetime) {
|
||||
// if ema10_open_vec.len() > 20
|
||||
// && ema10_close_vec.len() > 20
|
||||
// && ema10_open_vec.last().unwrap().close_time > server_epoch
|
||||
// && ema10_open_vec.last().unwrap().close_time == ema10_close_vec.last().unwrap().close_time
|
||||
// && ema10_open_vec.last().unwrap().close_time == ema200_close_vec.last().unwrap().close_time
|
||||
// {
|
||||
// if ema10_open_vec.last().unwrap().ema_value > ema10_close_vec.last().unwrap().ema_value
|
||||
// && current_info.0.to_f64().is_some_and(|a| a < ema200_close_vec.last().unwrap().ema_value)
|
||||
// {
|
||||
// values.current_price = current_info.0;
|
||||
// values.closetime = current_info.1;
|
||||
// do_buy = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if do_buy == false {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// current ADX(3, 3), current ADX > prev ADX
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// let adx_vec = adx(3, 3, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// let mut do_buy = false;
|
||||
// if let Some(adx_vec) = adx_vec.get(symbol) {
|
||||
// if let Some(last_idx) = adx_vec.iter().position(|elem| elem.close_time == values.closetime) {
|
||||
// if adx_vec.len() > 10
|
||||
// && adx_vec[last_idx].adx > adx_vec[last_idx-1].adx
|
||||
// {
|
||||
// do_buy = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if do_buy == false {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// supertrend(ATR period 30, multiplier: 2.0, 30m close price)
|
||||
let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
let supertrend_1m_map =
|
||||
supertrend(10, 1.5, true, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
for (symbol, values) in &mut filtered_data {
|
||||
let mut do_buy = false;
|
||||
if let Some(supertrend_vec) = supertrend_1m_map.get(symbol)
|
||||
{
|
||||
if supertrend_vec.len() > 10
|
||||
&& supertrend_vec.last().is_some_and(|a| a.close_time > server_epoch)
|
||||
&& supertrend_vec.last().unwrap().area == SuperTrendArea::DOWN
|
||||
&& supertrend_vec[supertrend_vec.len()-2].area == SuperTrendArea::DOWN {
|
||||
do_buy = true;
|
||||
}
|
||||
}
|
||||
|
||||
if do_buy == false {
|
||||
keys_to_remove.insert(symbol.clone());
|
||||
}
|
||||
}
|
||||
remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// BollingerBand (len:30, multiplier 3) 5 previous high price < BB upper
|
||||
let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
let bollingerband_map =
|
||||
bollingerband(30, 3.0, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
for (symbol, filtered_data) in &mut filtered_data {
|
||||
let mut do_buy = false;
|
||||
if let (Some(bb_vec), Some(rt_30m_vec)) = (
|
||||
bollingerband_map.get(symbol),
|
||||
alldata.rt_price_30m_vec.get(symbol),
|
||||
) {
|
||||
if rt_30m_vec.len() >= 10
|
||||
&& bb_vec.len() >= 10
|
||||
&& rt_30m_vec.last().unwrap().close_time > server_epoch
|
||||
{
|
||||
if let Ok(bb_search_result) = bb_vec.binary_search_by_key(
|
||||
&rt_30m_vec.last().unwrap().close_time,
|
||||
|BollingerBandData {
|
||||
sma,
|
||||
upperband,
|
||||
lowerband,
|
||||
close_time,
|
||||
}| *close_time,
|
||||
) {
|
||||
if bb_vec[bb_search_result].lowerband < rt_30m_vec[rt_30m_vec.len() - 1].low_price
|
||||
&& bb_vec[bb_search_result-1].lowerband < rt_30m_vec[rt_30m_vec.len() - 2].low_price
|
||||
&& bb_vec[bb_search_result-2].lowerband < rt_30m_vec[rt_30m_vec.len() - 3].low_price
|
||||
&& bb_vec[bb_search_result-3].lowerband < rt_30m_vec[rt_30m_vec.len() - 4].low_price
|
||||
&& bb_vec[bb_search_result-4].lowerband < rt_30m_vec[rt_30m_vec.len() - 5].low_price
|
||||
{
|
||||
do_buy = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if do_buy == false {
|
||||
keys_to_remove.insert(symbol.clone());
|
||||
}
|
||||
}
|
||||
remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// set target_price and stop_loss
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// let mut do_buy = false;
|
||||
// if let Some(realtime_price_vec) = alldata.rt_price_30m_vec.get(symbol)
|
||||
// {
|
||||
// let element_number = 5;
|
||||
// if let Some(truncated_vec) = realtime_price_vec.get(realtime_price_vec.len()-element_number..) {
|
||||
// let max_price = truncated_vec
|
||||
// .iter()
|
||||
// .max_by(|x, y| x.high_price.partial_cmp(&y.high_price).unwrap())
|
||||
// .unwrap().high_price;
|
||||
// if values.current_price.to_f64().is_some_and(|a| a < max_price) {
|
||||
// let mut stoploss_percent = ((values.current_price.to_f64().unwrap() - max_price) * 100.0) / values.current_price.to_f64().unwrap();
|
||||
// stoploss_percent = (stoploss_percent * 100.0).floor() / 100.0;
|
||||
// values.stoploss = rust_decimal::prelude::FromPrimitive::from_f64(stoploss_percent).unwrap();
|
||||
// let mut target_percent = stoploss_percent.abs() * 1.5;
|
||||
// target_percent = (target_percent * 100.0).floor() / 100.0;
|
||||
// values.target_price = rust_decimal::prelude::FromPrimitive::from_f64(target_percent).unwrap();
|
||||
|
||||
// do_buy = true;
|
||||
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if do_buy == false {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// Wiliams -40.0 < %R(100)
|
||||
let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
let mut wpr100_map = wiliams_percent_r(100, &alldata.rt_price_1m_vec, &filtered_data).await?;
|
||||
let server_epoch = get_server_epoch().await;
|
||||
for (symbol, values) in &mut filtered_data {
|
||||
let mut do_buy = false;
|
||||
if let Some(wpr100_vec) = wpr100_map.get(symbol) {
|
||||
if wpr100_vec.len() > 15
|
||||
&& wpr100_vec.last().unwrap().close_time > server_epoch
|
||||
&& wpr100_vec.last().unwrap().r_value > -20.0 {
|
||||
do_buy = true;
|
||||
}
|
||||
}
|
||||
if do_buy == false {
|
||||
keys_to_remove.insert(symbol.clone());
|
||||
}
|
||||
}
|
||||
remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// current Tema(15) < current Tema(30)
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// let tema_10 = tema(10, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
// let tema_30 = tema(30, &alldata.rt_price_30m_vec, &filtered_data).await?;
|
||||
// let server_epoch = get_server_epoch().await;
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// let mut do_buy = false;
|
||||
// let price_and_closetime = get_current_price_decimal(&symbol, &alldata.rt_price_1m_vec).await;
|
||||
// if let (Some(tema10_vec), Some(tema30_vec), Some(current_info)) = (tema_10.get(symbol), tema_30.get(symbol), price_and_closetime) {
|
||||
// if tema10_vec.len() > 10
|
||||
// && tema30_vec.len() > 10
|
||||
// && tema10_vec.last().unwrap().close_time == tema30_vec.last().unwrap().close_time
|
||||
// && tema10_vec.last().unwrap().close_time > server_epoch
|
||||
// && tema30_vec.last().unwrap().close_time > server_epoch
|
||||
// {
|
||||
// if tema10_vec[tema10_vec.len()-1].tema_value < tema30_vec[tema30_vec.len()-1].tema_value
|
||||
// && tema10_vec[tema10_vec.len()-2].tema_value < tema30_vec[tema30_vec.len()-2].tema_value
|
||||
// && tema10_vec[tema10_vec.len()-1].tema_value < tema10_vec[tema10_vec.len()-2].tema_value
|
||||
// {
|
||||
// do_buy = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if do_buy == false {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// supertrend(ATR period 10, multiplier: 3.0, 30m close price)
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// let server_epoch = get_server_epoch().await;
|
||||
// let supertrend_1m_map =
|
||||
// supertrend(60, 1.5, true, &alldata.rt_price_1m_vec, &filtered_data).await?;
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// let mut do_buy = false;
|
||||
// let price_and_closetime = get_current_price_decimal(&symbol, &alldata.rt_price_1m_vec).await;
|
||||
// if let (Some(supertrend_vec), Some(current_info)) = (
|
||||
// supertrend_1m_map.get(symbol),
|
||||
// price_and_closetime,
|
||||
// ) {
|
||||
// if supertrend_vec.last().unwrap().close_time > server_epoch
|
||||
// && supertrend_vec.last().unwrap().signal.as_ref().is_some_and(|a| *a == SuperTrendSignal::SELL)
|
||||
// {
|
||||
// values.current_price = current_info.0;
|
||||
// values.closetime = current_info.1;
|
||||
// do_buy = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if do_buy == false {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// Heatmap volume: filtering close price with Extra High is over the previous candle from 30 previous candles
|
||||
let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
let heatmap_volumes = heatmap_volume(
|
||||
30,
|
||||
30,
|
||||
4.0,
|
||||
1.5,
|
||||
0.5,
|
||||
-0.5,
|
||||
&filtered_data,
|
||||
&alldata.rt_price_30m_vec,
|
||||
)
|
||||
.await?;
|
||||
|
||||
for (symbol, values) in &mut filtered_data {
|
||||
let mut do_buy = true;
|
||||
if let (Some(heatmap_volume_vec), Some(rt_price_vec)) = (heatmap_volumes.get(symbol), alldata.rt_price_30m_vec.get(symbol)) {
|
||||
if heatmap_volume_vec.len() > 100
|
||||
&& heatmap_volume_vec.last().unwrap().close_time > server_epoch
|
||||
&& ((heatmap_volume_vec.last().unwrap().heatmap_level == HeatMapLevel::ExtraHigh && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec.last().unwrap().close_time).is_some_and(|x| x.candle_type == CandleType::DOWN))
|
||||
|| (heatmap_volume_vec[heatmap_volume_vec.len()-2].heatmap_level == HeatMapLevel::ExtraHigh && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec[heatmap_volume_vec.len()-2].close_time).is_some_and(|x| x.candle_type == CandleType::DOWN))
|
||||
|| (heatmap_volume_vec[heatmap_volume_vec.len()-3].heatmap_level == HeatMapLevel::ExtraHigh && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec[heatmap_volume_vec.len()-3].close_time).is_some_and(|x| x.candle_type == CandleType::DOWN))
|
||||
|| (heatmap_volume_vec[heatmap_volume_vec.len()-4].heatmap_level == HeatMapLevel::ExtraHigh && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec[heatmap_volume_vec.len()-4].close_time).is_some_and(|x| x.candle_type == CandleType::DOWN))
|
||||
|| (heatmap_volume_vec[heatmap_volume_vec.len()-5].heatmap_level == HeatMapLevel::ExtraHigh) && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec[heatmap_volume_vec.len()-5].close_time).is_some_and(|x| x.candle_type == CandleType::DOWN)
|
||||
|| (heatmap_volume_vec[heatmap_volume_vec.len()-6].heatmap_level == HeatMapLevel::ExtraHigh) && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec[heatmap_volume_vec.len()-6].close_time).is_some_and(|x| x.candle_type == CandleType::DOWN)
|
||||
|| (heatmap_volume_vec[heatmap_volume_vec.len()-7].heatmap_level == HeatMapLevel::ExtraHigh) && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec[heatmap_volume_vec.len()-7].close_time).is_some_and(|x| x.candle_type == CandleType::DOWN)
|
||||
|| (heatmap_volume_vec[heatmap_volume_vec.len()-8].heatmap_level == HeatMapLevel::ExtraHigh) && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec[heatmap_volume_vec.len()-8].close_time).is_some_and(|x| x.candle_type == CandleType::DOWN)
|
||||
|| (heatmap_volume_vec[heatmap_volume_vec.len()-9].heatmap_level == HeatMapLevel::ExtraHigh) && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec[heatmap_volume_vec.len()-9].close_time).is_some_and(|x| x.candle_type == CandleType::DOWN)
|
||||
|| (heatmap_volume_vec[heatmap_volume_vec.len()-10].heatmap_level == HeatMapLevel::ExtraHigh) && rt_price_vec.iter().find(|x| x.close_time == heatmap_volume_vec[heatmap_volume_vec.len()-10].close_time).is_some_and(|x| x.candle_type == CandleType::DOWN))
|
||||
{
|
||||
do_buy = false;
|
||||
}
|
||||
}
|
||||
|
||||
if do_buy == false {
|
||||
keys_to_remove.insert(symbol.clone());
|
||||
}
|
||||
}
|
||||
remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// let server_epoch = get_server_epoch().await;
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// let mut do_buy = false;
|
||||
// let opclo_sample_length: usize = 60; // 15 candle samsples
|
||||
// let mut target_profit_percent = 0.0;
|
||||
// if let Some(price_1m_vec) = alldata.rt_price_1m_vec.get(symbol) {
|
||||
// let vec_len = price_1m_vec.len();
|
||||
// if let Some(candles) =
|
||||
// price_1m_vec.get(vec_len - opclo_sample_length - 2..vec_len - 1)
|
||||
// {
|
||||
// let windows = candles.windows(2);
|
||||
// let mut sum_amplitude_candles = 0.0;
|
||||
// let mut sum_ratio_amp_body = 0.0;
|
||||
// let mut average_amplitude = 0.0;
|
||||
|
||||
// for window in windows {
|
||||
// sum_amplitude_candles += ((window.last().unwrap().high_price
|
||||
// - window.last().unwrap().low_price)
|
||||
// * 100.0)
|
||||
// / window.first().unwrap().close_price;
|
||||
// }
|
||||
// let average_amplitude = sum_amplitude_candles / opclo_sample_length as f64; // percent unit
|
||||
// let mut amplitude_variance = 0.0;
|
||||
|
||||
// let windows = candles.windows(2);
|
||||
// for window in windows {
|
||||
// amplitude_variance += ((((window.last().unwrap().high_price
|
||||
// - window.last().unwrap().low_price)
|
||||
// * 100.0)
|
||||
// / window.first().unwrap().close_price)
|
||||
// - average_amplitude)
|
||||
// .powi(2);
|
||||
// }
|
||||
// amplitude_variance = amplitude_variance / (opclo_sample_length - 1) as f64;
|
||||
// let standard_deviation_amplitude = amplitude_variance.sqrt();
|
||||
// target_profit_percent =
|
||||
// average_amplitude + (standard_deviation_amplitude * 2.0);
|
||||
|
||||
// if target_profit_percent * 2.0 > 0.14 {
|
||||
// do_buy = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if do_buy == false {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
// StochRSI (RSI_len: 30, StochRSI_len: 30, K: 2, D: 2) K_current < 70, K_prev < 70, K_prev_1 < 70
|
||||
// let server_epoch = get_server_epoch().await;
|
||||
// let mut keys_to_remove: HashSet<String> = HashSet::new();
|
||||
// let stoch_rsis = stoch_rsi(30, 30, 2, 2, &alldata.rt_price_1m_vec, &filtered_data).await?;
|
||||
// for (symbol, values) in &mut filtered_data {
|
||||
// let mut do_buy = false;
|
||||
// let price_and_closetime = get_current_price_decimal(&symbol, &alldata.rt_price_1m_vec).await;
|
||||
// if let (Some(stoch_rsi_vec), Some(current_info)) = (stoch_rsis.get(symbol), price_and_closetime) {
|
||||
// if server_epoch < current_info.1 {
|
||||
// let search_result = stoch_rsi_vec
|
||||
// .iter()
|
||||
// .position(|x| x.close_time == current_info.1);
|
||||
// if stoch_rsi_vec.len() > 10
|
||||
// && search_result.is_some_and(|a| {
|
||||
// stoch_rsi_vec[a].k < 80.0
|
||||
// && stoch_rsi_vec[a].k < stoch_rsi_vec[a].d
|
||||
// && stoch_rsi_vec[a - 1].k > 80.0
|
||||
// && stoch_rsi_vec[a - 2].k > 80.0
|
||||
// && stoch_rsi_vec[a - 3].k > 80.0
|
||||
// && stoch_rsi_vec[a - 4].k > 80.0
|
||||
// })
|
||||
// {
|
||||
// values.closetime = current_info.1;
|
||||
// values.current_price = current_info.0;
|
||||
// do_buy = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if do_buy == false {
|
||||
// keys_to_remove.insert(symbol.clone());
|
||||
// }
|
||||
// }
|
||||
// remove_keys(&mut filtered_data, keys_to_remove).await;
|
||||
|
||||
let final_filtered_data = future_duplicate_filter(Position::Short, &filtered_data, &future_exchange_info_map).await?;
|
||||
insert_future_coins(Position::Short, server_epoch, &final_filtered_data).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn list_up_for_sell(all_data: &AllData, futures_exchange_info_map: &HashMap<String, FuturesExchangeInfo>) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let filled_positions = select_short_filled_positions().await?;
|
||||
|
||||
let client = ClientBuilder::new()
|
||||
.timeout(tokio::time::Duration::from_millis(5000))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let server_epoch = get_server_epoch().await;
|
||||
// let mut supertrend_vec: Vec<SupertrendData> = Vec::new();
|
||||
let mut filtered_symbols: HashMap<String, FilteredDataValue> = HashMap::new();
|
||||
for element in &filled_positions {
|
||||
filtered_symbols.insert(element.symbol.clone(), FilteredDataValue::new());
|
||||
}
|
||||
let lr_map = linear_regression(30, 0, &all_data.rt_price_30m_vec, &filtered_symbols).await?;
|
||||
let sma_map: HashMap<String, Vec<crate::value_estimation_team::indicators::sma::SmaData>> = sma(30, &all_data.rt_price_30m_vec, &filtered_symbols).await?;
|
||||
let heatmap_30m_map = heatmap_volume(
|
||||
30,
|
||||
30,
|
||||
4.0,
|
||||
1.5,
|
||||
0.5,
|
||||
-0.5,
|
||||
&filtered_symbols,
|
||||
&all_data.rt_price_30m_vec,
|
||||
)
|
||||
.await?;
|
||||
let heatmap_1m_map = heatmap_volume(
|
||||
100,
|
||||
100,
|
||||
5.0,
|
||||
1.5,
|
||||
0.5,
|
||||
-0.5,
|
||||
&filtered_symbols,
|
||||
&all_data.rt_price_1m_vec,
|
||||
)
|
||||
.await?;
|
||||
let bollingerband_30m_map = bollingerband(30, 3.0, &all_data.rt_price_30m_vec, &filtered_symbols).await?;
|
||||
let bollingerband_1m_map = bollingerband(30, 4.5, &all_data.rt_price_1m_vec, &filtered_symbols).await?;
|
||||
let supertrend_30m_map = supertrend(10, 1.5, true, &all_data.rt_price_30m_vec, &filtered_symbols).await?;
|
||||
// let adx_vec = adx(15, 15, &all_data.rt_price_1m_vec, &filtered_symbols).await?;
|
||||
for element in filled_positions {
|
||||
let mut is_sell = false;
|
||||
|
||||
// TODO: BNB 코인이 있으면
|
||||
// let base_qty_to_be_ordered =
|
||||
// element.base_qty_ordered.round_dp_with_strategy(
|
||||
// lot_step_size.normalize().scale(),
|
||||
// RoundingStrategy::ToZero,
|
||||
// );
|
||||
// TODO: BNB 코인이 없으면
|
||||
if let (Some(lr_vec),
|
||||
Some(sma_vec),
|
||||
Some(heatmap_30m_vec),
|
||||
Some(heatmap_1m_vec),
|
||||
Some(bb_30m_vec),
|
||||
Some(bb_1m_vec),
|
||||
Some(rt_price_30m_vec),
|
||||
Some(rt_price_1m_vec),
|
||||
Some(supertrend_vec))
|
||||
= (lr_map.get(&element.symbol),
|
||||
sma_map.get(&element.symbol),
|
||||
heatmap_30m_map.get(&element.symbol),
|
||||
heatmap_1m_map.get(&element.symbol),
|
||||
bollingerband_30m_map.get(&element.symbol),
|
||||
bollingerband_1m_map.get(&element.symbol),
|
||||
all_data.rt_price_30m_vec.get(&element.symbol),
|
||||
all_data.rt_price_1m_vec.get(&element.symbol),
|
||||
supertrend_30m_map.get(&element.symbol)) {
|
||||
if !element.current_price.is_zero()
|
||||
&& lr_vec.len() > 10
|
||||
&& heatmap_30m_vec.len() > 10
|
||||
&& heatmap_1m_vec.len() > 10
|
||||
&& rt_price_30m_vec.len() > 10
|
||||
&& rt_price_1m_vec.len() > 10
|
||||
&& bb_30m_vec.len() > 10
|
||||
&& bb_1m_vec.len() > 10
|
||||
&& sma_vec.len() > 10
|
||||
&& supertrend_vec.len() > 10
|
||||
&& lr_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& sma_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& heatmap_30m_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& heatmap_1m_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& bb_30m_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& bb_1m_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& rt_price_30m_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& rt_price_1m_vec.last().is_some_and(|x| x.close_time > server_epoch)
|
||||
&& supertrend_vec.last().is_some_and(|x| x.close_time > server_epoch) {
|
||||
// if element.pure_profit_percent >= element.target_percent {
|
||||
// is_sell = true;
|
||||
// } else if element.pure_profit_percent <= element.stoploss_percent {
|
||||
// is_sell = true;
|
||||
// } else
|
||||
|
||||
if element.pure_profit_percent.is_sign_positive() && lr_vec.last().is_some_and(|x| x.r_squared >= 0.6) {
|
||||
is_sell = true;
|
||||
} else if server_epoch - element.close_time > 900_000
|
||||
&& lr_vec[lr_vec.len()-1].r_squared < lr_vec[lr_vec.len()-2].r_squared
|
||||
&& lr_vec[lr_vec.len()-2].r_squared > lr_vec[lr_vec.len()-3].r_squared
|
||||
&& lr_vec[lr_vec.len()-3].r_squared > lr_vec[lr_vec.len()-4].r_squared {
|
||||
is_sell = true;
|
||||
} else if server_epoch - element.close_time > 1_800_000
|
||||
&& lr_vec.last().unwrap().close_time == sma_vec.last().unwrap().close_time
|
||||
&& lr_vec[lr_vec.len()-1].lr_value > sma_vec[sma_vec.len()-1].sma_value
|
||||
&& lr_vec[lr_vec.len()-2].lr_value < sma_vec[sma_vec.len()-2].sma_value {
|
||||
is_sell = true;
|
||||
} else if element.pure_profit_percent.is_sign_positive() && heatmap_30m_vec.last().is_some_and(|x| x.heatmap_level == HeatMapLevel::ExtraHigh) {
|
||||
is_sell = true;
|
||||
} else if element.pure_profit_percent.is_sign_positive()
|
||||
&& (heatmap_1m_vec.last().is_some_and(|x| x.heatmap_level == HeatMapLevel::ExtraHigh)
|
||||
|| heatmap_1m_vec[heatmap_1m_vec.len()-2].heatmap_level == HeatMapLevel::ExtraHigh) {
|
||||
is_sell = true;
|
||||
} else if element.pure_profit_percent.is_sign_negative() && element.pure_profit_percent <= -5.0 {
|
||||
is_sell = true;
|
||||
} else if supertrend_vec.last().is_some_and(|x| x.signal.as_ref().is_some_and(|a| *a == SuperTrendSignal::BUY)) {
|
||||
is_sell = true;
|
||||
}
|
||||
|
||||
if is_sell == false {
|
||||
if let Ok(bb_search_result) = bb_30m_vec.binary_search_by_key(
|
||||
&rt_price_30m_vec.last().unwrap().close_time,
|
||||
|BollingerBandData {
|
||||
sma,
|
||||
upperband,
|
||||
lowerband,
|
||||
close_time,
|
||||
}| *close_time,
|
||||
) {
|
||||
if element.pure_profit_percent.is_sign_positive()
|
||||
&& bb_30m_vec[bb_search_result].lowerband >= rt_price_30m_vec[rt_price_30m_vec.len() - 1].low_price
|
||||
&& bb_30m_vec[bb_search_result-1].lowerband >= rt_price_30m_vec[rt_price_30m_vec.len() - 2].low_price
|
||||
{
|
||||
is_sell = true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(bb_search_result) = bb_1m_vec.binary_search_by_key(
|
||||
&rt_price_1m_vec.last().unwrap().close_time,
|
||||
|BollingerBandData {
|
||||
sma,
|
||||
upperband,
|
||||
lowerband,
|
||||
close_time,
|
||||
}| *close_time,
|
||||
) {
|
||||
if element.pure_profit_percent.is_sign_positive()
|
||||
&& bb_1m_vec[bb_search_result].lowerband >= rt_price_1m_vec[rt_price_1m_vec.len() - 1].low_price
|
||||
&& bb_1m_vec[bb_search_result-1].lowerband >= rt_price_1m_vec[rt_price_1m_vec.len() - 2].low_price
|
||||
{
|
||||
is_sell = true;
|
||||
}
|
||||
}
|
||||
|
||||
let minimum_candles = 5;
|
||||
let maximum_candles = 30;
|
||||
|
||||
if server_epoch - element.close_time > 1_800_000 * maximum_candles {
|
||||
is_sell = true;
|
||||
}
|
||||
}
|
||||
// for count_candles in minimum_candles..=maximum_candles {
|
||||
// if count_candles < maximum_candles
|
||||
// && server_epoch - element.transact_time
|
||||
// > (1_800_000) * count_candles
|
||||
// && ((element.pure_profit_percent > 0.0
|
||||
// && element.target_percent
|
||||
// * ((maximum_candles - count_candles) as f64
|
||||
// / (maximum_candles - minimum_candles + 1) as f64)
|
||||
// <= element.pure_profit_percent)
|
||||
// ||
|
||||
// (element.pure_profit_percent < 0.0
|
||||
// && element.stoploss_percent
|
||||
// * ((maximum_candles - count_candles) as f64
|
||||
// / (maximum_candles - minimum_candles + 1) as f64)
|
||||
// >= element.pure_profit_percent)
|
||||
// )
|
||||
// {
|
||||
// is_sell = true;
|
||||
// break;
|
||||
// } else {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
//} else if server_epoch - element.close_time > 300_000
|
||||
// && adx_vec.get(&element.symbol).is_some_and(|a| a.len() > 10 && a.last().is_some_and(|b| b.close_time > server_epoch && b.adx < 15.0)) {
|
||||
// is_sell = true;
|
||||
// }
|
||||
|
||||
if is_sell == true {
|
||||
limit_order_close(
|
||||
&element,
|
||||
TimeInForce::Gtc,
|
||||
element.current_price,
|
||||
element.base_qty_ordered,
|
||||
&futures_exchange_info_map,
|
||||
&client
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -8,7 +8,8 @@ pub mod strategy_007;
|
|||
pub mod strategy_008;
|
||||
pub mod strategy_009;
|
||||
pub mod strategy_010;
|
||||
pub mod future_strategy;
|
||||
pub mod future_strategy_short;
|
||||
pub mod future_strategy_long;
|
||||
// pub mod strategy_test;
|
||||
pub mod strategy_manager;
|
||||
|
||||
|
|
@ -21,13 +22,13 @@ use crate::value_estimation_team::datapoints::price_data::{CandleType, RealtimeP
|
|||
use crate::value_estimation_team::indicators::adx::{adx, AdxData};
|
||||
use crate::value_estimation_team::indicators::bollingerband::{bollingerband, BollingerBandData};
|
||||
use crate::value_estimation_team::indicators::dema::{dema, DemaData};
|
||||
use crate::value_estimation_team::indicators::ema::{ema, ema_opclo, EmaData};
|
||||
use crate::value_estimation_team::indicators::ema::{ema, ema_opclo, EmaData, ema_open};
|
||||
use crate::value_estimation_team::indicators::heatmap_volume::{
|
||||
heatmap_volume, HeatMapLevel, HeatmapVolumeData,
|
||||
};
|
||||
use crate::value_estimation_team::indicators::macd::{ema_macd, MacdData};
|
||||
use crate::value_estimation_team::indicators::rsi::{rsi, RsiData};
|
||||
use crate::value_estimation_team::indicators::sma::{sma, sma_opclo, SmaData};
|
||||
use crate::value_estimation_team::indicators::sma::{sma, sma_opclo,sma_open, SmaData};
|
||||
use crate::value_estimation_team::indicators::stoch_rsi::{stoch_rsi, StochRsiData};
|
||||
use crate::value_estimation_team::indicators::supertrend::{
|
||||
supertrend, SuperTrendArea, SuperTrendSignal, SupertrendData,
|
||||
|
|
@ -36,6 +37,8 @@ use crate::value_estimation_team::indicators::tema::{tema, TemaData};
|
|||
use crate::value_estimation_team::indicators::wiliams_percent_r::{
|
||||
wiliams_percent_r, WiliamsPercentR,
|
||||
};
|
||||
use crate::value_estimation_team::indicators::linear_regression::{LrData, linear_regression};
|
||||
use crate::future::Position;
|
||||
use futures::future::try_join_all;
|
||||
use reqwest::{Client, ClientBuilder};
|
||||
use rust_decimal::prelude::Zero;
|
||||
|
|
@ -44,8 +47,9 @@ use rust_decimal_macros::dec;
|
|||
use sqlx::FromRow;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
use strategy_manager::insert_pre_suggested_coins;
|
||||
use strategy_manager::{insert_pre_suggested_coins, insert_future_coins};
|
||||
use tokio::sync::Mutex;
|
||||
use crate::future::FuturesExchangeInfo;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AllData {
|
||||
|
|
@ -156,6 +160,54 @@ pub async fn duplicate_filter(
|
|||
Ok(filtered_data_c)
|
||||
}
|
||||
|
||||
pub async fn future_duplicate_filter(
|
||||
position: Position,
|
||||
original_filtered_data: &HashMap<String, FilteredDataValue>,
|
||||
future_exchange_info_map: &HashMap<String, FuturesExchangeInfo>
|
||||
) -> Result<HashMap<String, FilteredDataValue>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let inspect_table_name_1 = String::from("future_ordered_coin_list");
|
||||
|
||||
let mut filtered_data: HashMap<String, FilteredDataValue> = HashMap::new();
|
||||
let mut filtered_data_arc: Arc<Mutex<HashMap<String, FilteredDataValue>>> =
|
||||
Arc::new(Mutex::new(filtered_data));
|
||||
let mut task_vec = Vec::new();
|
||||
|
||||
for (symbol, filtered_data) in original_filtered_data {
|
||||
|
||||
let mut exists_condition_build = String::from("symbol=\'");
|
||||
exists_condition_build.push_str(symbol.as_str());
|
||||
// exists_condition_build.push_str("\' AND close_time=");
|
||||
// exists_condition_build.push_str(filtered_data.closetime.to_string().as_str());
|
||||
exists_condition_build.push_str("\' AND position=");
|
||||
exists_condition_build.push_str("\'");
|
||||
exists_condition_build.push_str(position.to_string().as_str());
|
||||
exists_condition_build.push_str("\'");
|
||||
|
||||
let exists_condition = Some(exists_condition_build);
|
||||
let exists_condition_c = exists_condition.clone();
|
||||
let inspect_table_name_1_c = inspect_table_name_1.clone();
|
||||
let future_exchange_info_map_c = future_exchange_info_map.clone();
|
||||
let symbol_c = symbol.clone();
|
||||
let filtered_data_c = filtered_data.clone();
|
||||
let filtered_data_arc_c = Arc::clone(&filtered_data_arc);
|
||||
task_vec.push(tokio::spawn(async move {
|
||||
if future_exchange_info_map_c.contains_key(&symbol_c) {
|
||||
let inspect_result_1 =
|
||||
exists_record(&inspect_table_name_1_c, &exists_condition_c).await;
|
||||
|
||||
if inspect_result_1 == false
|
||||
{
|
||||
let mut filtered_data_lock = filtered_data_arc_c.lock().await;
|
||||
filtered_data_lock.insert(symbol_c, filtered_data_c);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
try_join_all(task_vec).await?;
|
||||
let filtered_data_c = filtered_data_arc.lock().await.clone();
|
||||
Ok(filtered_data_c)
|
||||
}
|
||||
|
||||
pub async fn remove_keys(
|
||||
filtered_data: &mut HashMap<String, FilteredDataValue>,
|
||||
keys_to_remove: HashSet<String>,
|
||||
|
|
@ -189,14 +241,15 @@ pub async fn get_current_price_f64(
|
|||
pub async fn get_current_price_decimal(
|
||||
symbol: &String,
|
||||
rt_price_map: &HashMap<String, Vec<RealtimePriceData>>,
|
||||
) -> Option<Decimal> {
|
||||
) -> Option<(Decimal, i64)> {
|
||||
if let Some(rt_vec) = rt_price_map.get(symbol) {
|
||||
if rt_vec.last().is_some_and(|a| a.close_price.is_normal()) {
|
||||
let current_price = rust_decimal::prelude::FromPrimitive::from_f64(
|
||||
rt_vec.last().unwrap().close_price,
|
||||
)
|
||||
.unwrap();
|
||||
return Some(current_price);
|
||||
let close_time = rt_vec.last().unwrap().close_time;
|
||||
return Some((current_price, close_time));
|
||||
}
|
||||
}
|
||||
None
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::value_estimation_team::indicators::wiliams_percent_r;
|
|||
|
||||
use super::{
|
||||
adx, dec, decimal_add, decimal_div, decimal_mul, decimal_sub, dema, duplicate_filter, ema,
|
||||
ema_macd, exists_record, get_current_price, get_server_epoch, heatmap_volume,
|
||||
ema_macd, exists_record, get_server_epoch, heatmap_volume,
|
||||
insert_pre_suggested_coins, limit_order_sell, remove_keys, rsi, select_filled_buy_orders,
|
||||
stoch_rsi, supertrend, tema, try_join_all, update_record3, wiliams_percent_r, AdxData, AllData,
|
||||
Arc, BollingerBandData, Client, ClientBuilder, Decimal, DemaData, EmaData, ExchangeInfo,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use crate::coex::exchange_team::*;
|
||||
use crate::coex::order_team::*;
|
||||
use crate::future::Position;
|
||||
use csv::{DeserializeRecordsIter, StringRecord};
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
use rust_decimal::Decimal;
|
||||
use serde::Deserialize;
|
||||
use crate::future::FuturesExchangeInfo;
|
||||
|
||||
// use super::strategy_test;
|
||||
use super::{
|
||||
|
|
@ -30,6 +32,7 @@ struct Record {
|
|||
|
||||
pub async fn execute_list_up_for_buy(
|
||||
all_data: &AllData,
|
||||
future_exchange_info_map: &HashMap<String, FuturesExchangeInfo>
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
// crate::strategy_team::strategy_001::list_up_for_buy(all_data).await;
|
||||
// crate::strategy_team::strategy_002::list_up_for_buy(all_data).await;
|
||||
|
|
@ -38,9 +41,10 @@ pub async fn execute_list_up_for_buy(
|
|||
// crate::strategy_team::strategy_005::list_up_for_buy(all_data).await;
|
||||
// crate::strategy_team::strategy_006::list_up_for_buy(all_data).await;
|
||||
// crate::strategy_team::strategy_007::list_up_for_buy(all_data).await;
|
||||
crate::strategy_team::strategy_008::list_up_for_buy(all_data).await;
|
||||
crate::strategy_team::strategy_009::list_up_for_buy(all_data).await;
|
||||
crate::strategy_team::future_strategy::list_up_for_buy(all_data).await;
|
||||
// crate::strategy_team::strategy_008::list_up_for_buy(all_data).await;
|
||||
// crate::strategy_team::strategy_009::list_up_for_buy(all_data).await;
|
||||
crate::strategy_team::future_strategy_long::list_up_for_buy(all_data, &future_exchange_info_map).await;
|
||||
crate::strategy_team::future_strategy_short::list_up_for_buy(all_data, &future_exchange_info_map).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -48,6 +52,7 @@ pub async fn execute_list_up_for_buy(
|
|||
pub async fn execute_list_up_for_sell(
|
||||
all_data: &AllData,
|
||||
exchange_info_map: &HashMap<String, ExchangeInfo>,
|
||||
futures_exchange_info_map: &HashMap<String, FuturesExchangeInfo>,
|
||||
trade_fee_map: &HashMap<String, TradeFee>,
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
// crate::strategy_team::strategy_001::list_up_for_sell(&all_data, &exchange_info_map, &trade_fee_map).await;
|
||||
|
|
@ -67,20 +72,21 @@ pub async fn execute_list_up_for_sell(
|
|||
// &trade_fee_map,
|
||||
// )
|
||||
// .await;
|
||||
crate::strategy_team::strategy_008::list_up_for_sell(
|
||||
&all_data,
|
||||
&exchange_info_map,
|
||||
&trade_fee_map,
|
||||
)
|
||||
.await;
|
||||
|
||||
crate::strategy_team::strategy_009::list_up_for_sell(
|
||||
&all_data,
|
||||
&exchange_info_map,
|
||||
&trade_fee_map,
|
||||
)
|
||||
.await;
|
||||
// crate::strategy_team::strategy_008::list_up_for_sell(
|
||||
// &all_data,
|
||||
// &exchange_info_map,
|
||||
// &trade_fee_map,
|
||||
// )
|
||||
// .await;
|
||||
|
||||
// crate::strategy_team::strategy_009::list_up_for_sell(
|
||||
// &all_data,
|
||||
// &exchange_info_map,
|
||||
// &trade_fee_map,
|
||||
// )
|
||||
// .await;
|
||||
crate::strategy_team::future_strategy_long::list_up_for_sell(&all_data, futures_exchange_info_map).await;
|
||||
crate::strategy_team::future_strategy_short::list_up_for_sell(&all_data, futures_exchange_info_map).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -131,3 +137,60 @@ pub async fn insert_pre_suggested_coins(
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn insert_future_coins(
|
||||
position: Position,
|
||||
server_epoch: i64,
|
||||
filtered_coins: &HashMap<String, FilteredDataValue>
|
||||
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||
let mut insert_table_name = String::from("future_ordered_coin_list");
|
||||
let insert_columns = vec![
|
||||
"order_type",
|
||||
"status",
|
||||
"symbol",
|
||||
"order_id",
|
||||
"position",
|
||||
"registered_server_epoch",
|
||||
"transact_time",
|
||||
"close_time",
|
||||
"used_usdt",
|
||||
"expected_get_usdt",
|
||||
"pnl",
|
||||
"entry_price",
|
||||
"current_price",
|
||||
"target_percent",
|
||||
"stoploss_percent",
|
||||
"base_qty_ordered",
|
||||
"pure_profit_percent",
|
||||
"minimum_profit_percent",
|
||||
"maximum_profit_percent",
|
||||
];
|
||||
|
||||
for (symbol, filtered_data) in filtered_coins {
|
||||
let mut insert_values = vec![
|
||||
String::from("POSITIONING"), // order_type
|
||||
String::from("LISTUP"), // status
|
||||
symbol.clone(), // symbol
|
||||
0.to_string(), // order_id
|
||||
position.to_string(), // position
|
||||
server_epoch.to_string(), // registered_server_epoch
|
||||
0.to_string(), // transact_time
|
||||
filtered_data.closetime.to_string(), // close_time
|
||||
0.0.to_string(), // used_usdt
|
||||
0.0.to_string(), // expected_get_usdt
|
||||
0.0.to_string(), // pnl
|
||||
0.0.to_string(), // entry_price
|
||||
0.0.to_string(), // current_price
|
||||
filtered_data.target_price.to_string(), // target_percent
|
||||
filtered_data.stoploss.to_string(), // stoploss_percent
|
||||
0.0.to_string(), // base_qty_ordered
|
||||
0.0.to_string(), // pure_profit_percent
|
||||
0.0.to_string(), // minimum_profit_percent
|
||||
0.0.to_string(), // maximum_profit_percent
|
||||
];
|
||||
|
||||
insert_one_record(&insert_table_name, &insert_columns, &insert_values).await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,7 +132,67 @@ pub async fn ema_opclo(
|
|||
ema_prev = sma_for_initial_value;
|
||||
|
||||
for element in partial_vec2 {
|
||||
ema_t = (1.0 - alpha) * ema_prev + alpha * element.close_price;
|
||||
ema_t = (1.0 - alpha) * ema_prev + alpha * element.opclo_price;
|
||||
|
||||
ema_data.ema_value = ema_t;
|
||||
ema_data.close_time = element.close_time;
|
||||
ema_data_vec.push(ema_data.clone());
|
||||
|
||||
ema_prev = ema_t;
|
||||
}
|
||||
}
|
||||
let mut ema_data_wrapper_lock = ema_data_wrapper_arc_c.lock().await;
|
||||
ema_data_wrapper_lock.insert(symbol_c, ema_data_vec.clone());
|
||||
}));
|
||||
}
|
||||
}
|
||||
try_join_all(task_vec).await?;
|
||||
let a = ema_data_wrapper_arc.lock().await.to_owned();
|
||||
Ok(a)
|
||||
}
|
||||
|
||||
pub async fn ema_open(
|
||||
moving_number: usize,
|
||||
input_rt_data: &HashMap<String, Vec<RealtimePriceData>>,
|
||||
filtered_symbols: &HashMap<String, FilteredDataValue>,
|
||||
) -> Result<HashMap<String, Vec<EmaData>>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
if filtered_symbols.is_empty() {
|
||||
Err("Err")?;
|
||||
}
|
||||
|
||||
let alpha: f64 = 2.0 / (moving_number as f64 + 1.0);
|
||||
let mut ema_t: f64 = 0.0;
|
||||
let mut ema_prev: f64 = 0.0;
|
||||
|
||||
let mut ema_data_wrapper: HashMap<String, Vec<EmaData>> = HashMap::new();
|
||||
let mut ema_data_wrapper_arc = Arc::new(Mutex::new(ema_data_wrapper));
|
||||
let mut task_vec = Vec::new();
|
||||
for (symbol, filtered_data) in filtered_symbols {
|
||||
if let Some(rt_data_vec) = input_rt_data.get(symbol) {
|
||||
let ema_data_wrapper_arc_c = Arc::clone(&ema_data_wrapper_arc);
|
||||
let mut ema_data = EmaData::new();
|
||||
let mut ema_data_vec: Vec<EmaData> = Vec::new();
|
||||
let symbol_c = symbol.clone();
|
||||
let rt_data_vec_c = rt_data_vec.clone();
|
||||
task_vec.push(tokio::spawn(async move {
|
||||
if rt_data_vec_c.len() > moving_number {
|
||||
let partial_vec1 = rt_data_vec_c.get(..moving_number).unwrap();
|
||||
let partial_vec2 = rt_data_vec_c.get(moving_number..).unwrap();
|
||||
|
||||
let mut sma_for_initial_value = 0.0;
|
||||
for element in partial_vec1 {
|
||||
sma_for_initial_value += element.open_price;
|
||||
}
|
||||
sma_for_initial_value /= moving_number as f64;
|
||||
|
||||
ema_data.ema_value = sma_for_initial_value;
|
||||
ema_data.close_time = partial_vec1.last().unwrap().close_time;
|
||||
ema_data_vec.push(ema_data.clone());
|
||||
|
||||
ema_prev = sma_for_initial_value;
|
||||
|
||||
for element in partial_vec2 {
|
||||
ema_t = (1.0 - alpha) * ema_prev + alpha * element.open_price;
|
||||
|
||||
ema_data.ema_value = ema_t;
|
||||
ema_data.close_time = element.close_time;
|
||||
|
|
|
|||
96
src/value_estimation_team/indicators/linear_regression.rs
Normal file
96
src/value_estimation_team/indicators/linear_regression.rs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
#![allow(unused)]
|
||||
#![allow(warnings)]
|
||||
|
||||
use super::HashMap;
|
||||
use crate::database_control::*;
|
||||
use crate::strategy_team::FilteredDataValue;
|
||||
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
|
||||
use futures::future::try_join_all;
|
||||
use serde::Deserialize;
|
||||
use sqlx::FromRow;
|
||||
use std::sync::Arc;
|
||||
use tokio::{fs::*, io::AsyncWriteExt, sync::Mutex, time::*};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LrData {
|
||||
pub lr_value: f64, // linear regression value
|
||||
pub r_squared: f64,
|
||||
pub close_time: i64,
|
||||
}
|
||||
impl LrData {
|
||||
fn new() -> LrData {
|
||||
let a = LrData {
|
||||
lr_value: 0.0,
|
||||
r_squared: 0.0,
|
||||
close_time: 0,
|
||||
};
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
// Binance MA (closeprice)
|
||||
pub async fn linear_regression(
|
||||
length: usize,
|
||||
offset: usize,
|
||||
input_rt_data: &HashMap<String, Vec<RealtimePriceData>>,
|
||||
filtered_symbols: &HashMap<String, FilteredDataValue>,
|
||||
) -> Result<HashMap<String, Vec<LrData>>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
if filtered_symbols.is_empty() {
|
||||
Err("Err")?;
|
||||
}
|
||||
|
||||
let mut lr_data_wrapper: HashMap<String, Vec<LrData>> = HashMap::new();
|
||||
let mut lr_data_wrapper_arc = Arc::new(Mutex::new(lr_data_wrapper));
|
||||
|
||||
let mut task_vec = Vec::new();
|
||||
for (symbol, filtered_data) in filtered_symbols {
|
||||
if let Some(vec) = input_rt_data.get(symbol) {
|
||||
let lr_data_wrapper_arc_c = Arc::clone(&lr_data_wrapper_arc);
|
||||
let symbol_c = symbol.clone();
|
||||
|
||||
if vec.len() >= length {
|
||||
let rt_price_data = vec.clone();
|
||||
task_vec.push(tokio::spawn(async move {
|
||||
let mut lr_data_vec: Vec<LrData> = Vec::new();
|
||||
|
||||
for window in rt_price_data.windows(length) {
|
||||
// Calculate prediction of linear regression
|
||||
let mut lr_data = LrData::new();
|
||||
let x: Vec<f64> = (0..length).map(|x| x as f64).collect();
|
||||
let y: Vec<f64> = window.iter().map(|x| x.close_price).collect();
|
||||
|
||||
let x_mean: f64 = x.iter().sum::<f64>() / x.len() as f64;
|
||||
let y_mean: f64 = y.iter().sum::<f64>() / y.len() as f64;
|
||||
|
||||
let numerator: f64 = x.iter().zip(y.iter()).map(|(x_i, y_i)| (x_i - x_mean) * (y_i - y_mean)).sum();
|
||||
let denominator: f64 = x.iter().map(|x_i| (x_i - x_mean).powi(2)).sum();
|
||||
|
||||
let slope = numerator / denominator;
|
||||
let intercept = y_mean - slope * x_mean;
|
||||
|
||||
let linreg = intercept + slope * (length as f64 - 1.0 - offset as f64);
|
||||
|
||||
// Calculate R-Squared
|
||||
let ss_tot: f64 = y.iter().map(|y_i| (y_i - y_mean).powi(2)).sum();
|
||||
let ss_res: f64 = y.iter().zip(x.iter()).map(|(y_i, x_i)| {
|
||||
let y_pred = intercept + slope * x_i;
|
||||
(y_i - y_pred).powi(2)
|
||||
}).sum();
|
||||
let r_squared = 1.0 - (ss_res / ss_tot);
|
||||
let r_squared_rounded = (r_squared * 100.0).round() / 100.0;
|
||||
|
||||
lr_data.lr_value = linreg;
|
||||
lr_data.r_squared = r_squared_rounded;
|
||||
lr_data.close_time = window.last().unwrap().close_time;
|
||||
lr_data_vec.push(lr_data.clone());
|
||||
}
|
||||
let mut lr_data_wrapper_lock = lr_data_wrapper_arc_c.lock().await;
|
||||
lr_data_wrapper_lock.insert(symbol_c, lr_data_vec.clone());
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
try_join_all(task_vec).await?;
|
||||
let a = lr_data_wrapper_arc.lock().await.to_owned();
|
||||
Ok(a)
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ pub mod stoch_rsi;
|
|||
pub mod supertrend;
|
||||
pub mod tema;
|
||||
pub mod wiliams_percent_r;
|
||||
pub mod linear_regression;
|
||||
|
||||
use crate::strategy_team::FilteredDataValue;
|
||||
use crate::value_estimation_team::datapoints::price_data::RealtimePriceData;
|
||||
|
|
|
|||
|
|
@ -119,3 +119,51 @@ pub async fn sma_opclo(
|
|||
let a = sma_data_wrapper_arc.lock().await.to_owned();
|
||||
Ok(a)
|
||||
}
|
||||
|
||||
// Binance MA (open)
|
||||
pub async fn sma_open(
|
||||
moving_number: usize,
|
||||
input_rt_data: &HashMap<String, Vec<RealtimePriceData>>,
|
||||
filtered_symbols: &HashMap<String, FilteredDataValue>,
|
||||
) -> Result<HashMap<String, Vec<SmaData>>, Box<dyn std::error::Error + Send + Sync>> {
|
||||
if filtered_symbols.is_empty() {
|
||||
Err("Err")?;
|
||||
}
|
||||
|
||||
let mut sma_data_wrapper: HashMap<String, Vec<SmaData>> = HashMap::new();
|
||||
let mut sma_data_wrapper_arc = Arc::new(Mutex::new(sma_data_wrapper));
|
||||
|
||||
let mut task_vec = Vec::new();
|
||||
for (symbol, filtered_data) in filtered_symbols {
|
||||
if let Some(vec) = input_rt_data.get(symbol) {
|
||||
let sma_data_wrapper_arc_c = Arc::clone(&sma_data_wrapper_arc);
|
||||
let symbol_c = symbol.clone();
|
||||
let rt_price_data = vec.clone();
|
||||
task_vec.push(tokio::spawn(async move {
|
||||
let mut sma_data = SmaData::new();
|
||||
let mut sma_data_vec: Vec<SmaData> = Vec::new();
|
||||
|
||||
if rt_price_data.len() >= moving_number {
|
||||
let mut iter = rt_price_data.windows(moving_number);
|
||||
for buffer in iter {
|
||||
let mut avg = 0.0;
|
||||
for element in buffer {
|
||||
avg += element.open_price;
|
||||
}
|
||||
avg /= (moving_number as f64);
|
||||
|
||||
sma_data.sma_value = avg;
|
||||
sma_data.close_time = buffer.last().unwrap().close_time;
|
||||
sma_data_vec.push(sma_data.clone());
|
||||
}
|
||||
}
|
||||
let mut sma_data_wrapper_lock = sma_data_wrapper_arc_c.lock().await;
|
||||
sma_data_wrapper_lock.insert(symbol_c, sma_data_vec.clone());
|
||||
}));
|
||||
}
|
||||
}
|
||||
try_join_all(task_vec).await?;
|
||||
let a = sma_data_wrapper_arc.lock().await.to_owned();
|
||||
Ok(a)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user