Skip to main content

gateio_rs/api/spot/
create_price_order.rs

1use crate::http::{Credentials, Method, request::Request};
2use serde_json::{Map, Value, json};
3
4/// # SpotPriceTrigger
5///
6/// Trigger conditions for price-triggered orders
7///
8/// ##### price:
9/// Trigger price that will activate the order
10///
11/// ##### rule:
12/// Trigger rule:
13/// - ">=" : Order triggers when market price is greater than or equal to trigger price
14/// - "<=" : Order triggers when market price is less than or equal to trigger price
15///
16/// ##### expiration:
17/// Valid duration in seconds (optional)
18/// If not set, the order will remain active until manually cancelled
19#[derive(Debug, Clone)]
20pub struct SpotPriceTrigger {
21    /// Trigger price that will activate the order
22    pub price: String,
23    /// Trigger rule ('>=' or '<=')
24    pub rule: String,
25    /// Valid duration in seconds (optional)
26    pub expiration: Option<i64>,
27}
28
29impl SpotPriceTrigger {
30    /// Create a new price trigger condition
31    pub fn new(price: &str, rule: &str) -> Self {
32        Self {
33            price: price.to_owned(),
34            rule: rule.to_owned(),
35            expiration: None,
36        }
37    }
38
39    /// Set the trigger expiration time in seconds
40    pub fn expiration(mut self, expiration: i64) -> Self {
41        self.expiration = Some(expiration);
42        self
43    }
44}
45
46/// # SpotPricePutOrder
47///
48/// The order to be placed when the trigger condition is met
49///
50/// ##### order_type:
51/// Order type - currently only "limit" orders are supported for price-triggered orders
52///
53/// ##### side:
54/// Order side:
55/// - "buy" : Buy order
56/// - "sell" : Sell order
57///
58/// ##### price:
59/// Limit order price - the price at which the order will be placed when triggered
60///
61/// ##### amount:
62/// Order amount - the quantity to trade
63///
64/// ##### account:
65/// Trading account type:
66/// - "normal" : Normal spot trading account
67/// - "margin" : Margin trading account  
68/// - "unified" : Unified trading account
69///
70/// ##### time_in_force:
71/// Time in force for the triggered order:
72/// - "gtc" : Good Till Cancelled (default)
73/// - "ioc" : Immediate Or Cancel
74/// - "fok" : Fill Or Kill
75/// - "poc" : Post Only
76#[derive(Debug, Clone)]
77pub struct SpotPricePutOrder {
78    /// Order type (currently only "limit" supported)
79    pub order_type: String,
80    /// Order side ("buy" or "sell")
81    pub side: String,
82    /// Limit order price
83    pub price: String,
84    /// Order amount/quantity
85    pub amount: String,
86    /// Trading account type
87    pub account: Option<String>,
88    /// Time in force for the triggered order
89    pub time_in_force: Option<String>,
90}
91
92impl SpotPricePutOrder {
93    /// Create a new order to place when triggered
94    pub fn new(order_type: &str, side: &str, price: &str, amount: &str) -> Self {
95        Self {
96            order_type: order_type.to_owned(),
97            side: side.to_owned(),
98            price: price.to_owned(),
99            amount: amount.to_owned(),
100            account: None,
101            time_in_force: None,
102        }
103    }
104
105    /// Set the trading account type
106    pub fn account(mut self, account: &str) -> Self {
107        self.account = Some(account.to_owned());
108        self
109    }
110
111    /// Set the time in force for the triggered order
112    pub fn time_in_force(mut self, time_in_force: &str) -> Self {
113        self.time_in_force = Some(time_in_force.to_owned());
114        self
115    }
116}
117
118/// # Create a price-triggered order
119///
120/// A price-triggered order (also known as conditional order) will not enter the order book
121/// until the trigger condition is met. Once triggered, it will attempt to place a limit order
122/// at the preset price and amount.
123///
124/// ## Important Notes:
125///
126/// - The conditional order does not occupy your balance until it is triggered
127/// - Make sure to set aside enough balance for this order
128/// - A price condition order can only be triggered one time
129/// - When using "<=", the trigger price should be less than the current market price  
130/// - When using ">=", the trigger price should be greater than the current market price
131/// - Only limit orders are supported as the triggered order type
132///
133/// ## Status Values:
134/// - "open" : Waiting to trigger
135/// - "cancelled" : Manually cancelled  
136/// - "finish" : Successfully executed
137/// - "failed" : Failed to execute
138/// - "expired" : Expired
139///
140/// [Gate API Documentation](https://www.gate.com/docs/developers/apiv4/#create-a-price-triggered-order)
141pub struct CreatePriceOrder {
142    /// Trigger conditions for the order
143    pub trigger: SpotPriceTrigger,
144    /// Order details to execute when triggered
145    pub put: SpotPricePutOrder,
146    /// Currency pair for the order
147    pub market: String,
148    /// Request expiration time in milliseconds
149    pub x_gate_exp_time: Option<u128>,
150    /// API credentials for authentication
151    pub credentials: Option<Credentials>,
152}
153
154impl CreatePriceOrder {
155    /// Create a new price-triggered order request
156    pub fn new(
157        market: &str,
158        trigger_price: &str,
159        trigger_rule: &str,
160        order_side: &str,
161        order_price: &str,
162        order_amount: &str,
163    ) -> Self {
164        Self {
165            trigger: SpotPriceTrigger::new(trigger_price, trigger_rule),
166            put: SpotPricePutOrder::new("limit", order_side, order_price, order_amount),
167            market: market.to_owned(),
168            x_gate_exp_time: None,
169            credentials: None,
170        }
171    }
172
173    /// Set the trigger conditions
174    pub fn trigger(mut self, trigger: SpotPriceTrigger) -> Self {
175        self.trigger = trigger;
176        self
177    }
178
179    /// Set the order details to execute when triggered
180    pub fn put(mut self, put: SpotPricePutOrder) -> Self {
181        self.put = put;
182        self
183    }
184
185    /// Set the trigger expiration time in seconds
186    pub fn trigger_expiration(mut self, expiration: i64) -> Self {
187        self.trigger.expiration = Some(expiration);
188        self
189    }
190
191    /// Set the trading account type for the triggered order
192    pub fn account(mut self, account: &str) -> Self {
193        self.put.account = Some(account.to_owned());
194        self
195    }
196
197    /// Set the time in force for the triggered order
198    pub fn time_in_force(mut self, time_in_force: &str) -> Self {
199        self.put.time_in_force = Some(time_in_force.to_owned());
200        self
201    }
202
203    /// Specify the expiration time (milliseconds);
204    /// If the GATE receives the request time greater than the expiration time, the request will be rejected
205    pub fn x_gate_exp_time(mut self, x_gate_exp_time: u128) -> Self {
206        self.x_gate_exp_time = Some(x_gate_exp_time);
207        self
208    }
209
210    /// Set API credentials for authentication
211    pub fn credentials(mut self, creds: Credentials) -> Self {
212        self.credentials = Some(creds);
213        self
214    }
215}
216
217impl From<CreatePriceOrder> for Request {
218    fn from(request: CreatePriceOrder) -> Request {
219        let params = Vec::new();
220        let mut payload = Map::new();
221
222        // Add market (currency pair)
223        payload.insert("market".to_string(), json!(request.market));
224
225        // Add trigger object
226        let mut trigger_obj = Map::new();
227        trigger_obj.insert("price".to_string(), json!(request.trigger.price));
228        trigger_obj.insert("rule".to_string(), json!(request.trigger.rule));
229
230        if let Some(expiration) = request.trigger.expiration {
231            trigger_obj.insert("expiration".to_string(), json!(expiration));
232        }
233
234        payload.insert("trigger".to_string(), Value::Object(trigger_obj));
235
236        // Add put object (the order to be placed when triggered)
237        let mut put_obj = Map::new();
238        put_obj.insert("type".to_string(), json!(request.put.order_type));
239        put_obj.insert("side".to_string(), json!(request.put.side));
240        put_obj.insert("price".to_string(), json!(request.put.price));
241        put_obj.insert("amount".to_string(), json!(request.put.amount));
242
243        if let Some(account) = request.put.account {
244            put_obj.insert("account".to_string(), json!(account));
245        }
246
247        if let Some(time_in_force) = request.put.time_in_force {
248            put_obj.insert("time_in_force".to_string(), json!(time_in_force));
249        }
250
251        payload.insert("put".to_string(), Value::Object(put_obj));
252
253        let payload_json = Value::Object(payload);
254
255        Request {
256            method: Method::Post,
257            path: "/api/v4/spot/price_orders".into(),
258            params,
259            payload: payload_json.to_string(),
260            x_gate_exp_time: request.x_gate_exp_time,
261            credentials: request.credentials,
262            sign: true,
263        }
264    }
265}