1use nautilus_model::{
19 enums::{OrderSide, OrderType, TimeInForce, TriggerType},
20 identifiers::{ClientOrderId, InstrumentId, StrategyId},
21 orders::{Order, OrderAny},
22 types::{Price, Quantity},
23};
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct OrderIdentity {
28 pub client_order_id: ClientOrderId,
30 pub strategy_id: StrategyId,
32 pub instrument_id: InstrumentId,
34 pub order_side: OrderSide,
36 pub order_type: OrderType,
38}
39
40impl From<&OrderAny> for OrderIdentity {
41 fn from(order: &OrderAny) -> Self {
42 Self {
43 client_order_id: order.client_order_id(),
44 strategy_id: order.strategy_id(),
45 instrument_id: order.instrument_id(),
46 order_side: order.order_side(),
47 order_type: order.order_type(),
48 }
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub struct OrderContext {
55 pub identity: OrderIdentity,
57 pub quantity: Quantity,
59 pub price: Option<Price>,
61 pub trigger_price: Option<Price>,
63 pub trigger_type: Option<TriggerType>,
65 pub time_in_force: TimeInForce,
67 pub is_post_only: bool,
69 pub is_reduce_only: bool,
71 pub is_quote_quantity: bool,
73}
74
75impl From<&OrderAny> for OrderContext {
76 fn from(order: &OrderAny) -> Self {
77 Self {
78 identity: OrderIdentity::from(order),
79 quantity: order.quantity(),
80 price: order.price(),
81 trigger_price: order.trigger_price(),
82 trigger_type: order.trigger_type(),
83 time_in_force: order.time_in_force(),
84 is_post_only: order.is_post_only(),
85 is_reduce_only: order.is_reduce_only(),
86 is_quote_quantity: order.is_quote_quantity(),
87 }
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use nautilus_model::{
94 enums::{OrderSide, OrderType, TimeInForce, TriggerType},
95 identifiers::{ClientOrderId, InstrumentId, StrategyId},
96 orders::{OrderAny, OrderTestBuilder},
97 types::{Price, Quantity},
98 };
99 use rstest::rstest;
100
101 use super::{OrderContext, OrderIdentity};
102
103 #[rstest]
104 fn test_order_identity_from_order() {
105 let order = test_order(false, false, false);
106
107 let identity = OrderIdentity::from(&order);
108
109 assert_eq!(
110 identity,
111 OrderIdentity {
112 client_order_id: ClientOrderId::from("O-CONTEXT-001"),
113 strategy_id: StrategyId::from("S-CONTEXT-002"),
114 instrument_id: InstrumentId::from("ETH-USDT-PERP.TEST"),
115 order_side: OrderSide::Sell,
116 order_type: OrderType::StopLimit,
117 }
118 );
119 }
120
121 #[rstest]
122 #[case(true, false, false)]
123 #[case(false, true, false)]
124 #[case(false, false, true)]
125 fn test_order_context_from_order(
126 #[case] is_post_only: bool,
127 #[case] is_reduce_only: bool,
128 #[case] is_quote_quantity: bool,
129 ) {
130 let order = test_order(is_post_only, is_reduce_only, is_quote_quantity);
131
132 let context = OrderContext::from(&order);
133
134 assert_eq!(
135 context,
136 OrderContext {
137 identity: OrderIdentity {
138 client_order_id: ClientOrderId::from("O-CONTEXT-001"),
139 strategy_id: StrategyId::from("S-CONTEXT-002"),
140 instrument_id: InstrumentId::from("ETH-USDT-PERP.TEST"),
141 order_side: OrderSide::Sell,
142 order_type: OrderType::StopLimit,
143 },
144 quantity: Quantity::from("12.345"),
145 price: Some(Price::from("2345.67")),
146 trigger_price: Some(Price::from("2301.23")),
147 trigger_type: Some(TriggerType::MarkPrice),
148 time_in_force: TimeInForce::Day,
149 is_post_only,
150 is_reduce_only,
151 is_quote_quantity,
152 }
153 );
154 }
155
156 fn test_order(is_post_only: bool, is_reduce_only: bool, is_quote_quantity: bool) -> OrderAny {
157 OrderTestBuilder::new(OrderType::StopLimit)
158 .client_order_id(ClientOrderId::from("O-CONTEXT-001"))
159 .strategy_id(StrategyId::from("S-CONTEXT-002"))
160 .instrument_id(InstrumentId::from("ETH-USDT-PERP.TEST"))
161 .side(OrderSide::Sell)
162 .quantity(Quantity::from("12.345"))
163 .price(Price::from("2345.67"))
164 .trigger_price(Price::from("2301.23"))
165 .trigger_type(TriggerType::MarkPrice)
166 .time_in_force(TimeInForce::Day)
167 .post_only(is_post_only)
168 .reduce_only(is_reduce_only)
169 .quote_quantity(is_quote_quantity)
170 .build()
171 }
172}