use serde::{Deserialize, Serialize};
use crate::client::Client;
use crate::error::Error;
use crate::models::transaction::{
MarketOrderRejectTransaction, MarketOrderTransaction, OrderCancelRejectTransaction,
OrderCancelTransaction, OrderFillTransaction, StopLossOrderRejectTransaction,
StopLossOrderTransaction, TakeProfitOrderRejectTransaction, TakeProfitOrderTransaction,
TradeClientExtensionsModifyRejectTransaction, TradeClientExtensionsModifyTransaction,
TrailingStopLossOrderRejectTransaction, TrailingStopLossOrderTransaction,
};
use crate::models::{
AccountId, ClientExtensions, InstrumentName, StopLossDetails, TakeProfitDetails, Trade,
TradeId, TradeSpecifier, TradeStateFilter, TrailingStopLossDetails, TransactionId,
};
impl Client {
pub fn list_trades(&self, account_id: impl Into<AccountId>) -> ListTradesRequest {
ListTradesRequest {
client: self.clone(),
account_id: account_id.into(),
params: Vec::new(),
}
}
pub async fn list_open_trades(
&self,
account_id: impl Into<AccountId>,
) -> Result<ListTradesResponse, Error> {
let account_id = account_id.into();
self.execute(self.get(&["accounts", account_id.as_str(), "openTrades"]))
.await
}
pub async fn trade(
&self,
account_id: impl Into<AccountId>,
trade: impl Into<TradeSpecifier>,
) -> Result<TradeResponse, Error> {
let account_id = account_id.into();
let trade = trade.into();
self.execute(self.get(&["accounts", account_id.as_str(), "trades", trade.as_str()]))
.await
}
pub fn close_trade(
&self,
account_id: impl Into<AccountId>,
trade: impl Into<TradeSpecifier>,
) -> CloseTradeRequest {
CloseTradeRequest {
client: self.clone(),
account_id: account_id.into(),
trade: trade.into(),
units: None,
}
}
pub async fn set_trade_client_extensions(
&self,
account_id: impl Into<AccountId>,
trade: impl Into<TradeSpecifier>,
extensions: ClientExtensions,
) -> Result<SetTradeClientExtensionsResponse, Error> {
let account_id = account_id.into();
let trade = trade.into();
let request = self
.put(&[
"accounts",
account_id.as_str(),
"trades",
trade.as_str(),
"clientExtensions",
])
.json(&SetTradeClientExtensionsBody {
client_extensions: extensions,
});
self.execute(request).await
}
pub fn set_trade_dependent_orders(
&self,
account_id: impl Into<AccountId>,
trade: impl Into<TradeSpecifier>,
) -> SetTradeDependentOrdersRequest {
SetTradeDependentOrdersRequest {
client: self.clone(),
account_id: account_id.into(),
trade: trade.into(),
body: SetTradeDependentOrdersBody {
take_profit: None,
stop_loss: None,
trailing_stop_loss: None,
},
}
}
}
#[derive(Debug)]
pub struct ListTradesRequest {
client: Client,
account_id: AccountId,
params: Vec<(&'static str, String)>,
}
impl ListTradesRequest {
pub fn ids<I>(mut self, ids: I) -> Self
where
I: IntoIterator,
I::Item: Into<TradeId>,
{
let joined = ids
.into_iter()
.map(|id| id.into().0)
.collect::<Vec<_>>()
.join(",");
self.params.push(("ids", joined));
self
}
pub fn state(mut self, state: TradeStateFilter) -> Self {
self.params.push(("state", state.to_string()));
self
}
pub fn instrument(mut self, instrument: impl Into<InstrumentName>) -> Self {
self.params
.push(("instrument", instrument.into().as_str().to_owned()));
self
}
pub fn count(mut self, count: u32) -> Self {
self.params.push(("count", count.to_string()));
self
}
pub fn before_id(mut self, before_id: impl Into<TradeId>) -> Self {
self.params.push(("beforeID", before_id.into().0));
self
}
pub async fn send(self) -> Result<ListTradesResponse, Error> {
let request = self
.client
.get(&["accounts", self.account_id.as_str(), "trades"])
.query(&self.params);
self.client.execute(request).await
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ListTradesResponse {
#[serde(rename = "trades", default, skip_serializing_if = "Vec::is_empty")]
pub trades: Vec<Trade>,
#[serde(rename = "lastTransactionID", skip_serializing_if = "Option::is_none")]
pub last_transaction_id: Option<TransactionId>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TradeResponse {
#[serde(rename = "trade")]
pub trade: Trade,
#[serde(rename = "lastTransactionID", skip_serializing_if = "Option::is_none")]
pub last_transaction_id: Option<TransactionId>,
}
#[derive(Debug)]
pub struct CloseTradeRequest {
client: Client,
account_id: AccountId,
trade: TradeSpecifier,
units: Option<String>,
}
#[derive(Debug, Serialize)]
struct CloseTradeBody {
#[serde(skip_serializing_if = "Option::is_none")]
units: Option<String>,
}
impl CloseTradeRequest {
pub fn units(mut self, units: impl Into<String>) -> Self {
self.units = Some(units.into());
self
}
pub async fn send(self) -> Result<CloseTradeResponse, Error> {
let request = self
.client
.put(&[
"accounts",
self.account_id.as_str(),
"trades",
self.trade.as_str(),
"close",
])
.json(&CloseTradeBody { units: self.units });
self.client.execute(request).await
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CloseTradeResponse {
#[serde(
rename = "orderCreateTransaction",
skip_serializing_if = "Option::is_none"
)]
pub order_create_transaction: Option<MarketOrderTransaction>,
#[serde(
rename = "orderFillTransaction",
skip_serializing_if = "Option::is_none"
)]
pub order_fill_transaction: Option<OrderFillTransaction>,
#[serde(
rename = "orderCancelTransaction",
skip_serializing_if = "Option::is_none"
)]
pub order_cancel_transaction: Option<OrderCancelTransaction>,
#[serde(
rename = "relatedTransactionIDs",
default,
skip_serializing_if = "Vec::is_empty"
)]
pub related_transaction_ids: Vec<TransactionId>,
#[serde(rename = "lastTransactionID", skip_serializing_if = "Option::is_none")]
pub last_transaction_id: Option<TransactionId>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct CloseTradeRejectBody {
#[serde(
rename = "orderRejectTransaction",
skip_serializing_if = "Option::is_none"
)]
pub order_reject_transaction: Option<MarketOrderRejectTransaction>,
#[serde(
rename = "relatedTransactionIDs",
default,
skip_serializing_if = "Vec::is_empty"
)]
pub related_transaction_ids: Vec<TransactionId>,
#[serde(rename = "lastTransactionID", skip_serializing_if = "Option::is_none")]
pub last_transaction_id: Option<TransactionId>,
}
#[derive(Debug, Serialize)]
struct SetTradeClientExtensionsBody {
#[serde(rename = "clientExtensions")]
client_extensions: ClientExtensions,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SetTradeClientExtensionsResponse {
#[serde(
rename = "tradeClientExtensionsModifyTransaction",
skip_serializing_if = "Option::is_none"
)]
pub trade_client_extensions_modify_transaction: Option<TradeClientExtensionsModifyTransaction>,
#[serde(
rename = "relatedTransactionIDs",
default,
skip_serializing_if = "Vec::is_empty"
)]
pub related_transaction_ids: Vec<TransactionId>,
#[serde(rename = "lastTransactionID", skip_serializing_if = "Option::is_none")]
pub last_transaction_id: Option<TransactionId>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SetTradeClientExtensionsRejectBody {
#[serde(
rename = "tradeClientExtensionsModifyRejectTransaction",
skip_serializing_if = "Option::is_none"
)]
pub trade_client_extensions_modify_reject_transaction:
Option<TradeClientExtensionsModifyRejectTransaction>,
#[serde(
rename = "relatedTransactionIDs",
default,
skip_serializing_if = "Vec::is_empty"
)]
pub related_transaction_ids: Vec<TransactionId>,
#[serde(rename = "lastTransactionID", skip_serializing_if = "Option::is_none")]
pub last_transaction_id: Option<TransactionId>,
}
#[derive(Debug)]
pub struct SetTradeDependentOrdersRequest {
client: Client,
account_id: AccountId,
trade: TradeSpecifier,
body: SetTradeDependentOrdersBody,
}
#[derive(Debug, Serialize)]
struct SetTradeDependentOrdersBody {
#[serde(rename = "takeProfit", skip_serializing_if = "Option::is_none")]
take_profit: Option<Option<TakeProfitDetails>>,
#[serde(rename = "stopLoss", skip_serializing_if = "Option::is_none")]
stop_loss: Option<Option<StopLossDetails>>,
#[serde(rename = "trailingStopLoss", skip_serializing_if = "Option::is_none")]
trailing_stop_loss: Option<Option<TrailingStopLossDetails>>,
}
impl SetTradeDependentOrdersRequest {
pub fn take_profit(mut self, details: TakeProfitDetails) -> Self {
self.body.take_profit = Some(Some(details));
self
}
pub fn cancel_take_profit(mut self) -> Self {
self.body.take_profit = Some(None);
self
}
pub fn stop_loss(mut self, details: StopLossDetails) -> Self {
self.body.stop_loss = Some(Some(details));
self
}
pub fn cancel_stop_loss(mut self) -> Self {
self.body.stop_loss = Some(None);
self
}
pub fn trailing_stop_loss(mut self, details: TrailingStopLossDetails) -> Self {
self.body.trailing_stop_loss = Some(Some(details));
self
}
pub fn cancel_trailing_stop_loss(mut self) -> Self {
self.body.trailing_stop_loss = Some(None);
self
}
pub async fn send(self) -> Result<SetTradeDependentOrdersResponse, Error> {
let request = self
.client
.put(&[
"accounts",
self.account_id.as_str(),
"trades",
self.trade.as_str(),
"orders",
])
.json(&self.body);
self.client.execute(request).await
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SetTradeDependentOrdersResponse {
#[serde(
rename = "takeProfitOrderCancelTransaction",
skip_serializing_if = "Option::is_none"
)]
pub take_profit_order_cancel_transaction: Option<OrderCancelTransaction>,
#[serde(
rename = "takeProfitOrderTransaction",
skip_serializing_if = "Option::is_none"
)]
pub take_profit_order_transaction: Option<TakeProfitOrderTransaction>,
#[serde(
rename = "takeProfitOrderFillTransaction",
skip_serializing_if = "Option::is_none"
)]
pub take_profit_order_fill_transaction: Option<OrderFillTransaction>,
#[serde(
rename = "takeProfitOrderCreatedCancelTransaction",
skip_serializing_if = "Option::is_none"
)]
pub take_profit_order_created_cancel_transaction: Option<OrderCancelTransaction>,
#[serde(
rename = "stopLossOrderCancelTransaction",
skip_serializing_if = "Option::is_none"
)]
pub stop_loss_order_cancel_transaction: Option<OrderCancelTransaction>,
#[serde(
rename = "stopLossOrderTransaction",
skip_serializing_if = "Option::is_none"
)]
pub stop_loss_order_transaction: Option<StopLossOrderTransaction>,
#[serde(
rename = "stopLossOrderFillTransaction",
skip_serializing_if = "Option::is_none"
)]
pub stop_loss_order_fill_transaction: Option<OrderFillTransaction>,
#[serde(
rename = "stopLossOrderCreatedCancelTransaction",
skip_serializing_if = "Option::is_none"
)]
pub stop_loss_order_created_cancel_transaction: Option<OrderCancelTransaction>,
#[serde(
rename = "trailingStopLossOrderCancelTransaction",
skip_serializing_if = "Option::is_none"
)]
pub trailing_stop_loss_order_cancel_transaction: Option<OrderCancelTransaction>,
#[serde(
rename = "trailingStopLossOrderTransaction",
skip_serializing_if = "Option::is_none"
)]
pub trailing_stop_loss_order_transaction: Option<TrailingStopLossOrderTransaction>,
#[serde(
rename = "relatedTransactionIDs",
default,
skip_serializing_if = "Vec::is_empty"
)]
pub related_transaction_ids: Vec<TransactionId>,
#[serde(rename = "lastTransactionID", skip_serializing_if = "Option::is_none")]
pub last_transaction_id: Option<TransactionId>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct SetTradeDependentOrdersRejectBody {
#[serde(
rename = "takeProfitOrderCancelRejectTransaction",
skip_serializing_if = "Option::is_none"
)]
pub take_profit_order_cancel_reject_transaction: Option<OrderCancelRejectTransaction>,
#[serde(
rename = "takeProfitOrderRejectTransaction",
skip_serializing_if = "Option::is_none"
)]
pub take_profit_order_reject_transaction: Option<TakeProfitOrderRejectTransaction>,
#[serde(
rename = "stopLossOrderCancelRejectTransaction",
skip_serializing_if = "Option::is_none"
)]
pub stop_loss_order_cancel_reject_transaction: Option<OrderCancelRejectTransaction>,
#[serde(
rename = "stopLossOrderRejectTransaction",
skip_serializing_if = "Option::is_none"
)]
pub stop_loss_order_reject_transaction: Option<StopLossOrderRejectTransaction>,
#[serde(
rename = "trailingStopLossOrderCancelRejectTransaction",
skip_serializing_if = "Option::is_none"
)]
pub trailing_stop_loss_order_cancel_reject_transaction: Option<OrderCancelRejectTransaction>,
#[serde(
rename = "trailingStopLossOrderRejectTransaction",
skip_serializing_if = "Option::is_none"
)]
pub trailing_stop_loss_order_reject_transaction: Option<TrailingStopLossOrderRejectTransaction>,
#[serde(rename = "lastTransactionID", skip_serializing_if = "Option::is_none")]
pub last_transaction_id: Option<TransactionId>,
}