33use std:: env;
44use std:: path:: PathBuf ;
55
6+ use chrono:: { DateTime , Duration } ;
67use plotly:: common:: TickFormatStop ;
78use plotly:: layout:: { Axis , RangeSelector , RangeSlider , SelectorButton , SelectorStep , StepMode } ;
89use plotly:: { Candlestick , Layout , Ohlc , Plot , Scatter } ;
@@ -320,6 +321,169 @@ fn simple_ohlc_chart(show: bool, file_name: &str) {
320321}
321322// ANCHOR_END: simple_ohlc_chart
322323
324+ // ANCHOR: series_with_gaps_for_weekends_and_holidays
325+ fn series_with_gaps_for_weekends_and_holidays ( show : bool , file_name : & str ) {
326+ let data = load_apple_data ( ) ;
327+
328+ // Filter data for the specific date range as in the Python example
329+ let filtered_data: Vec < & FinData > = data
330+ . iter ( )
331+ . filter ( |d| d. date . as_str ( ) >= "2015-12-01" && d. date . as_str ( ) <= "2016-01-15" )
332+ . collect ( ) ;
333+
334+ let date: Vec < String > = filtered_data. iter ( ) . map ( |d| d. date . clone ( ) ) . collect ( ) ;
335+ let high: Vec < f64 > = filtered_data. iter ( ) . map ( |d| d. high ) . collect ( ) ;
336+
337+ let trace = Scatter :: new ( date, high) . mode ( plotly:: common:: Mode :: Markers ) ;
338+
339+ let mut plot = Plot :: new ( ) ;
340+ plot. add_trace ( trace) ;
341+
342+ let layout = Layout :: new ( )
343+ . title ( "Series with Weekend and Holiday Gaps" )
344+ . x_axis (
345+ Axis :: new ( )
346+ . range ( vec ! [ "2015-12-01" , "2016-01-15" ] )
347+ . title ( "Date" ) ,
348+ )
349+ . y_axis ( Axis :: new ( ) . title ( "Price" ) ) ;
350+ plot. set_layout ( layout) ;
351+
352+ let path = write_example_to_html ( & plot, file_name) ;
353+ if show {
354+ plot. show_html ( path) ;
355+ }
356+ }
357+ // ANCHOR_END: series_with_gaps_for_weekends_and_holidays
358+
359+ // ANCHOR: hiding_weekends_and_holidays_with_rangebreaks
360+ fn hiding_weekends_and_holidays_with_rangebreaks ( show : bool , file_name : & str ) {
361+ let data = load_apple_data ( ) ;
362+
363+ // Filter data for the specific date range as in the Python example
364+ let filtered_data: Vec < & FinData > = data
365+ . iter ( )
366+ . filter ( |d| d. date . as_str ( ) >= "2015-12-01" && d. date . as_str ( ) <= "2016-01-15" )
367+ . collect ( ) ;
368+
369+ let date: Vec < String > = filtered_data. iter ( ) . map ( |d| d. date . clone ( ) ) . collect ( ) ;
370+ let high: Vec < f64 > = filtered_data. iter ( ) . map ( |d| d. high ) . collect ( ) ;
371+
372+ let trace = Scatter :: new ( date, high) . mode ( plotly:: common:: Mode :: Markers ) ;
373+
374+ let mut plot = Plot :: new ( ) ;
375+ plot. add_trace ( trace) ;
376+
377+ let layout = Layout :: new ( )
378+ . title ( "Hide Weekend and Holiday Gaps with rangebreaks" )
379+ . x_axis (
380+ Axis :: new ( )
381+ . range ( vec ! [ "2015-12-01" , "2016-01-15" ] )
382+ . title ( "Date" )
383+ . range_breaks ( vec ! [
384+ plotly:: layout:: RangeBreak :: new( )
385+ . bounds( "sat" , "mon" ) , // hide weekends
386+ plotly:: layout:: RangeBreak :: new( )
387+ . values( vec![ "2015-12-25" , "2016-01-01" ] ) , // hide Christmas and New Year's
388+ ] ) ,
389+ )
390+ . y_axis ( Axis :: new ( ) . title ( "Price" ) ) ;
391+ plot. set_layout ( layout) ;
392+
393+ let path = write_example_to_html ( & plot, file_name) ;
394+ if show {
395+ plot. show_html ( path) ;
396+ }
397+ }
398+ // ANCHOR_END: hiding_weekends_and_holidays_with_rangebreaks
399+
400+ // Helper to generate random walk data for all hours in a week
401+ fn generate_business_hours_data ( ) -> ( Vec < String > , Vec < f64 > ) {
402+ use rand:: Rng ;
403+ use rand:: SeedableRng ;
404+ use rand_chacha:: ChaCha8Rng ;
405+
406+ let mut dates = Vec :: new ( ) ;
407+ let mut values = Vec :: new ( ) ;
408+ let mut current_value = 0.0 ;
409+ let mut rng = ChaCha8Rng :: seed_from_u64 ( 42 ) ;
410+ let start_date = DateTime :: parse_from_rfc3339 ( "2020-03-02T00:00:00Z" ) . unwrap ( ) ;
411+ for day in 0 ..5 {
412+ // Monday to Friday
413+ for hour in 0 ..24 {
414+ let current_date = start_date + Duration :: days ( day) + Duration :: hours ( hour) ;
415+ dates. push ( current_date. format ( "%Y-%m-%d %H:%M:%S" ) . to_string ( ) ) ;
416+ current_value += ( rng. gen :: < f64 > ( ) - 0.5 ) * 2.0 ;
417+ values. push ( current_value) ;
418+ }
419+ }
420+ ( dates, values)
421+ }
422+
423+ // ANCHOR: series_with_non_business_hours_gaps
424+ fn series_with_non_business_hours_gaps ( show : bool , file_name : & str ) {
425+ use chrono:: NaiveDateTime ;
426+ use chrono:: Timelike ;
427+ let ( dates, all_values) = generate_business_hours_data ( ) ;
428+ let mut values = Vec :: with_capacity ( all_values. len ( ) ) ;
429+
430+ for ( date_str, v) in dates. iter ( ) . zip ( all_values. iter ( ) ) {
431+ // Parse the date string to extract hour
432+ // Format is "2020-03-02 09:00:00"
433+ if let Ok ( datetime) = NaiveDateTime :: parse_from_str ( date_str, "%Y-%m-%d %H:%M:%S" ) {
434+ let hour = datetime. hour ( ) ;
435+ if ( 9 ..17 ) . contains ( & hour) {
436+ values. push ( * v) ;
437+ } else {
438+ values. push ( f64:: NAN ) ;
439+ }
440+ } else {
441+ values. push ( f64:: NAN ) ;
442+ }
443+ }
444+
445+ let trace = Scatter :: new ( dates, values) . mode ( plotly:: common:: Mode :: Markers ) ;
446+ let mut plot = Plot :: new ( ) ;
447+ plot. add_trace ( trace) ;
448+ let layout = Layout :: new ( )
449+ . title ( "Series with Non-Business Hour Gaps" )
450+ . x_axis ( Axis :: new ( ) . title ( "Time" ) . tick_format ( "%b %d, %Y %H:%M" ) )
451+ . y_axis ( Axis :: new ( ) . title ( "Value" ) ) ;
452+ plot. set_layout ( layout) ;
453+ let path = write_example_to_html ( & plot, file_name) ;
454+ if show {
455+ plot. show_html ( path) ;
456+ }
457+ }
458+ // ANCHOR_END: series_with_non_business_hours_gaps
459+
460+ // ANCHOR: hiding_non_business_hours_with_rangebreaks
461+ fn hiding_non_business_hours_with_rangebreaks ( show : bool , file_name : & str ) {
462+ let ( dates, values) = generate_business_hours_data ( ) ;
463+ let trace = Scatter :: new ( dates, values) . mode ( plotly:: common:: Mode :: Markers ) ;
464+ let mut plot = Plot :: new ( ) ;
465+ plot. add_trace ( trace) ;
466+ let layout = Layout :: new ( )
467+ . title ( "Hide Non-Business Hour Gaps with rangebreaks" )
468+ . x_axis (
469+ Axis :: new ( )
470+ . title ( "Time" )
471+ . tick_format ( "%b %d, %Y %H:%M" )
472+ . range_breaks ( vec ! [
473+ plotly:: layout:: RangeBreak :: new( )
474+ . bounds( "17" , "9" )
475+ . pattern( "hour" ) , // hide hours outside of 9am-5pm
476+ ] ) ,
477+ )
478+ . y_axis ( Axis :: new ( ) . title ( "Value" ) ) ;
479+ plot. set_layout ( layout) ;
480+ let path = write_example_to_html ( & plot, file_name) ;
481+ if show {
482+ plot. show_html ( path) ;
483+ }
484+ }
485+ // ANCHOR_END: hiding_non_business_hours_with_rangebreaks
486+
323487fn main ( ) {
324488 // Change false to true on any of these lines to display the example.
325489
@@ -341,4 +505,13 @@ fn main() {
341505
342506 // OHLC Charts
343507 simple_ohlc_chart ( false , "simple_ohlc_chart" ) ;
508+
509+ // Rangebreaks usage
510+ series_with_gaps_for_weekends_and_holidays ( false , "series_with_gaps_for_weekends_and_holidays" ) ;
511+ hiding_weekends_and_holidays_with_rangebreaks (
512+ false ,
513+ "hiding_weekends_and_holidays_with_rangebreaks" ,
514+ ) ;
515+ series_with_non_business_hours_gaps ( false , "series_with_non_business_hours_gaps" ) ;
516+ hiding_non_business_hours_with_rangebreaks ( false , "hiding_non_business_hours_with_rangebreaks" ) ;
344517}
0 commit comments