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;
18use 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 .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}