Skip to main content

rust_okx/api/account/requests/
history.rs

1use 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/// Query parameters for account bills.
9#[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    /// Create an empty account-bills query.
33    pub fn new() -> Self {
34        Self::default()
35    }
36
37    /// Set the instrument type filter.
38    pub fn inst_type(mut self, inst_type: InstType) -> Self {
39        self.inst_type = Some(inst_type);
40        self
41    }
42
43    /// Set the currency filter.
44    pub fn currency(mut self, ccy: impl Into<String>) -> Self {
45        self.ccy = Some(ccy.into());
46        self
47    }
48
49    /// Set the margin mode filter.
50    pub fn margin_mode(mut self, mgn_mode: TradeMode) -> Self {
51        self.mgn_mode = Some(mgn_mode);
52        self
53    }
54
55    /// Set the contract type filter.
56    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    /// Set the bill type filter.
62    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    /// Set the bill subtype filter.
68    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    /// Return records after this pagination cursor.
74    pub fn after(mut self, after: impl Into<String>) -> Self {
75        self.after = Some(after.into());
76        self
77    }
78
79    /// Return records before this pagination cursor.
80    pub fn before(mut self, before: impl Into<String>) -> Self {
81        self.before = Some(before.into());
82        self
83    }
84
85    /// Set the maximum number of rows to return.
86    pub fn limit(mut self, limit: u32) -> Self {
87        self.limit = Some(limit);
88        self
89    }
90}
91
92/// Query parameters for archived account bills.
93#[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    /// Create an empty archived-bills query.
105    pub fn new() -> Self {
106        Self::default()
107    }
108
109    /// Set the common bills filters.
110    pub fn filters(mut self, base: BillsRequest) -> Self {
111        self.base = base;
112        self
113    }
114
115    /// Set the begin timestamp.
116    pub fn begin(mut self, begin: impl Into<String>) -> Self {
117        self.begin = Some(begin.into());
118        self
119    }
120
121    /// Set the end timestamp.
122    pub fn end(mut self, end: impl Into<String>) -> Self {
123        self.end = Some(end.into());
124        self
125    }
126}
127
128/// Query parameters for position history.
129#[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    /// Create an empty position-history query.
151    pub fn new() -> Self {
152        Self::default()
153    }
154
155    /// Set the instrument type filter.
156    pub fn inst_type(mut self, inst_type: InstType) -> Self {
157        self.inst_type = Some(inst_type);
158        self
159    }
160
161    /// Set the instrument ID filter.
162    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    /// Set the margin mode filter.
168    pub fn margin_mode(mut self, mgn_mode: TradeMode) -> Self {
169        self.mgn_mode = Some(mgn_mode);
170        self
171    }
172
173    /// Set the OKX close type filter.
174    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    /// Set the position ID filter.
180    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    /// Return records after this pagination cursor.
186    pub fn after(mut self, after: impl Into<String>) -> Self {
187        self.after = Some(after.into());
188        self
189    }
190
191    /// Return records before this pagination cursor.
192    pub fn before(mut self, before: impl Into<String>) -> Self {
193        self.before = Some(before.into());
194        self
195    }
196
197    /// Set the maximum number of rows to return.
198    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}