use http::Method;
use serde::Serialize;
use tracing::instrument;
use crate::dealing::common::DealConfirmation;
use crate::dealing::positions::models::OpenPositionResponse;
use crate::error::Result;
use crate::models::common::{Currency, Direction, Epic, OrderType, TimeInForce};
use super::api::PositionsApi;
#[derive(Debug)]
pub struct Missing;
#[derive(Debug)]
pub struct Set<T>(pub(crate) T);
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub(super) struct OpenPositionBody {
pub currency_code: Currency,
pub direction: Direction,
pub epic: Epic,
pub expiry: String,
pub force_open: bool,
pub guaranteed_stop: bool,
pub order_type: OrderType,
pub size: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub level: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit_distance: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit_level: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stop_distance: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stop_level: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub trailing_stop: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub trailing_stop_increment: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub time_in_force: Option<TimeInForce>,
#[serde(skip_serializing_if = "Option::is_none")]
pub quote_id: Option<String>,
}
pub struct OpenPositionBuilder<'a, CC, Di, E, X, G, O, Si> {
pub(super) api: &'a PositionsApi<'a>,
pub(super) currency_code: CC,
pub(super) direction: Di,
pub(super) epic: E,
pub(super) expiry: X,
pub(super) guaranteed_stop: G,
pub(super) order_type: O,
pub(super) size: Si,
pub(super) force_open: bool,
pub(super) level: Option<f64>,
pub(super) limit_distance: Option<f64>,
pub(super) limit_level: Option<f64>,
pub(super) stop_distance: Option<f64>,
pub(super) stop_level: Option<f64>,
pub(super) trailing_stop: Option<bool>,
pub(super) trailing_stop_increment: Option<f64>,
pub(super) time_in_force: Option<TimeInForce>,
pub(super) quote_id: Option<String>,
}
impl<CC, Di, E, X, G, O, Si> std::fmt::Debug for OpenPositionBuilder<'_, CC, Di, E, X, G, O, Si> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OpenPositionBuilder")
.finish_non_exhaustive()
}
}
pub type ReadyOpenPositionBuilder<'a> = OpenPositionBuilder<
'a,
Set<Currency>,
Set<Direction>,
Set<Epic>,
Set<String>,
Set<bool>,
Set<OrderType>,
Set<f64>,
>;
impl<'a> OpenPositionBuilder<'a, Missing, Missing, Missing, Missing, Missing, Missing, Missing> {
pub(super) fn new(api: &'a PositionsApi<'a>) -> Self {
Self {
api,
currency_code: Missing,
direction: Missing,
epic: Missing,
expiry: Missing,
guaranteed_stop: Missing,
order_type: Missing,
size: Missing,
force_open: false,
level: None,
limit_distance: None,
limit_level: None,
stop_distance: None,
stop_level: None,
trailing_stop: None,
trailing_stop_increment: None,
time_in_force: None,
quote_id: None,
}
}
}
impl<'a, Di, E, X, G, O, Si> OpenPositionBuilder<'a, Missing, Di, E, X, G, O, Si> {
pub fn currency(
self,
c: impl Into<Currency>,
) -> OpenPositionBuilder<'a, Set<Currency>, Di, E, X, G, O, Si> {
OpenPositionBuilder {
api: self.api,
currency_code: Set(c.into()),
direction: self.direction,
epic: self.epic,
expiry: self.expiry,
guaranteed_stop: self.guaranteed_stop,
order_type: self.order_type,
size: self.size,
force_open: self.force_open,
level: self.level,
limit_distance: self.limit_distance,
limit_level: self.limit_level,
stop_distance: self.stop_distance,
stop_level: self.stop_level,
trailing_stop: self.trailing_stop,
trailing_stop_increment: self.trailing_stop_increment,
time_in_force: self.time_in_force,
quote_id: self.quote_id,
}
}
}
impl<'a, CC, E, X, G, O, Si> OpenPositionBuilder<'a, CC, Missing, E, X, G, O, Si> {
pub fn direction(
self,
d: Direction,
) -> OpenPositionBuilder<'a, CC, Set<Direction>, E, X, G, O, Si> {
OpenPositionBuilder {
api: self.api,
currency_code: self.currency_code,
direction: Set(d),
epic: self.epic,
expiry: self.expiry,
guaranteed_stop: self.guaranteed_stop,
order_type: self.order_type,
size: self.size,
force_open: self.force_open,
level: self.level,
limit_distance: self.limit_distance,
limit_level: self.limit_level,
stop_distance: self.stop_distance,
stop_level: self.stop_level,
trailing_stop: self.trailing_stop,
trailing_stop_increment: self.trailing_stop_increment,
time_in_force: self.time_in_force,
quote_id: self.quote_id,
}
}
}
impl<'a, CC, Di, X, G, O, Si> OpenPositionBuilder<'a, CC, Di, Missing, X, G, O, Si> {
pub fn epic(
self,
e: impl Into<Epic>,
) -> OpenPositionBuilder<'a, CC, Di, Set<Epic>, X, G, O, Si> {
OpenPositionBuilder {
api: self.api,
currency_code: self.currency_code,
direction: self.direction,
epic: Set(e.into()),
expiry: self.expiry,
guaranteed_stop: self.guaranteed_stop,
order_type: self.order_type,
size: self.size,
force_open: self.force_open,
level: self.level,
limit_distance: self.limit_distance,
limit_level: self.limit_level,
stop_distance: self.stop_distance,
stop_level: self.stop_level,
trailing_stop: self.trailing_stop,
trailing_stop_increment: self.trailing_stop_increment,
time_in_force: self.time_in_force,
quote_id: self.quote_id,
}
}
}
impl<'a, CC, Di, E, G, O, Si> OpenPositionBuilder<'a, CC, Di, E, Missing, G, O, Si> {
pub fn expiry(
self,
e: impl Into<String>,
) -> OpenPositionBuilder<'a, CC, Di, E, Set<String>, G, O, Si> {
OpenPositionBuilder {
api: self.api,
currency_code: self.currency_code,
direction: self.direction,
epic: self.epic,
expiry: Set(e.into()),
guaranteed_stop: self.guaranteed_stop,
order_type: self.order_type,
size: self.size,
force_open: self.force_open,
level: self.level,
limit_distance: self.limit_distance,
limit_level: self.limit_level,
stop_distance: self.stop_distance,
stop_level: self.stop_level,
trailing_stop: self.trailing_stop,
trailing_stop_increment: self.trailing_stop_increment,
time_in_force: self.time_in_force,
quote_id: self.quote_id,
}
}
}
impl<'a, CC, Di, E, X, O, Si> OpenPositionBuilder<'a, CC, Di, E, X, Missing, O, Si> {
pub fn guaranteed_stop(
self,
gs: bool,
) -> OpenPositionBuilder<'a, CC, Di, E, X, Set<bool>, O, Si> {
OpenPositionBuilder {
api: self.api,
currency_code: self.currency_code,
direction: self.direction,
epic: self.epic,
expiry: self.expiry,
guaranteed_stop: Set(gs),
order_type: self.order_type,
size: self.size,
force_open: self.force_open,
level: self.level,
limit_distance: self.limit_distance,
limit_level: self.limit_level,
stop_distance: self.stop_distance,
stop_level: self.stop_level,
trailing_stop: self.trailing_stop,
trailing_stop_increment: self.trailing_stop_increment,
time_in_force: self.time_in_force,
quote_id: self.quote_id,
}
}
}
impl<'a, CC, Di, E, X, G, Si> OpenPositionBuilder<'a, CC, Di, E, X, G, Missing, Si> {
pub fn order_type(
self,
ot: OrderType,
) -> OpenPositionBuilder<'a, CC, Di, E, X, G, Set<OrderType>, Si> {
OpenPositionBuilder {
api: self.api,
currency_code: self.currency_code,
direction: self.direction,
epic: self.epic,
expiry: self.expiry,
guaranteed_stop: self.guaranteed_stop,
order_type: Set(ot),
size: self.size,
force_open: self.force_open,
level: self.level,
limit_distance: self.limit_distance,
limit_level: self.limit_level,
stop_distance: self.stop_distance,
stop_level: self.stop_level,
trailing_stop: self.trailing_stop,
trailing_stop_increment: self.trailing_stop_increment,
time_in_force: self.time_in_force,
quote_id: self.quote_id,
}
}
}
impl<'a, CC, Di, E, X, G, O> OpenPositionBuilder<'a, CC, Di, E, X, G, O, Missing> {
pub fn size(self, s: f64) -> OpenPositionBuilder<'a, CC, Di, E, X, G, O, Set<f64>> {
OpenPositionBuilder {
api: self.api,
currency_code: self.currency_code,
direction: self.direction,
epic: self.epic,
expiry: self.expiry,
guaranteed_stop: self.guaranteed_stop,
order_type: self.order_type,
size: Set(s),
force_open: self.force_open,
level: self.level,
limit_distance: self.limit_distance,
limit_level: self.limit_level,
stop_distance: self.stop_distance,
stop_level: self.stop_level,
trailing_stop: self.trailing_stop,
trailing_stop_increment: self.trailing_stop_increment,
time_in_force: self.time_in_force,
quote_id: self.quote_id,
}
}
}
impl<CC, Di, E, X, G, O, Si> OpenPositionBuilder<'_, CC, Di, E, X, G, O, Si> {
pub fn force_open(mut self, fo: bool) -> Self {
self.force_open = fo;
self
}
pub fn level(mut self, l: f64) -> Self {
self.level = Some(l);
self
}
pub fn with_limit_distance(mut self, d: f64) -> Self {
self.limit_distance = Some(d);
self
}
pub fn with_limit_level(mut self, l: f64) -> Self {
self.limit_level = Some(l);
self
}
pub fn with_stop_distance(mut self, d: f64) -> Self {
self.stop_distance = Some(d);
self
}
pub fn with_stop_level(mut self, l: f64) -> Self {
self.stop_level = Some(l);
self
}
pub fn trailing_stop(mut self, ts: bool) -> Self {
self.trailing_stop = Some(ts);
self
}
pub fn trailing_stop_increment(mut self, tsi: f64) -> Self {
self.trailing_stop_increment = Some(tsi);
self
}
pub fn time_in_force(mut self, tif: TimeInForce) -> Self {
self.time_in_force = Some(tif);
self
}
pub fn quote_id(mut self, qid: impl Into<String>) -> Self {
self.quote_id = Some(qid.into());
self
}
}
impl ReadyOpenPositionBuilder<'_> {
#[instrument(skip_all, name = "dealing.positions.open")]
pub async fn send(self) -> Result<DealConfirmation> {
let api = self.api;
let body = OpenPositionBody {
currency_code: self.currency_code.0,
direction: self.direction.0,
epic: self.epic.0,
expiry: self.expiry.0,
force_open: self.force_open,
guaranteed_stop: self.guaranteed_stop.0,
order_type: self.order_type.0,
size: self.size.0,
level: self.level,
limit_distance: self.limit_distance,
limit_level: self.limit_level,
stop_distance: self.stop_distance,
stop_level: self.stop_level,
trailing_stop: self.trailing_stop,
trailing_stop_increment: self.trailing_stop_increment,
time_in_force: self.time_in_force,
quote_id: self.quote_id,
};
let resp: OpenPositionResponse = api
.client
.transport
.request(
Method::POST,
"positions/otc",
Some(2),
Some(&body),
&api.client.session,
)
.await?;
api.confirm(&resp.deal_reference).await
}
}