architect_api/cpty/
coinbase.rs

1use crate::{
2    folio::FolioMessage,
3    orderflow::*,
4    symbology::{
5        market::{MinOrderQuantityUnit, NormalizedMarketInfo},
6        MarketId,
7    },
8    Amount,
9};
10use derive::FromStrJson;
11#[cfg(feature = "netidx")]
12use derive::FromValue;
13#[cfg(feature = "netidx")]
14use netidx_derive::Pack;
15use rust_decimal::Decimal;
16use schemars::JsonSchema;
17use serde::{Deserialize, Serialize};
18use std::ops::{Deref, DerefMut};
19use uuid::Uuid;
20use zeroize::Zeroize;
21
22#[derive(Debug, Clone, FromStrJson, Serialize, Deserialize, Zeroize, JsonSchema)]
23#[cfg_attr(feature = "netidx", derive(Pack))]
24#[cfg_attr(feature = "netidx", derive(FromValue))]
25pub struct CoinbaseCredentials {
26    /// No way to tell from the API itself which portfolio the key controls,
27    /// so we need some help from the user.  Default/None to state that the
28    /// key controls the default portfolio; otherwise, state the portfolio
29    /// UUID.
30    #[serde(default)]
31    pub portfolio_id: Option<String>,
32    pub api_key: String,
33    pub api_secret: String,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
37#[cfg_attr(feature = "netidx", derive(Pack))]
38pub struct CoinbaseMarketInfo {
39    pub min_market_funds: Decimal,
40    pub status_message: Option<String>,
41    pub base_increment: Decimal,
42    pub quote_increment: Decimal,
43    pub trading_disabled: bool,
44    pub cancel_only: bool,
45    pub post_only: bool,
46    pub limit_only: bool,
47    pub fx_stablecoin: bool,
48    pub auction_mode: bool,
49    #[serde(default)]
50    #[cfg_attr(feature = "netidx", pack(default))]
51    pub is_delisted: bool,
52}
53
54impl NormalizedMarketInfo for CoinbaseMarketInfo {
55    fn tick_size(&self) -> Decimal {
56        self.quote_increment
57    }
58
59    fn step_size(&self) -> Decimal {
60        self.base_increment
61    }
62
63    fn min_order_quantity(&self) -> Amount<Decimal, MinOrderQuantityUnit> {
64        return Amount::new(self.min_market_funds, MinOrderQuantityUnit::Quote);
65    }
66
67    fn is_delisted(&self) -> bool {
68        self.is_delisted
69    }
70}
71
72// CR alee: consider BatchCancel
73#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
74#[cfg_attr(feature = "netidx", derive(Pack))]
75#[cfg_attr(feature = "netidx", derive(FromValue))]
76pub enum CoinbaseMessage {
77    Order(CoinbaseOrder),
78    Cancel(Cancel),
79    Reject(Reject),
80    Ack(Ack),
81    Fill(CoinbaseFill),
82    CancelAll(CancelAll),
83    Out(Out),
84    Folio(FolioMessage),
85    ExchangeOrderUpdate(CoinbaseExchangeOrderUpdate),
86    ExchangeAck(OrderId, Uuid),
87    ExchangeFills(Vec<CoinbaseFill>),
88    ExchangeExternalOrderUpdate(MarketId, Uuid),
89    ExchangeExternalOrderNew(Uuid, CoinbaseOrder),
90    ExchangeExternalOrderOut(Uuid),
91}
92
93impl TryInto<OrderflowMessage> for &CoinbaseMessage {
94    type Error = ();
95
96    fn try_into(self) -> Result<OrderflowMessage, ()> {
97        match self {
98            CoinbaseMessage::Order(o) => Ok(OrderflowMessage::Order(**o)),
99            CoinbaseMessage::Cancel(c) => Ok(OrderflowMessage::Cancel(*c)),
100            CoinbaseMessage::Reject(r) => Ok(OrderflowMessage::Reject(r.clone())),
101            CoinbaseMessage::Ack(a) => Ok(OrderflowMessage::Ack(*a)),
102            CoinbaseMessage::CancelAll(cclall) => {
103                Ok(OrderflowMessage::CancelAll(*cclall))
104            }
105            CoinbaseMessage::Fill(f) => Ok(OrderflowMessage::Fill(**f)),
106            CoinbaseMessage::Out(o) => Ok(OrderflowMessage::Out(*o)),
107            CoinbaseMessage::Folio(..)
108            | CoinbaseMessage::ExchangeOrderUpdate(..)
109            | CoinbaseMessage::ExchangeAck(..)
110            | CoinbaseMessage::ExchangeFills(..)
111            | CoinbaseMessage::ExchangeExternalOrderUpdate(..)
112            | CoinbaseMessage::ExchangeExternalOrderNew(..)
113            | CoinbaseMessage::ExchangeExternalOrderOut(..) => Err(()),
114        }
115    }
116}
117
118impl TryInto<CoinbaseMessage> for &OrderflowMessage {
119    type Error = ();
120
121    fn try_into(self) -> Result<CoinbaseMessage, ()> {
122        match self {
123            OrderflowMessage::Order(o) => {
124                Ok(CoinbaseMessage::Order(CoinbaseOrder { order: *o }))
125            }
126            OrderflowMessage::Cancel(c) => Ok(CoinbaseMessage::Cancel(*c)),
127            OrderflowMessage::Reject(r) => Ok(CoinbaseMessage::Reject(r.clone())),
128            OrderflowMessage::Ack(a) => Ok(CoinbaseMessage::Ack(*a)),
129            OrderflowMessage::Fill(_) => Err(()),
130            OrderflowMessage::Out(o) => Ok(CoinbaseMessage::Out(*o)),
131            OrderflowMessage::CancelAll(ccl) => Ok(CoinbaseMessage::CancelAll(*ccl)),
132        }
133    }
134}
135
136impl TryInto<FolioMessage> for &CoinbaseMessage {
137    type Error = ();
138
139    fn try_into(self) -> Result<FolioMessage, ()> {
140        match self {
141            CoinbaseMessage::Folio(f) => Ok(f.clone()),
142            _ => Err(()),
143        }
144    }
145}
146
147impl TryFrom<&FolioMessage> for CoinbaseMessage {
148    type Error = ();
149
150    fn try_from(f: &FolioMessage) -> Result<Self, ()> {
151        Ok(Self::Folio(f.clone()))
152    }
153}
154
155#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
156#[cfg_attr(feature = "netidx", derive(Pack))]
157pub struct CoinbaseOrder {
158    #[serde(flatten)]
159    pub order: Order,
160}
161
162impl From<Order> for CoinbaseOrder {
163    fn from(order: Order) -> Self {
164        Self { order }
165    }
166}
167
168impl Deref for CoinbaseOrder {
169    type Target = Order;
170
171    fn deref(&self) -> &Self::Target {
172        &self.order
173    }
174}
175
176impl DerefMut for CoinbaseOrder {
177    fn deref_mut(&mut self) -> &mut Self::Target {
178        &mut self.order
179    }
180}
181
182#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
183#[cfg_attr(feature = "netidx", derive(Pack))]
184pub struct CoinbaseFill {
185    #[serde(flatten)]
186    pub fill: Result<Fill, AberrantFill>,
187    pub exchange_trade_id: Uuid,
188    pub exchange_order_id: Uuid,
189}
190
191impl Deref for CoinbaseFill {
192    type Target = Result<Fill, AberrantFill>;
193
194    fn deref(&self) -> &Self::Target {
195        &self.fill
196    }
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
200#[cfg_attr(feature = "netidx", derive(Pack))]
201#[cfg_attr(feature = "netidx", derive(FromValue))]
202pub struct CoinbaseExchangeOrderUpdate {
203    pub order_id: OrderId,
204    pub out: bool,
205}
206
207impl std::fmt::Display for CoinbaseMarketInfo {
208    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209        write!(f, "{}", serde_json::to_string_pretty(self).unwrap())?;
210        Ok(())
211    }
212}