deribit_ws_data/ws_data.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//! Example binary demonstrating Deribit WebSocket data streaming.
17//!
18//! This example connects to the Deribit WebSocket API and subscribes to trade data.
19//! Supports both aggregated (100ms) and raw data streams.
20//!
21//! # Environment Variables
22//!
23//! For raw streams (required):
24//! - `DERIBIT_TESTNET_API_KEY`: Your Deribit testnet API key
25//! - `DERIBIT_TESTNET_API_SECRET`: Your Deribit testnet API secret
26//!
27//! For mainnet raw streams:
28//! - `DERIBIT_API_KEY`: Your Deribit mainnet API key
29//! - `DERIBIT_API_SECRET`: Your Deribit mainnet API secret
30//!
31//! # Usage
32//!
33//! ```bash
34//! # Aggregated 100ms streams (no auth required)
35//! cargo run -p nautilus-deribit --bin deribit-ws-data
36//!
37//! # Raw streams (requires auth)
38//! cargo run -p nautilus-deribit --bin deribit-ws-data -- --raw --testnet
39//! ```
40
41use std::env;
42
43use futures_util::StreamExt;
44use nautilus_deribit::{
45 http::{client::DeribitHttpClient, models::DeribitCurrency},
46 websocket::{client::DeribitWebSocketClient, enums::DeribitUpdateInterval},
47};
48use nautilus_model::identifiers::InstrumentId;
49use tokio::{pin, signal};
50use tracing::level_filters::LevelFilter;
51
52#[tokio::main]
53async fn main() -> Result<(), Box<dyn std::error::Error>> {
54 tracing_subscriber::fmt()
55 .with_max_level(LevelFilter::DEBUG)
56 .init();
57
58 let args: Vec<String> = env::args().collect();
59 let is_testnet = args.iter().any(|a| a == "--testnet");
60 let use_raw = args.iter().any(|a| a == "--raw");
61
62 tracing::info!(
63 "Starting Deribit WebSocket data example ({}, {})",
64 if is_testnet { "testnet" } else { "mainnet" },
65 if use_raw { "raw" } else { "100ms" }
66 );
67
68 // Fetch instruments via HTTP to get proper instrument metadata
69 let http_client = DeribitHttpClient::new(
70 None, // base_url
71 is_testnet, None, // timeout_secs
72 None, // max_retries
73 None, // retry_delay_ms
74 None, // retry_delay_max_ms
75 None, // proxy_url
76 )?;
77 tracing::info!("Fetching BTC instruments from Deribit...");
78 let instruments = http_client
79 .request_instruments(DeribitCurrency::BTC, None)
80 .await?;
81 tracing::info!("Fetched {} instruments", instruments.len());
82
83 // Create WebSocket client based on whether raw streams are requested
84 let mut ws_client = if use_raw {
85 tracing::info!("Creating authenticated client for raw streams");
86 DeribitWebSocketClient::with_credentials(is_testnet)?
87 } else {
88 tracing::info!("Creating public client for 100ms streams");
89 DeribitWebSocketClient::new_public(is_testnet)?
90 };
91
92 ws_client.cache_instruments(instruments);
93 tracing::info!("Connecting to Deribit WebSocket...");
94 ws_client.connect().await?;
95 tracing::info!("Connected to Deribit WebSocket");
96
97 // Authenticate if using raw streams
98 if use_raw {
99 tracing::info!("Authenticating WebSocket connection for raw streams...");
100 ws_client.authenticate_session().await?;
101 tracing::info!("Authentication successful");
102 }
103
104 // Set interval based on mode
105 let interval = if use_raw {
106 Some(DeribitUpdateInterval::Raw)
107 } else {
108 None // Uses default 100ms
109 };
110
111 // Subscribe to trades for BTC-PERPETUAL
112 let instrument_id = InstrumentId::from("BTC-PERPETUAL.DERIBIT");
113 tracing::info!(
114 "Subscribing to trades for {instrument_id} (interval: {})",
115 interval.map_or("100ms", |i| i.as_str())
116 );
117 ws_client.subscribe_trades(instrument_id, interval).await?;
118
119 // Optional: Subscribe to other data types
120 // ws_client.subscribe_book(instrument_id, interval).await?;
121 // ws_client.subscribe_ticker(instrument_id, interval).await?;
122 // ws_client.subscribe_quotes(instrument_id).await?;
123
124 // Create a future that completes on CTRL+C
125 let sigint = signal::ctrl_c();
126 pin!(sigint);
127
128 let stream = ws_client.stream();
129 tokio::pin!(stream);
130
131 tracing::info!("Listening for market data... Press Ctrl+C to exit");
132
133 loop {
134 tokio::select! {
135 Some(msg) = stream.next() => {
136 tracing::info!("{msg:?}");
137 }
138 _ = &mut sigint => {
139 tracing::info!("Received SIGINT, closing connection...");
140 ws_client.close().await?;
141 break;
142 }
143 else => break,
144 }
145 }
146
147 tracing::info!("Deribit WebSocket example finished");
148 Ok(())
149}