truefix_ig_client/
streaming.rs1use lightstreamer_rs::{
4 Client, ClientConfig, Credentials, FieldSchema, ItemGroup, ServerAddress, Snapshot,
5 Subscription, SubscriptionMode, Updates,
6};
7
8use crate::error::IgResult;
9
10const MARKET_FIELDS: [&str; 11] = [
11 "BID",
12 "OFFER",
13 "HIGH",
14 "LOW",
15 "MID_OPEN",
16 "CHANGE",
17 "CHANGE_PCT",
18 "UPDATE_TIME",
19 "MARKET_STATE",
20 "MARKET_DELAY",
21 "LTV",
22];
23const ACCOUNT_FIELDS: [&str; 5] = ["PNL", "DEPOSIT", "USED_MARGIN", "AVAILABLE_CASH", "FUNDS"];
24const TRADE_FIELDS: [&str; 3] = ["CONFIRMS", "OPU", "WOU"];
25
26pub struct IgStreamingClient {
30 client: Client,
31 account_id: String,
32}
33
34impl std::fmt::Debug for IgStreamingClient {
35 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 formatter
37 .debug_struct("IgStreamingClient")
38 .field("account_id", &"<redacted>")
39 .finish_non_exhaustive()
40 }
41}
42
43impl IgStreamingClient {
44 pub(crate) async fn connect(
45 endpoint: &str,
46 account_id: String,
47 cst: String,
48 x_security_token: String,
49 ) -> IgResult<Self> {
50 let password = format!("CST-{cst}|XST-{x_security_token}");
51 let config = ClientConfig::builder(ServerAddress::try_new(endpoint)?)
52 .with_credentials(Credentials::new(&account_id, password))
53 .build()?;
54 let (client, session_events) = Client::connect(config).await?;
55 drop(session_events);
56 Ok(Self { client, account_id })
57 }
58
59 pub async fn subscribe_markets<I, S>(&self, epics: I) -> IgResult<Updates>
61 where
62 I: IntoIterator<Item = S>,
63 S: AsRef<str>,
64 {
65 let items = epics
66 .into_iter()
67 .map(|epic| format!("MARKET:{}", epic.as_ref()));
68 self.subscribe(SubscriptionMode::Merge, items, MARKET_FIELDS, Snapshot::On)
69 .await
70 }
71
72 pub async fn subscribe_account(&self) -> IgResult<Updates> {
74 self.subscribe(
75 SubscriptionMode::Merge,
76 [format!("ACCOUNT:{}", self.account_id)],
77 ACCOUNT_FIELDS,
78 Snapshot::On,
79 )
80 .await
81 }
82
83 pub async fn subscribe_trades(&self) -> IgResult<Updates> {
85 self.subscribe(
86 SubscriptionMode::Distinct,
87 [format!("TRADE:{}", self.account_id)],
88 TRADE_FIELDS,
89 Snapshot::On,
90 )
91 .await
92 }
93
94 pub async fn disconnect(self) -> IgResult<()> {
96 self.client.disconnect().await?;
97 Ok(())
98 }
99
100 async fn subscribe<I, S, F>(
101 &self,
102 mode: SubscriptionMode,
103 items: I,
104 fields: F,
105 snapshot: Snapshot,
106 ) -> IgResult<Updates>
107 where
108 I: IntoIterator<Item = S>,
109 S: Into<String>,
110 F: IntoIterator,
111 F::Item: Into<String>,
112 {
113 let subscription = Subscription::new(
114 mode,
115 ItemGroup::from_items(items)?,
116 FieldSchema::from_fields(fields)?,
117 )
118 .with_snapshot(snapshot);
119 Ok(self.client.subscribe(subscription).await?)
120 }
121}