Skip to main content

Alerts

Trait Alerts 

Source
pub trait Alerts: MaybeSend + 'static {
    // Required methods
    fn watch(&self, events: Events<AlertEvent>);
    fn save(&self, alert: Alert, reply: Reply<()>);
    fn remove(&self, id: AlertId, reply: Reply<()>);

    // Provided method
    fn engine(&self) -> Option<&AlertEngine> { ... }
}
Expand description

The host’s alerts. The host stores and evaluates them, on its own prices or on a server; the library shows what the host pushes and sends every change the user makes through save and remove. With the alert-engine feature, services::AlertEngine is a ready-made implementation that evaluates the alerts on the bars the host feeds it; without it the library evaluates nothing.

The host is the source of truth. The terminal shows only the alerts and triggers that watch pushed: an alert the user creates appears once the host pushes it, and the terminal never marks an alert triggered on its own. When the host pushes AlertEvent::Triggered, the terminal shows a popup and an entry in the alert log; the status it shows is the one the host last pushed in an AlertEvent::Upsert. Sound and delivery outside the application (email, push notifications) are the host’s: it can play a sound or send a message when it pushes the trigger.

Evaluation is the host’s, so these rules are the contract between the library that edits an alert and the service that runs it. A channel is inclusive at both boundaries. The state conditions InsideChannel and OutsideChannel hold on every evaluation while the value is in that state, so pair them with a frequency that limits repeats, whereas entering and exiting hold only on the evaluation that crosses a boundary. AlertFrequency::OncePerMinute allows one trigger per UTC clock minute, from hh:mm:00 up to but excluding the next minute, whatever the chart’s interval. An alert’s schedule allows triggers only when NotificationSchedule::allows accepts the trigger time. Outside it, keep observing values for crossings without consuming an occurrence or disarming an alert that fires once.

A StrategyFills alert receives each execution of a host-named run through AlertEngine::strategy_fills, with strategies and alert-engine. Its event carries the fill time, execution price and a message such as “Buy 2 at 187.50, position 4”. The host supplies bars and retains the run; delivery outside the application remains the host’s responsibility.

These methods are called on the UI thread, while the terminal shows a frame or runs a command, and must return at once. Do the work elsewhere and answer through the Reply or push through the Events you were given, from any thread, runtime or browser task. A Reply dropped unanswered reports FeedError::Unanswered.

use std::sync::Mutex;

use egui_charts::data::Reply;
use egui_charts::model::{Alert, AlertId};
use egui_charts::services::{AlertEvent, Alerts, Events};

/// Alerts held in memory, lost when the application exits. Answers inline. A real host also
/// evaluates the alerts on its prices and pushes `AlertEvent::Triggered` when one fires.
#[derive(Default)]
struct InMemory(Mutex<State>);

#[derive(Default)]
struct State {
    alerts: Vec<Alert>,
    events: Option<Events<AlertEvent>>,
    next_id: u64,
}

impl Alerts for InMemory {
    fn watch(&self, events: Events<AlertEvent>) {
        let mut state = self.0.lock().unwrap();
        for alert in &state.alerts {
            events.push(AlertEvent::Upsert(alert.clone()));
        }
        state.events = Some(events);
    }

    fn save(&self, mut alert: Alert, reply: Reply<()>) {
        let mut state = self.0.lock().unwrap();
        if alert.id.is_none() {
            state.next_id += 1;
            alert.id = Some(AlertId(state.next_id.to_string()));
        }
        state.alerts.retain(|kept| kept.id != alert.id);
        state.alerts.push(alert.clone());
        if let Some(events) = &state.events {
            events.push(AlertEvent::Upsert(alert));
        }
        reply.send(Ok(()));
    }

    fn remove(&self, id: AlertId, reply: Reply<()>) {
        let mut state = self.0.lock().unwrap();
        state.alerts.retain(|kept| kept.id.as_ref() != Some(&id));
        if let Some(events) = &state.events {
            events.push(AlertEvent::Remove(id));
        }
        reply.send(Ok(()));
    }
}

let alerts: std::sync::Arc<dyn Alerts> = std::sync::Arc::new(InMemory::default());

Required Methods§

Source

fn watch(&self, events: Events<AlertEvent>)

Pushes the host’s alerts, then every change and trigger. Called when the terminal starts and again after a lagged stream, each time with a new stream that starts from no alerts: push the current list first, then keep pushing until events is inactive. On the first call, also push the triggers that fired while the application was closed; do not push them again on a later call.

The stream takes at most 10,000 pushes between two frames, the current list and the missed triggers included: send at most that many at once (for example only the most recent triggers), or the stream lags on every call and no alert shows.

Source

fn save(&self, alert: Alert, reply: Reply<()>)

Creates (alert.id is None) or replaces an alert. Keep alert.inputs with the alert: its independent study parameters and source view must survive copies and restoration. Answer Ok once the change is kept; the alert appears when watch pushes it, so push the saved alert, with its id, as AlertEvent::Upsert.

Source

fn remove(&self, id: AlertId, reply: Reply<()>)

Deletes the alert id. Answer Ok once it is deleted and push AlertEvent::Remove.

Provided Methods§

Source

fn engine(&self) -> Option<&AlertEngine>

The local evaluator, when this service uses one. The assembled terminal feeds it from its datafeed, including subjects that no live chart shows. Server-backed services keep the default so evaluation remains on the server.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§