Skip to main content

egui_charts/services/
signals.rs

1//! Markers, levels and zones from the host's analytics.
2
3use super::Events;
4use crate::data::MaybeSend;
5use crate::model::{Signal, SignalId, Timeframe};
6
7/// The host's analytics as markers, levels and zones. The library computes nothing: the terminal
8/// draws what the service pushes, lets the user hide signals by category, and reports clicks.
9/// Connect it through `Chart::set_signal_source` (with the `widget` feature).
10///
11/// Methods are called on the UI thread and must return at once. Compute elsewhere and push from
12/// any thread, runtime or browser task.
13///
14/// ```
15/// use egui_charts::model::{Direction, Signal, SignalId, SignalShape, Timeframe};
16/// use egui_charts::services::{Events, SignalEvent, Signals};
17/// use std::time::Duration;
18///
19/// /// Pushes a marker every second, in place of a host's own analysis.
20/// struct Breakouts;
21///
22/// impl Signals for Breakouts {
23///     fn subscribe(&self, symbol: &str, _: Timeframe, events: Events<SignalEvent>) {
24///         let symbol = symbol.to_owned();
25///         std::thread::spawn(move || {
26///             let mut count = 0;
27///             while events.is_active() {
28///                 std::thread::sleep(Duration::from_secs(1));
29///                 count += 1;
30///                 events.push(SignalEvent::Upsert(Signal {
31///                     id: SignalId(format!("{symbol}-breakout-{count}")),
32///                     shape: SignalShape::Marker {
33///                         time: chrono::Utc::now(),
34///                         price: None,
35///                         direction: Direction::Up,
36///                     },
37///                     category: "Breakouts".into(),
38///                     label: Some("B".into()),
39///                     tooltip: Some(format!("Breakout {count} on {symbol}")),
40///                 }));
41///             }
42///         });
43///     }
44/// }
45/// ```
46pub trait Signals: MaybeSend + 'static {
47    /// Starts pushing the signals of `symbol` at `timeframe` into `events` until it is inactive.
48    /// Push the current signals first: each subscription starts from none. The terminal subscribes
49    /// again on every symbol or interval change and after the stream lagged, each time with a new
50    /// stream, and stops the old one.
51    ///
52    /// The stream takes at most 10,000 pushes between two frames, the current signals included:
53    /// send at most that many at once (for example the most recent ones, or those near the loaded
54    /// range), or the stream lags on every subscription and no signal shows.
55    fn subscribe(&self, symbol: &str, timeframe: Timeframe, events: Events<SignalEvent>);
56
57    /// The user clicked the signal `id`, which this service pushed. The default does nothing.
58    fn clicked(&self, id: &SignalId) {
59        let _ = id;
60    }
61}
62
63/// A change to the signals of one subscription.
64#[derive(Clone, Debug, PartialEq)]
65#[non_exhaustive]
66pub enum SignalEvent {
67    /// Adds the signal, or replaces the one with its id.
68    Upsert(Signal),
69    /// Removes the signal with this id, if any.
70    Remove(SignalId),
71    /// Removes every signal of the subscription.
72    Clear,
73}