1use crate::{
2 orderflow::{AberrantFill, Fill},
3 symbology::*,
4 utils::{half_open_range::ClampSign, messaging::MaybeRequest},
5 AccountId, Dir, HalfOpenRange,
6};
7use chrono::{DateTime, NaiveDate, Utc};
8#[cfg(feature = "netidx")]
9use derive::FromValue;
10#[cfg(feature = "netidx")]
11use netidx_derive::Pack;
12use rust_decimal::Decimal;
13use schemars::JsonSchema;
14use serde::{Deserialize, Serialize};
15use std::{
16 collections::{BTreeMap, BTreeSet},
17 sync::Arc,
18};
19use uuid::Uuid;
20
21pub static SCHEMA: &'static str = include_str!("schema.sql");
22
23#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
29#[cfg_attr(feature = "netidx", derive(Pack))]
30#[cfg_attr(feature = "netidx", derive(FromValue))]
31pub enum FolioMessage {
32 GetFillsQuery(MarketFilter, HalfOpenRange<Option<DateTime<Utc>>>),
33 GetFillsQueryResponse(
34 MarketFilter,
35 HalfOpenRange<Option<DateTime<Utc>>>,
36 Arc<Vec<Result<Fill, AberrantFill>>>,
37 ),
38 GetFills(Uuid, GetFills),
39 Fills(Option<Uuid>, CptyId, Result<Fills, GetFillsError>), RealtimeFill(Result<Fill, AberrantFill>),
42 GetAllAccountSummaries(Uuid, Option<Arc<BTreeSet<AccountId>>>),
47 AllAccountSummaries(Uuid, Vec<(CptyId, Arc<AccountSummaries>)>),
48 GetAccountSummaries(Uuid, CptyId, Option<Arc<BTreeSet<AccountId>>>),
55 AccountSummaries(Option<Uuid>, CptyId, Option<Arc<AccountSummaries>>),
58 UpdateAccountSummaries,
60 SyncFillsForward,
62 SyncFillsBackward(CptyId),
63 InvalidateSyncBefore(CptyId, DateTime<Utc>),
64 InvalidateSyncAfter(CptyId, DateTime<Utc>),
65 GetSyncStatus(Uuid, CptyId),
66 GetSyncStatusResponse(Uuid, FolioSyncStatus),
67 SnapshotBalances,
69}
70
71#[derive(Copy, Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
72#[cfg_attr(feature = "netidx", derive(Pack))]
73#[cfg_attr(feature = "netidx", derive(FromValue))]
74pub struct MarketFilter {
75 pub venue: Option<VenueId>,
76 pub route: Option<RouteId>,
77 pub base: Option<ProductId>,
78 pub quote: Option<ProductId>,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
82#[cfg_attr(feature = "netidx", derive(Pack))]
83#[cfg_attr(feature = "netidx", derive(FromValue))]
84pub struct AccountSummaries {
85 pub snapshot_ts: DateTime<Utc>,
86 pub by_account: BTreeMap<AccountId, AccountSummary>,
87}
88
89impl AccountSummaries {
90 pub fn filter_accounts(&self, accounts: &BTreeSet<AccountId>) -> Self {
91 let by_account = self
92 .by_account
93 .iter()
94 .filter_map(|(account_id, summary)| {
95 if accounts.contains(account_id) {
96 Some((*account_id, summary.clone()))
97 } else {
98 None
99 }
100 })
101 .collect();
102 Self { snapshot_ts: self.snapshot_ts, by_account }
103 }
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
107#[cfg_attr(feature = "netidx", derive(Pack))]
108#[cfg_attr(feature = "netidx", derive(FromValue))]
109pub struct AccountSummary {
110 pub balances: BTreeMap<ProductId, Balance>,
111 pub positions: Vec<Position>,
114 pub profit_loss: Option<Decimal>,
115 pub clearing_venue: Option<VenueId>,
116}
117
118impl AccountSummary {
119 pub fn from_simple_balances(balances: BTreeMap<ProductId, Decimal>) -> Self {
120 Self {
121 balances: balances
122 .into_iter()
123 .map(|(product_id, total)| {
124 (product_id, Balance { total: Some(total), ..Default::default() })
125 })
126 .collect(),
127 positions: vec![],
128 ..Default::default()
129 }
130 }
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
134#[cfg_attr(feature = "netidx", derive(Pack))]
135#[cfg_attr(feature = "netidx", derive(FromValue))]
136pub struct Balance {
137 pub total: Option<Decimal>,
140
141 pub total_margin: Option<Decimal>,
143
144 pub position_margin: Option<Decimal>,
146
147 pub purchasing_power: Option<Decimal>,
149
150 pub cash_excess: Option<Decimal>,
152
153 pub yesterday_balance: Option<Decimal>,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
158#[cfg_attr(feature = "netidx", derive(Pack))]
159#[cfg_attr(feature = "netidx", derive(FromValue))]
160pub struct Position {
161 pub market_id: MarketId,
162
163 pub quantity: Option<Decimal>,
164
165 pub average_price: Option<Decimal>,
167
168 pub trade_time: Option<DateTime<Utc>>,
169
170 pub trade_date: Option<NaiveDate>,
173
174 pub dir: Dir,
175
176 pub break_even_price: Option<Decimal>,
177
178 pub liquidation_price: Option<Decimal>,
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
188#[cfg_attr(feature = "netidx", derive(Pack))]
189#[cfg_attr(feature = "netidx", derive(FromValue))]
190pub struct GetFills {
191 pub cpty: CptyId,
192 pub range: HalfOpenRange<Option<DateTime<Utc>>>,
193 pub clamp_sign: ClampSign,
194}
195
196#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
197#[cfg_attr(feature = "netidx", derive(Pack))]
198#[cfg_attr(feature = "netidx", derive(FromValue))]
199pub enum GetFillsError {
200 #[serde(other)]
201 #[cfg_attr(feature = "netidx", pack(other))]
202 Unknown,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
206#[cfg_attr(feature = "netidx", derive(Pack))]
207#[cfg_attr(feature = "netidx", derive(FromValue))]
208pub struct Fills {
209 pub range: HalfOpenRange<Option<DateTime<Utc>>>,
210 pub fills: Arc<Vec<Result<Fill, AberrantFill>>>,
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
214#[cfg_attr(feature = "netidx", derive(Pack))]
215#[cfg_attr(feature = "netidx", derive(FromValue))]
216pub struct FolioSyncStatus {
217 pub synced_range: Option<HalfOpenRange<DateTime<Utc>>>,
218 pub beginning_of_time: DateTime<Utc>,
219 pub forward_sync_backoff: Option<DateTime<Utc>>,
220 pub backfill_backoff: Option<DateTime<Utc>>,
221}
222
223impl MaybeRequest for FolioMessage {
224 fn request_id(&self) -> Option<Uuid> {
225 match self {
226 FolioMessage::GetFills(id, ..)
227 | FolioMessage::GetSyncStatus(id, ..)
228 | FolioMessage::GetAccountSummaries(id, ..)
229 | FolioMessage::GetAllAccountSummaries(id, ..) => Some(*id),
230 _ => None,
231 }
232 }
233
234 fn response_id(&self) -> Option<Uuid> {
235 match self {
236 FolioMessage::Fills(id, ..) | FolioMessage::AccountSummaries(id, ..) => *id,
237 FolioMessage::GetSyncStatusResponse(id, ..)
238 | FolioMessage::AllAccountSummaries(id, ..) => Some(*id),
239 _ => None,
240 }
241 }
242}