use serde::Serialize;
#[derive(Serialize, Debug)]
pub struct Order {
r#type: String,
size: Option<f64>,
price: Option<f64>,
side: OrderSide,
client_oid: Option<String>,
self_trade_prevention: Option<SelfTradePrevention>,
time_in_force: Option<TimeInForce>,
cancel_after: Option<CancelAfter>,
post_only: Option<bool>,
funds: Option<f64>,
product_id: String,
stp: Option<String>,
stop: Option<OrderStop>,
stop_price: Option<f64>,
}
impl Order {
pub fn market_builder(
side: OrderSide,
product_id: &str,
size_or_funds: SizeOrFunds,
) -> impl SharedOptions {
OrderBuilder {
r#type: "market".to_string(),
size: match size_or_funds {
SizeOrFunds::Size(n) => Some(n),
_ => None,
},
price: None,
side,
client_oid: None,
self_trade_prevention: None,
time_in_force: None,
cancel_after: None,
post_only: None,
funds: match size_or_funds {
SizeOrFunds::Funds(n) => Some(n),
_ => None,
},
product_id: product_id.to_string(),
stp: None,
stop: None,
stop_price: None,
}
}
pub fn limit_builder(
side: OrderSide,
product_id: &str,
price: f64,
size: f64,
) -> impl LimitOptions + SharedOptions {
OrderBuilder {
r#type: "limit".to_string(),
size: Some(size),
price: Some(price),
side: side,
client_oid: None,
self_trade_prevention: None,
time_in_force: None,
cancel_after: None,
post_only: None,
funds: None,
product_id: product_id.to_string(),
stp: None,
stop: None,
stop_price: None,
}
}
pub fn stop_builder(
side: OrderSide,
product_id: &str,
price: f64,
size: f64,
stop_price: f64,
stop: OrderStop,
) -> impl SharedOptions {
OrderBuilder {
r#type: "limit".to_string(),
size: Some(size),
price: Some(price),
side: side,
client_oid: None,
self_trade_prevention: None,
time_in_force: None,
cancel_after: None,
post_only: None,
funds: None,
product_id: product_id.to_string(),
stp: None,
stop: Some(stop),
stop_price: Some(stop_price),
}
}
}
pub struct OrderBuilder {
r#type: String,
size: Option<f64>,
price: Option<f64>,
side: OrderSide,
client_oid: Option<String>,
self_trade_prevention: Option<SelfTradePrevention>,
time_in_force: Option<TimeInForce>,
cancel_after: Option<CancelAfter>,
post_only: Option<bool>,
funds: Option<f64>,
product_id: String,
stp: Option<String>,
stop: Option<OrderStop>,
stop_price: Option<f64>,
}
impl OrderBuilder {
pub fn market(
side: OrderSide,
product_id: &str,
size_or_funds: SizeOrFunds,
) -> impl SharedOptions {
Self {
r#type: "market".to_string(),
size: match size_or_funds {
SizeOrFunds::Size(n) => Some(n),
_ => None,
},
price: None,
side,
client_oid: None,
self_trade_prevention: None,
time_in_force: None,
cancel_after: None,
post_only: None,
funds: match size_or_funds {
SizeOrFunds::Funds(n) => Some(n),
_ => None,
},
product_id: product_id.to_string(),
stp: None,
stop: None,
stop_price: None,
}
}
pub fn limit(
side: OrderSide,
product_id: &str,
price: f64,
size: f64,
) -> impl LimitOptions + SharedOptions {
Self {
r#type: "limit".to_string(),
size: Some(size),
price: Some(price),
side: side,
client_oid: None,
self_trade_prevention: None,
time_in_force: None,
cancel_after: None,
post_only: None,
funds: None,
product_id: product_id.to_string(),
stp: None,
stop: None,
stop_price: None,
}
}
pub fn stop(
side: OrderSide,
product_id: &str,
price: f64,
size: f64,
stop_price: f64,
stop: OrderStop,
) -> impl SharedOptions {
Self {
r#type: "limit".to_string(),
size: Some(size),
price: Some(price),
side: side,
client_oid: None,
self_trade_prevention: None,
time_in_force: None,
cancel_after: None,
post_only: None,
funds: None,
product_id: product_id.to_string(),
stp: None,
stop: Some(stop),
stop_price: Some(stop_price),
}
}
}
pub trait SharedOptions {
fn self_trade_prevention(self, self_trade_prevention: SelfTradePrevention) -> Self;
fn client_oid(self, client_oid: String) -> Self;
fn build(self) -> Order;
}
impl SharedOptions for OrderBuilder {
fn self_trade_prevention(mut self, self_trade_prevention: SelfTradePrevention) -> Self {
self.self_trade_prevention = Some(self_trade_prevention);
self
}
fn client_oid(mut self, client_oid: String) -> Self {
self.client_oid = Some(client_oid);
self
}
fn build(self) -> Order {
Order {
r#type: self.r#type,
size: self.size,
price: self.price,
side: self.side,
client_oid: self.client_oid,
self_trade_prevention: self.self_trade_prevention,
time_in_force: self.time_in_force,
cancel_after: self.cancel_after,
post_only: self.post_only,
funds: self.funds,
product_id: self.product_id,
stp: self.stp,
stop: self.stop,
stop_price: self.stop_price,
}
}
}
pub trait LimitOptions {
fn time_in_force(self, time_in_force: TimeInForce) -> Self;
}
impl LimitOptions for OrderBuilder {
fn time_in_force(mut self, time_in_force: TimeInForce) -> Self {
match time_in_force {
TimeInForce::GoodTillTime {
cancel_after,
post_only,
} => {
self.cancel_after = Some(cancel_after);
self.post_only = Some(post_only);
}
TimeInForce::GoodTillCancel { post_only } => self.post_only = Some(post_only),
_ => {}
}
self.time_in_force = Some(time_in_force);
self
}
}
#[derive(Clone, Copy, Debug)]
pub enum OrderSide {
Buy,
Sell,
}
#[derive(Clone, Copy, Debug)]
pub enum OrderStop {
Loss,
Entry,
}
#[derive(Clone, Copy, Debug)]
pub enum SizeOrFunds {
Size(f64),
Funds(f64),
}
#[derive(Clone, Copy, Debug)]
pub enum TimeInForce {
GoodTillCancel {
post_only: bool,
},
GoodTillTime {
cancel_after: CancelAfter,
post_only: bool,
},
ImmediateOrCancel,
FillOrKill,
}
#[derive(Clone, Copy, Debug)]
pub enum CancelAfter {
Minute,
Hour,
Day,
}
#[derive(Clone, Copy, Debug)]
pub enum SelfTradePrevention {
DecreaseCancel,
CancelOldest,
CancelNewest,
CancelBoth,
}
impl serde::Serialize for SelfTradePrevention {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
match self {
Self::CancelBoth => serializer.serialize_str("cb"),
Self::DecreaseCancel => serializer.serialize_str("dc"),
Self::CancelOldest => serializer.serialize_str("co"),
Self::CancelNewest => serializer.serialize_str("cn"),
}
}
}
impl serde::Serialize for OrderStop {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
match self {
Self::Loss => serializer.serialize_str("loss"),
Self::Entry => serializer.serialize_str("entry"),
}
}
}
impl serde::Serialize for TimeInForce {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
match self {
Self::GoodTillCancel { post_only: _ } => serializer.serialize_str("GTC"),
Self::GoodTillTime {
cancel_after: _,
post_only: _,
} => serializer.serialize_str("GTT"),
Self::ImmediateOrCancel => serializer.serialize_str("IOC"),
Self::FillOrKill => serializer.serialize_str("FOK"),
}
}
}
impl serde::Serialize for CancelAfter {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
match self {
Self::Minute => serializer.serialize_str("min"),
Self::Hour => serializer.serialize_str("hour"),
Self::Day => serializer.serialize_str("day"),
}
}
}
impl serde::Serialize for OrderSide {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
match self {
Self::Buy => serializer.serialize_str("buy"),
Self::Sell => serializer.serialize_str("sell"),
}
}
}
impl serde::Serialize for SizeOrFunds {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
match *self {
Self::Size(size) => serializer.serialize_f64(size),
Self::Funds(funds) => serializer.serialize_f64(funds),
}
}
}