egui_charts/lib.rs
1// Without the widget these items are not built; point their links at the feature table.
2#![cfg_attr(
3 not(feature = "widget"),
4 doc = "
5[`Chart`]: crate#feature-flags
6[`Chart::show`]: crate#feature-flags
7[`ChartBuilder::with_feed`]: crate#feature-flags
8[`guide`]: crate#feature-flags
9[`widget`]: crate#feature-flags
10[`config`]: crate#feature-flags
11[`theme`]: crate#feature-flags
12[`styles`]: crate#feature-flags
13[`localization`]: crate#feature-flags
14[`theme::apply_to_egui`]: crate#feature-flags
15[`localization::set_translator`]: crate#feature-flags
16"
17)]
18//! Interactive financial charts for [egui](https://docs.rs/egui), using your historical and streaming data.
19//!
20//! [`Chart`] provides price and volume panes, pan and zoom, crosshairs and live bar
21//! updates on native and WebAssembly targets. Optional features add built-in studies,
22//! drawing tools, order flow visualizations and application UI such as toolbars,
23//! replay controls, trading panels and dialogs. The host supplies market data,
24//! external accounts and durable storage through the crate's contracts.
25//!
26//! # Quick start
27//!
28//! Enable `widget` for a chart without application UI or built-in studies:
29//!
30//! ```toml
31//! egui-charts = { git = "https://github.com/userFRM/egui-charts", branch = "main", default-features = false, features = ["widget"] }
32//! ```
33//!
34//! Create the chart once with bars ordered from oldest to newest, then call
35//! [`Chart::show`] in your egui UI callback each frame:
36//!
37//! ```
38//! # #[cfg(feature = "widget")] {
39//! use egui_charts::{Chart, model::{Bar, BarData}};
40//!
41//! let bars = vec![
42//! Bar::new("2024-01-01T00:00:00Z".parse().unwrap(),
43//! 100.0, 102.0, 99.0, 101.0, 1_000.0),
44//! Bar::new("2024-01-01T00:01:00Z".parse().unwrap(),
45//! 101.0, 103.0, 100.0, 102.0, 1_200.0),
46//! ];
47//! let mut chart = Chart::new(BarData::from_bars(bars));
48//!
49//! # egui::__run_test_ui(|ui| {
50//! chart.show(ui);
51//! # });
52//! # }
53//! ```
54//!
55//! For live data, merge bars with `Chart::update(widget::SeriesId::MAIN, bar)`.
56//! [`data::Datafeed`] supports paged history and streaming bars, trades and depth;
57//! attach a feed with [`ChartBuilder::with_feed`]. Feed methods return promptly,
58//! answer requests through [`data::Reply`] and push updates through [`data::Sink`].
59//! A symbol identifies the host's instrument; setting it does not fetch data without
60//! a feed. See the [`guide`] for complete integration examples.
61//!
62//! # API map
63//!
64//! | Module | Use it for |
65//! |---|---|
66//! | [`widget`] | `Chart`, `ChartBuilder`, events, panes, chart layouts, replay and export |
67//! | [`model`] | Bars, instruments, intervals, chart types, price transforms and host data records |
68//! | [`data`] | Historical and streaming data contracts, aggregation and order flow data |
69//! | [`studies`] | The `Indicator` extension point, calculation helpers and built-in studies |
70//! | `drawings` | Drawing tools, selection, undo and timestamp-based persistence |
71//! | [`scales`] | Price ranges, coordinate mapping, axis ticks and label formatting |
72//! | [`config`] | Chart appearance and interaction options |
73//! | [`services`] | Host storage, symbol search, quotes, alerts, signals and broker contracts |
74//! | `strategies` | Strategies that trade on closed bars and the backtest engine that runs them |
75//! | [`theme`], [`tokens`], [`styles`] | Theme presets, design tokens and UI styling helpers |
76//! | `ui`, `ui_kit` | Chart controls and reusable UI components placed in the host's layout |
77//! | `icons` | Embedded SVG icons |
78//! | [`localization`] | Host translations scoped to an egui context |
79//! | [`prelude`] | Common types and traits for chart integration |
80//!
81//! # Feature flags
82//!
83//! The defaults are `ui`, `locales`, `timezones`, `csv` and `png-export`. With `default-features = false`,
84//! the data model, scales, feed and service contracts, tokens and study extension
85//! points are available without an egui dependency.
86//!
87//! | Feature | Default | Enables |
88//! |---|---|---|
89//! | `widget` | via `ui` | The chart widget, rendering, configuration, themes, styling and localization |
90//! | `drawings` | via `ui` | Drawing tools, interaction, undo and persistence; enables `widget` |
91//! | `studies` | via `ui` | Built-in studies, `IndicatorFactory` and study templates; usable without egui |
92//! | `alert-engine` | off | `services::AlertEngine`, an `Alerts` service that evaluates alerts on the bars the host feeds it; needs no egui |
93//! | `detectors` | off | `services::OrderFlowDetectors`, a `Signals` service that finds order-flow events in the trades and depth the host feeds it; needs no egui |
94//! | `strategies` | off | `strategies::Backtest`, which runs a `Strategy` over bars and returns a `StrategyReport`; needs no egui |
95//! | `icons` | via `ui` | Embedded SVG icons and their pixel-aligned loader; enables `widget` |
96//! | `ui` | on | Toolbars, panels, dialogs and `ui_kit`; enables `icons`, `studies`, `drawings`, `csv` and `png-export` |
97//! | `locales` | on | Localized month and weekday names and AM/PM markers through chrono; otherwise English |
98//! | `timezones` | on | IANA zones and daylight-saving rules through chrono-tz; otherwise UTC, system local time and fixed offsets |
99//! | `csv` | on | CSV import/export for bars, strategy reports and chart data; enabled by `ui` |
100//! | `png-export` | on | CPU PNG export and image textures embedded in SVG export; enables `widget`, enabled by `ui` |
101//! | `serde` | off | `Serialize` and `Deserialize` for `theme::Theme` |
102//!
103//! Without default features, enable `csv` to use CSV methods; JSON I/O stays available.
104//! Enable `png-export` for PNG methods and textured SVG output. Without it, SVG
105//! exports text and vector geometry and returns `ExportError::Unsupported` for image
106//! textures; renderer frames from `Chart::export_image` remain available.
107//! With `icons` alone, the chart installs only its embedded icon loader. General SVG
108//! images require `ui` or an image loader installed by the host.
109//!
110//! # Application integration
111//!
112//! Keep a chart across frames so its viewport, studies and drawings persist.
113//! Application UI components return actions for the host to apply. The crate does
114//! not connect to a market data provider on its own.
115//!
116//! Apply [`theme::apply_to_egui`] at startup and after changing the theme. It installs
117//! the bundled font and applies UI colors and spacing. Use
118//! [`localization::set_translator`] for UI text; the host owns translation resources
119//! and additional fonts. Chart date and number labels use the chart's formatters.
120//!
121//! The [`guide`] covers historical and streaming data, custom studies, drawing
122//! persistence, synchronized charts and host strategy and script results.
123//!
124//! # API compatibility
125//!
126//! The 0.3 release line uses egui 0.37. Use a compatible egui version in your host
127//! so types such as `egui::Ui` agree across the dependency graph. Before 1.0,
128//! breaking API changes require a minor release; afterward they require a major
129//! release. An incompatible upgrade of exposed egui types is a breaking change.
130//!
131//! Construct extensible options with their constructors or `Default`, then assign
132//! public fields. Include a fallback arm when matching a `#[non_exhaustive]` enum.
133
134// ─── Crate-level lint configuration ──────────────────────────────────────────
135#![warn(unnameable_types)]
136#![allow(clippy::too_many_arguments)]
137#![allow(clippy::module_inception)]
138#![allow(clippy::needless_range_loop)]
139#![cfg_attr(docsrs, feature(doc_cfg))]
140
141// Engine code names colours as `egui::Color32`; without the widget that path
142// resolves to `ecolor`, the crate egui re-exports them from.
143#[cfg(not(feature = "widget"))]
144extern crate ecolor as egui;
145
146// ─── Core engine modules ─────────────────────────────────────────────────────
147
148#[cfg(feature = "widget")]
149#[cfg_attr(docsrs, doc(cfg(feature = "widget")))]
150pub mod chart;
151#[cfg(feature = "drawings")]
152#[cfg_attr(docsrs, doc(cfg(feature = "drawings")))]
153#[warn(missing_docs)]
154pub mod drawings;
155pub mod model;
156#[cfg(feature = "widget")]
157mod painting;
158pub mod prelude;
159pub mod scales;
160#[warn(missing_docs)]
161pub mod studies;
162
163// ─── Configuration & data ────────────────────────────────────────────────────
164
165#[cfg(feature = "widget")]
166#[cfg_attr(docsrs, doc(cfg(feature = "widget")))]
167pub mod config;
168pub mod data;
169pub mod services;
170#[cfg(feature = "strategies")]
171#[cfg_attr(docsrs, doc(cfg(feature = "strategies")))]
172#[warn(missing_docs)]
173pub mod strategies;
174
175// ─── Theme & design system ───────────────────────────────────────────────────
176
177#[cfg(feature = "icons")]
178#[cfg_attr(docsrs, doc(cfg(feature = "icons")))]
179pub mod icons;
180#[cfg(feature = "widget")]
181#[cfg_attr(docsrs, doc(cfg(feature = "widget")))]
182pub mod styles;
183#[cfg(feature = "widget")]
184#[cfg_attr(docsrs, doc(cfg(feature = "widget")))]
185pub mod theme;
186pub mod tokens;
187
188// ─── Widget layer ────────────────────────────────────────────────────────────
189
190#[cfg(feature = "widget")]
191#[cfg_attr(docsrs, doc(cfg(feature = "widget")))]
192pub mod localization;
193#[cfg(feature = "widget")]
194#[cfg_attr(docsrs, doc(cfg(feature = "widget")))]
195#[warn(missing_docs)]
196pub mod widget;
197
198#[cfg(feature = "widget")]
199#[cfg_attr(docsrs, doc(cfg(feature = "widget")))]
200#[doc = include_str!("../docs/builder-guide.md")]
201#[warn(missing_docs)]
202pub mod guide {}
203
204// ─── Application-level UI (feature-gated) ────────────────────────────────────
205
206#[cfg(feature = "ui")]
207#[cfg_attr(docsrs, doc(cfg(feature = "ui")))]
208pub mod ui_kit;
209
210#[cfg(feature = "ui")]
211#[cfg_attr(docsrs, doc(cfg(feature = "ui")))]
212pub mod ui;
213
214// ─── Convenience re-exports ──────────────────────────────────────────────────
215
216pub use model::ChartType;
217#[cfg(feature = "widget")]
218#[cfg_attr(docsrs, doc(cfg(feature = "widget")))]
219pub use widget::{Chart, ChartBuilder, RightClickTarget};
220
221// Compiles and runs the README's Rust snippets as doctests.
222#[cfg(all(doctest, feature = "ui"))]
223#[doc = include_str!("../README.md")]
224pub struct ReadmeDoctests;
225
226// Compile the documentation pages alongside their shared source guides.
227#[cfg(all(doctest, feature = "ui"))]
228#[doc = include_str!("../docs/site/src/content/docs/chart-types.mdx")]
229pub struct SiteChartTypesDoctests;
230
231#[cfg(all(doctest, feature = "ui"))]
232#[doc = include_str!("../docs/site/src/content/docs/data-model.mdx")]
233pub struct SiteDataModelDoctests;
234
235#[cfg(all(doctest, feature = "ui"))]
236#[doc = include_str!("../docs/site/src/content/docs/data.mdx")]
237pub struct SiteDataDoctests;
238
239#[cfg(all(doctest, feature = "ui"))]
240#[doc = include_str!("../docs/site/src/content/docs/first-chart.mdx")]
241pub struct SiteFirstChartDoctests;
242
243#[cfg(all(doctest, feature = "ui"))]
244#[doc = include_str!("../docs/site/src/content/docs/studies.mdx")]
245pub struct SiteStudiesDoctests;
246
247#[cfg(all(doctest, feature = "ui"))]
248#[doc = include_str!("../docs/site/src/content/docs/theming.mdx")]
249pub struct SiteThemingDoctests;
250
251#[cfg(all(doctest, feature = "ui"))]
252#[doc = include_str!("../docs/site/src/content/docs/migration.mdx")]
253pub struct SiteMigrationDoctests;
254
255#[cfg(all(doctest, feature = "ui"))]
256#[doc = include_str!("../docs/study-data.md")]
257pub struct StudyDataGuideDoctests;
258
259#[cfg(all(doctest, feature = "ui"))]
260#[doc = include_str!("../docs/strategy-report.md")]
261pub struct StrategyReportGuideDoctests;
262
263#[cfg(all(test, feature = "ui"))]
264mod tests {
265 use crate::drawings::DrawingToolType;
266 use crate::studies::IndicatorFactory;
267
268 /// Every number followed, directly or after one or two words, by "drawing tool...",
269 /// "indicator..." or "chart type..." (ignoring surrounding punctuation) in the README, the
270 /// package description, the crate docs and the ROADMAP must match the code.
271 #[test]
272 fn stated_counts_match_code() {
273 let docs = [
274 include_str!("../README.md"),
275 include_str!("../Cargo.toml"),
276 include_str!("lib.rs"),
277 include_str!("../ROADMAP.md"),
278 ];
279 let counts: [(&[&str], usize); 3] = [
280 (&["drawing", "tool"], DrawingToolType::all().len()),
281 (&["indicator"], IndicatorFactory::new().count()),
282 (&["chart", "type"], crate::model::ChartType::all().len()),
283 ];
284 for (noun, count) in counts {
285 let mut stated = 0;
286 for doc in docs {
287 let words: Vec<&str> = doc
288 .split(|c: char| c.is_whitespace() || c == '>')
289 .filter(|w| !w.is_empty())
290 .collect();
291 fn bare(w: &str) -> &str {
292 w.trim_start_matches(['*', '_', '(', '['])
293 }
294 for (i, word) in words.iter().enumerate() {
295 // Markdown emphasis and brackets around a number are not part of it,
296 // but a trailing "+" is, so "130+" still fails.
297 let number =
298 bare(word).trim_end_matches(['*', '_', ',', '.', ':', ';', ')', ']']);
299 if !number.starts_with(|c: char| c.is_ascii_digit())
300 || !number.chars().all(|c| c.is_ascii_digit() || c == '+')
301 {
302 continue;
303 }
304 let names_noun = (i + 1..=i + 3).any(|k| {
305 noun.iter()
306 .enumerate()
307 .all(|(j, n)| words.get(k + j).is_some_and(|w| bare(w).starts_with(n)))
308 });
309 if names_noun {
310 assert_eq!(number, count.to_string(), "stated count of {noun:?}");
311 stated += 1;
312 }
313 }
314 }
315 assert!(stated > 0, "no stated count of {noun:?} found");
316 }
317 }
318}