Skip to main content

brokapi_finam/map/
portfolio.rs

1//! Account / portfolio mapping.
2
3use std::time::Instant;
4
5use brokapi_core::{Balance, Execution, Money, Portfolio, Position};
6use brokapi_finam_protocol::pb::grpc::tradeapi::v1;
7use brokapi_finam_protocol::pb::grpc::tradeapi::v1::accounts::GetAccountResponse;
8
9use crate::map::order::side_from_pb;
10use crate::util::{dec0, instrument_from_symbol, money, price, price0, qty0, timestamps};
11
12fn positions(resp: &GetAccountResponse, now: Instant) -> Vec<Position> {
13    resp.positions
14        .iter()
15        .map(|p| Position {
16            instrument: instrument_from_symbol(&p.symbol),
17            // Signed: long positive, short negative.
18            qty: qty0(&p.quantity),
19            // Absent for FORTS positions.
20            avg_price: price(&p.average_price),
21            current_price: price(&p.current_price),
22            ts: timestamps(None, now),
23        })
24        .collect()
25}
26
27/// One balance per currency in `cash`. Finam reports no blocked amount per currency, so
28/// `blocked` is 0.
29fn balances(resp: &GetAccountResponse, now: Instant) -> Vec<Balance> {
30    resp.cash
31        .iter()
32        .map(|m| {
33            let (currency, available) = money(m);
34            Balance {
35                currency,
36                available,
37                blocked: Money::ZERO,
38                ts: timestamps(None, now),
39            }
40        })
41        .collect()
42}
43
44/// `GetAccount` answer -> portfolio: one balance per currency of `cash` and one position per
45/// holding. Quantity is signed: a short position is negative. `average_price` is `None` when
46/// Finam does not send it (futures positions).
47pub fn map_account(resp: &GetAccountResponse, now: Instant) -> Portfolio {
48    Portfolio {
49        balances: balances(resp, now),
50        positions: positions(resp, now),
51        ts: timestamps(None, now),
52    }
53}
54
55/// `AccountTrade` -> execution. `cid` is resolved by the caller (comment / order index).
56pub fn map_trade(
57    t: &v1::AccountTrade,
58    cid: Option<brokapi_core::ClientOrderId>,
59    now: Instant,
60) -> Execution {
61    Execution {
62        cid,
63        broker_order_id: brokapi_core::BrokerOrderId::new(t.order_id.clone()),
64        instrument: instrument_from_symbol(&t.symbol),
65        side: side_from_pb(t.side),
66        price: price0(&t.price),
67        qty: qty0(&t.size),
68        // Only historical trades carry a commission; its currency is not part of `Execution`.
69        commission: t
70            .commission
71            .as_ref()
72            .map(|c| Money::new(dec0(&c.value)))
73            .unwrap_or(Money::ZERO),
74        trade_id: t.trade_id.clone(),
75        ts: timestamps(t.timestamp.as_ref(), now),
76    }
77}