Compare commits

..

115 Commits

Author SHA1 Message Date
8b8cbb0dce Update filtering 2024-06-23 16:50:23 +09:00
6dfdc719c3 Update filtering 2024-06-08 20:00:54 +09:00
35d008450d Update filtering 2024-06-08 18:09:38 +09:00
7ea865ef54 Update filtering 2024-06-08 02:44:44 +09:00
3d821c7ec3 Update filtering 2024-06-08 02:08:20 +09:00
eae8a367dd Update filtering 2024-06-08 00:33:13 +09:00
73e72bdd3c Update filtering 2024-06-07 15:51:07 +09:00
6bb2b2df8e Update filtering 2024-06-06 19:35:46 +09:00
c0cef48c50 Update filtering 2024-06-06 14:40:37 +09:00
64f1ef7851 Update filtering 2024-06-06 14:29:49 +09:00
805d8c87e8 Update filtering 2024-06-06 14:14:09 +09:00
98a29873b9 Update filtering 2024-06-06 06:20:32 +09:00
0efc7505cb Update filtering 2024-06-06 06:01:25 +09:00
1e7be2681f Add r_squared value 2024-06-06 03:58:04 +09:00
b4dbf9e1d2 Update filtering 2024-06-06 02:40:20 +09:00
653c3835d4 Update filtering 2024-06-06 02:39:26 +09:00
0c1e8d1949 Update filtering 2024-06-06 02:19:10 +09:00
0ba0d3702d Implement linear regression 2024-06-06 02:19:03 +09:00
3990205979 Update filtering 2024-06-02 17:34:51 +09:00
54f70b47eb Update filtering 2024-06-02 02:13:08 +09:00
bbaf39173d Update filtering 2024-06-02 01:59:41 +09:00
2b08bbfd66 update filtering 2024-06-01 21:08:15 +09:00
33221f253a Update filtering 2024-06-01 20:29:44 +09:00
a3a61f24c6 Update filtering 2024-06-01 20:12:34 +09:00
dcfbf64036 Update filtering 2024-06-01 20:07:52 +09:00
f3cb66f5f6 Chnage strategy from 1m to 30m 2024-06-01 20:00:54 +09:00
9540191955 Update filtering 2024-06-01 17:57:32 +09:00
7fbf68ba11 Update filtering 2024-06-01 17:25:16 +09:00
069f1c85a6 Update filtering 2024-06-01 14:13:04 +09:00
7c0b8960cb Update filtering 2024-06-01 14:01:53 +09:00
80dad1d352 Update filtering 2024-06-01 13:42:55 +09:00
fadf1ea26a Update filtering 2024-06-01 13:31:24 +09:00
757400b35e Update filtering 2024-06-01 13:26:39 +09:00
e3479e346a Update filtering 2024-06-01 13:23:44 +09:00
83017ea8e3 Make closed position moved after wait time 2024-06-01 13:14:25 +09:00
daf1fc1256 Update filtering 2024-06-01 04:46:36 +09:00
7c1e12bcbb Update filtering 2024-06-01 04:28:40 +09:00
7f88bab770 Update filtering 2024-06-01 04:11:20 +09:00
2fa4696e81 Update filtering 2024-06-01 02:56:21 +09:00
4afe543dc4 Update filtering 2024-05-30 23:06:20 +09:00
5123ac7647 Make to buy shorter timeout 2024-05-30 22:31:35 +09:00
aac7d00e9f Update filtering 2024-05-30 19:36:26 +09:00
7483258922 Fix referring wrong index 2024-05-30 02:38:25 +09:00
65f523eee1 Make closed positions moved to new table 2024-05-30 02:33:05 +09:00
62fad62a4d Update filtering 2024-05-30 02:08:26 +09:00
f42570edc0 Update filtering 2024-05-28 00:35:16 +09:00
85935e553b Seperate quantity of close position 2024-05-26 16:30:47 +09:00
1c8f0d1eec Update filtering 2024-05-26 08:22:38 +09:00
02251bb171 Update filtering 2024-05-26 08:11:16 +09:00
2e2222377f Update filtering 2024-05-26 07:55:35 +09:00
59a5e540ac Update filtering 2024-05-26 07:53:45 +09:00
31f211c3c2 Fix wrong selection 2024-05-26 07:07:29 +09:00
75f4432090 Update filtering 2024-05-26 01:42:09 +09:00
a4ecc2df5b Update filtering 2024-05-26 00:48:46 +09:00
f438ae502b Update filtering 2024-05-25 23:42:27 +09:00
5dd0873add Update filtering 2024-05-25 21:19:48 +09:00
d50ac9b12e Update filtering 2024-05-25 21:16:26 +09:00
64e6e92747 Update filtering 2024-05-25 19:20:06 +09:00
1e64873e4c Update filtering 2024-05-25 17:45:09 +09:00
26bd898d25 Update filtering 2024-05-25 16:28:00 +09:00
143e06b1b1 Fix time comparison 2024-05-25 16:15:29 +09:00
9c5a3f0a93 Update filtering 2024-05-25 07:56:10 +09:00
12d97c109a Update filtering 2024-05-25 07:54:37 +09:00
c691999599 Update filtering 2024-05-25 06:55:29 +09:00
c1ed9af720 Update filtering 2024-05-25 06:29:23 +09:00
566ea51ee6 Update filtering 2024-05-25 05:38:19 +09:00
e1f50efd79 Update filtering 2024-05-25 05:15:55 +09:00
bc8b1f12d5 Update filtering 2024-05-25 04:50:10 +09:00
ab6ae5e667 Update filtering 2024-05-25 04:22:41 +09:00
583787432e Update filtering 2024-05-25 03:24:43 +09:00
0425911286 Update filtering 2024-05-25 03:05:34 +09:00
83a20c4393 Fix wrong index 2024-05-25 03:02:46 +09:00
0778575b74 Add checking boundary 2024-05-25 02:57:20 +09:00
a0a9f254e1 Change trade fee 2024-05-25 02:49:14 +09:00
ddd690fd66 Delete unnecessary code 2024-05-25 02:48:59 +09:00
2f83c28077 Update filtering 2024-05-25 02:27:24 +09:00
2561a1081d Update filtering 2024-05-25 00:37:31 +09:00
b867d231fa Update filtering 2024-05-24 22:57:19 +09:00
80741086d9 Make retrieve vector reversed 2024-05-24 22:30:51 +09:00
ede6c9e29d Update filtering 2024-05-24 22:07:46 +09:00
d61cc14bd5 shorten timeup 2024-05-24 21:21:25 +09:00
40193b171a Change to reterive all symbols 2024-05-24 17:02:08 +09:00
87cde15b37 Renaming 2024-05-24 17:01:31 +09:00
5937ee9be0 Add filtering 2024-05-23 00:14:50 +09:00
512bc46b39 Add strategy for long position 2024-05-23 00:14:30 +09:00
367809a919 Delete unnecessary code 2024-05-21 18:42:21 +09:00
4bce70f7b7 Fix wrong update 2024-05-21 18:40:15 +09:00
982a679703 Fix selection of filled positions 2024-05-21 16:10:51 +09:00
80252e235b Fix update record 2024-05-21 16:02:06 +09:00
1e06080ce6 Implement test code of closing query 2024-05-21 14:40:52 +09:00
a3675db92d Decrease unit trade usdt 2024-05-21 14:17:39 +09:00
5e5f0cbc05 Implement closing position 2024-05-21 14:16:45 +09:00
1337a78d73 Fix wrong insertion of characters 2024-05-21 12:57:08 +09:00
2d50f9cdbb Fix wrong column name 2024-05-21 12:47:28 +09:00
2fbce18948 Change term 2024-05-21 12:06:00 +09:00
2d19927048 Fix wrong calculation of PNL 2024-05-21 11:44:35 +09:00
6fad7b0ec4 Fix calculation of trade fee 2024-05-21 01:00:00 +09:00
970971e005 Fix calculation of profit 2024-05-21 00:43:11 +09:00
26e32279eb Replace right function 2024-05-20 23:44:23 +09:00
e68804aaf7 Change function 2024-05-20 23:29:41 +09:00
229a4c38ae Make parallel operation of setting future 2024-05-20 23:19:15 +09:00
b9621ccc72 Remove unwrap() 2024-05-20 22:41:25 +09:00
3549fbf289 Make to delete timeup LISTUP positions 2024-05-20 22:34:34 +09:00
6a1541bc95 Fix wrong subtract operation 2024-05-20 22:27:34 +09:00
6bec61cb2f Change timeout 2024-05-20 22:22:02 +09:00
30bc530f21 Change variable type 2024-05-20 22:05:07 +09:00
d2efd67c4f Change to lowercase 2024-05-20 04:08:09 +09:00
f92786fdcf Fix wrong print message 2024-05-20 04:05:37 +09:00
162ff464c3 Change code not to show unnecessary error print 2024-05-20 03:57:57 +09:00
3152d79e5c Arrange colsole print 2024-05-20 03:49:57 +09:00
5849f924e8 Increase receive window 2024-05-20 03:48:43 +09:00
5bc08eaf10 Change order 2024-05-20 03:37:32 +09:00
1822e82bb0 Merge branch 'master' of http://192.168.1.100:3000/Sik/tradingbot into future 2024-05-20 03:28:08 +09:00
0eca6971db Implement entry positioning 2024-05-20 03:16:21 +09:00
d8592f65f6 Set urls 2024-05-17 23:53:30 +09:00
17 changed files with 3854 additions and 532 deletions

View File

@ -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
View 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("&timestamp=");
query_build.push_str(&timestamp);
query_build.push_str("&recvWindow=");
query_build.push_str(&recv_window_size);
let mut secret_key = String::new();
unsafe {
if RUNNING_MODE == TEST {
secret_key.push_str(SECRET_KEY_TESTNET);
} else {
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

File diff suppressed because it is too large Load Diff

434
src/future/table_mgmt.rs Normal file
View 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;
}
}
}
}

View File

@ -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'...");

View File

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

View File

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

View File

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

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

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

View File

@ -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

View File

@ -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,

View File

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

View File

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

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

View File

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

View File

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