1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
//! Schwab streamer WebSocket.
//!
//! The streamer is a single multiplexed WebSocket: one connection carries
//! every subscribed service (level-one quotes, chart bars, book ladders,
//! screeners, account activity, admin). [`SchwabClient::streamer`](crate::SchwabClient::streamer)
//! opens it and returns a read/write pair:
//!
//! - [`WriteHalf`] sends commands. Call [`WriteHalf::login`] first; every
//! subscribe/add/unsubscribe/view request goes through this side.
//! - [`ReadHalf`] receives frames. Each [`ReadHalf::recv`] call yields one
//! [`StreamerResponse`] (data, response, heartbeat). Frame parsing
//! happens inline; no background task is spawned.
//!
//! Both halves share the underlying socket through an internal mutex, so
//! they may be moved into separate tasks freely.
//!
//! Subscribe entry points on [`WriteHalf`] (e.g. [`WriteHalf::equities`]
//! for LEVELONE_EQUITIES, [`WriteHalf::chart_equity`] for CHART_EQUITY)
//! return a typed [`SubscribeRequest`] that takes keys, fields, and the
//! [`SubscriptionCommand`] (subscribe/add/unsubscribe/view).
//!
//! Connection lifecycle is exposed via [`ReadHalf::events`], a
//! `tokio::sync::watch` channel of [`ConnectionEvent`].
//!
//! # Examples
//!
//! Connect, log in, subscribe to level-one equities, and read ticks. The
//! write half is cheap to clone and drives commands; the read half is
//! polled with [`ReadHalf::recv`].
//!
//! ```no_run
//! use schwab_sdk::{AuthToken, SchwabClient, StreamerResponse};
//! use schwab_sdk::streamer::DataContent;
//! use schwab_sdk::streamer::level_one::equities::Field;
//!
//! # async fn run() -> schwab_sdk::Result<()> {
//! let client = SchwabClient::new(AuthToken::new("token"));
//!
//! let (mut read, write) = client.streamer().await?;
//! // The bearer is pulled from the client's token provider.
//! write.login().await?;
//!
//! write
//! .equities()
//! .subscribe(["AAPL", "MSFT"])
//! .fields([Field::Symbol, Field::BidPrice, Field::AskPrice, Field::LastPrice])
//! .send()
//! .await?;
//!
//! loop {
//! match read.recv().await? {
//! StreamerResponse::Data(payloads) => {
//! for payload in payloads {
//! if let DataContent::LevelOneEquities(ticks) = payload.content {
//! for tick in ticks {
//! println!("{}: {:?}", tick.key, tick.last_price);
//! }
//! }
//! }
//! }
//! StreamerResponse::Notify(_) => { /* heartbeat */ }
//! StreamerResponse::Response(acks) => {
//! for ack in acks {
//! println!("{:?} {:?}: {}", ack.service, ack.command, ack.content.message);
//! }
//! }
//! // `StreamerResponse` is non-exhaustive.
//! _ => {}
//! }
//! }
//! # }
//! ```
pub use ;
pub use ;
pub use ;
pub use StreamerRequest;
pub use ;
pub use ;