nautilus_bybit/http/
parse.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Parsing functions for Bybit HTTP API responses.
17
18use super::models::{
19    BybitInstrumentInverseResponse, BybitInstrumentLinearResponse, BybitInstrumentOptionResponse,
20    BybitInstrumentSpotResponse, BybitKlinesResponse, BybitServerTimeResponse,
21    BybitTickersLinearResponse, BybitTickersOptionResponse, BybitTickersSpotResponse,
22    BybitTradesResponse,
23};
24use crate::common::models::BybitResponse;
25
26/// Parses a Bybit server time response from raw JSON bytes.
27///
28/// # Errors
29///
30/// Returns an error if deserialization fails.
31pub fn parse_server_time_response(data: &[u8]) -> anyhow::Result<BybitServerTimeResponse> {
32    let response = serde_json::from_slice::<BybitServerTimeResponse>(data)?;
33    validate_response(&response)?;
34    Ok(response)
35}
36
37/// Parses a Bybit spot instruments response from raw JSON bytes.
38///
39/// # Errors
40///
41/// Returns an error if deserialization fails.
42pub fn parse_instruments_spot_response(data: &[u8]) -> anyhow::Result<BybitInstrumentSpotResponse> {
43    let response = serde_json::from_slice::<BybitInstrumentSpotResponse>(data)?;
44    validate_response(&response)?;
45    Ok(response)
46}
47
48/// Parses a Bybit linear instruments response from raw JSON bytes.
49///
50/// # Errors
51///
52/// Returns an error if deserialization fails.
53pub fn parse_instruments_linear_response(
54    data: &[u8],
55) -> anyhow::Result<BybitInstrumentLinearResponse> {
56    let response = serde_json::from_slice::<BybitInstrumentLinearResponse>(data)?;
57    validate_response(&response)?;
58    Ok(response)
59}
60
61/// Parses a Bybit inverse instruments response from raw JSON bytes.
62///
63/// # Errors
64///
65/// Returns an error if deserialization fails.
66pub fn parse_instruments_inverse_response(
67    data: &[u8],
68) -> anyhow::Result<BybitInstrumentInverseResponse> {
69    let response = serde_json::from_slice::<BybitInstrumentInverseResponse>(data)?;
70    validate_response(&response)?;
71    Ok(response)
72}
73
74/// Parses a Bybit option instruments response from raw JSON bytes.
75///
76/// # Errors
77///
78/// Returns an error if deserialization fails.
79pub fn parse_instruments_option_response(
80    data: &[u8],
81) -> anyhow::Result<BybitInstrumentOptionResponse> {
82    let response = serde_json::from_slice::<BybitInstrumentOptionResponse>(data)?;
83    validate_response(&response)?;
84    Ok(response)
85}
86
87/// Parses a Bybit spot tickers response from raw JSON bytes.
88///
89/// # Errors
90///
91/// Returns an error if deserialization fails.
92pub fn parse_tickers_spot_response(data: &[u8]) -> anyhow::Result<BybitTickersSpotResponse> {
93    let response = serde_json::from_slice::<BybitTickersSpotResponse>(data)?;
94    validate_response(&response)?;
95    Ok(response)
96}
97
98/// Parses a Bybit linear tickers response from raw JSON bytes.
99///
100/// # Errors
101///
102/// Returns an error if deserialization fails.
103pub fn parse_tickers_linear_response(data: &[u8]) -> anyhow::Result<BybitTickersLinearResponse> {
104    let response = serde_json::from_slice::<BybitTickersLinearResponse>(data)?;
105    validate_response(&response)?;
106    Ok(response)
107}
108
109/// Parses a Bybit option tickers response from raw JSON bytes.
110///
111/// # Errors
112///
113/// Returns an error if deserialization fails.
114pub fn parse_tickers_option_response(data: &[u8]) -> anyhow::Result<BybitTickersOptionResponse> {
115    let response = serde_json::from_slice::<BybitTickersOptionResponse>(data)?;
116    validate_response(&response)?;
117    Ok(response)
118}
119
120/// Parses a Bybit klines response from raw JSON bytes.
121///
122/// # Errors
123///
124/// Returns an error if deserialization fails.
125pub fn parse_klines_response(data: &[u8]) -> anyhow::Result<BybitKlinesResponse> {
126    let response = serde_json::from_slice::<BybitKlinesResponse>(data)?;
127    validate_response(&response)?;
128    Ok(response)
129}
130
131/// Parses a Bybit trades response from raw JSON bytes.
132///
133/// # Errors
134///
135/// Returns an error if deserialization fails.
136pub fn parse_trades_response(data: &[u8]) -> anyhow::Result<BybitTradesResponse> {
137    let response = serde_json::from_slice::<BybitTradesResponse>(data)?;
138    validate_response(&response)?;
139    Ok(response)
140}
141
142/// Validates that a Bybit response has a successful return code.
143///
144/// # Errors
145///
146/// Returns an error if the response indicates a failure.
147fn validate_response<T>(response: &BybitResponse<T>) -> anyhow::Result<()> {
148    if response.ret_code != 0 {
149        anyhow::bail!(
150            "Bybit API error {}: {}",
151            response.ret_code,
152            response.ret_msg
153        );
154    }
155    Ok(())
156}
157
158////////////////////////////////////////////////////////////////////////////////
159// Tests
160////////////////////////////////////////////////////////////////////////////////
161
162#[cfg(test)]
163mod tests {
164    use rstest::rstest;
165
166    use super::*;
167    use crate::common::testing::load_test_json;
168
169    #[rstest]
170    fn test_parse_instruments_linear_response() {
171        let json = load_test_json("http_get_instruments_linear.json");
172        let result = parse_instruments_linear_response(json.as_bytes());
173        assert!(result.is_ok());
174
175        let response = result.unwrap();
176        assert_eq!(response.ret_code, 0);
177        assert!(!response.result.list.is_empty());
178    }
179
180    #[rstest]
181    fn test_parse_instruments_spot_response() {
182        let json = load_test_json("http_get_instruments_spot.json");
183        let result = parse_instruments_spot_response(json.as_bytes());
184        assert!(result.is_ok());
185
186        let response = result.unwrap();
187        assert_eq!(response.ret_code, 0);
188        assert!(!response.result.list.is_empty());
189    }
190
191    #[rstest]
192    fn test_parse_instruments_inverse_response() {
193        let json = load_test_json("http_get_instruments_inverse.json");
194        let result = parse_instruments_inverse_response(json.as_bytes());
195        assert!(result.is_ok());
196
197        let response = result.unwrap();
198        assert_eq!(response.ret_code, 0);
199        assert!(!response.result.list.is_empty());
200    }
201
202    #[rstest]
203    fn test_parse_instruments_option_response() {
204        let json = load_test_json("http_get_instruments_option.json");
205        let result = parse_instruments_option_response(json.as_bytes());
206        assert!(result.is_ok());
207
208        let response = result.unwrap();
209        assert_eq!(response.ret_code, 0);
210        assert!(!response.result.list.is_empty());
211    }
212
213    #[rstest]
214    fn test_parse_klines_response() {
215        let json = load_test_json("http_get_klines_linear.json");
216        let result = parse_klines_response(json.as_bytes());
217        assert!(result.is_ok());
218
219        let response = result.unwrap();
220        assert_eq!(response.ret_code, 0);
221        assert!(!response.result.list.is_empty());
222    }
223
224    #[rstest]
225    fn test_parse_trades_response() {
226        let json = load_test_json("http_get_trades_recent.json");
227        let result = parse_trades_response(json.as_bytes());
228        assert!(result.is_ok());
229
230        let response = result.unwrap();
231        assert_eq!(response.ret_code, 0);
232        assert!(!response.result.list.is_empty());
233    }
234}