tastytrade 0.1.0

Library for trading through tastytrade's API
Documentation
use derive_builder::Builder;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use std::fmt;

use crate::accounts::AccountNumber;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum PriceEffect {
    Debit,
    Credit,
    None,
}

impl fmt::Display for PriceEffect {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            PriceEffect::Debit => write!(f, "Debit"),
            PriceEffect::Credit => write!(f, "Credit"),
            PriceEffect::None => write!(f, "None"),
        }
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum Action {
    #[serde(rename = "Buy to Open")]
    BuyToOpen,
    #[serde(rename = "Sell to Open")]
    SellToOpen,
    #[serde(rename = "Buy to Close")]
    BuyToClose,
    #[serde(rename = "Sell to Close")]
    SellToClose,
    Sell,
    Buy,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum InstrumentType {
    Equity,
    #[serde(rename = "Equity Option")]
    EquityOption,
    #[serde(rename = "Equity Offering")]
    EquityOffering,
    Future,
    #[serde(rename = "Future Option")]
    FutureOption,
    Cryptocurrency,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum OrderType {
    Limit,
    Market,
    #[serde(rename = "Marketable Limit")]
    MarketableLimit,
    Stop,
    #[serde(rename = "Stop Limit")]
    StopLimit,
    #[serde(rename = "Notional Market")]
    NotionalMarket,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum TimeInForce {
    #[serde(rename = "Day")]
    Day,
    #[serde(rename = "GTC")]
    Gtc,
    #[serde(rename = "GTD")]
    Gtd,
    #[serde(rename = "Ext")]
    Ext,
    #[serde(rename = "GTC Ext")]
    GTCExt,
    #[serde(rename = "IOC")]
    Ioc,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum OrderStatus {
    Received,
    Routed,
    #[serde(rename = "In Flight")]
    InFlight,
    Live,
    #[serde(rename = "Cancel Requested")]
    CancelRequested,
    #[serde(rename = "Replace Requested")]
    ReplaceRequested,
    Contingent,
    Filled,
    Cancelled,
    Expired,
    Rejected,
    Removed,
    #[serde(rename = "Partially Removed")]
    PartiallyRemoved,
}

impl fmt::Display for OrderStatus {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            OrderStatus::Received => write!(f, "Received"),
            OrderStatus::Routed => write!(f, "Routed"),
            OrderStatus::InFlight => write!(f, "In Flight"),
            OrderStatus::Live => write!(f, "Live"),
            OrderStatus::CancelRequested => write!(f, "Cancel Requested"),
            OrderStatus::ReplaceRequested => write!(f, "Replace Requested"),
            OrderStatus::Contingent => write!(f, "Contingent"),
            OrderStatus::Filled => write!(f, "Filled"),
            OrderStatus::Cancelled => write!(f, "Cancelled"),
            OrderStatus::Expired => write!(f, "Expired"),
            OrderStatus::Rejected => write!(f, "Rejected"),
            OrderStatus::Removed => write!(f, "Removed"),
            OrderStatus::PartiallyRemoved => write!(f, "Partially Removed"),
        }
    }
}

#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(transparent)]
pub struct Symbol(pub String);

impl<T: AsRef<str>> From<T> for Symbol {
    fn from(value: T) -> Self {
        Self(value.as_ref().to_owned())
    }
}

pub trait AsSymbol {
    fn as_symbol(&self) -> Symbol;
}

impl<T: AsRef<str>> AsSymbol for T {
    fn as_symbol(&self) -> Symbol {
        Symbol(self.as_ref().to_owned())
    }
}

impl AsSymbol for Symbol {
    fn as_symbol(&self) -> Symbol {
        self.clone()
    }
}

impl AsSymbol for &Symbol {
    fn as_symbol(&self) -> Symbol {
        (*self).clone()
    }
}

#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(transparent)]
pub struct OrderId(pub u64);

#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct LiveOrderRecord {
    pub id: OrderId,
    pub account_number: AccountNumber,
    pub time_in_force: TimeInForce,
    pub order_type: OrderType,
    pub size: u64,
    pub underlying_symbol: Symbol,
    #[serde(with = "rust_decimal::serde::arbitrary_precision")]
    pub price: Decimal,
    pub price_effect: PriceEffect,
    pub status: OrderStatus,
    pub cancellable: bool,
    pub editable: bool,
    pub edited: bool,
}

#[allow(dead_code)]
#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct LiveOrderLeg {
    pub instrument_type: InstrumentType,
    pub symbol: Symbol,
    pub quantity: u64,
    pub remaining_quantity: u64,
    pub action: Action,
    pub fills: Vec<String>,
}

#[derive(Builder, Serialize)]
#[serde(rename_all = "kebab-case")]
#[builder(setter(into))]
pub struct Order {
    time_in_force: TimeInForce,
    order_type: OrderType,

    #[serde(with = "rust_decimal::serde::arbitrary_precision")]
    price: Decimal,
    price_effect: PriceEffect,
    legs: Vec<OrderLeg>,
}

#[derive(Builder, Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "kebab-case")]
#[builder(setter(into))]
pub struct OrderLeg {
    instrument_type: InstrumentType,
    symbol: Symbol,
    #[serde(with = "rust_decimal::serde::float")]
    quantity: Decimal,
    action: Action,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct OrderPlacedResult {
    pub order: LiveOrderRecord,
    pub warnings: Vec<Warning>,
    pub buying_power_effect: BuyingPowerEffect,
    pub fee_calculation: FeeCalculation,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct DryRunResult {
    pub order: DryRunRecord,
    pub warnings: Vec<Warning>,
    pub buying_power_effect: BuyingPowerEffect,
    pub fee_calculation: FeeCalculation,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct DryRunRecord {
    pub account_number: AccountNumber,
    pub time_in_force: TimeInForce,
    pub order_type: OrderType,
    pub size: u64,
    pub underlying_symbol: Symbol,
    #[serde(with = "rust_decimal::serde::arbitrary_precision")]
    pub price: Decimal,
    pub price_effect: PriceEffect,
    pub status: OrderStatus,
    pub cancellable: bool,
    pub editable: bool,
    pub edited: bool,
    pub legs: Vec<OrderLeg>,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct BuyingPowerEffect {
    #[serde(with = "rust_decimal::serde::arbitrary_precision")]
    pub change_in_margin_requirement: Decimal,
    pub change_in_margin_requirement_effect: PriceEffect,
    #[serde(with = "rust_decimal::serde::arbitrary_precision")]
    pub change_in_buying_power: Decimal,
    pub change_in_buying_power_effect: PriceEffect,
    #[serde(with = "rust_decimal::serde::arbitrary_precision")]
    pub current_buying_power: Decimal,
    pub current_buying_power_effect: PriceEffect,
    #[serde(with = "rust_decimal::serde::arbitrary_precision")]
    pub impact: Decimal,
    pub effect: PriceEffect,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct FeeCalculation {
    #[serde(with = "rust_decimal::serde::arbitrary_precision")]
    pub total_fees: Decimal,
    pub total_fees_effect: PriceEffect,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Warning {}