1use nautilus_model::{
19 data::BarSpecification,
20 enums::{
21 BarAggregation, LiquiditySide, MarketStatusAction, OrderSide, OrderStatus, OrderType,
22 PositionSide,
23 },
24};
25use serde::{Deserialize, Serialize};
26use strum::{AsRefStr, Display, EnumIter, EnumString, IntoStaticStr};
27
28use crate::{error::DydxError, grpc::types::ChainId};
29
30#[derive(
32 Copy,
33 Clone,
34 Debug,
35 Display,
36 PartialEq,
37 Eq,
38 Hash,
39 AsRefStr,
40 EnumIter,
41 EnumString,
42 Serialize,
43 Deserialize,
44)]
45#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
46pub enum DydxOrderStatus {
47 Open,
49 Filled,
51 Canceled,
53 BestEffortCanceled,
55 PartiallyFilled,
57 BestEffortOpened,
59 Untriggered,
61}
62
63impl From<DydxOrderStatus> for OrderStatus {
64 fn from(value: DydxOrderStatus) -> Self {
65 match value {
66 DydxOrderStatus::Open | DydxOrderStatus::BestEffortOpened => Self::Accepted,
67 DydxOrderStatus::PartiallyFilled => Self::PartiallyFilled,
68 DydxOrderStatus::Filled => Self::Filled,
69 DydxOrderStatus::Canceled | DydxOrderStatus::BestEffortCanceled => Self::Canceled,
70 DydxOrderStatus::Untriggered => Self::PendingUpdate,
71 }
72 }
73}
74
75#[derive(
77 Copy,
78 Clone,
79 Debug,
80 Display,
81 PartialEq,
82 Eq,
83 Hash,
84 AsRefStr,
85 EnumIter,
86 EnumString,
87 Serialize,
88 Deserialize,
89)]
90#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
91pub enum DydxTimeInForce {
92 Gtt,
94 Fok,
96 Ioc,
98}
99
100#[derive(
102 Copy,
103 Clone,
104 Debug,
105 Display,
106 PartialEq,
107 Eq,
108 Hash,
109 AsRefStr,
110 EnumIter,
111 EnumString,
112 Serialize,
113 Deserialize,
114)]
115#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
116#[cfg_attr(
117 feature = "python",
118 pyo3::pyclass(
119 module = "nautilus_trader.core.nautilus_pyo3.dydx",
120 eq,
121 eq_int,
122 from_py_object
123 )
124)]
125#[cfg_attr(
126 feature = "python",
127 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.dydx")
128)]
129pub enum DydxOrderSide {
130 Buy,
132 Sell,
134}
135
136impl TryFrom<OrderSide> for DydxOrderSide {
137 type Error = DydxError;
138
139 fn try_from(value: OrderSide) -> Result<Self, Self::Error> {
140 match value {
141 OrderSide::Buy => Ok(Self::Buy),
142 OrderSide::Sell => Ok(Self::Sell),
143 _ => Err(DydxError::InvalidOrderSide(format!("{value:?}"))),
144 }
145 }
146}
147
148impl DydxOrderSide {
149 pub fn try_from_order_side(value: OrderSide) -> anyhow::Result<Self> {
155 Self::try_from(value).map_err(|e| anyhow::anyhow!("{e}"))
156 }
157}
158
159impl From<DydxOrderSide> for OrderSide {
160 fn from(side: DydxOrderSide) -> Self {
161 match side {
162 DydxOrderSide::Buy => Self::Buy,
163 DydxOrderSide::Sell => Self::Sell,
164 }
165 }
166}
167
168#[derive(
170 Copy,
171 Clone,
172 Debug,
173 Display,
174 PartialEq,
175 Eq,
176 Hash,
177 AsRefStr,
178 EnumIter,
179 EnumString,
180 Serialize,
181 Deserialize,
182)]
183#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
184#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
185#[cfg_attr(
186 feature = "python",
187 pyo3::pyclass(
188 module = "nautilus_trader.core.nautilus_pyo3.dydx",
189 eq,
190 eq_int,
191 from_py_object
192 )
193)]
194#[cfg_attr(
195 feature = "python",
196 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.dydx")
197)]
198pub enum DydxOrderType {
199 Limit,
201 Market,
203 StopLimit,
205 StopMarket,
207 TakeProfitLimit,
209 TakeProfitMarket,
211 TrailingStop,
213}
214
215impl TryFrom<OrderType> for DydxOrderType {
216 type Error = DydxError;
217
218 fn try_from(value: OrderType) -> Result<Self, Self::Error> {
219 match value {
220 OrderType::Market => Ok(Self::Market),
221 OrderType::Limit => Ok(Self::Limit),
222 OrderType::StopMarket => Ok(Self::StopMarket),
223 OrderType::StopLimit => Ok(Self::StopLimit),
224 OrderType::MarketIfTouched => Ok(Self::TakeProfitMarket),
225 OrderType::LimitIfTouched => Ok(Self::TakeProfitLimit),
226 OrderType::TrailingStopMarket | OrderType::TrailingStopLimit => Ok(Self::TrailingStop),
227 OrderType::MarketToLimit => Err(DydxError::UnsupportedOrderType(format!("{value:?}"))),
228 }
229 }
230}
231
232impl DydxOrderType {
233 pub fn try_from_order_type(value: OrderType) -> anyhow::Result<Self> {
239 Self::try_from(value).map_err(|e| anyhow::anyhow!("{e}"))
240 }
241
242 #[must_use]
244 pub const fn is_conditional(&self) -> bool {
245 matches!(
246 self,
247 Self::StopLimit
248 | Self::StopMarket
249 | Self::TakeProfitLimit
250 | Self::TakeProfitMarket
251 | Self::TrailingStop
252 )
253 }
254
255 #[must_use]
257 pub const fn condition_type(&self) -> DydxConditionType {
258 match self {
259 Self::StopLimit | Self::StopMarket => DydxConditionType::StopLoss,
260 Self::TakeProfitLimit | Self::TakeProfitMarket => DydxConditionType::TakeProfit,
261 _ => DydxConditionType::Unspecified,
262 }
263 }
264
265 #[must_use]
267 pub const fn is_market_execution(&self) -> bool {
268 matches!(
269 self,
270 Self::Market | Self::StopMarket | Self::TakeProfitMarket
271 )
272 }
273}
274
275impl From<DydxOrderType> for OrderType {
276 fn from(value: DydxOrderType) -> Self {
277 match value {
278 DydxOrderType::Market => Self::Market,
279 DydxOrderType::Limit => Self::Limit,
280 DydxOrderType::StopMarket => Self::StopMarket,
281 DydxOrderType::StopLimit => Self::StopLimit,
282 DydxOrderType::TakeProfitMarket => Self::MarketIfTouched,
283 DydxOrderType::TakeProfitLimit => Self::LimitIfTouched,
284 DydxOrderType::TrailingStop => Self::TrailingStopMarket,
285 }
286 }
287}
288
289#[derive(
291 Copy,
292 Clone,
293 Debug,
294 Display,
295 PartialEq,
296 Eq,
297 Hash,
298 AsRefStr,
299 EnumIter,
300 EnumString,
301 Serialize,
302 Deserialize,
303)]
304#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
305pub enum DydxOrderExecution {
306 Default,
308 Ioc,
310 Fok,
312 PostOnly,
314}
315
316#[derive(
318 Copy, Clone, Debug, Display, PartialEq, Eq, Hash, AsRefStr, EnumIter, Serialize, Deserialize,
319)]
320pub enum DydxOrderFlags {
321 ShortTerm = 0,
323 Conditional = 32,
325 LongTerm = 64,
327}
328
329#[derive(
335 Copy,
336 Clone,
337 Debug,
338 Display,
339 PartialEq,
340 Eq,
341 Hash,
342 AsRefStr,
343 EnumIter,
344 EnumString,
345 Serialize,
346 Deserialize,
347)]
348#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
349pub enum DydxConditionType {
350 Unspecified,
352 StopLoss,
354 TakeProfit,
356}
357
358#[derive(
360 Copy,
361 Clone,
362 Debug,
363 Display,
364 PartialEq,
365 Eq,
366 Hash,
367 AsRefStr,
368 EnumIter,
369 EnumString,
370 Serialize,
371 Deserialize,
372)]
373#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
374pub enum DydxPositionSide {
375 Long,
377 Short,
379}
380
381impl From<DydxPositionSide> for PositionSide {
382 fn from(value: DydxPositionSide) -> Self {
383 match value {
384 DydxPositionSide::Long => Self::Long,
385 DydxPositionSide::Short => Self::Short,
386 }
387 }
388}
389
390#[derive(
392 Copy,
393 Clone,
394 Debug,
395 Display,
396 PartialEq,
397 Eq,
398 Hash,
399 AsRefStr,
400 EnumIter,
401 EnumString,
402 Serialize,
403 Deserialize,
404)]
405#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
406pub enum DydxPositionStatus {
407 Open,
409 Closed,
411 Liquidated,
413}
414
415impl DydxPositionStatus {
416 #[must_use]
418 pub const fn is_closed(&self) -> bool {
419 matches!(self, Self::Closed | Self::Liquidated)
420 }
421}
422
423#[derive(
425 Copy,
426 Clone,
427 Debug,
428 Display,
429 PartialEq,
430 Eq,
431 Hash,
432 AsRefStr,
433 EnumIter,
434 EnumString,
435 Serialize,
436 Deserialize,
437)]
438#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
439pub enum DydxMarketStatus {
440 Active,
442 Paused,
444 CancelOnly,
446 PostOnly,
448 Initializing,
450 FinalSettlement,
452}
453
454impl From<DydxMarketStatus> for MarketStatusAction {
455 fn from(value: DydxMarketStatus) -> Self {
456 match value {
457 DydxMarketStatus::Active => Self::Trading,
458 DydxMarketStatus::Paused => Self::Pause,
459 DydxMarketStatus::CancelOnly => Self::Halt,
460 DydxMarketStatus::PostOnly => Self::Quoting,
461 DydxMarketStatus::Initializing => Self::PreOpen,
462 DydxMarketStatus::FinalSettlement => Self::Close,
463 }
464 }
465}
466
467#[derive(
469 Copy,
470 Clone,
471 Debug,
472 Display,
473 PartialEq,
474 Eq,
475 Hash,
476 AsRefStr,
477 EnumIter,
478 EnumString,
479 Serialize,
480 Deserialize,
481)]
482#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
483pub enum DydxFillType {
484 Limit,
486 Liquidated,
488 Liquidation,
490 Deleveraged,
492 Offsetting,
494}
495
496#[derive(
498 Copy,
499 Clone,
500 Debug,
501 Display,
502 PartialEq,
503 Eq,
504 Hash,
505 AsRefStr,
506 EnumIter,
507 EnumString,
508 Serialize,
509 Deserialize,
510)]
511#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
512pub enum DydxLiquidity {
513 Maker,
515 Taker,
517}
518
519impl From<DydxLiquidity> for LiquiditySide {
520 fn from(value: DydxLiquidity) -> Self {
521 match value {
522 DydxLiquidity::Maker => Self::Maker,
523 DydxLiquidity::Taker => Self::Taker,
524 }
525 }
526}
527
528impl From<LiquiditySide> for DydxLiquidity {
529 fn from(value: LiquiditySide) -> Self {
530 match value {
531 LiquiditySide::Maker => Self::Maker,
532 LiquiditySide::Taker => Self::Taker,
533 LiquiditySide::NoLiquiditySide => Self::Taker, }
535 }
536}
537
538#[derive(
540 Copy,
541 Clone,
542 Debug,
543 Display,
544 PartialEq,
545 Eq,
546 Hash,
547 AsRefStr,
548 EnumIter,
549 EnumString,
550 Serialize,
551 Deserialize,
552)]
553#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
554pub enum DydxTickerType {
555 Perpetual,
557}
558
559#[derive(
563 Copy,
564 Clone,
565 Debug,
566 Display,
567 PartialEq,
568 Eq,
569 Hash,
570 AsRefStr,
571 EnumIter,
572 EnumString,
573 Serialize,
574 Deserialize,
575)]
576#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
577pub enum DydxTradeType {
578 Limit,
580 Market,
582 Liquidated,
584 TwapSuborder,
586 StopLimit,
588 TakeProfitLimit,
590}
591
592#[derive(
594 Copy,
595 Clone,
596 Debug,
597 Display,
598 PartialEq,
599 Eq,
600 Hash,
601 AsRefStr,
602 EnumIter,
603 EnumString,
604 Serialize,
605 Deserialize,
606)]
607#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
608#[cfg_attr(
609 feature = "python",
610 pyo3::pyclass(
611 module = "nautilus_trader.core.nautilus_pyo3.dydx",
612 eq,
613 eq_int,
614 from_py_object
615 )
616)]
617#[cfg_attr(
618 feature = "python",
619 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.dydx")
620)]
621pub enum DydxTransferType {
622 TransferIn,
624 TransferOut,
626 Deposit,
628 Withdrawal,
630}
631
632#[derive(
634 Copy,
635 Clone,
636 Debug,
637 Display,
638 PartialEq,
639 Eq,
640 Hash,
641 AsRefStr,
642 IntoStaticStr,
643 EnumIter,
644 EnumString,
645 Serialize,
646 Deserialize,
647)]
648#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
649#[derive(Default)]
650#[cfg_attr(
651 feature = "python",
652 pyo3::pyclass(
653 module = "nautilus_trader.core.nautilus_pyo3.dydx",
654 eq,
655 eq_int,
656 from_py_object
657 )
658)]
659#[cfg_attr(
660 feature = "python",
661 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.dydx")
662)]
663pub enum DydxCandleResolution {
664 #[serde(rename = "1MIN")]
666 #[strum(serialize = "1MIN")]
667 #[default]
668 OneMinute,
669 #[serde(rename = "5MINS")]
671 #[strum(serialize = "5MINS")]
672 FiveMinutes,
673 #[serde(rename = "15MINS")]
675 #[strum(serialize = "15MINS")]
676 FifteenMinutes,
677 #[serde(rename = "30MINS")]
679 #[strum(serialize = "30MINS")]
680 ThirtyMinutes,
681 #[serde(rename = "1HOUR")]
683 #[strum(serialize = "1HOUR")]
684 OneHour,
685 #[serde(rename = "4HOURS")]
687 #[strum(serialize = "4HOURS")]
688 FourHours,
689 #[serde(rename = "1DAY")]
691 #[strum(serialize = "1DAY")]
692 OneDay,
693}
694
695impl DydxCandleResolution {
696 pub fn from_bar_spec(spec: &BarSpecification) -> anyhow::Result<Self> {
702 match spec.step.get() {
703 1 => match spec.aggregation {
704 BarAggregation::Minute => Ok(Self::OneMinute),
705 BarAggregation::Hour => Ok(Self::OneHour),
706 BarAggregation::Day => Ok(Self::OneDay),
707 _ => anyhow::bail!("Unsupported bar aggregation: {:?}", spec.aggregation),
708 },
709 5 if spec.aggregation == BarAggregation::Minute => Ok(Self::FiveMinutes),
710 15 if spec.aggregation == BarAggregation::Minute => Ok(Self::FifteenMinutes),
711 30 if spec.aggregation == BarAggregation::Minute => Ok(Self::ThirtyMinutes),
712 4 if spec.aggregation == BarAggregation::Hour => Ok(Self::FourHours),
713 step => anyhow::bail!(
714 "Unsupported bar step: {step} with aggregation {:?}",
715 spec.aggregation
716 ),
717 }
718 }
719}
720
721#[derive(
725 Copy,
726 Clone,
727 Debug,
728 Default,
729 Display,
730 PartialEq,
731 Eq,
732 Hash,
733 AsRefStr,
734 EnumIter,
735 EnumString,
736 Serialize,
737 Deserialize,
738)]
739#[strum(serialize_all = "lowercase")]
740#[serde(rename_all = "lowercase")]
741#[cfg_attr(
742 feature = "python",
743 pyo3::pyclass(
744 eq,
745 eq_int,
746 module = "nautilus_trader.core.nautilus_pyo3.dydx",
747 from_py_object,
748 rename_all = "SCREAMING_SNAKE_CASE",
749 )
750)]
751#[cfg_attr(
752 feature = "python",
753 pyo3_stub_gen::derive::gen_stub_pyclass_enum(module = "nautilus_trader.dydx")
754)]
755pub enum DydxNetwork {
756 #[default]
758 Mainnet,
759 Testnet,
761}
762
763impl DydxNetwork {
764 #[must_use]
766 pub const fn chain_id(self) -> ChainId {
767 match self {
768 Self::Mainnet => ChainId::Mainnet1,
769 Self::Testnet => ChainId::Testnet4,
770 }
771 }
772
773 #[must_use]
775 pub const fn as_str(self) -> &'static str {
776 match self {
777 Self::Mainnet => "mainnet",
778 Self::Testnet => "testnet",
779 }
780 }
781}
782
783#[cfg(test)]
784mod tests {
785 use rstest::rstest;
786
787 use super::*;
788
789 #[rstest]
790 fn test_order_status_conversion() {
791 assert_eq!(
792 OrderStatus::from(DydxOrderStatus::Open),
793 OrderStatus::Accepted
794 );
795 assert_eq!(
796 OrderStatus::from(DydxOrderStatus::Filled),
797 OrderStatus::Filled
798 );
799 assert_eq!(
800 OrderStatus::from(DydxOrderStatus::Canceled),
801 OrderStatus::Canceled
802 );
803 }
804
805 #[rstest]
806 fn test_liquidity_conversion() {
807 assert_eq!(
808 LiquiditySide::from(DydxLiquidity::Maker),
809 LiquiditySide::Maker
810 );
811 assert_eq!(
812 LiquiditySide::from(DydxLiquidity::Taker),
813 LiquiditySide::Taker
814 );
815 }
816
817 #[rstest]
818 fn test_order_type_is_conditional() {
819 assert!(DydxOrderType::StopLimit.is_conditional());
820 assert!(DydxOrderType::StopMarket.is_conditional());
821 assert!(DydxOrderType::TakeProfitLimit.is_conditional());
822 assert!(DydxOrderType::TakeProfitMarket.is_conditional());
823 assert!(DydxOrderType::TrailingStop.is_conditional());
824 assert!(!DydxOrderType::Limit.is_conditional());
825 assert!(!DydxOrderType::Market.is_conditional());
826 }
827
828 #[rstest]
829 fn test_condition_type_mapping() {
830 assert_eq!(
831 DydxOrderType::StopLimit.condition_type(),
832 DydxConditionType::StopLoss
833 );
834 assert_eq!(
835 DydxOrderType::StopMarket.condition_type(),
836 DydxConditionType::StopLoss
837 );
838 assert_eq!(
839 DydxOrderType::TakeProfitLimit.condition_type(),
840 DydxConditionType::TakeProfit
841 );
842 assert_eq!(
843 DydxOrderType::TakeProfitMarket.condition_type(),
844 DydxConditionType::TakeProfit
845 );
846 assert_eq!(
847 DydxOrderType::Limit.condition_type(),
848 DydxConditionType::Unspecified
849 );
850 }
851
852 #[rstest]
853 fn test_is_market_execution() {
854 assert!(DydxOrderType::Market.is_market_execution());
855 assert!(DydxOrderType::StopMarket.is_market_execution());
856 assert!(DydxOrderType::TakeProfitMarket.is_market_execution());
857 assert!(!DydxOrderType::Limit.is_market_execution());
858 assert!(!DydxOrderType::StopLimit.is_market_execution());
859 assert!(!DydxOrderType::TakeProfitLimit.is_market_execution());
860 }
861
862 #[rstest]
863 fn test_order_type_to_nautilus() {
864 assert_eq!(OrderType::from(DydxOrderType::Market), OrderType::Market);
865 assert_eq!(OrderType::from(DydxOrderType::Limit), OrderType::Limit);
866 assert_eq!(
867 OrderType::from(DydxOrderType::StopMarket),
868 OrderType::StopMarket
869 );
870 assert_eq!(
871 OrderType::from(DydxOrderType::StopLimit),
872 OrderType::StopLimit
873 );
874 }
875
876 #[rstest]
877 fn test_order_side_conversion_from_nautilus() {
878 assert_eq!(
879 DydxOrderSide::try_from(OrderSide::Buy).unwrap(),
880 DydxOrderSide::Buy
881 );
882 assert_eq!(
883 DydxOrderSide::try_from(OrderSide::Sell).unwrap(),
884 DydxOrderSide::Sell
885 );
886 assert!(DydxOrderSide::try_from(OrderSide::NoOrderSide).is_err());
887 }
888
889 #[rstest]
890 fn test_order_side_conversion_to_nautilus() {
891 assert_eq!(OrderSide::from(DydxOrderSide::Buy), OrderSide::Buy);
892 assert_eq!(OrderSide::from(DydxOrderSide::Sell), OrderSide::Sell);
893 }
894
895 #[rstest]
896 fn test_order_type_conversion_from_nautilus() {
897 assert_eq!(
898 DydxOrderType::try_from(OrderType::Market).unwrap(),
899 DydxOrderType::Market
900 );
901 assert_eq!(
902 DydxOrderType::try_from(OrderType::Limit).unwrap(),
903 DydxOrderType::Limit
904 );
905 assert_eq!(
906 DydxOrderType::try_from(OrderType::StopMarket).unwrap(),
907 DydxOrderType::StopMarket
908 );
909 assert_eq!(
910 DydxOrderType::try_from(OrderType::StopLimit).unwrap(),
911 DydxOrderType::StopLimit
912 );
913 assert!(DydxOrderType::try_from(OrderType::MarketToLimit).is_err());
914 }
915
916 #[rstest]
917 fn test_order_type_conversion_to_nautilus() {
918 assert_eq!(OrderType::from(DydxOrderType::Market), OrderType::Market);
919 assert_eq!(OrderType::from(DydxOrderType::Limit), OrderType::Limit);
920 assert_eq!(
921 OrderType::from(DydxOrderType::StopMarket),
922 OrderType::StopMarket
923 );
924 assert_eq!(
925 OrderType::from(DydxOrderType::StopLimit),
926 OrderType::StopLimit
927 );
928 }
929
930 #[rstest]
931 fn test_dydx_network_chain_id_mapping() {
932 assert_eq!(DydxNetwork::Mainnet.chain_id(), ChainId::Mainnet1);
934 assert_eq!(DydxNetwork::Testnet.chain_id(), ChainId::Testnet4);
935 }
936
937 #[rstest]
938 fn test_dydx_network_as_str() {
939 assert_eq!(DydxNetwork::Mainnet.as_str(), "mainnet");
941 assert_eq!(DydxNetwork::Testnet.as_str(), "testnet");
942 }
943
944 #[rstest]
945 fn test_dydx_network_default() {
946 assert_eq!(DydxNetwork::default(), DydxNetwork::Mainnet);
948 }
949
950 #[rstest]
951 fn test_dydx_network_serde_lowercase() {
952 let mainnet = DydxNetwork::Mainnet;
954 let json = serde_json::to_string(&mainnet).unwrap();
955 assert_eq!(json, "\"mainnet\"");
956
957 let deserialized: DydxNetwork = serde_json::from_str("\"mainnet\"").unwrap();
958 assert_eq!(deserialized, DydxNetwork::Mainnet);
959
960 let testnet = DydxNetwork::Testnet;
961 let json = serde_json::to_string(&testnet).unwrap();
962 assert_eq!(json, "\"testnet\"");
963
964 let deserialized: DydxNetwork = serde_json::from_str("\"testnet\"").unwrap();
965 assert_eq!(deserialized, DydxNetwork::Testnet);
966 }
967}