Implement closing position

This commit is contained in:
Sik Yoon 2024-05-21 14:16:45 +09:00
parent 1337a78d73
commit 5e5f0cbc05
3 changed files with 197 additions and 82 deletions

View File

@ -88,8 +88,6 @@ pub async fn entry_position(
// order the symbol based on base_qty_ordered and current_price // order the symbol based on base_qty_ordered and current_price
limit_order_entry( limit_order_entry(
&element, &element,
futures_exchange_info_map,
trade_fee,
TimeInForce::Gtc, TimeInForce::Gtc,
entry_price, entry_price,
base_qty_ordered, base_qty_ordered,
@ -108,8 +106,6 @@ pub async fn entry_position(
pub async fn limit_order_entry( pub async fn limit_order_entry(
entry_coin_info: &PositionCoinList, entry_coin_info: &PositionCoinList,
exchange_info_map: &HashMap<String, FuturesExchangeInfo>,
trade_fee: Decimal,
tif: TimeInForce, tif: TimeInForce,
order_price: Decimal, order_price: Decimal,
order_quantity: Decimal, order_quantity: Decimal,
@ -136,7 +132,7 @@ pub async fn limit_order_entry(
sub_future_available_usdt(used_usdt).await; sub_future_available_usdt(used_usdt).await;
println!("SIMUL {} {}", entry_coin_info.position, entry_coin_info.symbol); println!("SIMUL positining {} {}", entry_coin_info.position, entry_coin_info.symbol);
} else { } else {
// building URL and API-keys // building URL and API-keys
let mut url = String::new(); let mut url = String::new();
@ -209,10 +205,148 @@ pub async fn limit_order_entry(
update_values.push((String::from("status"), String::from("NEW"))); update_values.push((String::from("status"), String::from("NEW")));
} else if T.get("status").unwrap().as_str().unwrap() == "FILLED" { } else if T.get("status").unwrap().as_str().unwrap() == "FILLED" {
update_values.push((String::from("status"), String::from("FILLED"))); update_values.push((String::from("status"), String::from("FILLED")));
println!("{} {}", entry_coin_info.position, entry_coin_info.symbol); println!("positioning {} {}", entry_coin_info.position, entry_coin_info.symbol);
} else if T.get("status").unwrap().as_str().unwrap() == "PARTIALLY_FILLED" { } else if T.get("status").unwrap().as_str().unwrap() == "PARTIALLY_FILLED" {
update_values.push((String::from("status"), String::from("PARTIALLY_FILLED"))); update_values.push((String::from("status"), String::from("PARTIALLY_FILLED")));
println!("Partially filled {} {}", entry_coin_info.position, entry_coin_info.symbol); println!("positioning {} {} (Partially filled)", entry_coin_info.position, entry_coin_info.symbol);
}
let base_qty_ordered = rust_decimal::prelude::FromStr::from_str(
T.get("origQty").unwrap().as_str().unwrap(),
)
.unwrap();
let cummulative_quote_qty = rust_decimal::prelude::FromStr::from_str(
T.get("cumQuote").unwrap().as_str().unwrap(),
)
.unwrap();
// let base_asset_precision = exchange_info_vec
// .iter()
// .find(|exchange_info| exchange_info.symbol == element.symbol)
// .unwrap()
// .base_asset_precision;
let entry_price = decimal_div(cummulative_quote_qty, base_qty_ordered)
.round_dp_with_strategy(8, RoundingStrategy::ToZero);
update_values.push((String::from("used_usdt"), cummulative_quote_qty.to_string()));
update_values.push((String::from("entry_price"), entry_price.to_string()));
update_values.push((String::from("base_qty_ordered"), base_qty_ordered.to_string()));
// reflect available_usdt in [asset_manage_announcement]
// sub_available_usdt(cummulative_quote_qty).await;
sub_future_available_usdt(cummulative_quote_qty).await;
let update_condition = vec![(String::from("id"), entry_coin_info.id.to_string())];
update_record2(&update_table_name, &update_values, &update_condition)
.await.unwrap();
}
},
Err(e) => {
log::warn!("order failed!: {}", body);
}
}
}
}
Ok(())
}
pub async fn limit_order_close(
entry_coin_info: &PositionCoinList,
tif: TimeInForce,
order_price: Decimal,
order_quantity: Decimal,
client: &Client,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let update_table_name = String::from("future_ordered_coin_list");
let server_epoch = get_server_epoch().await;
unsafe {
if RUNNING_MODE == SIMUL {
let mut update_values = vec![
(String::from("order_type"), String::from("CLOSING")),
(String::from("status"), String::from("FILLED")),
];
let update_condition = vec![(String::from("id"), entry_coin_info.id.to_string())];
update_record2(&update_table_name, &update_values, &update_condition)
.await.unwrap();
println!("SIMUL closed {} {}", entry_coin_info.position, entry_coin_info.symbol);
} else {
// building URL and API-keys
let mut url = String::new();
let mut api_key = String::new();
if RUNNING_MODE == TEST {
url.push_str(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/v1/order?";
url.push_str(endpoint_url);
let mut url_build = String::new();
// add parameters into URL
url_build.push_str("&symbol=");
url_build.push_str(&entry_coin_info.symbol);
if entry_coin_info.position.contains("Long") {
url_build.push_str("&side=SELL");
} else {
url_build.push_str("&side=BUY");
}
url_build.push_str("&type=LIMIT");
url_build.push_str("&quantity=");
url_build.push_str(order_quantity.to_string().as_str());
url_build.push_str("&price=");
url_build.push_str(order_price.to_string().as_str());
match tif {
TimeInForce::Gtc => {
url_build.push_str("&timeInForce=GTC");
}
TimeInForce::Ioc => {
url_build.push_str("&timeInForce=IOC");
}
TimeInForce::Fok => {
url_build.push_str("&timeInForce=FOK");
}
}
hmac_signature(&mut url_build).await;
url.push_str(&url_build);
let res = client
.post(&url)
.header("X-MBX-APIKEY", api_key)
.send()
.await
.unwrap();
let body = res.text_with_charset("utf-8").await.unwrap();
// deserialize JSON and then insert record into table
let v = serde_json::from_str::<Value>(body.as_str());
match v {
Ok(T) => {
if T.get("code").is_some() { // when request failed
} else {
// when request succeed
let mut update_values = vec![];
update_values.push((String::from("order_type"), (String::from("CLOSING"))));
update_values.push((String::from("order_id"), T.get("orderId").unwrap().as_u64().unwrap().to_string()));
update_values.push((String::from("transact_time"), server_epoch.to_string()));
// status
if T.get("status").unwrap().as_str().unwrap() == "NEW" {
update_values.push((String::from("status"), String::from("NEW")));
} else if T.get("status").unwrap().as_str().unwrap() == "FILLED" {
update_values.push((String::from("status"), String::from("FILLED")));
println!("Closed {} {}", entry_coin_info.position, entry_coin_info.symbol);
} else if T.get("status").unwrap().as_str().unwrap() == "PARTIALLY_FILLED" {
update_values.push((String::from("status"), String::from("PARTIALLY_FILLED")));
println!("Closing {} {} (Partially filled)", entry_coin_info.position, entry_coin_info.symbol);
} }
let base_qty_ordered = rust_decimal::prelude::FromStr::from_str( let base_qty_ordered = rust_decimal::prelude::FromStr::from_str(

View File

@ -13,6 +13,7 @@ use super::{
use crate::future::{Position, FuturesExchangeInfo}; use crate::future::{Position, FuturesExchangeInfo};
use crate::future::table_mgmt::select_filled_positions; use crate::future::table_mgmt::select_filled_positions;
use crate::future::order::{limit_order_close, TimeInForce};
// BUY conditions // BUY conditions
pub async fn list_up_for_buy( pub async fn list_up_for_buy(
@ -74,68 +75,48 @@ pub async fn list_up_for_buy(
Ok(()) Ok(())
} }
// pub async fn list_up_for_sell( pub async fn list_up_for_sell() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// all_data: &AllData, let filled_positions = select_filled_positions().await?;
// exchange_info_map: &HashMap<String, ExchangeInfo>,
// trade_fee_map: &HashMap<String, TradeFee>,
// ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// let filled_positions = select_filled_positions().await?;
// let client = ClientBuilder::new() let client = ClientBuilder::new()
// .timeout(tokio::time::Duration::from_millis(5000)) .timeout(tokio::time::Duration::from_millis(5000))
// .build() .build()
// .unwrap(); .unwrap();
// let server_epoch = get_server_epoch().await; let server_epoch = get_server_epoch().await;
// for element in filled_positions { for element in filled_positions {
// let mut is_sell = false; let mut is_sell = false;
// if element.used_usdt >= dec!(10.0) { // TODO: BNB 코인이 있으면
// if let (Some(exchange_info), Some(tradefee)) = (
// exchange_info_map.get(&element.symbol),
// trade_fee_map.get(&element.symbol),
// ) {
// 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 = // let base_qty_to_be_ordered =
// element.base_qty_fee_adjusted.round_dp_with_strategy( // element.base_qty_ordered.round_dp_with_strategy(
// lot_step_size.normalize().scale(), // lot_step_size.normalize().scale(),
// RoundingStrategy::ToZero, // RoundingStrategy::ToZero,
// ); // );
// TODO: BNB 코인이 없으면
// if !element.current_price.is_zero() { if !element.current_price.is_zero() {
// if element.pure_profit_percent >= 1.0 { if element.pure_profit_percent >= 1.0 {
// is_sell = true; is_sell = true;
// } else if element.pure_profit_percent <= -0.8 { } else if element.pure_profit_percent <= -1.0 {
// is_sell = true; is_sell = true;
// } else if server_epoch - element.transact_time >= (1_800_000) * 1 { } else if server_epoch - element.transact_time >= (1_800_000) * 1 {
// // time up selling // time up selling
// is_sell = true; is_sell = true;
// } }
// if is_sell == true { if is_sell == true {
// limit_order_sell( limit_order_close(
// &element, &element,
// element.current_price, TimeInForce::Gtc,
// base_qty_to_be_ordered, element.current_price,
// &client, element.base_qty_ordered,
// &exchange_info_map, &client
// &trade_fee_map, )
// ) .await;
// .await; }
// } }
// } }
// }
// }
// }
// Ok(()) Ok(())
// } }

View File

@ -41,8 +41,8 @@ pub async fn execute_list_up_for_buy(
// crate::strategy_team::strategy_005::list_up_for_buy(all_data).await; // 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_006::list_up_for_buy(all_data).await;
// crate::strategy_team::strategy_007::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_008::list_up_for_buy(all_data).await;
crate::strategy_team::strategy_009::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, &future_exchange_info_map).await; crate::strategy_team::future_strategy::list_up_for_buy(all_data, &future_exchange_info_map).await;
Ok(()) Ok(())
@ -70,20 +70,20 @@ pub async fn execute_list_up_for_sell(
// &trade_fee_map, // &trade_fee_map,
// ) // )
// .await; // .await;
crate::strategy_team::strategy_008::list_up_for_sell( // crate::strategy_team::strategy_008::list_up_for_sell(
&all_data, // &all_data,
&exchange_info_map, // &exchange_info_map,
&trade_fee_map, // &trade_fee_map,
) // )
.await; // .await;
crate::strategy_team::strategy_009::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::list_up_for_sell().await;
Ok(()) Ok(())
} }