qust_io/output/
plot.rs

1#![allow(unused_imports)]
2use crate::{output::excel::Value, prelude::StatsString};
3
4use super::profile::{Stats, PnlModify};
5use qust::prelude::*;
6use plotters::{
7    coord::{
8        ranged1d::{DefaultFormatting, KeyPointHint, AsRangedCoord, ValueFormatter},
9        types::RangedCoordf32,
10        Shift,
11        CoordTranslate, ReverseCoordTranslate,
12    },
13    style::{RGBColor, RelativeSize},
14    evcxr,
15    prelude::*, element::{PointCollection, Drawable},
16};
17use chrono::Datelike;
18// use plotters_backend::{DrawingErrorKind, text_anchor::{HPos, Pos, VPos}, BackendColor};
19use std::{ops::Range, borrow::Borrow};
20
21const color: RGBColor = WHITE;
22const color_bg: RGBColor = RGBColor(40, 40, 40);
23
24mod my_axis {
25    use chrono::Timelike;
26    use plotters::coord::ranged1d::NoDefaultFormatting;
27
28    use super::*;
29
30    #[derive(Debug, Clone)]
31    pub struct MyAxis<'a, T>(pub &'a [T]);
32    
33    impl<'a, T> Ranged for MyAxis<'a, T>
34    where
35        T: PartialOrd + Clone,
36        Self: GetKeyPoints<ValueType = T>,
37    {
38        type FormatOption = NoDefaultFormatting;
39        type ValueType = T;
40    
41        fn range(&self) -> Range<Self::ValueType> {
42            self.0.first().unwrap().clone()..self.0.last().unwrap().clone()
43        }
44    
45        fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
46            let g = &self.0;
47            let a = (g.iter().position(|v| v >= value).unwrap_or_default() as f64) / (g.len() as f64);
48            limit.0 + ((a * f64::from(limit.1 - limit.0)) as i32)
49        }
50        fn key_points<Hint: KeyPointHint>(&self, _hint: Hint) -> Vec<Self::ValueType> {
51            self.get_key_points()
52        }
53    }
54
55    trait GetKeyPoints {
56        type ValueType;
57        fn get_key_points(&self) -> Vec<Self::ValueType>;
58    }
59
60    impl GetKeyPoints for MyAxis<'_, da> {
61        type ValueType = da;
62        fn get_key_points(&self) -> Vec<Self::ValueType> {
63            let years_num = self.0.map(|x| x.year()).unique().len();
64           let mut res = match years_num {
65                1 => self.0.find_first_ele(|x| (x.year(), x.month())),
66                _ => self.0.find_first_ele(|x| x.year()),
67            };
68            res.push(*self.0.last().unwrap());
69            res
70        }
71    }
72
73    impl GetKeyPoints for MyAxis<'_, dt> {
74        type ValueType = dt;
75        fn get_key_points(&self) -> Vec<Self::ValueType> {
76            let dates_num = self.0.map(|x| x.date()).unique().len();
77            match dates_num {
78                1 => self.0.find_first_ele(|x| x.hour()),
79                _ => self.0.find_first_ele(|x| x.date()),
80            }
81        }
82    }
83    
84    impl ValueFormatter<dt> for MyAxis<'_, dt> {
85        fn format(value: &dt) -> String {
86            value.to_string()
87        }
88    
89        fn format_ext(&self, value: &dt) -> String {
90            let n = (*self.0.last().unwrap() - self.0[0]).num_days();
91            if self.0.len() > 10 && n >= 1 {
92                value.date().to_string()
93            } else {
94                value.format("%H:%M:%S").to_string()
95            }
96        }
97    }
98
99    impl ValueFormatter<da> for MyAxis<'_, da> {
100        fn format(value: &da) -> String {
101            value.to_string()
102        }
103        fn format_ext(&self, value: &da) -> String {
104            if value.year() == self.0.last().unwrap().year() {
105                if value == self.0.first().unwrap() {
106                    value.format("%Y").to_string()
107                } else {
108                    value.format("%m%d").to_string()
109                }
110            } else {
111                value.format("%Y").to_string()
112            }
113        }
114    }
115
116    impl<'a, T> DiscreteRanged for MyAxis<'a, T>
117    where
118        T: PartialOrd + Clone,
119        MyAxis<'a, T>: Ranged<ValueType = T>,
120    {
121        fn size(&self) -> usize {
122            self.0.len()
123        }
124    
125        fn index_of(&self, value: &Self::ValueType) -> Option<usize> {
126            self.0.iter().position(|x| value >= x)
127        }
128    
129        fn from_index(&self, index: usize) -> Option<Self::ValueType> {
130            self.0.get(index).cloned()
131        }
132    }
133
134    #[derive(Clone)]
135    pub struct AxisNumber<'a, T>(pub &'a [T]);
136
137    impl<'a> Ranged for AxisNumber<'a, f32>
138    {
139        type FormatOption = NoDefaultFormatting;
140        type ValueType = f32;
141
142        fn range(&self) -> Range<Self::ValueType> {
143            self.0.agg(RollFunc::Min) .. self.0.agg(RollFunc::Max)
144        }
145
146        fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
147            let r = self.range();
148            if r.start == r.end {
149                return (limit.1 - limit.0) / 2;
150            }
151            let logic_length = (*value - r.start) / (r.end - r.start);
152
153            let actual_length = limit.1 - limit.0;
154
155            if actual_length == 0 {
156                return limit.1;
157            }
158
159            if actual_length > 0 {
160                limit.0 + (actual_length as f64 * logic_length as f64 + 1e-3).floor() as i32
161            } else {
162                limit.0 + (actual_length as f64 * logic_length as f64 - 1e-3).ceil() as i32
163            }
164        }
165
166        fn key_points<Hint: KeyPointHint>(&self, _hint: Hint) -> Vec<Self::ValueType> {
167            let (s, e) = self.range().pip(|x| (x.start, x.end));
168            let step = ((e - s) / 5.).max(1.);
169            (s as usize .. e as usize)
170                .step_by(step as usize)
171                .map(|x| x as f32)
172                .collect_vec()
173        }
174    }
175
176    impl ValueFormatter<f32> for AxisNumber<'_, f32> {
177        fn format_ext(&self, value: &f32) -> String {
178            let v = self.0.agg(RollFunc::Max).abs().max(self.0.agg(RollFunc::Min).abs());
179            let (div_num, suffix) = match (v * 10_000.) as usize {
180                0..=10                      => (0.0001,     "bp".to_string()),
181                11..=10_000_000             => (1.,         "".to_string()),
182                10_000_001..=10_000_000_000 => (1000.,      "k".to_string()),
183                _                           => (1_000_000., "m".to_string()),
184            };
185            format!("{:.0}{}", value / div_num, suffix)
186        }
187    }
188
189    pub fn plot_text<T: std::fmt::Display>(area: &DrawingArea<SVGBackend, Shift>, data: &T, posi: (i32, i32)) {
190        let mut multi_text: MultiLineText<(i32, i32), String> = MultiLineText::new(
191            posi,
192            ("Consolas", RelativeSize::Smaller(0.05), &RGBColor(117, 163, 209)).into_text_style(area),
193        );
194        data.to_string()
195            .lines()
196            .for_each(|x| multi_text.push_line(x.to_string()));
197        area.draw(&multi_text).unwrap();
198    }
199}
200
201
202use my_axis::{ MyAxis, AxisNumber, plot_text };
203pub struct PlotWithText<T, N> {
204    x: T,
205    y: N,
206    caption: Option<String>,
207    text: Option<String>,
208}
209
210trait GetAxis<X, Y> {
211    fn get_x_axis(&self) -> MyAxis<X>;
212    fn get_y_axis(&self) -> AxisNumber<Y>;
213}
214
215impl<T, N, X, Y> GetAxis<X, Y> for PlotWithText<T, N>
216where
217    T: AsRef<[X]>,
218    N: AsRef<[Y]>,
219{
220    fn get_x_axis(&self) -> MyAxis<X> {
221        MyAxis(self.x.as_ref())
222    }
223
224    fn get_y_axis(&self) -> AxisNumber<Y> {
225        AxisNumber(self.y.as_ref())
226    }
227}
228
229pub trait BuildChart<X, Y> {
230    fn build_chart(&self, area: &DrawingArea<SVGBackend, Shift>);
231}
232
233impl<T, N, X, Y> BuildChart<X, Y> for PlotWithText<T, N>
234where
235    for<'a> MyAxis<'a, X>: Ranged<ValueType = X> + ValueFormatter<X>,
236    for<'a> AxisNumber<'a, Y>: Ranged<ValueType = Y> + ValueFormatter<Y>,
237    X: Clone + 'static,
238    Y: Clone + 'static,
239    Self: GetAxis<X, Y>,
240{
241    fn build_chart(&self, area: &DrawingArea<SVGBackend, Shift>) {
242        let mut chart = ChartBuilder::on(area)
243            // .caption(&self.caption.clone().unwrap_or_default(), (FontFamily::Name(""), 12, "bold").into_font().with_color(color))
244            // .caption(&self.caption.clone().unwrap_or_default(), ("Consolas", RelativeSize::Smaller(0.05), &color).into_text_style(area))
245            .margin(5)
246            .x_label_area_size(30)
247            .y_label_area_size(70)
248            .build_cartesian_2d(self.get_x_axis(), self.get_y_axis())
249            .unwrap();
250        chart
251            .draw_series(LineSeries::new(
252                self.get_x_axis().0.iter().zip(self.get_y_axis().0.iter()).map(|(x, y)| (x.clone(), y.clone())),
253                color,
254            ))
255            .unwrap();
256        chart
257            .configure_mesh()
258            .x_label_style(&color)
259            .y_label_style(&color)
260            .disable_x_mesh()
261            .disable_y_mesh()
262            .axis_style(color)
263            .draw()
264            .unwrap();
265        if let Some(s) = &self.caption {
266            plot_text(area, s, (area.dim_in_pixel().0 as i32 / 2, 0));
267        }
268        if let Some(s) = &self.text {
269            plot_text(area, s, (100, 10));
270        }
271    }
272}
273
274
275impl<T, N, K, J> From<(T, N, K, J)> for PlotWithText<T, N>
276where
277    Option<String>: From<K>,
278    Option<String>: From<J>,
279{
280    fn from(value: (T, N, K, J)) -> Self {
281        PlotWithText { 
282            x: value.0,  
283            y: value.1, 
284            caption: value.2.into(), 
285            text: value.3.into() 
286        }
287    }
288}
289impl<T, N, K> From<(T, N, K)> for PlotWithText<T, N>
290where
291    Option<String>: From<K>,
292{
293    fn from(value: (T, N, K)) -> Self {
294        (value.0, value.1, value.2, None).into()
295    }
296}
297
298impl<T, N> From<(T, N)> for PlotWithText<T, N>
299{
300    fn from(value: (T, N)) -> Self {
301        (value.0, value.1, None, None).into()
302    }
303}
304
305pub trait BuildCharts<X, Y> {
306    fn build_charts(&self, area: &DrawingArea<SVGBackend, Shift>, n: (u32, u32));
307}
308
309impl<T: BuildChart<X, Y>, X, Y> BuildCharts<X, Y> for [T] {
310    fn build_charts(&self, area: &DrawingArea<SVGBackend, Shift>, n: (u32, u32)) {
311        let sub_areas: Vec<DrawingArea<SVGBackend, Shift>> =
312            area.split_evenly((n.0 as usize, n.1 as usize));
313        izip!(self.iter(), sub_areas.iter()).for_each(|(x, area)| {
314            x.build_chart(area);
315        });
316    }
317}
318fn split_size(x: u32, y: u32) -> (u32, u32) {
319    (x / y + ({ if x % y == 0 { 0 } else { 1 }}), y)
320}
321fn layout_size(x: u32, y: u32) -> (u32, u32) {
322    let single_col_size = match y {
323        1 => 600,
324        2 => 450,
325        3..=5 => 400,
326        _ => 280,
327    };
328    let sum_row_len = (((single_col_size as f32) / 1.8f32) as u32) * x;
329    let sum_col_len = single_col_size * y;
330    (sum_col_len, sum_row_len)
331}
332
333pub trait Plot<T, X, Y> {
334    fn plot(&self) -> evcxr::SVGWrapper;
335}
336
337impl<T: BuildChart<X, Y>, X, Y> Plot<i32, X, Y> for T {
338    fn plot(&self) -> evcxr::SVGWrapper {
339        evcxr_figure((600, 300), |root| {
340            root.fill(&color_bg)?;
341            self.build_chart(&root);
342            Ok(())
343        })
344    }
345}
346
347impl Plot<i32, da, f32> for PnlRes<da> {
348    fn plot(&self) -> evcxr::SVGWrapper {
349        let p: PlotWithText<_, _> = (&self.0, self.1[0].cumsum(), None, self.stats().to_string()).into();
350        p.plot()
351    }
352}
353
354impl Plot<i32, dt, f32> for PnlRes<dt> {
355    fn plot(&self) -> evcxr::SVGWrapper {
356        let p: PlotWithText<_, _> = (&self.0, self.1[0].cumsum(), None, self.da().stats().to_string()).into();
357        p.plot()
358    }
359}
360
361impl<T> Plot<usize, da, f32> for T
362where
363    for<'a> PnlRes<da>: From<&'a T>,
364    T: 'static,
365{
366    fn plot(&self) -> evcxr::SVGWrapper {
367        <&T as Into<PnlRes<da>>>::into(self).plot()
368    }
369}
370
371pub trait Aplot<X, Y> {
372    fn aplot(&self, col: usize) -> evcxr::SVGWrapper;
373}
374
375impl<T, X, Y> Aplot<X, Y> for [T]
376where
377    [T]: BuildCharts<X, Y>,
378{
379    fn aplot(&self, cols: usize) -> evcxr::SVGWrapper {
380        let grid_size = split_size(self.len() as u32, cols as u32);
381        let sum_size = layout_size(grid_size.0, grid_size.1);
382        evcxr_figure(sum_size, |root| {
383            root.fill(&color_bg)?;
384            self.build_charts(&root, grid_size);
385            Ok(())
386        })
387    }
388}
389
390impl<T, X> Aplot<X, f32> for Vec<InfoPnlRes<T, X>>
391where
392    T: std::fmt::Display,
393    for<'a> PlotWithText<&'a [X], Vec<f32>>: BuildChart<X, f32> + GetAxis<X, f32>,
394    X: Clone + PartialOrd + 'static,
395    for<'a> MyAxis<'a, X>: Ranged<ValueType = X> + ValueFormatter<X>,
396{
397    fn aplot(&self, col: usize) -> evcxr::SVGWrapper {
398        self.iter()
399            .map(|x| {
400                let g: PlotWithText<_, _> = (&x.1.0, x.1.1[0].cumsum(), x.0.to_string(), None).into();
401                g
402            })
403            .collect_vec()
404            .aplot(col)
405    }
406}
407
408impl Aplot<da, f32> for [PnlRes<da>] {
409    fn aplot(&self, col: usize) -> evcxr::SVGWrapper {
410        self.iter()
411            .map(|x| InfoPnlRes(estring, x.clone()))
412            .collect_vec()
413            .aplot(col)
414    }
415}
416
417type InfoOutput = (Option<String>, Option<String>);
418pub trait PnlWithInfo {
419    type Input;
420    fn with_info(&self, f: impl Fn(&Self::Input) -> InfoOutput) -> Vec<PlotWithText<&Vec<da>, v32>>;
421    fn with_stats(&self) -> Vec<PlotWithText<&Vec<da>, v32>>
422    where
423        Self::Input: Stats,
424    {
425        self.with_info(|x| (None, x.stats().to_string().into()))
426    }
427}
428
429impl<T> PnlWithInfo for [InfoPnlRes<T, da>] {
430    type Input = InfoPnlRes<T, da>;
431    fn with_info(&self, f: impl Fn(&Self::Input) -> (Option<String>, Option<String>)) -> Vec<PlotWithText<&Vec<da>, v32>>  {
432        self.iter()
433            .map(|x| {
434                let (c1, c2) = f(x);
435                PlotWithText {
436                    x: &x.1.0,
437                    y: x.1.1[0].cumsum(),
438                    caption: c1,
439                    text: c2,
440                }
441            })
442            .collect_vec()
443    }
444}
445impl PnlWithInfo for [PnlRes<da>] {
446    type Input = PnlRes<da>;
447    fn with_info(&self, f: impl Fn(&Self::Input) -> (Option<String>, Option<String>)) -> Vec<PlotWithText<&Vec<da>, v32>>  {
448        self.iter()
449            .map(|x| {
450                let (c1, c2) = f(x);
451                PlotWithText {
452                    x: &x.0,
453                    y: x.1[0].cumsum(),
454                    caption: c1,
455                    text: c2,
456                }
457            })
458            .collect_vec()
459    }
460}
461
462type Ipr<T, N> = Vec<InfoPnlRes<T, N>>;
463lazy_static! {
464    pub static ref split_time: Vec<ForCompare<dt>> = vec![
465        Between((20150101).to_da().to_dt()..(20170101).to_da().to_dt()),
466        Between((20170101).to_da().to_dt()..(20220101).to_da().to_dt()),
467        Between((20220101).to_da().to_dt()..(20231010).to_da().to_dt()),
468        2023.to_year().after(),
469        Between((20150101).to_da().to_dt()..(20230531).to_da().to_dt()),
470    ];
471    pub static ref y2015: ForCompare<dt> = 2015.to_year().after();
472    pub static ref y2018: ForCompare<dt> = 2018.to_year().after();
473    pub static ref y2020: ForCompare<dt> = 2020.to_year().after();
474    pub static ref y2021: ForCompare<dt> = 2021.to_year().after();
475    pub static ref y2022: ForCompare<dt> = 2022.to_year().after();
476    pub static ref y2023: ForCompare<dt> = 2023.to_year().after();
477    pub static ref y2024: ForCompare<dt> = 2024.to_year().after();
478    pub static ref y_da_begin: ForCompare<dt> = 20210601.to_da().after();
479    pub static ref y2021_split: Vec<ForCompare<dt>> = vec![2021.to_year().before(), 2021.to_year().after()];
480    pub static ref split_ticker: fn(&Stra) -> Ticker = |x: &Stra| -> Ticker { x.ident.ticker };
481    pub static ref split_stra: fn(&Stra) -> String = |x: &Stra| -> String { x.name.frame().to_string() };
482    pub static ref split_inter: fn(&Stra) -> String = |x: &Stra| -> String { x.ident.inter.debug_string() };
483    pub static ref pip_sum_pnl: fn(Vec<PnlRes<da>>) -> Vec<PnlRes<da>> = |x: Vec<PnlRes<da>>| {
484        let mut x = x;
485        let x_sum = x.sum();
486        x.push(x_sum);
487        x
488    };
489    pub static ref pip_sum_info: fn(Ipr<Ticker, da>) -> Ipr<Ticker, da> = |x: Ipr<Ticker, da>| {
490        let mut x = x;
491        let x_sum = InfoPnlRes(aler, x.sum());
492        x.push(x_sum);
493        x
494    };
495    pub static ref info_ticker_with_stats: fn(&InfoPnlRes<Stra, da>) -> InfoOutput =
496        |x: &InfoPnlRes<Stra, da>| (x.0.ident.ticker.debug_string().into(), x.1.stats().to_string().into());
497    pub static ref info_stra_name_stats: fn(&InfoPnlRes<Stra, da>) -> InfoOutput = 
498        |x: &InfoPnlRes<Stra, da>| (x.0.stats_string().into(), x.1.stats().to_string().into());
499}
500
501
502pub trait ShortPlot<'a>: AsRef<DiStral<'a>> {
503    fn short_calc1(&self, x1: CommSlip, x2: f32, x3: usize) -> PnlRes<da> {
504        self.as_ref()
505            .calc(Aee(x1.tuple()))
506            .pnl_modify(150, x2)
507            .da()
508            .sum()
509            .get_part(x3.to_year().after())
510    }
511    fn short_calc2(&self, x3: usize) -> PnlRes<da> {
512        self.short_calc1(cs1.clone(), 18_000_000., x3)
513    }
514    fn short_calc3(&self, n: usize) -> PnlRes<da> {
515        self.as_ref()
516            .calc(cs1)
517            .sum()
518            .get_part(n.to_year().after())
519    }
520    fn short_calc4(&self) -> PnlRes<da> {
521        self.short_calc1(cs2.clone(), 18_000_000., 2023)
522    }
523    fn short_calc5(&self) -> PnlRes<da> {
524        self.as_ref().calc(cs2).sum().get_part(y2023.clone())
525    }
526}
527impl<'a, T: AsRef<DiStral<'a>>> ShortPlot<'a> for T {}
528
529pub trait ShortDaPlot<T, N> {
530    fn short_da_plot(&self) -> evcxr::SVGWrapper;
531}
532
533impl<T, N, K, J> ShortDaPlot<(N, J), u32> for [T]
534where
535    Self: PnlSumInnerDay<N, Output = Vec<K>>,
536    [K]: PnlSum<J, Output = PnlRes<da>>,
537{
538    fn short_da_plot(&self) -> evcxr::SVGWrapper {
539        self.da().sum().plot()
540    }
541}
542
543impl<T, N> ShortDaPlot<N, u64> for [T]
544where
545    Self: PnlSum<N, Output = PnlRes<da>>,
546{
547    fn short_da_plot(&self) -> evcxr::SVGWrapper {
548        self.sum().plot()
549    }
550}