use crate::trading::okx::okx_client;
use reqwest::Method;
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};
use crate::trading::okx::okx_client::OkxApiResponse;
use anyhow::{anyhow, Result};
use crate::app_init;
use crate::trading::order::swap_ordr::SwapOrder;
#[derive(Serialize, Deserialize, Debug)]
pub struct CandleData {
pub ts: String,
pub o: String,
pub h: String,
pub l: String,
pub c: String,
pub vol: String,
pub vol_ccy: String,
pub vol_ccy_quote: String,
pub confirm: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Ts {
ts: String,
}
pub type CandleResponse = OkxApiResponse<Vec<CandleData>>;
pub type TimeResponse = OkxApiResponse<Vec<Ts>>;
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct OrderResponse {
pub code: String,
pub msg: Option<String>,
pub data: Vec<OrderResponseData>,
pub in_time: String,
pub out_time: String,
}
pub enum Side {
BUY,
SELL,
}
impl Display for Side {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Side::BUY => write!(f, "buy"),
Side::SELL => write!(f, "sell"),
}
}
}
pub enum MgnMode {}
#[derive(Clone, Copy)]
pub enum PosSide {
LONG,
SHORT,
}
impl Display for PosSide {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
PosSide::LONG => write!(f, "long"),
PosSide::SHORT => write!(f, "short"),
}
}
}
impl PartialEq for PosSide {
fn eq(&self, other: &Self) -> bool {
self.to_string() == other.to_string()
}
}
pub enum OrdType {
LIMIT,
MARKET,
PostOnly,
FOK,
Ioc,
OptimalLimitIoc,
}
impl Display for OrdType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
OrdType::LIMIT => write!(f, "limit"),
OrdType::MARKET => write!(f, "market"),
OrdType::PostOnly => write!(f, "post_only"),
OrdType::FOK => write!(f, "fok"),
OrdType::Ioc => write!(f, "ioc"),
OrdType::OptimalLimitIoc => write!(f, "optimal_limit_ioc"),
}
}
}
pub enum TdMode {
ISOLATED,
CROSS,
CASH,
}
pub enum TpOrdKind {
CONDITION,
LIMIT,
}
impl Display for TpOrdKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
TpOrdKind::CONDITION => write!(f, "condition"),
TpOrdKind::LIMIT => write!(f, "limit"),
}
}
}
impl Display for TdMode {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
TdMode::ISOLATED => write!(f, "isolated"),
TdMode::CROSS => write!(f, "cross"),
TdMode::CASH => write!(f, "cash"),
}
}
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct OrderRequest {
pub inst_id: String,
pub td_mode: String,
pub ccy: Option<String>,
pub cl_ord_id: Option<String>,
pub tag: Option<String>,
pub side: String,
pub pos_side: Option<String>,
pub ord_type: String,
pub sz: String,
pub px: Option<String>,
pub px_usd: Option<String>,
pub px_vol: Option<String>,
pub reduce_only: Option<bool>,
pub tgt_ccy: Option<String>,
pub ban_amend: Option<bool>,
pub quick_mgn_type: Option<String>,
pub stp_id: Option<String>,
pub stp_mode: Option<String>,
pub attach_algo_ords: Option<Vec<AttachAlgoOrd>>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct AttachAlgoOrd {
pub attach_algo_cl_ord_id: Option<String>,
pub tp_trigger_px: Option<String>,
pub tp_ord_px: Option<String>,
pub tp_ord_kind: Option<String>,
pub sl_trigger_px: Option<String>,
pub sl_ord_px: Option<String>,
pub tp_trigger_px_type: Option<String>,
pub sl_trigger_px_type: Option<String>,
pub sz: Option<String>,
pub amend_px_on_trigger_type: Option<i32>,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct OrderResponseData {
pub ord_id: String,
pub cl_ord_id: Option<String>,
pub tag: Option<String>,
pub ts: String,
pub s_code: String,
pub s_msg: Option<String>,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct CloseOrderRequest {
pub inst_id: String,
pub pos_side: Option<String>,
pub mgn_mode: String,
pub ccy: Option<String>,
pub auto_cxl: Option<bool>,
pub cl_ord_id: Option<String>,
pub tag: Option<String>,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct CloseOrderResponseData {
pub inst_id: String,
pub pos_side: Option<String>,
pub cl_ord_id: Option<String>,
pub tag: Option<String>,
}
type CloseOrderResponse = OkxApiResponse<Vec<CloseOrderResponseData>>;
pub struct OkxTradePositonClient {}
impl OkxTradePositonClient {
pub fn new() -> Self {
OkxTradePositonClient {}
}
pub async fn order(
&self,
params: OrderRequest,
) -> anyhow::Result<Vec<OrderResponseData>> {
let path = "/api/v5/trade/order";
let body = &serde_json::to_string(¶ms).unwrap();
debug!("send place order okx_request params:{}", body);
let res: Vec<OrderResponseData> = okx_client::get_okx_client()
.send_request(Method::POST, &path, body)
.await?;
Ok(res)
}
pub async fn close_position(
&self,
params: CloseOrderRequest,
) -> Result<Vec<CloseOrderResponseData>, anyhow::Error> {
let path = "/api/v5/trade/close-position";
let body = &serde_json::to_string(¶ms).unwrap();
debug!("send close_position okx_request params:{}", body);
let res: Result<CloseOrderResponse> = okx_client::get_okx_client()
.send_request(Method::POST, &path, body)
.await;
Ok(res.unwrap().data)
}
}
#[tokio::test]
async fn test_order() {
app_init().await;
let okx_trade = OkxTradePositonClient::new();
let params = OrderRequest {
inst_id: "BTC-USDT-SWAP".to_string(),
td_mode: "cross".to_string(),
ccy: None,
cl_ord_id: Some(SwapOrder::new().get_cl_ord_id()),
tag: None,
side: "sell".to_string(),
pos_side: Some("short".to_string()),
ord_type: "market".to_string(),
sz: "1".to_string(),
px: None,
px_usd: None,
px_vol: None,
reduce_only: None,
tgt_ccy: None,
ban_amend: None,
quick_mgn_type: None,
stp_id: None,
stp_mode: None,
attach_algo_ords: None,
};
let res = okx_trade.order(params).await;
println!("res: {:?}", res);
assert!(res.is_ok());
assert_eq!(res.unwrap().get(0).unwrap().ord_id.len() > 0, true);
}