1#![allow(missing_docs)]
2use serde::{Deserialize, Serialize};
5
6use crate::types::enums::*;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
18#[allow(non_camel_case_types)]
19pub enum MultiOrderExchangeSegment {
20 NSE_EQ,
21 NSE_FNO,
22 NSE_COMM,
23 BSE_EQ,
24 BSE_FNO,
25 MCX_COMM,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
30#[allow(non_camel_case_types)]
31pub enum MultiOrderProductType {
32 CNC,
33 INTRADAY,
34 MARGIN,
35 MTF,
36}
37
38#[derive(Debug, Clone, Serialize)]
42#[serde(rename_all = "camelCase")]
43pub struct MultiOrderItemRequest {
44 pub sequence: String,
46 #[serde(skip_serializing_if = "Option::is_none")]
47 pub correlation_id: Option<String>,
48 pub transaction_type: TransactionType,
49 pub exchange_segment: MultiOrderExchangeSegment,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub product_type: Option<MultiOrderProductType>,
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub order_type: Option<OrderType>,
54 #[serde(skip_serializing_if = "Option::is_none")]
55 pub validity: Option<Validity>,
56 #[serde(skip_serializing_if = "Option::is_none")]
57 pub security_id: Option<String>,
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub quantity: Option<u32>,
60 #[serde(skip_serializing_if = "Option::is_none")]
61 pub after_market_order: Option<bool>,
62 #[serde(skip_serializing_if = "Option::is_none")]
63 pub amo_time: Option<AmoTime>,
64 #[serde(skip_serializing_if = "Option::is_none")]
65 pub price: Option<f64>,
66 #[serde(skip_serializing_if = "Option::is_none")]
67 pub trigger_price: Option<f64>,
68 #[serde(skip_serializing_if = "Option::is_none")]
69 pub disclosed_quantity: Option<u32>,
70}
71
72#[derive(Debug, Clone, Serialize)]
74#[serde(rename_all = "camelCase")]
75pub struct MultiOrderRequest {
76 pub dhan_client_id: String,
77 pub orders: Vec<MultiOrderItemRequest>,
78}
79
80impl MultiOrderRequest {
81 pub fn validate(&self) -> std::result::Result<(), &'static str> {
83 if self.dhan_client_id.trim().is_empty() {
84 return Err("multi order dhan_client_id cannot be empty");
85 }
86 if self.orders.is_empty() {
87 return Err("multi order orders cannot be empty");
88 }
89 if self.orders.iter().any(|order| {
90 order
91 .correlation_id
92 .as_ref()
93 .is_some_and(|value| value.chars().count() > 30)
94 }) {
95 return Err("multi order correlation_id cannot exceed 30 characters");
96 }
97 if self.orders.iter().any(|order| {
98 order.quantity.is_some_and(|value| value > i32::MAX as u32)
99 || order
100 .disclosed_quantity
101 .is_some_and(|value| value > i32::MAX as u32)
102 }) {
103 return Err("multi order quantities cannot exceed the documented int32 range");
104 }
105 Ok(())
106 }
107}
108
109#[derive(Debug, Clone, Deserialize)]
111#[serde(rename_all = "camelCase")]
112pub struct MultiOrderItemResponse {
113 pub order_id: Option<String>,
114 pub sequence: Option<String>,
115 pub order_status: Option<String>,
116}
117
118#[derive(Debug, Clone, Deserialize)]
120pub struct MultiOrderResponse {
121 #[serde(default)]
122 pub orders: Vec<MultiOrderItemResponse>,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
131#[serde(rename_all = "camelCase")]
132pub struct AlertCondition {
133 pub comparison_type: String,
135 pub exchange_segment: ExchangeSegment,
137 pub security_id: String,
139 #[serde(skip_serializing_if = "Option::is_none")]
141 pub indicator_name: Option<String>,
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub time_frame: Option<String>,
145 pub operator: String,
147 #[serde(skip_serializing_if = "Option::is_none")]
149 pub comparing_value: Option<serde_json::Value>,
150 #[serde(skip_serializing_if = "Option::is_none")]
152 pub comparing_indicator_name: Option<String>,
153 #[serde(skip_serializing_if = "Option::is_none")]
155 pub exp_date: Option<String>,
156 #[serde(skip_serializing_if = "Option::is_none")]
158 pub frequency: Option<String>,
159 #[serde(skip_serializing_if = "Option::is_none")]
161 pub user_note: Option<String>,
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize)]
170#[serde(rename_all = "camelCase")]
171pub struct AlertOrder {
172 pub transaction_type: TransactionType,
173 pub exchange_segment: ExchangeSegment,
174 pub product_type: ProductType,
175 pub order_type: OrderType,
176 pub security_id: String,
177 pub quantity: u64,
178 pub validity: Validity,
179 pub price: String,
181 #[serde(skip_serializing_if = "Option::is_none")]
183 pub disc_quantity: Option<String>,
184 #[serde(skip_serializing_if = "Option::is_none")]
186 pub trigger_price: Option<String>,
187}
188
189#[derive(Debug, Clone, Serialize)]
197#[serde(rename_all = "camelCase")]
198pub struct ConditionalTriggerRequest {
199 pub dhan_client_id: String,
200 #[serde(skip_serializing_if = "Option::is_none")]
202 pub alert_id: Option<String>,
203 pub condition: AlertCondition,
204 pub orders: Vec<AlertOrder>,
205}
206
207#[derive(Debug, Clone, Deserialize)]
213#[serde(rename_all = "camelCase")]
214pub struct ConditionalTriggerResponse {
215 pub alert_id: String,
216 pub alert_status: String,
217}
218
219#[derive(Debug, Clone, Deserialize)]
225#[serde(rename_all = "camelCase")]
226pub struct ConditionalTriggerDetail {
227 pub alert_id: Option<String>,
228 pub alert_status: Option<String>,
229 pub created_time: Option<String>,
230 pub triggered_time: Option<String>,
231 #[serde(default)]
232 pub last_price: Option<serde_json::Value>,
233 pub condition: Option<AlertCondition>,
234 #[serde(default)]
235 pub orders: Vec<AlertOrder>,
236}