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 #[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#[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}