1use crate::errors::{ProtocolError, Result};
6use bigdecimal::BigDecimal;
7use std::str::FromStr;
8use chrono::{DateTime, Utc};
9use serde::{Deserialize, Serialize};
10use super::blockchain::bigdecimal_to_nash_prec;
11use lazy_static::lazy_static;
12
13#[derive(Clone, Debug, Copy, PartialEq, Hash, Eq)]
16pub enum Blockchain {
17 NEO,
18 Ethereum,
19 Bitcoin,
20}
21
22lazy_static! {
23 static ref BLOCKCHAINS: Vec<Blockchain> = {
24 vec![Blockchain::Bitcoin, Blockchain::Ethereum, Blockchain::NEO]
25 };
26}
27
28impl Blockchain {
29 pub fn all() -> &'static Vec<Blockchain> {
30 &BLOCKCHAINS
31 }
32}
33
34#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
38pub enum Asset {
39 ETH,
40 BAT,
41 OMG,
42 USDC,
43 USDT,
44 ZRX,
45 LINK,
46 QNT,
47 RLC,
48 ANT,
49 BTC,
50 NEO,
51 GAS,
52 TRAC,
53 GUNTHY,
54 NNN,
55 NOIA
56}
57
58impl Asset {
59 pub fn blockchain(&self) -> Blockchain {
61 match self {
62 Self::ETH => Blockchain::Ethereum,
63 Self::USDC => Blockchain::Ethereum,
64 Self::USDT => Blockchain::Ethereum,
65 Self::BAT => Blockchain::Ethereum,
66 Self::OMG => Blockchain::Ethereum,
67 Self::ZRX => Blockchain::Ethereum,
68 Self::LINK => Blockchain::Ethereum,
69 Self::QNT => Blockchain::Ethereum,
70 Self::RLC => Blockchain::Ethereum,
71 Self::ANT => Blockchain::Ethereum,
72 Self::TRAC => Blockchain::Ethereum,
73 Self::GUNTHY => Blockchain::Ethereum,
74 Self::BTC => Blockchain::Bitcoin,
75 Self::NEO => Blockchain::NEO,
76 Self::GAS => Blockchain::NEO,
77 Self::NNN => Blockchain::NEO,
78 Self::NOIA => Blockchain::Ethereum
79 }
80 }
81
82 pub fn name(&self) -> &'static str {
86 match self {
87 Self::ETH => "eth",
88 Self::USDC => "usdc",
89 Self::USDT => "usdt",
90 Self::BAT => "bat",
91 Self::OMG => "omg",
92 Self::ZRX => "zrx",
93 Self::LINK => "link",
94 Self::QNT => "qnt",
95 Self::RLC => "rlc",
96 Self::ANT => "ant",
97 Self::BTC => "btc",
98 Self::NEO => "neo",
99 Self::GAS => "gas",
100 Self::TRAC => "trac",
101 Self::GUNTHY => "gunthy",
102 Self::NNN => "nnn",
103 Self::NOIA => "noia"
104 }
105 }
106
107 pub fn from_str(asset_str: &str) -> Result<Self> {
108 match asset_str {
109 "eth" => Ok(Self::ETH),
110 "usdc" => Ok(Self::USDC),
111 "usdt" => Ok(Self::USDT),
112 "bat" => Ok(Self::BAT),
113 "omg" => Ok(Self::OMG),
114 "zrx" => Ok(Self::ZRX),
115 "link" => Ok(Self::LINK),
116 "qnt" => Ok(Self::QNT),
117 "rlc" => Ok(Self::RLC),
118 "ant" => Ok(Self::ANT),
119 "btc" => Ok(Self::BTC),
120 "neo" => Ok(Self::NEO),
121 "gas" => Ok(Self::GAS),
122 "trac" => Ok(Self::TRAC),
123 "gunthy" => Ok(Self::GUNTHY),
124 "nnn" => Ok(Self::NNN),
125 "noia" => Ok(Self::NOIA),
126 _ => Err(ProtocolError("Asset not known")),
127 }
128 }
129
130 pub fn assets() -> Vec<Self> {
133 vec![
134 Self::ETH,
135 Self::USDC,
136 Self::USDT,
137 Self::BAT,
138 Self::OMG,
139 Self::ZRX,
140 Self::LINK,
141 Self::QNT,
142 Self::ANT,
143 Self::BTC,
144 Self::NEO,
145 Self::GAS,
146 Self::TRAC,
147 Self::GUNTHY,
148 Self::NNN,
149 ]
150 }
151}
152
153#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
156pub struct AssetofPrecision {
157 pub asset: Asset,
158 pub precision: u32,
159}
160
161impl Into<Asset> for AssetofPrecision {
163 fn into(self) -> Asset {
164 self.asset
165 }
166}
167
168impl AssetofPrecision {
169 pub fn with_amount(&self, amount_str: &str) -> Result<AssetAmount> {
172 let amount = Amount::new(amount_str, self.precision)?;
173 Ok(AssetAmount {
174 asset: *self,
175 amount,
176 })
177 }
178}
179
180impl Asset {
182 pub fn with_precision(&self, precision: u32) -> AssetofPrecision {
184 AssetofPrecision {
185 asset: *self,
186 precision,
187 }
188 }
189}
190
191#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
193pub struct AssetAmount {
194 pub asset: AssetofPrecision,
195 pub amount: Amount,
196}
197
198impl AssetAmount {
199 pub fn exchange_at(&self, rate: &Rate, into_asset: AssetofPrecision) -> Result<AssetAmount> {
201 let new_amount = self.amount.to_bigdecimal() * rate.to_bigdecimal()?;
202 Ok(AssetAmount {
203 asset: into_asset,
204 amount: Amount::from_bigdecimal(new_amount, into_asset.precision),
205 })
206 }
207}
208
209#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
212pub struct Market {
213 pub asset_a: AssetofPrecision,
215 pub asset_b: AssetofPrecision,
216 pub min_trade_size_a: AssetAmount,
217 pub min_trade_size_b: AssetAmount,
218}
219
220impl Market {
221 pub fn new(
227 asset_a: AssetofPrecision,
228 asset_b: AssetofPrecision,
229 min_trade_size_a: AssetAmount,
230 min_trade_size_b: AssetAmount
231 ) -> Self {
232 Self {
233 asset_a,
234 asset_b,
235 min_trade_size_a,
236 min_trade_size_b
237 }
238 }
239
240 pub fn market_name(&self) -> String {
242 format!(
243 "{}_{}",
244 self.asset_a.asset.name(),
245 self.asset_b.asset.name()
246 )
247 }
248
249 pub fn blockchains(&self) -> Vec<Blockchain> {
252 let chain_a = self.asset_a.asset.blockchain();
253 let chain_b = self.asset_b.asset.blockchain();
254 if chain_a == chain_b {
255 vec![chain_a]
256 } else {
257 vec![chain_a, chain_b]
258 }
259 }
260
261 pub fn get_asset(&self, asset_name: &str) -> Result<AssetofPrecision> {
263 if asset_name == self.asset_a.asset.name() {
264 Ok(self.asset_a.clone())
265 } else if asset_name == self.asset_b.asset.name() {
266 Ok(self.asset_b.clone())
267 } else {
268 Err(ProtocolError("Asset not associated with market"))
269 }
270 }
271
272 pub fn invert(&self) -> Market {
273 Market::new(
274 self.asset_b.clone(),
275 self.asset_a.clone(),
276 self.min_trade_size_b.clone(),
277 self.min_trade_size_a.clone(),
278 )
279 }
280}
281
282#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
285#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
286pub enum BuyOrSell {
287 Buy,
288 Sell,
289}
290
291#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
293#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
294pub enum OrderType {
295 Market,
296 Limit,
297 StopMarket,
298 StopLimit,
299}
300
301impl std::fmt::Display for OrderType {
302 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
303 write!(f, "{:?}", self)
304 }
305}
306
307#[derive(Clone, Debug, PartialEq)]
310pub enum Rate {
311 OrderRate(OrderRate),
312 MaxOrderRate,
313 MinOrderRate,
314 FeeRate(FeeRate),
315 MaxFeeRate,
316 MinFeeRate,
317}
318
319impl From<OrderRate> for Rate {
324 fn from(rate: OrderRate) -> Self {
325 Self::OrderRate(rate)
326 }
327}
328
329impl Rate {
330 pub fn to_bigdecimal(&self) -> Result<BigDecimal> {
332 let num = match self {
333 Self::FeeRate(rate) | Self::OrderRate(rate) => rate.inner.clone(),
334 Self::MaxOrderRate | Self::MaxFeeRate => {
336 BigDecimal::from_str("0.0025").unwrap()
338 }
339 Self::MinOrderRate | Self::MinFeeRate => 0.into(),
340 };
341 Ok(num)
342 }
343
344 pub fn round(&self, precision: i64) -> Result<Self> {
346 match self {
347 Self::OrderRate(rate) => Ok(Self::OrderRate(rate.round(precision))),
348 _ => Err(ProtocolError(
349 "Cannot round a Rate that is not an OrderRate"
350 ))
351 }
352 }
353
354 pub fn invert_rate(&self, precision: Option<u32>) -> Result<Self> {
355 match self {
356 Self::OrderRate(rate) => Ok(Self::OrderRate(rate.invert_rate(precision))),
357 _ => Err(ProtocolError(
358 "Cannot invert a Rate that is not an OrderRate",
359 )),
360 }
361 }
362
363 pub fn subtract_fee(&self, fee: BigDecimal) -> Result<OrderRate> {
365 let as_order_rate = OrderRate {
366 inner: self.to_bigdecimal()?,
367 };
368 Ok(as_order_rate.subtract_fee(fee))
369 }
370}
371
372#[derive(Clone, Debug, PartialEq)]
384pub struct OrderRate {
385 inner: BigDecimal
386}
387
388impl OrderRate {
389 pub fn new(str_num: &str) -> Result<Self> {
391 BigDecimal::from_str(str_num)
392 .map_err(|_| ProtocolError("String to BigDecimal failed in creating OrderRate"))
393 .map(|inner| Self { inner })
394 }
395
396 pub fn from_bigdecimal(decimal: BigDecimal) -> Self {
398 Self { inner: decimal }
399 }
400
401 pub fn round(&self, precision: i64) -> Self {
403 let inner = self.inner.round(precision);
404 Self { inner }
405 }
406
407 pub fn invert_rate(&self, precision: Option<u32>) -> Self {
410 let mut inverse = self.inner.inverse();
411 if let Some(precision) = precision {
412 let scale_num = BigDecimal::from(u64::pow(10, precision));
413 inverse = (&self.inner * &scale_num).with_scale(0) / scale_num;
414 }
415 Self { inner: inverse }
416 }
417
418 pub fn to_bigdecimal(&self) -> BigDecimal {
420 self.inner.clone()
421 }
422
423 pub fn subtract_fee(&self, fee: BigDecimal) -> Self {
427 let fee_multiplier = BigDecimal::from(1) - fee;
428 let inner = &self.inner * &fee_multiplier;
429 OrderRate { inner }
430 }
431}
432
433type FeeRate = OrderRate;
434
435#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
441pub struct Amount {
442 pub precision: u32,
443 pub value: BigDecimal,
444}
445
446impl Amount {
447 pub fn new(str_num: &str, precision: u32) -> Result<Self> {
449 let value = BigDecimal::from_str(str_num)
450 .map_err(|_| ProtocolError("String to BigDecimal failed in creating Amount"))?;
451 let adjust_precision = bigdecimal_to_nash_prec(&value, precision);
452 Ok(Self { value: adjust_precision, precision })
453 }
454
455 pub fn from_bigdecimal(value: BigDecimal, precision: u32) -> Self {
456 Self { value, precision }
457 }
458
459 pub fn to_bigdecimal(&self) -> BigDecimal {
461 self.value.clone()
462 }
463}
464
465#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)]
470pub enum Nonce {
471 Value(u32),
472 Crosschain,
473}
474
475impl Nonce {
476 pub fn crosschain() -> u32 {
477 0xffff_ffff
478 }
479}
480
481impl Into<i64> for Nonce {
483 fn into(self) -> i64 {
484 match self {
485 Self::Value(value) => value as i64,
486 Self::Crosschain => Nonce::crosschain() as i64,
487 }
488 }
489}
490
491impl Into<u32> for Nonce {
492 fn into(self) -> u32 {
493 match self {
494 Self::Value(value) => value as u32,
495 Self::Crosschain => Nonce::crosschain() as u32,
496 }
497 }
498}
499
500impl From<u32> for Nonce {
501 fn from(val: u32) -> Self {
502 if val == Nonce::crosschain() {
503 Self::Crosschain
504 } else {
505 Self::Value(val)
506 }
507 }
508}
509
510impl From<&u32> for Nonce {
511 fn from(val: &u32) -> Self {
512 if val == &Nonce::crosschain() {
513 Self::Crosschain
514 } else {
515 Self::Value(*val)
516 }
517 }
518}
519
520#[derive(Clone, Debug, PartialEq)]
521pub enum CandleInterval {
522 FifteenMinute,
523 FiveMinute,
524 FourHour,
525 OneDay,
526 OneHour,
527 OneMinute,
528 OneMonth,
529 OneWeek,
530 SixHour,
531 ThirtyMinute,
532 ThreeHour,
533 TwelveHour,
534}
535
536#[derive(Debug)]
537pub struct Candle {
538 pub a_volume: BigDecimal,
539 pub b_volume: BigDecimal,
540 pub close_price: BigDecimal,
541 pub high_price: BigDecimal,
542 pub low_price: BigDecimal,
543 pub open_price: BigDecimal,
544 pub interval: CandleInterval,
545 pub interval_start: DateTime<Utc>,
546}
547#[derive(Clone, Copy, Debug)]
548pub struct DateTimeRange {
549 pub start: DateTime<Utc>,
550 pub stop: DateTime<Utc>,
551}
552
553#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
555#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
556pub enum OrderStatus {
557 Pending,
558 Open,
559 Filled,
560 Canceled,
561}
562
563#[derive(Clone, Debug, PartialEq)]
565pub enum AccountTradeSide {
566 Maker,
567 Taker,
568 None,
570}
571
572#[derive(Clone, Debug)]
573pub struct Trade {
574 pub id: String,
575 pub taker_order_id: String,
576 pub maker_order_id: String,
577 pub amount: BigDecimal,
578 pub executed_at: DateTime<Utc>,
579 pub account_side: AccountTradeSide,
580 pub maker_fee: BigDecimal,
581 pub taker_fee: BigDecimal,
582 pub maker_recieved: BigDecimal,
583 pub taker_recieved: BigDecimal,
584 pub market: String,
585 pub direction: BuyOrSell,
586 pub limit_price: BigDecimal,
587}
588
589#[derive(Clone, Copy, Debug, PartialEq)]
590pub enum OrderCancellationPolicy {
591 FillOrKill,
592 GoodTilCancelled,
593 GoodTilTime(DateTime<Utc>),
594 ImmediateOrCancel,
595}
596
597#[derive(Clone, Copy, Debug, PartialEq)]
598pub enum OrderCancellationReason {
599 AdminCancelled,
600 Expiration,
601 InvalidForOrderbookState,
602 NoFill,
603 User,
604}
605
606#[derive(Clone, Debug)]
607pub struct Order {
608 pub id: String,
609 pub client_order_id: Option<String>,
610 pub amount_placed: BigDecimal,
612 pub amount_remaining: BigDecimal,
614 pub amount_executed: BigDecimal,
616 pub limit_price: Option<BigDecimal>,
617 pub stop_price: Option<BigDecimal>,
618 pub placed_at: DateTime<Utc>,
619 pub buy_or_sell: BuyOrSell,
620 pub cancellation_policy: Option<OrderCancellationPolicy>,
621 pub cancellation_reason: Option<OrderCancellationReason>,
622 pub market: String,
623 pub order_type: OrderType,
624 pub status: OrderStatus,
625 pub trades: Vec<Trade>,
626}
627
628#[derive(Clone, Debug, Serialize, Deserialize)]
630pub struct OrderbookOrder {
631 pub price: String,
632 pub amount: BigDecimal,
633}
634
635#[cfg(test)]
636mod tests {
637 use super::{BigDecimal, FromStr, OrderRate};
638 use std::convert::TryInto;
639
640 #[test]
641 fn fee_rate_conversion_precision() {
642 let rate = OrderRate::new("150").unwrap();
643 let inverted_rate = rate.invert_rate(None);
644 let minus_fee = inverted_rate.subtract_fee(BigDecimal::from_str("0.0025").unwrap());
645 let payload = minus_fee.to_be_bytes(8).unwrap();
646 assert_eq!(665000, u64::from_be_bytes(payload.try_into().unwrap()));
647 }
648
649 #[test]
650 fn round() {
651 let n = OrderRate::new("26.249999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999996325").expect("Couldn't create OrderRate.");
652 assert_eq!(n.round(3), OrderRate::new("26.25").unwrap());
653 assert_eq!(n.round(2), OrderRate::new("26.25").unwrap());
654 assert_eq!(n.round(1), OrderRate::new("26.2").unwrap());
655 assert_eq!(n.round(0), OrderRate::new("26.0").unwrap());
656 let n = OrderRate::new("14.45652173").unwrap();
657 assert_eq!(n.round(7), OrderRate::new("14.4565217").unwrap());
658 assert_eq!(n.round(6), OrderRate::new("14.456522").unwrap());
659 assert_eq!(n.round(0), OrderRate::new("14.0").unwrap());
660 }
661}