1use std::fmt;
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
31pub enum ChartType {
32 Bars,
35 #[default]
37 Candles,
38 HollowCandles,
40 VolumeCandles,
42
43 Line,
46 LineWithMarkers,
48 StepLine,
50
51 Area,
54 HlcArea,
56 Baseline,
58
59 HighLow,
62 Range,
64
65 Renko,
68 Kagi,
70 LineBreak,
72 Heikin,
74 PointAndFigure,
76
77 VolumeFootprint,
80 TimePriceOpportunity,
82 SessionVolume,
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
88pub enum ChartTypeCategory {
89 Standard,
91 LineBased,
93 AreaBased,
95 Japanese,
97 RangeBased,
99 Advanced,
101}
102
103impl ChartTypeCategory {
104 pub fn name(&self) -> &'static str {
106 match self {
107 Self::Standard => "Standard",
108 Self::LineBased => "Line",
109 Self::AreaBased => "Area",
110 Self::Japanese => "Japanese",
111 Self::RangeBased => "Range",
112 Self::Advanced => "Advanced",
113 }
114 }
115
116 pub fn all() -> &'static [ChartTypeCategory] {
118 &[
119 Self::Standard,
120 Self::LineBased,
121 Self::AreaBased,
122 Self::Japanese,
123 Self::RangeBased,
124 Self::Advanced,
125 ]
126 }
127}
128
129impl ChartType {
130 pub fn all() -> &'static [ChartType] {
132 &[
133 ChartType::Bars,
134 ChartType::Candles,
135 ChartType::HollowCandles,
136 ChartType::VolumeCandles,
137 ChartType::Line,
138 ChartType::LineWithMarkers,
139 ChartType::StepLine,
140 ChartType::Area,
141 ChartType::HlcArea,
142 ChartType::Baseline,
143 ChartType::HighLow,
144 ChartType::VolumeFootprint,
145 ChartType::TimePriceOpportunity,
146 ChartType::SessionVolume,
147 ChartType::LineBreak,
148 ChartType::Kagi,
149 ChartType::Range,
150 ChartType::PointAndFigure,
151 ChartType::Renko,
152 ChartType::Heikin,
153 ]
154 }
155
156 pub fn name(&self) -> &'static str {
158 match self {
159 ChartType::Bars => "Bars",
160 ChartType::Candles => "Candles",
161 ChartType::HollowCandles => "Hollow candles",
162 ChartType::VolumeCandles => "Volume candles",
163 ChartType::Line => "Line",
164 ChartType::LineWithMarkers => "Line with markers",
165 ChartType::StepLine => "Step line",
166 ChartType::Area => "Area",
167 ChartType::HlcArea => "HLC area",
168 ChartType::Baseline => "Baseline",
169 ChartType::HighLow => "High-low",
170 ChartType::VolumeFootprint => "Volume footprint",
171 ChartType::TimePriceOpportunity => "Time Price Opportunity",
172 ChartType::SessionVolume => "Session volume",
173 ChartType::LineBreak => "Line break",
174 ChartType::Kagi => "Kagi",
175 ChartType::Range => "Range",
176 ChartType::PointAndFigure => "Point & Figure",
177 ChartType::Renko => "Renko",
178 ChartType::Heikin => "Heikin Ashi",
179 }
180 }
181
182 pub fn as_str(&self) -> &'static str {
184 self.name()
185 }
186
187 pub fn description(&self) -> &'static str {
189 match self {
190 ChartType::Bars => "OHLC bars with tick marks",
191 ChartType::Candles => "Japanese candlesticks",
192 ChartType::HollowCandles => "Hollow when close > open",
193 ChartType::VolumeCandles => "Width based on volume",
194 ChartType::Line => "Close price line",
195 ChartType::LineWithMarkers => "Line with data points",
196 ChartType::StepLine => "Stepped line chart",
197 ChartType::Area => "Filled area chart",
198 ChartType::HlcArea => "High-Low-Close area",
199 ChartType::Baseline => "Baseline comparison",
200 ChartType::HighLow => "High-Low range",
201 ChartType::VolumeFootprint => "Order flow analysis",
202 ChartType::TimePriceOpportunity => "TPO / Market Profile",
203 ChartType::SessionVolume => "Volume by session",
204 ChartType::LineBreak => "Three line break",
205 ChartType::Kagi => "Kagi chart",
206 ChartType::Range => "Range bars",
207 ChartType::PointAndFigure => "X and O chart",
208 ChartType::Renko => "Renko bricks",
209 ChartType::Heikin => "Heikin-Ashi candles",
210 }
211 }
212
213 pub fn desc(&self) -> &'static str {
215 self.description()
216 }
217
218 pub fn category(&self) -> ChartTypeCategory {
220 match self {
221 ChartType::Bars
222 | ChartType::Candles
223 | ChartType::HollowCandles
224 | ChartType::VolumeCandles => ChartTypeCategory::Standard,
225
226 ChartType::Line | ChartType::LineWithMarkers | ChartType::StepLine => {
227 ChartTypeCategory::LineBased
228 }
229
230 ChartType::Area | ChartType::HlcArea | ChartType::Baseline => {
231 ChartTypeCategory::AreaBased
232 }
233
234 ChartType::Renko
235 | ChartType::Kagi
236 | ChartType::LineBreak
237 | ChartType::Heikin
238 | ChartType::PointAndFigure => ChartTypeCategory::Japanese,
239
240 ChartType::HighLow | ChartType::Range => ChartTypeCategory::RangeBased,
241
242 ChartType::VolumeFootprint
243 | ChartType::TimePriceOpportunity
244 | ChartType::SessionVolume => ChartTypeCategory::Advanced,
245 }
246 }
247
248 pub fn in_category(category: ChartTypeCategory) -> Vec<ChartType> {
250 Self::all()
251 .iter()
252 .copied()
253 .filter(|ct| ct.category() == category)
254 .collect()
255 }
256
257 pub fn uses_ohlc(&self) -> bool {
259 match self {
260 ChartType::Bars
261 | ChartType::Candles
262 | ChartType::HollowCandles
263 | ChartType::VolumeCandles
264 | ChartType::HlcArea
265 | ChartType::HighLow
266 | ChartType::Renko
267 | ChartType::Kagi
268 | ChartType::LineBreak
269 | ChartType::Heikin
270 | ChartType::Range
271 | ChartType::PointAndFigure
272 | ChartType::VolumeFootprint
273 | ChartType::TimePriceOpportunity
274 | ChartType::SessionVolume => true,
275
276 ChartType::Line
277 | ChartType::LineWithMarkers
278 | ChartType::StepLine
279 | ChartType::Area
280 | ChartType::Baseline => false,
281 }
282 }
283
284 pub fn supports_volume(&self) -> bool {
286 match self {
287 ChartType::Renko
289 | ChartType::Kagi
290 | ChartType::PointAndFigure
291 | ChartType::Range
292 | ChartType::LineBreak => false,
293
294 ChartType::VolumeFootprint | ChartType::SessionVolume => false,
296
297 _ => true,
298 }
299 }
300
301 pub fn requires_parameters(&self) -> bool {
303 matches!(
304 self,
305 ChartType::Renko
306 | ChartType::Kagi
307 | ChartType::Range
308 | ChartType::PointAndFigure
309 | ChartType::LineBreak
310 | ChartType::Baseline
311 )
312 }
313
314 pub fn is_time_independent(&self) -> bool {
316 matches!(
317 self,
318 ChartType::Renko
319 | ChartType::Kagi
320 | ChartType::Range
321 | ChartType::PointAndFigure
322 | ChartType::LineBreak
323 )
324 }
325
326 pub fn transforms_data(&self) -> bool {
328 matches!(
329 self,
330 ChartType::Renko
331 | ChartType::Kagi
332 | ChartType::Range
333 | ChartType::PointAndFigure
334 | ChartType::LineBreak
335 | ChartType::Heikin
336 )
337 }
338}
339
340impl fmt::Display for ChartType {
341 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
342 write!(f, "{}", self.name())
343 }
344}
345
346use crate::model::enums::ChartStyle;
349
350impl From<ChartStyle> for ChartType {
352 fn from(style: ChartStyle) -> Self {
353 match style {
354 ChartStyle::Bar => ChartType::Bars,
355 ChartStyle::Candle => ChartType::Candles,
356 ChartStyle::Line => ChartType::Line,
357 ChartStyle::Area => ChartType::Area,
358 ChartStyle::Renko => ChartType::Renko,
359 ChartStyle::Kagi => ChartType::Kagi,
360 ChartStyle::PnF => ChartType::PointAndFigure,
361 ChartStyle::LineBreak => ChartType::LineBreak,
362 ChartStyle::HeikinAshi => ChartType::Heikin,
363 ChartStyle::HollowCandle => ChartType::HollowCandles,
364 ChartStyle::Baseline => ChartType::Baseline,
365 ChartStyle::HighLow => ChartType::HighLow,
366 ChartStyle::Column => ChartType::Bars, ChartStyle::LineWithMarkers => ChartType::LineWithMarkers,
368 ChartStyle::Stepline => ChartType::StepLine,
369 ChartStyle::HLCArea => ChartType::HlcArea,
370 ChartStyle::VolCandle => ChartType::VolumeCandles,
371 ChartStyle::HLCBars => ChartType::HighLow, }
373 }
374}
375
376impl ChartType {
377 pub fn to_chart_style(&self) -> Option<ChartStyle> {
382 match self {
383 ChartType::Bars => Some(ChartStyle::Bar),
384 ChartType::Candles => Some(ChartStyle::Candle),
385 ChartType::HollowCandles => Some(ChartStyle::HollowCandle),
386 ChartType::VolumeCandles => Some(ChartStyle::VolCandle),
387 ChartType::Line => Some(ChartStyle::Line),
388 ChartType::LineWithMarkers => Some(ChartStyle::LineWithMarkers),
389 ChartType::StepLine => Some(ChartStyle::Stepline),
390 ChartType::Area => Some(ChartStyle::Area),
391 ChartType::HlcArea => Some(ChartStyle::HLCArea),
392 ChartType::Baseline => Some(ChartStyle::Baseline),
393 ChartType::HighLow => Some(ChartStyle::HighLow),
394 ChartType::Renko => Some(ChartStyle::Renko),
395 ChartType::Kagi => Some(ChartStyle::Kagi),
396 ChartType::LineBreak => Some(ChartStyle::LineBreak),
397 ChartType::Heikin => Some(ChartStyle::HeikinAshi),
398 ChartType::PointAndFigure => Some(ChartStyle::PnF),
399 ChartType::Range
401 | ChartType::VolumeFootprint
402 | ChartType::TimePriceOpportunity
403 | ChartType::SessionVolume => None,
404 }
405 }
406}
407
408#[derive(Debug, Clone)]
410pub struct ChartTypeParams {
411 pub renko_brick_size: f64,
413 pub kagi_reversal: f64,
415 pub range_size: f64,
417 pub pnf_box_size: f64,
419 pub pnf_reversal: u32,
421 pub line_break_count: usize,
423 pub baseline_price: f64,
425}
426
427impl Default for ChartTypeParams {
428 fn default() -> Self {
429 Self {
430 renko_brick_size: 1.0,
431 kagi_reversal: 4.0,
432 range_size: 10.0,
433 pnf_box_size: 1.0,
434 pnf_reversal: 3,
435 line_break_count: 3,
436 baseline_price: 0.0, }
438 }
439}
440
441impl ChartTypeParams {
442 pub fn with_atr_renko(atr: f64, multiplier: f64) -> Self {
444 Self {
445 renko_brick_size: atr * multiplier,
446 ..Default::default()
447 }
448 }
449}
450
451#[cfg(test)]
452mod tests {
453 use super::*;
454
455 #[test]
456 fn test_all_chart_types_count() {
457 assert_eq!(ChartType::all().len(), 20);
458 }
459
460 #[test]
461 fn test_default_is_candles() {
462 assert_eq!(ChartType::default(), ChartType::Candles);
463 }
464
465 #[test]
466 fn test_category_grouping() {
467 let standard = ChartType::in_category(ChartTypeCategory::Standard);
468 assert!(standard.contains(&ChartType::Candles));
469 assert!(standard.contains(&ChartType::Bars));
470 assert!(!standard.contains(&ChartType::Line));
471 }
472
473 #[test]
474 fn test_ohlc_vs_single_value() {
475 assert!(ChartType::Candles.uses_ohlc());
476 assert!(!ChartType::Line.uses_ohlc());
477 }
478
479 #[test]
480 fn test_volume_support() {
481 assert!(ChartType::Candles.supports_volume());
482 assert!(!ChartType::Renko.supports_volume());
483 }
484
485 #[test]
486 fn test_time_independence() {
487 assert!(ChartType::Renko.is_time_independent());
488 assert!(!ChartType::Candles.is_time_independent());
489 }
490
491 #[test]
492 fn test_display_trait() {
493 assert_eq!(format!("{}", ChartType::Candles), "Candles");
494 assert_eq!(format!("{}", ChartType::Heikin), "Heikin Ashi");
495 }
496}