rust_okx/api/account/requests/
history.rs1use serde::Serialize;
2
3use crate::model::{
4 InstType, RequestValidationError, TradeMode, ValidateRequest, optional_non_empty,
5 optional_one_of, optional_unsigned_integer_string, range_u64,
6};
7
8#[derive(Debug, Clone, Default, Serialize)]
10pub struct BillsRequest {
11 #[serde(rename = "instType", skip_serializing_if = "Option::is_none")]
12 inst_type: Option<InstType>,
13 #[serde(skip_serializing_if = "Option::is_none")]
14 ccy: Option<String>,
15 #[serde(rename = "mgnMode", skip_serializing_if = "Option::is_none")]
16 mgn_mode: Option<TradeMode>,
17 #[serde(rename = "ctType", skip_serializing_if = "Option::is_none")]
18 ct_type: Option<String>,
19 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
20 bill_type: Option<String>,
21 #[serde(rename = "subType", skip_serializing_if = "Option::is_none")]
22 sub_type: Option<String>,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 after: Option<String>,
25 #[serde(skip_serializing_if = "Option::is_none")]
26 before: Option<String>,
27 #[serde(skip_serializing_if = "Option::is_none")]
28 limit: Option<u32>,
29}
30
31impl BillsRequest {
32 pub fn new() -> Self {
34 Self::default()
35 }
36
37 pub fn inst_type(mut self, inst_type: InstType) -> Self {
39 self.inst_type = Some(inst_type);
40 self
41 }
42
43 pub fn currency(mut self, ccy: impl Into<String>) -> Self {
45 self.ccy = Some(ccy.into());
46 self
47 }
48
49 pub fn margin_mode(mut self, mgn_mode: TradeMode) -> Self {
51 self.mgn_mode = Some(mgn_mode);
52 self
53 }
54
55 pub fn contract_type(mut self, ct_type: impl Into<String>) -> Self {
57 self.ct_type = Some(ct_type.into());
58 self
59 }
60
61 pub fn bill_type(mut self, bill_type: impl Into<String>) -> Self {
63 self.bill_type = Some(bill_type.into());
64 self
65 }
66
67 pub fn sub_type(mut self, sub_type: impl Into<String>) -> Self {
69 self.sub_type = Some(sub_type.into());
70 self
71 }
72
73 pub fn after(mut self, after: impl Into<String>) -> Self {
75 self.after = Some(after.into());
76 self
77 }
78
79 pub fn before(mut self, before: impl Into<String>) -> Self {
81 self.before = Some(before.into());
82 self
83 }
84
85 pub fn limit(mut self, limit: u32) -> Self {
87 self.limit = Some(limit);
88 self
89 }
90}
91
92#[derive(Debug, Clone, Default, Serialize)]
94pub struct BillsArchiveRequest {
95 #[serde(flatten)]
96 base: BillsRequest,
97 #[serde(skip_serializing_if = "Option::is_none")]
98 begin: Option<String>,
99 #[serde(skip_serializing_if = "Option::is_none")]
100 end: Option<String>,
101}
102
103impl BillsArchiveRequest {
104 pub fn new() -> Self {
106 Self::default()
107 }
108
109 pub fn filters(mut self, base: BillsRequest) -> Self {
111 self.base = base;
112 self
113 }
114
115 pub fn begin(mut self, begin: impl Into<String>) -> Self {
117 self.begin = Some(begin.into());
118 self
119 }
120
121 pub fn end(mut self, end: impl Into<String>) -> Self {
123 self.end = Some(end.into());
124 self
125 }
126}
127
128#[derive(Debug, Clone, Default, Serialize)]
130pub struct PositionsHistoryRequest {
131 #[serde(rename = "instType", skip_serializing_if = "Option::is_none")]
132 inst_type: Option<InstType>,
133 #[serde(rename = "instId", skip_serializing_if = "Option::is_none")]
134 inst_id: Option<String>,
135 #[serde(rename = "mgnMode", skip_serializing_if = "Option::is_none")]
136 mgn_mode: Option<TradeMode>,
137 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
138 close_type: Option<String>,
139 #[serde(rename = "posId", skip_serializing_if = "Option::is_none")]
140 pos_id: Option<String>,
141 #[serde(skip_serializing_if = "Option::is_none")]
142 after: Option<String>,
143 #[serde(skip_serializing_if = "Option::is_none")]
144 before: Option<String>,
145 #[serde(skip_serializing_if = "Option::is_none")]
146 limit: Option<u32>,
147}
148
149impl PositionsHistoryRequest {
150 pub fn new() -> Self {
152 Self::default()
153 }
154
155 pub fn inst_type(mut self, inst_type: InstType) -> Self {
157 self.inst_type = Some(inst_type);
158 self
159 }
160
161 pub fn inst_id(mut self, inst_id: impl Into<String>) -> Self {
163 self.inst_id = Some(inst_id.into());
164 self
165 }
166
167 pub fn margin_mode(mut self, mgn_mode: TradeMode) -> Self {
169 self.mgn_mode = Some(mgn_mode);
170 self
171 }
172
173 pub fn close_type(mut self, close_type: impl Into<String>) -> Self {
175 self.close_type = Some(close_type.into());
176 self
177 }
178
179 pub fn position_id(mut self, pos_id: impl Into<String>) -> Self {
181 self.pos_id = Some(pos_id.into());
182 self
183 }
184
185 pub fn after(mut self, after: impl Into<String>) -> Self {
187 self.after = Some(after.into());
188 self
189 }
190
191 pub fn before(mut self, before: impl Into<String>) -> Self {
193 self.before = Some(before.into());
194 self
195 }
196
197 pub fn limit(mut self, limit: u32) -> Self {
199 self.limit = Some(limit);
200 self
201 }
202}
203
204fn validate_pagination(
205 after: Option<&str>,
206 before: Option<&str>,
207 limit: Option<u32>,
208) -> Result<(), RequestValidationError> {
209 optional_unsigned_integer_string("after", after)?;
210 optional_unsigned_integer_string("before", before)?;
211 if let Some(limit) = limit {
212 range_u64("limit", u64::from(limit), 1, 100)?;
213 }
214 Ok(())
215}
216
217impl ValidateRequest for BillsRequest {
218 fn validate(&self) -> Result<(), RequestValidationError> {
219 optional_non_empty("ccy", self.ccy.as_deref())?;
220 optional_non_empty("ctType", self.ct_type.as_deref())?;
221 optional_unsigned_integer_string("type", self.bill_type.as_deref())?;
222 optional_unsigned_integer_string("subType", self.sub_type.as_deref())?;
223 validate_pagination(self.after.as_deref(), self.before.as_deref(), self.limit)
224 }
225}
226
227impl ValidateRequest for BillsArchiveRequest {
228 fn validate(&self) -> Result<(), RequestValidationError> {
229 self.base.validate()?;
230 optional_unsigned_integer_string("begin", self.begin.as_deref())?;
231 optional_unsigned_integer_string("end", self.end.as_deref())?;
232 Ok(())
233 }
234}
235
236impl ValidateRequest for PositionsHistoryRequest {
237 fn validate(&self) -> Result<(), RequestValidationError> {
238 if matches!(self.inst_type, Some(InstType::Spot)) {
239 return Err(RequestValidationError::InvalidFormat {
240 field: "instType",
241 expected: "MARGIN, SWAP, FUTURES, OPTION, or EVENTS",
242 });
243 }
244 optional_non_empty("instId", self.inst_id.as_deref())?;
245 optional_one_of(
246 "type",
247 self.close_type.as_deref(),
248 &["1", "2", "3", "4", "5", "6"],
249 "an integer from 1 through 6",
250 )?;
251 optional_non_empty("posId", self.pos_id.as_deref())?;
252 validate_pagination(self.after.as_deref(), self.before.as_deref(), self.limit)
253 }
254}