use serde::{Deserialize, Serialize};
use time::{Date, OffsetDateTime};
use super::{
BalanceType, PositionSide, RegCodeType, Symbol, option_date_yyyymmdd, option_timestamp_ms,
};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Balance {
#[serde(rename = "accountId", alias = "a")]
pub account_id: String,
#[serde(rename = "currencyCode", alias = "cc")]
pub currency_code: String,
#[serde(rename = "cashBalance", alias = "cb", default)]
pub cash_balance: Option<f64>,
#[serde(rename = "cashBalanceAvailable", alias = "cba", default)]
pub cash_balance_available: Option<f64>,
#[serde(rename = "openTradeEquity", alias = "ote", default)]
pub open_trade_equity: Option<f64>,
#[serde(rename = "totalEquity", alias = "te", default)]
pub total_equity: Option<f64>,
#[serde(rename = "cashAddedToday", alias = "cbta", default)]
pub cash_added_today: Option<f64>,
#[serde(rename = "netLiquidity", alias = "nl", default)]
pub net_liquidity: Option<f64>,
#[serde(rename = "netLiquidityAvailable", alias = "nla", default)]
pub net_liquidity_available: Option<f64>,
#[serde(rename = "daysOnCall", alias = "dc", default)]
pub days_on_call: Option<i64>,
#[serde(rename = "balanceType", alias = "bt", default)]
pub balance_type: Option<BalanceType>,
#[serde(rename = "marginInfo", alias = "mi", default)]
pub margin_info: Option<MarginInfo>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MarginInfo {
#[serde(rename = "accountId", alias = "a")]
pub account_id: String,
#[serde(rename = "currencyCode", alias = "cc")]
pub currency_code: String,
#[serde(rename = "marginO", alias = "mo", default)]
pub margin_o: Option<MarginDetail>,
#[serde(rename = "marginOW", alias = "mow", default)]
pub margin_ow: Option<MarginDetail>,
#[serde(rename = "marginOWI", alias = "mowi", default)]
pub margin_owi: Option<MarginDetail>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MarginDetail {
#[serde(rename = "marginError", alias = "me", default)]
pub margin_error: Option<String>,
#[serde(rename = "errorSymbols", alias = "es", default)]
pub error_symbols: Option<String>,
#[serde(rename = "initialRiskMargin", alias = "irm", default)]
pub initial_risk_margin: Option<f64>,
#[serde(rename = "maintenanceRiskMargin", alias = "mrm", default)]
pub maintenance_risk_margin: Option<f64>,
#[serde(rename = "initialTotalMargin", alias = "itm", default)]
pub initial_total_margin: Option<f64>,
#[serde(rename = "maintenanceTotalMargin", alias = "mtm", default)]
pub maintenance_total_margin: Option<f64>,
#[serde(rename = "isEstimated", alias = "ie", default)]
pub is_estimated: Option<bool>,
#[serde(
rename = "asOfTime",
alias = "t",
default,
with = "option_timestamp_ms"
)]
pub as_of_time: Option<OffsetDateTime>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Position {
#[serde(rename = "accountId", alias = "a")]
pub account_id: String,
#[serde(rename = "currencyCode", alias = "cc", default)]
pub currency_code: Option<String>,
#[serde(rename = "exchSym", alias = "s", default)]
pub exch_sym: Option<Symbol>,
#[serde(rename = "positionId", alias = "pId", default)]
pub position_id: Option<String>,
#[serde(alias = "q", default)]
pub quantity: Option<f64>,
#[serde(alias = "p", default)]
pub price: Option<f64>,
#[serde(
rename = "dateOpened",
alias = "do",
default,
with = "option_date_yyyymmdd"
)]
pub date_opened: Option<Date>,
#[serde(alias = "sd", default)]
pub side: Option<PositionSide>,
#[serde(rename = "unrealizedPL", alias = "upl", default)]
pub unrealized_pl: Option<f64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RiskInfo {
#[serde(rename = "accountId", alias = "a")]
pub account_id: String,
#[serde(rename = "regCode", alias = "rc", default)]
pub reg_code: Option<RegCodeType>,
#[serde(rename = "currencyCode", alias = "cc", default)]
pub currency_code: Option<String>,
#[serde(rename = "liquidationValue", alias = "lv", default)]
pub liquidation_value: Option<f64>,
#[serde(rename = "startNetLiquidationValue", alias = "snlv", default)]
pub start_net_liquidation_value: Option<f64>,
#[serde(rename = "currentNetLiquidationValue", alias = "cnlv", default)]
pub current_net_liquidation_value: Option<f64>,
#[serde(rename = "maxNetLiquidationValue", alias = "mnlv", default)]
pub max_net_liquidation_value: Option<f64>,
#[serde(rename = "maxNetLiquidationValueMultiDay", default)]
pub max_net_liquidation_value_multi_day: Option<f64>,
#[serde(rename = "liquidationEvents", alias = "le", default)]
pub liquidation_events: Option<Vec<i32>>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AccountPositions {
#[serde(rename = "accountId", alias = "a")]
pub account_id: String,
#[serde(alias = "p", default)]
pub positions: Vec<Position>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn balance_from_rest() {
let json = r#"{
"accountId": "ACC001",
"currencyCode": "USD",
"cashBalance": 50000.0,
"totalEquity": 55000.0,
"balanceType": "CURRENT_OPEN"
}"#;
let b: Balance = serde_json::from_str(json).unwrap();
assert_eq!(b.account_id, "ACC001");
assert_eq!(b.cash_balance, Some(50000.0));
assert_eq!(b.balance_type, Some(BalanceType::CurrentOpen));
}
#[test]
fn balance_from_streaming() {
let json = r#"{
"a": "ACC001",
"cc": "USD",
"cb": 50000.0,
"te": 55000.0,
"bt": "CURRENT_OPEN"
}"#;
let b: Balance = serde_json::from_str(json).unwrap();
assert_eq!(b.account_id, "ACC001");
assert_eq!(b.cash_balance, Some(50000.0));
assert_eq!(b.balance_type, Some(BalanceType::CurrentOpen));
}
#[test]
fn position_from_rest() {
let json = r#"{
"accountId": "ACC001",
"exchSym": "XCME:ES.U16",
"quantity": 2.0,
"price": 4500.0,
"side": "LONG"
}"#;
let p: Position = serde_json::from_str(json).unwrap();
assert_eq!(p.account_id, "ACC001");
assert_eq!(p.side, Some(PositionSide::Long));
}
#[test]
fn position_from_streaming() {
let json = r#"{
"a": "ACC001",
"s": "XCME:ES.U16",
"q": 2.0,
"p": 4500.0,
"sd": "LONG"
}"#;
let p: Position = serde_json::from_str(json).unwrap();
assert_eq!(p.account_id, "ACC001");
assert_eq!(p.quantity, Some(2.0));
assert_eq!(p.side, Some(PositionSide::Long));
}
#[test]
fn risk_info_from_streaming() {
let json = r#"{
"a": "ACC001",
"rc": "COMBINED",
"cc": "USD",
"lv": 10000.0,
"cnlv": 55000.0
}"#;
let r: RiskInfo = serde_json::from_str(json).unwrap();
assert_eq!(r.account_id, "ACC001");
assert_eq!(r.reg_code, Some(RegCodeType::Combined));
assert_eq!(r.liquidation_value, Some(10000.0));
}
}