tradestation-api 0.1.1

Complete TradeStation REST API v3 wrapper for Rust
Documentation
//! Stream DTOs (newline-delimited JSON wire types) for the TradeStation
//! v3 streaming surface, split out of the single-file `streaming` module
//! to keep each file focused.

use serde::Deserialize;

/// Stream status messages from TradeStation.
///
/// These appear inline in the stream data to signal events like end-of-snapshot
/// or a server-initiated disconnect.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct StreamStatus {
    /// Status code: "EndSnapshot", "GoAway", etc.
    pub status: String,
    /// Optional message with additional details.
    #[serde(default)]
    pub message: Option<String>,
}

/// A streaming quote update.
///
/// Contains real-time quote data or a stream status message. Use [`StreamQuote::is_status`]
/// to distinguish between the two.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct StreamQuote {
    /// Ticker symbol.
    pub symbol: Option<String>,
    /// Last traded price.
    pub last: Option<String>,
    /// Best ask price.
    pub ask: Option<String>,
    /// Best bid price.
    pub bid: Option<String>,
    /// Cumulative volume.
    pub volume: Option<String>,
    /// Time of the last trade.
    #[serde(default)]
    pub trade_time: Option<String>,
    /// Stream status (present only for status messages).
    #[serde(default)]
    pub status: Option<String>,
}

impl StreamQuote {
    /// Whether this is a status message rather than a quote update.
    pub fn is_status(&self) -> bool {
        self.status.is_some()
    }

    /// Whether this is a GoAway message indicating reconnection is needed.
    pub fn is_go_away(&self) -> bool {
        self.status.as_deref() == Some("GoAway")
    }
}

/// A streaming bar (OHLCV) update.
///
/// Delivered via [`Client::stream_bars`](crate::Client::stream_bars). Contains partial or completed bar data.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct StreamBar {
    /// Highest price during the bar period.
    pub high: Option<String>,
    /// Lowest price during the bar period.
    pub low: Option<String>,
    /// Opening price.
    pub open: Option<String>,
    /// Closing price (updates in real time for the current bar).
    pub close: Option<String>,
    /// Bar timestamp.
    pub time_stamp: Option<String>,
    /// Total volume during the bar period.
    pub total_volume: Option<String>,
    /// Stream status (present only for top-level status messages).
    #[serde(default)]
    pub status: Option<String>,
    /// Per-bar finalization status: `"Open"` while the minute's bar is
    /// still forming (TS re-emits it on every trade under the same
    /// `TimeStamp`) or `"Closed"` once the bar is finalized. Distinct from
    /// `status` (the top-level stream control line). `rename_all =
    /// "PascalCase"` maps this field to the wire `BarStatus`.
    #[serde(default)]
    pub bar_status: Option<String>,
}

impl StreamBar {
    /// Whether this is a status message rather than bar data.
    pub fn is_status(&self) -> bool {
        self.status.is_some()
    }
}

/// A streaming market depth (Level 2) quote.
///
/// Delivered via [`Client::stream_market_depth_quotes`](crate::Client::stream_market_depth_quotes).
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct StreamMarketDepthQuote {
    /// Ticker symbol.
    pub symbol: Option<String>,
    /// Ask price at this depth level.
    pub ask: Option<String>,
    /// Ask size at this depth level.
    pub ask_size: Option<String>,
    /// Bid price at this depth level.
    pub bid: Option<String>,
    /// Bid size at this depth level.
    pub bid_size: Option<String>,
    /// Side of the book ("Ask" or "Bid").
    #[serde(default)]
    pub side: Option<String>,
    /// Stream status (present only for status messages).
    #[serde(default)]
    pub status: Option<String>,
}

impl StreamMarketDepthQuote {
    /// Whether this is a status message rather than depth data.
    pub fn is_status(&self) -> bool {
        self.status.is_some()
    }
}

/// A streaming market depth aggregate summary.
///
/// Delivered via [`Client::stream_market_depth_aggregates`](crate::Client::stream_market_depth_aggregates).
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct StreamMarketDepthAggregate {
    /// Ticker symbol.
    pub symbol: Option<String>,
    /// Total ask size across all levels.
    pub total_ask_size: Option<String>,
    /// Total bid size across all levels.
    pub total_bid_size: Option<String>,
    /// Number of price levels.
    #[serde(default)]
    pub levels: Option<u32>,
    /// Stream status (present only for status messages).
    #[serde(default)]
    pub status: Option<String>,
}

impl StreamMarketDepthAggregate {
    /// Whether this is a status message rather than aggregate data.
    pub fn is_status(&self) -> bool {
        self.status.is_some()
    }
}

/// A streaming option chain update.
///
/// Delivered via [`Client::stream_option_chains`](crate::Client::stream_option_chains).
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct StreamOptionChain {
    /// Option symbol.
    pub symbol: Option<String>,
    /// Underlying ticker symbol.
    pub underlying: Option<String>,
    /// Option type ("Call" or "Put").
    #[serde(default, rename = "Type")]
    pub option_type: Option<String>,
    /// Strike price.
    pub strike_price: Option<String>,
    /// Expiration date.
    pub expiration_date: Option<String>,
    /// Best bid price.
    pub bid: Option<String>,
    /// Best ask price.
    pub ask: Option<String>,
    /// Last traded price.
    pub last: Option<String>,
    /// Stream status (present only for status messages).
    #[serde(default)]
    pub status: Option<String>,
}

impl StreamOptionChain {
    /// Whether this is a status message rather than chain data.
    pub fn is_status(&self) -> bool {
        self.status.is_some()
    }
}

/// A streaming option quote update.
///
/// Delivered via [`Client::stream_option_quotes`](crate::Client::stream_option_quotes).
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct StreamOptionQuote {
    /// Option symbol.
    pub symbol: Option<String>,
    /// Best bid price.
    pub bid: Option<String>,
    /// Best ask price.
    pub ask: Option<String>,
    /// Last traded price.
    pub last: Option<String>,
    /// Cumulative volume.
    pub volume: Option<String>,
    /// Open interest.
    #[serde(default)]
    pub open_interest: Option<String>,
    /// Stream status (present only for status messages).
    #[serde(default)]
    pub status: Option<String>,
}

impl StreamOptionQuote {
    /// Whether this is a status message rather than quote data.
    pub fn is_status(&self) -> bool {
        self.status.is_some()
    }
}

/// A streaming order status update.
///
/// Delivered via [`Client::stream_orders`](crate::Client::stream_orders) and [`Client::stream_orders_by_id`](crate::Client::stream_orders_by_id).
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct StreamOrder {
    /// Order identifier.
    pub order_id: Option<String>,
    /// Account this order belongs to.
    pub account_id: Option<String>,
    /// Ticker symbol.
    pub symbol: Option<String>,
    /// Ordered quantity.
    pub quantity: Option<String>,
    /// Order type.
    pub order_type: Option<String>,
    /// Current order status.
    #[serde(default)]
    pub order_status: Option<String>,
    /// Filled quantity.
    #[serde(default)]
    pub filled_quantity: Option<String>,
    /// Stream status (present only for status messages).
    #[serde(default)]
    pub status: Option<String>,
}

impl StreamOrder {
    /// Whether this is a status message rather than an order update.
    pub fn is_status(&self) -> bool {
        self.status.is_some()
    }
}

/// A streaming position update.
///
/// Delivered via [`Client::stream_positions`](crate::Client::stream_positions).
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct StreamPosition {
    /// Account holding this position.
    pub account_id: Option<String>,
    /// Ticker symbol.
    pub symbol: Option<String>,
    /// Current position quantity.
    pub quantity: Option<String>,
    /// Average entry price.
    pub average_price: Option<String>,
    /// Last traded price.
    pub last: Option<String>,
    /// Unrealized P&L.
    #[serde(default)]
    pub unrealized_profit_loss: Option<String>,
    /// Stream status (present only for status messages).
    #[serde(default)]
    pub status: Option<String>,
}

impl StreamPosition {
    /// Whether this is a status message rather than a position update.
    pub fn is_status(&self) -> bool {
        self.status.is_some()
    }
}