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}