1 unstable release

new 0.1.2 Mar 8, 2025
0.1.1 Mar 8, 2025
0.1.0 Mar 7, 2025

#46 in WebSocket

Download history

55 downloads per month

MIT license

160KB
2K SLoC

dxlink

dxlink is a Rust client library for the DXLink WebSocket protocol used by tastytrade for real-time market data. This library provides a clean and type-safe API for connecting to DXLink servers, subscribing to market events, and processing real-time market data.

Features

  • Full implementation of the DXLink WebSocket protocol (AsyncAPI 2.4.0)
  • Strongly typed event definitions for Quote, Trade, Greeks, and more
  • Async/await based API for efficient resource usage
  • Automatic handling of authentication and connection maintenance
  • Support for multiple subscription channels
  • Callback and stream-based APIs for event processing
  • Robust error handling and reconnection logic

ref: https://raw.githubusercontent.com/dxFeed/dxLink/refs/heads/main/dxlink-specification/asyncapi.yml

Example

Here's a basic example of using the library to connect to a DXLink server and subscribe to market data:

use std::error::Error;
use dxlink::{DXLinkClient, EventType, FeedSubscription, MarketEvent};
use tokio::time::sleep;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {

    // Create a new DXLink client with the API token
    // (typically obtained from tastytrade API)
    use tracing::info;
let token = "your_api_token_here";
    let url = "wss://tasty-openapi-ws.dxfeed.com/realtime";
    let mut client = DXLinkClient::new(url, token);

    // Connect to the DXLink server
    client.connect().await?;

    // Create a feed channel with AUTO contract type
    let channel_id = client.create_feed_channel("AUTO").await?;

    // Configure the channel for Quote and Trade events
    client.setup_feed(channel_id, &[EventType::Quote, EventType::Trade]).await?;

    // Register a callback for specific symbol
    client.on_event("SPY", |event| {
        info!("Event received for SPY: {:?}", event);
    });

    // Get a stream for all events
    let mut event_stream = client.event_stream()?;

    // Process events in a separate task
    tokio::spawn(async move {
        while let Some(event) = event_stream.recv().await {
            match &event {
                MarketEvent::Quote(quote) => {
                    info!(
                        "Quote: {} - Bid: {} x {}, Ask: {} x {}",
                        quote.event_symbol,
                        quote.bid_price,
                        quote.bid_size,
                        quote.ask_price,
                        quote.ask_size
                    );
                },
                MarketEvent::Trade(trade) => {
                    info!(
                        "Trade: {} - Price: {}, Size: {}, Volume: {}",
                        trade.event_symbol,
                        trade.price,
                        trade.size,
                        trade.day_volume
                    );
                },
                _ => info!("Other event type: {:?}", event),
            }
        }
    });

    // Subscribe to some symbols
    let subscriptions = vec![
        FeedSubscription {
            event_type: "Quote".to_string(),
            symbol: "SPY".to_string(),
            from_time: None,
            source: None,
        },
        FeedSubscription {
            event_type: "Trade".to_string(),
            symbol: "SPY".to_string(),
            from_time: None,
            source: None,
        },
    ];

    client.subscribe(channel_id, subscriptions).await?;

    // Keep the connection active for some time
    sleep(Duration::from_secs(60)).await;

    // Cleanup
    client.disconnect().await?;

    Ok(())
}

Working with historical data

DXLink supports subscribing to historical data through Candle events. When subscribing to candle events, you need to specify the period, type, and a timestamp from which to fetch the data:

use dxlink::FeedSubscription;
use std::time::{SystemTime, UNIX_EPOCH};

// Get current timestamp in milliseconds
let now = SystemTime::now()
    .duration_since(UNIX_EPOCH)
    .unwrap()
    .as_millis() as i64;

// Timestamp for 24 hours ago
let one_day_ago = now - (24 * 60 * 60 * 1000);

// Subscribe to 5-minute candles for SPY for the last 24 hours
let candle_subscription = FeedSubscription {
    event_type: "Candle".to_string(),
    symbol: "SPY{=5m}".to_string(),  // 5-minute candles
    from_time: Some(one_day_ago),
    source: None,
};

Error Handling

The library uses a custom error type DXLinkError that encompasses various error cases that can occur when interacting with the DXLink API:

use tracing::{error, info};
use dxlink::{DXLinkClient, DXLinkError};

async fn example_error_handling() {
    let mut client = DXLinkClient::new("wss://example.com", "token");
    match client.connect().await {
        Ok(_) => info!("Connected successfully!"),
        Err(DXLinkError::Authentication(e)) => error!("Authentication failed: {}", e),
        Err(DXLinkError::Connection(e)) => error!("Connection error: {}", e),
        Err(e) => error!("Other error: {}", e),
    }
}

Available Event Types

The library supports the following event types:

  • Quote - Current bid/ask prices and sizes
  • Trade - Last trade information
  • Greeks - Option greeks data (delta, gamma, theta, etc.)
  • Summary - Daily summary information
  • Profile - Instrument profile information
  • Candle - OHLC (Open, High, Low, Close) data for time periods
  • And more!

License

This project is licensed under the MIT License. See the LICENSE file for details.

Setup Instructions

  1. Clone the repository:
git clone https://github.com/joaquinbejar/DXlink
cd DXlink
  1. Build the project:
make build
  1. Run tests:
make test
  1. Format the code:
make fmt
  1. Run linting:
make lint
  1. Clean the project:
make clean
  1. Run the project:
make run
  1. Fix issues:
make fix
  1. Run pre-push checks:
make pre-push
  1. Generate documentation:
make doc
  1. Publish the package:
make publish
  1. Generate coverage report:
make coverage

Testing

To run unit tests:

make test

To run tests with coverage:

make coverage

Contribution and Contact

We welcome contributions to this project! If you would like to contribute, please follow these steps:

  1. Fork the repository.
  2. Create a new branch for your feature or bug fix.
  3. Make your changes and ensure that the project still builds and all tests pass.
  4. Commit your changes and push your branch to your forked repository.
  5. Submit a pull request to the main repository.

If you have any questions, issues, or would like to provide feedback, please feel free to contact the project maintainer:

Joaquín Béjar García

We appreciate your interest and look forward to your contributions!

License: MIT

Dependencies

~6–17MB
~233K SLoC