brokapi_alor/map/
portfolio.rs1use std::time::Instant;
4
5use brokapi_alor_protocol::http::models::PositionRecord;
6use brokapi_core::{Balance, Money, Portfolio, Position, Price, Qty};
7
8use super::timestamps;
9use crate::util::instrument_id;
10
11pub fn is_money(r: &PositionRecord) -> bool {
13 r.is_currency == Some(true)
14}
15
16pub fn balance(r: &PositionRecord, now: Instant) -> Balance {
19 Balance {
20 currency: r.symbol.clone(),
21 available: Money::new(r.qty_units.unwrap_or_default()),
22 blocked: Money::ZERO,
23 ts: timestamps(now, None),
24 }
25}
26
27pub fn position(r: &PositionRecord, board: &str, now: Instant) -> Position {
31 Position {
32 instrument: instrument_id(&r.symbol, board),
33 qty: Qty::new(r.qty_units.unwrap_or_default()),
34 avg_price: r.avg_price.filter(|p| !p.is_zero()).map(Price::new),
35 current_price: None,
36 ts: timestamps(now, None),
37 }
38}
39
40pub fn portfolio(
42 records: &[PositionRecord],
43 board_of: impl Fn(&str) -> String,
44 now: Instant,
45) -> Portfolio {
46 let mut balances = Vec::new();
47 let mut positions = Vec::new();
48 for r in records {
49 if is_money(r) {
50 balances.push(balance(r, now));
51 } else if r.qty_units.is_some_and(|q| !q.is_zero()) {
52 positions.push(position(r, &board_of(&r.symbol), now));
53 }
54 }
55 Portfolio {
56 balances,
57 positions,
58 ts: timestamps(now, None),
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn money_is_a_balance_and_instruments_are_positions() {
68 let recs: Vec<PositionRecord> = serde_json::from_str(
69 r#"[{"symbol":"RUB","qtyUnits":100000.55,"isCurrency":true},
70 {"symbol":"SBER","qtyUnits":30,"avgPrice":250.1,"isCurrency":false},
71 {"symbol":"GAZP","qtyUnits":0,"isCurrency":false}]"#,
72 )
73 .unwrap();
74 let p = portfolio(&recs, |_| "TQBR".to_string(), Instant::now());
75 assert_eq!(p.balances.len(), 1);
76 assert_eq!(p.balances[0].currency, "RUB");
77 assert_eq!(p.balances[0].available.to_string(), "100000.55");
78 assert_eq!(p.positions.len(), 1);
80 assert_eq!(p.positions[0].instrument.key.display(), "SBER@TQBR");
81 assert_eq!(p.positions[0].qty, Qty::from(30));
82 }
83}