1use serde::{Deserialize, Serialize};
17use strum::{AsRefStr, Display, EnumIter, EnumString};
18
19#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
21#[cfg_attr(
22 feature = "python",
23 pyo3::pyclass(
24 module = "nautilus_trader.core.nautilus_pyo3.coinbase",
25 eq,
26 from_py_object,
27 rename_all = "SCREAMING_SNAKE_CASE"
28 )
29)]
30#[cfg_attr(
31 feature = "python",
32 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.coinbase")
33)]
34pub enum CoinbaseEnvironment {
35 #[default]
37 Live,
38 Sandbox,
40}
41
42impl CoinbaseEnvironment {
43 #[must_use]
45 pub const fn is_sandbox(self) -> bool {
46 matches!(self, Self::Sandbox)
47 }
48}
49
50#[derive(
52 Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString, EnumIter,
53)]
54#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
55#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
56pub enum CoinbaseProductType {
57 Spot,
58 Future,
59 #[serde(rename = "UNKNOWN_PRODUCT_TYPE")]
60 #[strum(serialize = "UNKNOWN_PRODUCT_TYPE")]
61 Unknown,
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString)]
66#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
67#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
68pub enum CoinbaseOrderSide {
69 Buy,
70 Sell,
71 #[serde(rename = "UNKNOWN_ORDER_SIDE")]
72 #[strum(serialize = "UNKNOWN_ORDER_SIDE")]
73 Unknown,
74}
75
76#[derive(
78 Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString, AsRefStr,
79)]
80#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
81#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
82pub enum CoinbaseOrderType {
83 #[serde(rename = "UNKNOWN_ORDER_TYPE")]
84 #[strum(serialize = "UNKNOWN_ORDER_TYPE")]
85 Unknown,
86 #[serde(alias = "Market")]
87 Market,
88 #[serde(alias = "Limit")]
89 Limit,
90 #[serde(alias = "Stop")]
91 Stop,
92 #[serde(alias = "StopLimit", alias = "Stop Limit")]
93 StopLimit,
94 #[serde(alias = "Bracket")]
95 Bracket,
96 Twap,
97 #[serde(alias = "Roll Open")]
98 RollOpen,
99 #[serde(alias = "Roll Close")]
100 RollClose,
101 Liquidation,
102 Scaled,
103}
104
105#[derive(
107 Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString, AsRefStr,
108)]
109#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
110#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
111pub enum CoinbaseOrderStatus {
112 Pending,
113 Open,
114 Filled,
115 Cancelled,
116 Expired,
117 Failed,
118 #[serde(rename = "UNKNOWN_ORDER_STATUS")]
119 #[strum(serialize = "UNKNOWN_ORDER_STATUS")]
120 Unknown,
121 Queued,
122 CancelQueued,
123 EditQueued,
124}
125
126impl CoinbaseOrderStatus {
127 #[must_use]
130 pub const fn is_terminal(self) -> bool {
131 matches!(
132 self,
133 Self::Filled | Self::Cancelled | Self::Expired | Self::Failed
134 )
135 }
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString)]
140#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
141#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
142pub enum CoinbaseTimeInForce {
143 #[serde(rename = "UNKNOWN_TIME_IN_FORCE")]
144 #[strum(serialize = "UNKNOWN_TIME_IN_FORCE")]
145 Unknown,
146 GoodUntilDateTime,
147 GoodUntilCancelled,
148 ImmediateOrCancel,
149 FillOrKill,
150}
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString)]
154#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
155#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
156pub enum CoinbaseTriggerStatus {
157 #[serde(rename = "UNKNOWN_TRIGGER_STATUS")]
158 #[strum(serialize = "UNKNOWN_TRIGGER_STATUS")]
159 Unknown,
160 InvalidOrderType,
161 StopPending,
162 StopTriggered,
163}
164
165#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString)]
167#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
168#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
169pub enum CoinbaseOrderPlacementSource {
170 #[serde(rename = "UNKNOWN_PLACEMENT_SOURCE")]
171 #[strum(serialize = "UNKNOWN_PLACEMENT_SOURCE")]
172 Unknown,
173 RetailSimple,
174 RetailAdvanced,
175}
176
177#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString)]
179#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
180#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
181#[cfg_attr(
182 feature = "python",
183 pyo3::pyclass(
184 module = "nautilus_trader.core.nautilus_pyo3.coinbase",
185 eq,
186 eq_int,
187 frozen,
188 from_py_object,
189 rename_all = "SCREAMING_SNAKE_CASE"
190 )
191)]
192#[cfg_attr(
193 feature = "python",
194 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.coinbase")
195)]
196pub enum CoinbaseMarginType {
197 #[serde(alias = "Cross")]
198 Cross,
199 #[serde(alias = "Isolated")]
200 Isolated,
201}
202
203#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString)]
205#[serde(rename_all = "snake_case")]
206#[strum(serialize_all = "snake_case")]
207pub enum CoinbaseProductStatus {
208 Online,
209 Offline,
210 Delisted,
211 #[serde(rename = "")]
213 #[strum(serialize = "")]
214 Unset,
215}
216
217#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString)]
219#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
220#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
221pub enum CoinbaseProductVenue {
222 Cbe,
224 Fcm,
226}
227
228#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString)]
230pub enum CoinbaseFcmTradingSessionState {
231 #[serde(rename = "FCM_TRADING_SESSION_STATE_UNDEFINED")]
232 #[strum(serialize = "FCM_TRADING_SESSION_STATE_UNDEFINED")]
233 Undefined,
234 #[serde(rename = "FCM_TRADING_SESSION_STATE_OPEN")]
235 #[strum(serialize = "FCM_TRADING_SESSION_STATE_OPEN")]
236 Open,
237}
238
239#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString)]
241pub enum CoinbaseFcmTradingSessionClosedReason {
242 #[serde(rename = "FCM_TRADING_SESSION_CLOSED_REASON_UNDEFINED")]
243 #[strum(serialize = "FCM_TRADING_SESSION_CLOSED_REASON_UNDEFINED")]
244 Undefined,
245 #[serde(rename = "FCM_TRADING_SESSION_CLOSED_REASON_EXCHANGE_MAINTENANCE")]
246 #[strum(serialize = "FCM_TRADING_SESSION_CLOSED_REASON_EXCHANGE_MAINTENANCE")]
247 ExchangeMaintenance,
248}
249
250#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString)]
252pub enum CoinbaseRiskManagedBy {
253 #[serde(rename = "UNKNOWN_RISK_MANAGEMENT_TYPE")]
254 #[strum(serialize = "UNKNOWN_RISK_MANAGEMENT_TYPE")]
255 Unknown,
256 #[serde(rename = "MANAGED_BY_FCM")]
257 #[strum(serialize = "MANAGED_BY_FCM")]
258 ManagedByFcm,
259 #[serde(rename = "MANAGED_BY_VENUE")]
260 #[strum(serialize = "MANAGED_BY_VENUE")]
261 ManagedByVenue,
262}
263
264#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString)]
266#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
267#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
268pub enum CoinbaseAccountType {
269 #[serde(alias = "ACCOUNT_TYPE_CRYPTO")]
274 #[strum(to_string = "CRYPTO", serialize = "ACCOUNT_TYPE_CRYPTO")]
275 Crypto,
276 #[serde(alias = "ACCOUNT_TYPE_FIAT")]
277 #[strum(to_string = "FIAT", serialize = "ACCOUNT_TYPE_FIAT")]
278 Fiat,
279}
280
281#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString)]
283pub enum CoinbaseFillTradeType {
284 #[serde(rename = "FILL")]
285 #[strum(serialize = "FILL")]
286 Fill,
287}
288
289#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString)]
291pub enum CoinbaseFcmPositionSide {
292 #[serde(rename = "FUTURES_POSITION_SIDE_UNSPECIFIED")]
293 #[strum(serialize = "FUTURES_POSITION_SIDE_UNSPECIFIED")]
294 Unspecified,
295 #[serde(rename = "LONG")]
296 #[strum(serialize = "LONG")]
297 Long,
298 #[serde(rename = "SHORT")]
299 #[strum(serialize = "SHORT")]
300 Short,
301}
302
303#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString)]
305pub enum CoinbaseMarginWindowType {
306 #[serde(rename = "FCM_MARGIN_WINDOW_TYPE_INTRADAY")]
307 #[strum(serialize = "FCM_MARGIN_WINDOW_TYPE_INTRADAY")]
308 Intraday,
309 #[serde(rename = "FCM_MARGIN_WINDOW_TYPE_OVERNIGHT")]
310 #[strum(serialize = "FCM_MARGIN_WINDOW_TYPE_OVERNIGHT")]
311 Overnight,
312}
313
314#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString)]
316pub enum CoinbaseMarginLevel {
317 #[serde(rename = "MARGIN_LEVEL_TYPE_BASE")]
318 #[strum(serialize = "MARGIN_LEVEL_TYPE_BASE")]
319 Base,
320}
321
322#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString)]
324#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
325#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
326pub enum CoinbaseContractExpiryType {
327 #[serde(
328 rename = "UNKNOWN_CONTRACT_EXPIRY_TYPE",
329 alias = "UNKNOWN_CONTRACT_EXPIRY"
330 )]
331 #[strum(
332 serialize = "UNKNOWN_CONTRACT_EXPIRY_TYPE",
333 serialize = "UNKNOWN_CONTRACT_EXPIRY"
334 )]
335 Unknown,
336 Expiring,
337 #[serde(rename = "PERPETUAL")]
339 #[strum(serialize = "PERPETUAL")]
340 Perpetual,
341}
342
343#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString)]
345pub enum CoinbaseFuturesAssetType {
346 #[serde(rename = "FUTURES_ASSET_TYPE_CRYPTO")]
347 #[strum(serialize = "FUTURES_ASSET_TYPE_CRYPTO")]
348 Crypto,
349 #[serde(rename = "FUTURES_ASSET_TYPE_ENERGY")]
350 #[strum(serialize = "FUTURES_ASSET_TYPE_ENERGY")]
351 Energy,
352 #[serde(rename = "FUTURES_ASSET_TYPE_METALS")]
353 #[strum(serialize = "FUTURES_ASSET_TYPE_METALS")]
354 Metals,
355 #[serde(rename = "FUTURES_ASSET_TYPE_STOCKS")]
356 #[strum(serialize = "FUTURES_ASSET_TYPE_STOCKS")]
357 Stocks,
358}
359
360#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString)]
362#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
363#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
364pub enum CoinbaseLiquidityIndicator {
365 Maker,
366 Taker,
367 Unknown,
368}
369
370#[derive(
372 Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString, AsRefStr,
373)]
374pub enum CoinbaseStopDirection {
375 #[serde(rename = "STOP_DIRECTION_STOP_DOWN")]
376 #[strum(serialize = "STOP_DIRECTION_STOP_DOWN")]
377 StopDown,
378 #[serde(rename = "STOP_DIRECTION_STOP_UP")]
379 #[strum(serialize = "STOP_DIRECTION_STOP_UP")]
380 StopUp,
381}
382
383#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString)]
385#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
386#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
387pub enum CoinbaseGranularity {
388 OneMinute,
389 FiveMinute,
390 FifteenMinute,
391 ThirtyMinute,
392 OneHour,
393 TwoHour,
394 SixHour,
395 OneDay,
396}
397
398#[derive(
400 Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display, EnumString, AsRefStr,
401)]
402#[serde(rename_all = "snake_case")]
403#[strum(serialize_all = "snake_case")]
404pub enum CoinbaseWsChannel {
405 Level2,
406 MarketTrades,
407 Ticker,
408 TickerBatch,
409 Candles,
410 User,
411 Heartbeats,
412 FuturesBalanceSummary,
413 Status,
414}
415
416impl CoinbaseWsChannel {
417 #[must_use]
419 pub fn requires_auth(&self) -> bool {
420 matches!(self, Self::User | Self::FuturesBalanceSummary)
421 }
422}
423
424#[cfg(test)]
425mod tests {
426 use std::str::FromStr;
427
428 use rstest::rstest;
429
430 use super::*;
431
432 #[rstest]
433 #[case(CoinbaseProductType::Spot, "SPOT")]
434 #[case(CoinbaseProductType::Future, "FUTURE")]
435 fn test_product_type_display(#[case] variant: CoinbaseProductType, #[case] expected: &str) {
436 assert_eq!(variant.to_string(), expected);
437 }
438
439 #[rstest]
440 #[case("BUY", CoinbaseOrderSide::Buy)]
441 #[case("SELL", CoinbaseOrderSide::Sell)]
442 fn test_order_side_from_str(#[case] input: &str, #[case] expected: CoinbaseOrderSide) {
443 assert_eq!(CoinbaseOrderSide::from_str(input).unwrap(), expected);
444 }
445
446 #[rstest]
447 #[case(CoinbaseOrderStatus::Filled, true)]
448 #[case(CoinbaseOrderStatus::Cancelled, true)]
449 #[case(CoinbaseOrderStatus::Expired, true)]
450 #[case(CoinbaseOrderStatus::Failed, true)]
451 #[case(CoinbaseOrderStatus::Open, false)]
452 #[case(CoinbaseOrderStatus::Pending, false)]
453 #[case(CoinbaseOrderStatus::Queued, false)]
454 #[case(CoinbaseOrderStatus::CancelQueued, false)]
455 #[case(CoinbaseOrderStatus::EditQueued, false)]
456 #[case(CoinbaseOrderStatus::Unknown, false)]
457 fn test_order_status_is_terminal(#[case] status: CoinbaseOrderStatus, #[case] expected: bool) {
458 assert_eq!(status.is_terminal(), expected);
459 }
460
461 #[rstest]
462 fn test_ws_channel_requires_auth() {
463 assert!(CoinbaseWsChannel::User.requires_auth());
464 assert!(CoinbaseWsChannel::FuturesBalanceSummary.requires_auth());
465 assert!(!CoinbaseWsChannel::Level2.requires_auth());
466 assert!(!CoinbaseWsChannel::MarketTrades.requires_auth());
467 assert!(!CoinbaseWsChannel::Ticker.requires_auth());
468 }
469
470 #[rstest]
471 fn test_order_status_serialization() {
472 let status = CoinbaseOrderStatus::Open;
473 let json = serde_json::to_string(&status).unwrap();
474 assert_eq!(json, "\"OPEN\"");
475
476 let deserialized: CoinbaseOrderStatus = serde_json::from_str("\"CANCELLED\"").unwrap();
477 assert_eq!(deserialized, CoinbaseOrderStatus::Cancelled);
478 }
479
480 #[rstest]
481 fn test_screaming_snake_case_multi_word() {
482 let json = serde_json::to_string(&CoinbaseOrderType::StopLimit).unwrap();
483 assert_eq!(json, "\"STOP_LIMIT\"");
484
485 let json = serde_json::to_string(&CoinbaseOrderStatus::CancelQueued).unwrap();
486 assert_eq!(json, "\"CANCEL_QUEUED\"");
487
488 let json = serde_json::to_string(&CoinbaseTimeInForce::GoodUntilDateTime).unwrap();
489 assert_eq!(json, "\"GOOD_UNTIL_DATE_TIME\"");
490
491 let json = serde_json::to_string(&CoinbaseGranularity::FifteenMinute).unwrap();
492 assert_eq!(json, "\"FIFTEEN_MINUTE\"");
493 }
494
495 #[rstest]
496 fn test_order_type_accepts_title_case_aliases() {
497 let order_type: CoinbaseOrderType = serde_json::from_str("\"Limit\"").unwrap();
498 assert_eq!(order_type, CoinbaseOrderType::Limit);
499
500 let order_type: CoinbaseOrderType = serde_json::from_str("\"StopLimit\"").unwrap();
501 assert_eq!(order_type, CoinbaseOrderType::StopLimit);
502
503 let order_type: CoinbaseOrderType = serde_json::from_str("\"Stop Limit\"").unwrap();
504 assert_eq!(order_type, CoinbaseOrderType::StopLimit);
505
506 let order_type = CoinbaseOrderType::from_str("STOP_LIMIT").unwrap();
507 assert_eq!(order_type, CoinbaseOrderType::StopLimit);
508
509 let order_type = CoinbaseOrderType::from_str("TWAP").unwrap();
510 assert_eq!(order_type, CoinbaseOrderType::Twap);
511
512 let order_type = CoinbaseOrderType::from_str("LIQUIDATION").unwrap();
513 assert_eq!(order_type, CoinbaseOrderType::Liquidation);
514 }
515
516 #[rstest]
517 fn test_account_type_accepts_current_wire_values() {
518 let account_type: CoinbaseAccountType = serde_json::from_str("\"FIAT\"").unwrap();
519 assert_eq!(account_type, CoinbaseAccountType::Fiat);
520
521 let account_type = CoinbaseAccountType::from_str("CRYPTO").unwrap();
522 assert_eq!(account_type, CoinbaseAccountType::Crypto);
523 }
524
525 #[rstest]
530 fn test_account_type_accepts_qualified_wire_values() {
531 let account_type: CoinbaseAccountType =
532 serde_json::from_str("\"ACCOUNT_TYPE_CRYPTO\"").unwrap();
533 assert_eq!(account_type, CoinbaseAccountType::Crypto);
534
535 let account_type: CoinbaseAccountType =
536 serde_json::from_str("\"ACCOUNT_TYPE_FIAT\"").unwrap();
537 assert_eq!(account_type, CoinbaseAccountType::Fiat);
538
539 let account_type = CoinbaseAccountType::from_str("ACCOUNT_TYPE_CRYPTO").unwrap();
540 assert_eq!(account_type, CoinbaseAccountType::Crypto);
541 }
542
543 #[rstest]
546 fn test_account_type_display_uses_short_form() {
547 assert_eq!(CoinbaseAccountType::Crypto.to_string(), "CRYPTO");
548 assert_eq!(CoinbaseAccountType::Fiat.to_string(), "FIAT");
549 assert_eq!(
550 serde_json::to_string(&CoinbaseAccountType::Crypto).unwrap(),
551 "\"CRYPTO\""
552 );
553 }
554
555 #[rstest]
556 fn test_margin_type_accepts_request_and_ws_spellings() {
557 let margin_type: CoinbaseMarginType = serde_json::from_str("\"CROSS\"").unwrap();
558 assert_eq!(margin_type, CoinbaseMarginType::Cross);
559
560 let margin_type: CoinbaseMarginType = serde_json::from_str("\"Cross\"").unwrap();
561 assert_eq!(margin_type, CoinbaseMarginType::Cross);
562 }
563
564 #[rstest]
565 fn test_contract_expiry_type_accepts_websocket_alias() {
566 let expiry_type: CoinbaseContractExpiryType =
567 serde_json::from_str("\"UNKNOWN_CONTRACT_EXPIRY\"").unwrap();
568 assert_eq!(expiry_type, CoinbaseContractExpiryType::Unknown);
569 }
570
571 #[rstest]
572 fn test_ws_channel_snake_case() {
573 let json = serde_json::to_string(&CoinbaseWsChannel::Level2).unwrap();
574 assert_eq!(json, "\"level2\"");
575
576 let json = serde_json::to_string(&CoinbaseWsChannel::MarketTrades).unwrap();
577 assert_eq!(json, "\"market_trades\"");
578
579 let json = serde_json::to_string(&CoinbaseWsChannel::FuturesBalanceSummary).unwrap();
580 assert_eq!(json, "\"futures_balance_summary\"");
581 }
582
583 #[rstest]
584 fn test_unknown_variants_have_qualified_names() {
585 let json = serde_json::to_string(&CoinbaseProductType::Unknown).unwrap();
586 assert_eq!(json, "\"UNKNOWN_PRODUCT_TYPE\"");
587
588 let json = serde_json::to_string(&CoinbaseOrderSide::Unknown).unwrap();
589 assert_eq!(json, "\"UNKNOWN_ORDER_SIDE\"");
590
591 let json = serde_json::to_string(&CoinbaseOrderType::Unknown).unwrap();
592 assert_eq!(json, "\"UNKNOWN_ORDER_TYPE\"");
593
594 let json = serde_json::to_string(&CoinbaseOrderStatus::Unknown).unwrap();
595 assert_eq!(json, "\"UNKNOWN_ORDER_STATUS\"");
596
597 let json = serde_json::to_string(&CoinbaseTimeInForce::Unknown).unwrap();
598 assert_eq!(json, "\"UNKNOWN_TIME_IN_FORCE\"");
599
600 let json = serde_json::to_string(&CoinbaseTriggerStatus::Unknown).unwrap();
601 assert_eq!(json, "\"UNKNOWN_TRIGGER_STATUS\"");
602
603 let json = serde_json::to_string(&CoinbaseOrderPlacementSource::Unknown).unwrap();
604 assert_eq!(json, "\"UNKNOWN_PLACEMENT_SOURCE\"");
605
606 let json = serde_json::to_string(&CoinbaseContractExpiryType::Unknown).unwrap();
607 assert_eq!(json, "\"UNKNOWN_CONTRACT_EXPIRY_TYPE\"");
608
609 let json = serde_json::to_string(&CoinbaseRiskManagedBy::Unknown).unwrap();
610 assert_eq!(json, "\"UNKNOWN_RISK_MANAGEMENT_TYPE\"");
611 }
612}