architect_api/cpty/
kraken.rs

1use crate::{
2    folio::FolioMessage,
3    orderflow::{
4        AberrantFill, Ack, Cancel, CancelAll, Fill, Order, OrderType, OrderflowMessage,
5        Out, Reject, TimeInForce,
6    },
7    symbology::market::{MinOrderQuantityUnit, NormalizedMarketInfo},
8    Amount, Dir, OrderId,
9};
10use chrono::{DateTime, Utc};
11use derive::FromStrJson;
12#[cfg(feature = "netidx")]
13use derive::FromValue;
14#[cfg(feature = "netidx")]
15use netidx_derive::Pack;
16use rust_decimal::{prelude::FromPrimitive, Decimal};
17use schemars::JsonSchema;
18use serde::{Deserialize, Serialize};
19use std::ops::{Deref, DerefMut};
20use zeroize::Zeroize;
21
22#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
23#[cfg_attr(feature = "netidx", derive(Pack))]
24pub enum Status {
25    #[serde(alias = "online")]
26    Online,
27    #[serde(alias = "cancel_only")]
28    CancelOnly,
29    #[serde(alias = "post_only")]
30    PostOnly,
31    #[serde(alias = "limit_only")]
32    LimitOnly,
33    #[serde(alias = "reduce_only")]
34    ReduceOnly,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
38#[cfg_attr(feature = "netidx", derive(Pack))]
39pub struct KrakenMarketInfo {
40    pub altname: String,
41    pub wsname: String,
42    pub aclass_base: String,
43    pub base: String,
44    pub aclass_quote: String,
45    pub quote: String,
46    pub pair_decimals: u64,
47    pub cost_decimals: u64,
48    pub lot_decimals: u64,
49    pub lot_multiplier: u64,
50    pub margin_call: u64,
51    pub margin_stop: u64,
52    pub fee_volume_currency: String,
53    pub ordermin: Decimal,
54    pub costmin: Decimal,
55    pub tick_size: Decimal,
56    pub status: Status,
57    pub long_position_limit: Option<u64>,
58    pub short_position_limit: Option<u64>,
59}
60
61impl NormalizedMarketInfo for KrakenMarketInfo {
62    fn tick_size(&self) -> Decimal {
63        self.tick_size
64    }
65
66    fn step_size(&self) -> Decimal {
67        Decimal::from_f64(10f64.powi(-(self.lot_decimals as i32)))
68            .expect(&format!("could not compute step_size: {:?}", self))
69    }
70
71    fn min_order_quantity(&self) -> Amount<Decimal, MinOrderQuantityUnit> {
72        return Amount::new(self.ordermin, MinOrderQuantityUnit::Base);
73    }
74
75    fn is_delisted(&self) -> bool {
76        false
77    }
78}
79
80impl std::fmt::Display for KrakenMarketInfo {
81    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82        write!(f, "{}", serde_json::to_string_pretty(self).unwrap())?;
83        Ok(())
84    }
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
88#[cfg_attr(feature = "netidx", derive(Pack))]
89#[cfg_attr(feature = "netidx", derive(FromValue))]
90pub enum KrakenMessage {
91    Initialize,
92    Order(KrakenOrder),
93    Cancel(Cancel),
94    CancelAll(CancelAll),
95    Reject(Reject),
96    Ack(Ack),
97    Fill(KrakenFill),
98    Out(Out),
99    ExchangeOrderUpdate(
100        KrakenUserRef,
101        KrakenExchangeId,
102        Option<(OrderId, KrakenExternalOrder)>,
103        bool, /* out */
104    ),
105    ExchangeAck(OrderId, KrakenExchangeId),
106    ExchangeFill(KrakenExternalFill),
107    ExchangeExternalOrderNew(OrderId, KrakenExternalOrder),
108    ExchangeExternalOrderOut(KrakenExchangeId),
109    ExchangeExternalFill(KrakenExternalFill),
110    Folio(FolioMessage),
111}
112
113impl TryInto<OrderflowMessage> for &KrakenMessage {
114    type Error = ();
115
116    fn try_into(self) -> Result<OrderflowMessage, ()> {
117        match self {
118            KrakenMessage::Order(o) => Ok(OrderflowMessage::Order(**o)),
119            KrakenMessage::Cancel(c) => Ok(OrderflowMessage::Cancel(*c)),
120            KrakenMessage::CancelAll(ca) => Ok(OrderflowMessage::CancelAll(ca.clone())),
121            KrakenMessage::Reject(r) => Ok(OrderflowMessage::Reject(r.clone())),
122            KrakenMessage::Ack(a) => Ok(OrderflowMessage::Ack(*a)),
123            KrakenMessage::Fill(f) => Ok(OrderflowMessage::Fill(**f)),
124            KrakenMessage::Out(o) => Ok(OrderflowMessage::Out(*o)),
125            KrakenMessage::ExchangeOrderUpdate(..)
126            | KrakenMessage::Initialize
127            | KrakenMessage::ExchangeAck(..)
128            | KrakenMessage::ExchangeFill(..)
129            | KrakenMessage::ExchangeExternalOrderNew(..)
130            | KrakenMessage::ExchangeExternalFill(..)
131            | KrakenMessage::ExchangeExternalOrderOut(..)
132            | KrakenMessage::Folio(..) => Err(()),
133        }
134    }
135}
136
137impl TryInto<KrakenMessage> for &OrderflowMessage {
138    type Error = ();
139
140    fn try_into(self) -> Result<KrakenMessage, ()> {
141        match self {
142            OrderflowMessage::Order(o) => {
143                Ok(KrakenMessage::Order(KrakenOrder { order: *o }))
144            }
145            OrderflowMessage::Cancel(c) => Ok(KrakenMessage::Cancel(*c)),
146            OrderflowMessage::Reject(r) => Ok(KrakenMessage::Reject(r.clone())),
147            OrderflowMessage::Ack(a) => Ok(KrakenMessage::Ack(*a)),
148            OrderflowMessage::Fill(_) => Err(()),
149            OrderflowMessage::CancelAll(ca) => Ok(KrakenMessage::CancelAll(ca.clone())),
150            OrderflowMessage::Out(o) => Ok(KrakenMessage::Out(*o)),
151        }
152    }
153}
154
155impl TryInto<FolioMessage> for &KrakenMessage {
156    type Error = ();
157
158    fn try_into(self) -> Result<FolioMessage, ()> {
159        match self {
160            KrakenMessage::Folio(f) => Ok(f.clone()),
161            _ => Err(()),
162        }
163    }
164}
165
166impl TryFrom<&FolioMessage> for KrakenMessage {
167    type Error = ();
168
169    fn try_from(f: &FolioMessage) -> Result<Self, ()> {
170        Ok(Self::Folio(f.clone()))
171    }
172}
173
174pub type KrakenExchangeId = String;
175pub type KrakenUserRef = i32;
176
177#[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)]
178#[cfg_attr(feature = "netidx", derive(Pack))]
179pub struct KrakenOrder {
180    #[serde(flatten)]
181    pub order: Order,
182}
183
184impl From<Order> for KrakenOrder {
185    fn from(order: Order) -> Self {
186        Self { order }
187    }
188}
189
190impl Deref for KrakenOrder {
191    type Target = Order;
192
193    fn deref(&self) -> &Self::Target {
194        &self.order
195    }
196}
197
198impl DerefMut for KrakenOrder {
199    fn deref_mut(&mut self) -> &mut Self::Target {
200        &mut self.order
201    }
202}
203
204#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
205#[cfg_attr(feature = "netidx", derive(Pack))]
206pub struct KrakenFill {
207    #[serde(flatten)]
208    pub fill: Result<Fill, AberrantFill>,
209    pub exchange_trade_id: KrakenExchangeId,
210    pub exchange_order_id: KrakenExchangeId,
211}
212
213impl Deref for KrakenFill {
214    type Target = Result<Fill, AberrantFill>;
215
216    fn deref(&self) -> &Self::Target {
217        &self.fill
218    }
219}
220
221#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
222#[cfg_attr(feature = "netidx", derive(Pack))]
223pub struct KrakenExternalOrder {
224    pub exchange_symbol: String,
225    pub exchange_order_id: KrakenExchangeId,
226    pub user_reference_id: KrakenUserRef,
227    pub quantity: Decimal,
228    pub trigger_price: Option<Decimal>,
229    pub dir: Dir,
230    pub expiration: Option<DateTime<Utc>>,
231    pub order_type: OrderType,
232    pub time_in_force: TimeInForce,
233}
234
235#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
236#[cfg_attr(feature = "netidx", derive(Pack))]
237pub struct KrakenExternalFill {
238    pub exchange_order_id: KrakenExchangeId,
239    pub exchange_trade_id: KrakenExchangeId,
240    pub user_reference_id: Option<KrakenUserRef>,
241    pub time: DateTime<Utc>,
242    pub quantity: Decimal,
243    pub price: Decimal,
244    pub dir: Dir,
245}
246
247#[derive(Debug, Clone, FromStrJson, Serialize, Deserialize, Zeroize, JsonSchema)]
248#[cfg_attr(feature = "netidx", derive(Pack))]
249#[cfg_attr(feature = "netidx", derive(FromValue))]
250pub struct KrakenCredentials {
251    /// Account name for the API key--must be user generated since there's
252    /// no way to programmatically determine this.  If not provided, the
253    /// account name defaults to "DEFAULT".
254    #[serde(default)]
255    pub account_name: Option<String>,
256    pub api_key: String,
257    pub api_secret: String,
258}