Skip to main content

deribit_http/model/response/
trigger.rs

1/******************************************************************************
2   Author: Joaquín Béjar García
3   Email: jb@taunais.com
4   Date: 7/3/26
5******************************************************************************/
6//! Trigger order response models
7
8use pretty_simple_display::{DebugPretty, DisplaySimple};
9use serde::{Deserialize, Serialize};
10use serde_with::skip_serializing_none;
11
12/// A single entry in the trigger order history
13///
14/// Represents a trigger order event such as creation, activation,
15/// execution, or cancellation.
16#[skip_serializing_none]
17#[derive(DebugPretty, DisplaySimple, Clone, PartialEq, Serialize, Deserialize)]
18pub struct TriggerOrderHistoryEntry {
19    /// Timestamp of the event in milliseconds since Unix epoch
20    pub timestamp: i64,
21    /// Trigger type: "index_price", "mark_price", or "last_price"
22    pub trigger: Option<String>,
23    /// Trigger price (only for future trigger orders)
24    pub trigger_price: Option<f64>,
25    /// Maximum deviation from price peak for trailing trigger orders
26    pub trigger_offset: Option<f64>,
27    /// ID of the trigger order before triggering
28    pub trigger_order_id: String,
29    /// Unique order identifier after triggering
30    pub order_id: Option<String>,
31    /// Order state: "triggered", "cancelled", or "rejected"
32    pub order_state: String,
33    /// Unique instrument identifier
34    pub instrument_name: String,
35    /// Type of last request: "cancel" or "trigger:order"
36    pub request: Option<String>,
37    /// Direction: "buy" or "sell"
38    pub direction: String,
39    /// Price in base currency
40    pub price: Option<f64>,
41    /// Order size (USD for perpetual/inverse, base currency for options/linear)
42    pub amount: f64,
43    /// True for reduce-only orders
44    pub reduce_only: Option<bool>,
45    /// True for post-only orders
46    pub post_only: Option<bool>,
47    /// Order type: "limit" or "market"
48    pub order_type: Option<String>,
49    /// User defined label
50    pub label: Option<String>,
51    /// True if order can be triggered by another order
52    pub linked_order_type: Option<String>,
53    /// Unique reference for OCO (one_cancels_others) pair
54    pub oco_ref: Option<String>,
55    /// Source of the order linked to trigger order
56    pub trigger_source: Option<String>,
57    /// Last update timestamp in milliseconds since Unix epoch
58    pub last_update_timestamp: Option<i64>,
59}
60
61/// Response from get_trigger_order_history endpoint
62///
63/// Contains a list of trigger order history entries and an optional
64/// continuation token for pagination.
65#[skip_serializing_none]
66#[derive(DebugPretty, DisplaySimple, Clone, PartialEq, Serialize, Deserialize)]
67pub struct TriggerOrderHistoryResponse {
68    /// List of trigger order history entries
69    pub entries: Vec<TriggerOrderHistoryEntry>,
70    /// Continuation token for pagination
71    pub continuation: Option<String>,
72}
73
74impl TriggerOrderHistoryResponse {
75    /// Create a new trigger order history response
76    pub fn new(entries: Vec<TriggerOrderHistoryEntry>) -> Self {
77        Self {
78            entries,
79            continuation: None,
80        }
81    }
82
83    /// Create response with continuation token
84    pub fn with_continuation(entries: Vec<TriggerOrderHistoryEntry>, continuation: String) -> Self {
85        Self {
86            entries,
87            continuation: Some(continuation),
88        }
89    }
90
91    /// Check if there are more results
92    pub fn has_more(&self) -> bool {
93        self.continuation.is_some()
94    }
95
96    /// Get the number of entries
97    pub fn len(&self) -> usize {
98        self.entries.len()
99    }
100
101    /// Check if the response is empty
102    pub fn is_empty(&self) -> bool {
103        self.entries.is_empty()
104    }
105}
106
107impl Default for TriggerOrderHistoryResponse {
108    fn default() -> Self {
109        Self::new(Vec::new())
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn test_trigger_order_history_response_new() {
119        let response = TriggerOrderHistoryResponse::new(vec![]);
120        assert!(response.entries.is_empty());
121        assert!(response.continuation.is_none());
122        assert!(!response.has_more());
123    }
124
125    #[test]
126    fn test_trigger_order_history_response_with_continuation() {
127        let response =
128            TriggerOrderHistoryResponse::with_continuation(vec![], "token123".to_string());
129        assert!(response.entries.is_empty());
130        assert_eq!(response.continuation, Some("token123".to_string()));
131        assert!(response.has_more());
132    }
133
134    #[test]
135    fn test_trigger_order_history_entry_deserialization() {
136        let json = r#"{
137            "timestamp": 1555918941451,
138            "trigger": "index_price",
139            "trigger_price": 5285.0,
140            "trigger_order_id": "SLIS-103",
141            "order_id": "671473",
142            "order_state": "triggered",
143            "instrument_name": "BTC-PERPETUAL",
144            "request": "trigger:order",
145            "direction": "buy",
146            "price": 5179.28,
147            "amount": 10.0
148        }"#;
149
150        let entry: TriggerOrderHistoryEntry = serde_json::from_str(json).unwrap();
151        assert_eq!(entry.timestamp, 1555918941451);
152        assert_eq!(entry.trigger, Some("index_price".to_string()));
153        assert_eq!(entry.trigger_price, Some(5285.0));
154        assert_eq!(entry.trigger_order_id, "SLIS-103");
155        assert_eq!(entry.order_id, Some("671473".to_string()));
156        assert_eq!(entry.order_state, "triggered");
157        assert_eq!(entry.instrument_name, "BTC-PERPETUAL");
158        assert_eq!(entry.direction, "buy");
159        assert_eq!(entry.amount, 10.0);
160    }
161}