| 1 | use chrono::offset::{Local, TimeZone}; |
| 2 | use chrono::{Date, Duration}; |
| 3 | use plotters::prelude::*; |
| 4 | fn parse_time(t: &str) -> Date<Local> { |
| 5 | Local |
| 6 | .datetime_from_str(&format!("{} 0:0" , t), "%Y-%m-%d %H:%M" ) |
| 7 | .unwrap() |
| 8 | .date() |
| 9 | } |
| 10 | const OUT_FILE_NAME: &'static str = "plotters-doc-data/stock.png" ; |
| 11 | fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 12 | let data = get_data(); |
| 13 | let root = BitMapBackend::new(OUT_FILE_NAME, (1024, 768)).into_drawing_area(); |
| 14 | root.fill(&WHITE)?; |
| 15 | |
| 16 | let (to_date, from_date) = ( |
| 17 | parse_time(&data[0].0) + Duration::days(1), |
| 18 | parse_time(&data[29].0) - Duration::days(1), |
| 19 | ); |
| 20 | |
| 21 | let mut chart = ChartBuilder::on(&root) |
| 22 | .x_label_area_size(40) |
| 23 | .y_label_area_size(40) |
| 24 | .caption("MSFT Stock Price" , ("sans-serif" , 50.0).into_font()) |
| 25 | .build_cartesian_2d(from_date..to_date, 110f32..135f32)?; |
| 26 | |
| 27 | chart.configure_mesh().light_line_style(&WHITE).draw()?; |
| 28 | |
| 29 | chart.draw_series( |
| 30 | data.iter().map(|x| { |
| 31 | CandleStick::new(parse_time(x.0), x.1, x.2, x.3, x.4, GREEN.filled(), RED, 15) |
| 32 | }), |
| 33 | )?; |
| 34 | |
| 35 | // To avoid the IO failure being ignored silently, we manually call the present function |
| 36 | root.present().expect("Unable to write result to file, please make sure 'plotters-doc-data' dir exists under current dir" ); |
| 37 | println!("Result has been saved to {}" , OUT_FILE_NAME); |
| 38 | |
| 39 | Ok(()) |
| 40 | } |
| 41 | |
| 42 | fn get_data() -> Vec<(&'static str, f32, f32, f32, f32)> { |
| 43 | return vec![ |
| 44 | ("2019-04-25" , 130.0600, 131.3700, 128.8300, 129.1500), |
| 45 | ("2019-04-24" , 125.7900, 125.8500, 124.5200, 125.0100), |
| 46 | ("2019-04-23" , 124.1000, 125.5800, 123.8300, 125.4400), |
| 47 | ("2019-04-22" , 122.6200, 124.0000, 122.5700, 123.7600), |
| 48 | ("2019-04-18" , 122.1900, 123.5200, 121.3018, 123.3700), |
| 49 | ("2019-04-17" , 121.2400, 121.8500, 120.5400, 121.7700), |
| 50 | ("2019-04-16" , 121.6400, 121.6500, 120.1000, 120.7700), |
| 51 | ("2019-04-15" , 120.9400, 121.5800, 120.5700, 121.0500), |
| 52 | ("2019-04-12" , 120.6400, 120.9800, 120.3700, 120.9500), |
| 53 | ("2019-04-11" , 120.5400, 120.8500, 119.9200, 120.3300), |
| 54 | ("2019-04-10" , 119.7600, 120.3500, 119.5400, 120.1900), |
| 55 | ("2019-04-09" , 118.6300, 119.5400, 118.5800, 119.2800), |
| 56 | ("2019-04-08" , 119.8100, 120.0200, 118.6400, 119.9300), |
| 57 | ("2019-04-05" , 119.3900, 120.2300, 119.3700, 119.8900), |
| 58 | ("2019-04-04" , 120.1000, 120.2300, 118.3800, 119.3600), |
| 59 | ("2019-04-03" , 119.8600, 120.4300, 119.1500, 119.9700), |
| 60 | ("2019-04-02" , 119.0600, 119.4800, 118.5200, 119.1900), |
| 61 | ("2019-04-01" , 118.9500, 119.1085, 118.1000, 119.0200), |
| 62 | ("2019-03-29" , 118.0700, 118.3200, 116.9600, 117.9400), |
| 63 | ("2019-03-28" , 117.4400, 117.5800, 116.1300, 116.9300), |
| 64 | ("2019-03-27" , 117.8750, 118.2100, 115.5215, 116.7700), |
| 65 | ("2019-03-26" , 118.6200, 118.7050, 116.8500, 117.9100), |
| 66 | ("2019-03-25" , 116.5600, 118.0100, 116.3224, 117.6600), |
| 67 | ("2019-03-22" , 119.5000, 119.5900, 117.0400, 117.0500), |
| 68 | ("2019-03-21" , 117.1350, 120.8200, 117.0900, 120.2200), |
| 69 | ("2019-03-20" , 117.3900, 118.7500, 116.7100, 117.5200), |
| 70 | ("2019-03-19" , 118.0900, 118.4400, 116.9900, 117.6500), |
| 71 | ("2019-03-18" , 116.1700, 117.6100, 116.0500, 117.5700), |
| 72 | ("2019-03-15" , 115.3400, 117.2500, 114.5900, 115.9100), |
| 73 | ("2019-03-14" , 114.5400, 115.2000, 114.3300, 114.5900), |
| 74 | ]; |
| 75 | } |
| 76 | #[test] |
| 77 | fn entry_point() { |
| 78 | main().unwrap() |
| 79 | } |
| 80 | |