use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetInstrumentsRequest {
#[serde(rename = "OMSId")]
pub oms_id: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetInstrumentRequest {
#[serde(rename = "OMSId")]
pub oms_id: i64,
#[serde(rename = "InstrumentId")]
pub instrument_id: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Instrument {
#[serde(rename = "OMSId", alias = "omsId")]
pub oms_id: i64,
#[serde(rename = "InstrumentId", alias = "instrumentId")]
pub instrument_id: i64,
#[serde(rename = "Symbol", alias = "symbol")]
pub symbol: String,
#[serde(rename = "Product1", alias = "product1")]
pub product1: i64,
#[serde(rename = "Product1Symbol", alias = "product1Symbol")]
pub product1_symbol: String,
#[serde(rename = "Product2", alias = "product2")]
pub product2: i64,
#[serde(rename = "Product2Symbol", alias = "product2Symbol")]
pub product2_symbol: String,
#[serde(rename = "InstrumentType", alias = "instrumentType")]
pub instrument_type: String,
#[serde(rename = "VenueInstrumentId", alias = "venueInstrumentId")]
pub venue_instrument_id: i64,
#[serde(rename = "VenueId", alias = "venueId")]
pub venue_id: i64,
#[serde(rename = "SortIndex", alias = "sortIndex")]
pub sort_index: i64,
#[serde(rename = "SessionStatus", alias = "sessionStatus")]
pub session_status: String,
#[serde(rename = "PreviousSessionStatus", alias = "previousSessionStatus")]
pub previous_session_status: String,
#[serde(rename = "SessionStatusDateTime", alias = "sessionStatusDateTime")]
pub session_status_date_time: String,
#[serde(rename = "SelfTradePrevention", alias = "selfTradePrevention")]
pub self_trade_prevention: bool,
#[serde(rename = "QuantityIncrement", alias = "quantityIncrement")]
pub quantity_increment: f64,
#[serde(rename = "PriceIncrement", alias = "priceIncrement")]
pub price_increment: f64,
}
impl Instrument {
pub fn trading_pair(&self) -> String {
format!("{}/{}", self.product1_symbol, self.product2_symbol)
}
pub fn base_symbol(&self) -> &str { &self.product1_symbol }
pub fn quote_symbol(&self) -> &str { &self.product2_symbol }
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetProductsRequest {
#[serde(rename = "OMSId")]
pub oms_id: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetProductRequest {
#[serde(rename = "OMSId")]
pub oms_id: i64,
#[serde(rename = "ProductId")]
pub product_id: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Product {
pub oms_id: i64,
pub product_id: i64,
pub product: String,
pub product_full_name: String,
pub product_type: String,
pub decimal_places: i32,
pub tick_size: f64,
pub no_fees: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_instruments_deserialize() {
let json = r#"[{"omsId":1,"instrumentId":1,"symbol":"BTCCAD","product1":1,"product1Symbol":"BTC","product2":2,"product2Symbol":"CAD","instrumentType":"Standard","venueInstrumentId":1,"venueId":1,"sortIndex":0,"sessionStatus":"Running","previousSessionStatus":"Running","sessionStatusDateTime":"2024-01-01T00:00:00Z","selfTradePrevention":true,"quantityIncrement":0.00001,"priceIncrement":0.01}]"#;
let instruments: Vec<Instrument> = serde_json::from_str(json).unwrap();
assert_eq!(instruments.len(), 1);
let instrument = &instruments[0];
assert_eq!(instrument.oms_id, 1);
assert_eq!(instrument.instrument_id, 1);
assert_eq!(instrument.symbol, "BTCCAD");
assert_eq!(instrument.product1, 1);
assert_eq!(instrument.product1_symbol, "BTC");
assert_eq!(instrument.product2, 2);
assert_eq!(instrument.product2_symbol, "CAD");
assert_eq!(instrument.instrument_type, "Standard");
assert_eq!(instrument.venue_instrument_id, 1);
assert_eq!(instrument.venue_id, 1);
assert_eq!(instrument.sort_index, 0);
assert_eq!(instrument.session_status, "Running");
assert_eq!(instrument.previous_session_status, "Running");
assert!(instrument.self_trade_prevention);
assert!((instrument.quantity_increment - 0.00001).abs() < f64::EPSILON);
assert!((instrument.price_increment - 0.01).abs() < f64::EPSILON);
}
#[test]
fn test_products_deserialize() {
let json = r#"[{"omsId":1,"productId":1,"product":"BTC","productFullName":"Bitcoin","productType":"CryptoCurrency","decimalPlaces":8,"tickSize":0.00000001,"noFees":false},{"omsId":1,"productId":2,"product":"CAD","productFullName":"Canadian Dollar","productType":"NationalCurrency","decimalPlaces":2,"tickSize":0.01,"noFees":false}]"#;
let products: Vec<Product> = serde_json::from_str(json).unwrap();
assert_eq!(products.len(), 2);
let btc = &products[0];
assert_eq!(btc.oms_id, 1);
assert_eq!(btc.product_id, 1);
assert_eq!(btc.product, "BTC");
assert_eq!(btc.product_full_name, "Bitcoin");
assert_eq!(btc.product_type, "CryptoCurrency");
assert_eq!(btc.decimal_places, 8);
assert!((btc.tick_size - 0.00000001).abs() < f64::EPSILON);
assert!(!btc.no_fees);
let cad = &products[1];
assert_eq!(cad.oms_id, 1);
assert_eq!(cad.product_id, 2);
assert_eq!(cad.product, "CAD");
assert_eq!(cad.product_full_name, "Canadian Dollar");
assert_eq!(cad.product_type, "NationalCurrency");
assert_eq!(cad.decimal_places, 2);
assert!((cad.tick_size - 0.01).abs() < f64::EPSILON);
assert!(!cad.no_fees);
}
#[test]
fn test_trading_pair_format() {
let instrument = Instrument {
oms_id: 1,
instrument_id: 1,
symbol: "BTCCAD".to_string(),
product1: 1,
product1_symbol: "BTC".to_string(),
product2: 2,
product2_symbol: "CAD".to_string(),
instrument_type: "Standard".to_string(),
venue_instrument_id: 1,
venue_id: 1,
sort_index: 0,
session_status: "Running".to_string(),
previous_session_status: "Running".to_string(),
session_status_date_time: "2024-01-01T00:00:00Z".to_string(),
self_trade_prevention: true,
quantity_increment: 0.00001,
price_increment: 0.01,
};
assert_eq!(instrument.trading_pair(), "BTC/CAD");
}
#[test]
fn test_base_quote_symbols() {
let instrument = Instrument {
oms_id: 1,
instrument_id: 1,
symbol: "ETHCAD".to_string(),
product1: 3,
product1_symbol: "ETH".to_string(),
product2: 2,
product2_symbol: "CAD".to_string(),
instrument_type: "Standard".to_string(),
venue_instrument_id: 2,
venue_id: 1,
sort_index: 1,
session_status: "Running".to_string(),
previous_session_status: "Running".to_string(),
session_status_date_time: "2024-01-01T00:00:00Z".to_string(),
self_trade_prevention: true,
quantity_increment: 0.0001,
price_increment: 0.01,
};
assert_eq!(instrument.base_symbol(), "ETH");
assert_eq!(instrument.quote_symbol(), "CAD");
}
#[test]
fn test_get_instruments_request_serialize() {
let request = GetInstrumentsRequest {
oms_id: 1
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("\"OMSId\""));
assert!(json.contains(":1"));
}
#[test]
fn test_get_instrument_request_serialize() {
let request = GetInstrumentRequest {
oms_id: 1,
instrument_id: 5,
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("\"OMSId\""));
assert!(json.contains("\"InstrumentId\""));
}
#[test]
fn test_get_products_request_serialize() {
let request = GetProductsRequest {
oms_id: 1
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("\"OMSId\""));
}
#[test]
fn test_get_product_request_serialize() {
let request = GetProductRequest {
oms_id: 1,
product_id: 3,
};
let json = serde_json::to_string(&request).unwrap();
assert!(json.contains("\"OMSId\""));
assert!(json.contains("\"ProductId\""));
}
}