@@ -552,8 +552,9 @@ def plot_rolling_returns(
552
552
live_start_date : datetime, optional
553
553
The point in time when the strategy began live trading, after
554
554
its backtest period.
555
- cone_std : float, optional
556
- The standard deviation to use for the cone plots.
555
+ cone_std : float, or tuple, optional
556
+ If float, The standard deviation to use for the cone plots.
557
+ If tuple, Tuple of standard deviation values to use for the cone plots
557
558
- The cone is a normal distribution with this standard deviation
558
559
centered around a linear regression.
559
560
legend_loc : matplotlib.loc, optional
@@ -573,6 +574,23 @@ def plot_rolling_returns(
573
574
The axes that were plotted on.
574
575
575
576
"""
577
+ def draw_cone (returns , num_stdev , live_start_date , ax ):
578
+ cone_df = timeseries .cone_rolling (
579
+ returns ,
580
+ num_stdev = num_stdev ,
581
+ cone_fit_end_date = live_start_date )
582
+
583
+ cone_in_sample = cone_df [cone_df .index < live_start_date ]
584
+ cone_out_of_sample = cone_df [cone_df .index > live_start_date ]
585
+ cone_out_of_sample = cone_out_of_sample [
586
+ cone_out_of_sample .index < returns .index [- 1 ]]
587
+
588
+ ax .fill_between (cone_out_of_sample .index ,
589
+ cone_out_of_sample .sd_down ,
590
+ cone_out_of_sample .sd_up ,
591
+ color = 'steelblue' , alpha = 0.25 )
592
+
593
+ return cone_in_sample , cone_out_of_sample
576
594
577
595
if ax is None :
578
596
ax = plt .gca ()
@@ -582,12 +600,9 @@ def plot_rolling_returns(
582
600
'factor_returns.' )
583
601
elif volatility_match and factor_returns is not None :
584
602
bmark_vol = factor_returns .loc [returns .index ].std ()
585
- df_cum_rets = timeseries .cum_returns (
586
- (returns / returns .std ()) * bmark_vol ,
587
- 1.0
588
- )
589
- else :
590
- df_cum_rets = timeseries .cum_returns (returns , 1.0 )
603
+ returns = (returns / returns .std ()) * bmark_vol
604
+
605
+ df_cum_rets = timeseries .cum_returns (returns , 1.0 )
591
606
592
607
y_axis_formatter = FuncFormatter (utils .one_dec_places )
593
608
ax .yaxis .set_major_formatter (FuncFormatter (y_axis_formatter ))
@@ -611,25 +626,27 @@ def plot_rolling_returns(
611
626
label = 'Live' , ax = ax , ** kwargs )
612
627
613
628
if cone_std is not None :
614
- cone_df = timeseries .cone_rolling (
615
- returns ,
616
- num_stdev = cone_std ,
617
- cone_fit_end_date = live_start_date )
618
-
619
- cone_df_fit = cone_df [cone_df .index < live_start_date ]
620
-
621
- cone_df_live = cone_df [cone_df .index > live_start_date ]
622
- cone_df_live = cone_df_live [cone_df_live .index < returns .index [- 1 ]]
623
-
624
- cone_df_fit ['line' ].plot (
629
+ # check to see if cone_std was passed as a single value and,
630
+ # if so, just convert to list automatically
631
+ if isinstance (cone_std , float ):
632
+ cone_std = [cone_std ]
633
+
634
+ for cone_i in cone_std :
635
+ cone_in_sample , cone_out_of_sample = draw_cone (
636
+ returns ,
637
+ cone_i ,
638
+ live_start_date ,
639
+ ax )
640
+
641
+ cone_in_sample ['line' ].plot (
625
642
ax = ax ,
626
643
ls = '--' ,
627
644
label = 'Backtest trend' ,
628
645
lw = 2 ,
629
646
color = 'forestgreen' ,
630
647
alpha = 0.7 ,
631
648
** kwargs )
632
- cone_df_live ['line' ].plot (
649
+ cone_out_of_sample ['line' ].plot (
633
650
ax = ax ,
634
651
ls = '--' ,
635
652
label = 'Predicted trend' ,
@@ -638,11 +655,6 @@ def plot_rolling_returns(
638
655
alpha = 0.7 ,
639
656
** kwargs )
640
657
641
- ax .fill_between (cone_df_live .index ,
642
- cone_df_live .sd_down ,
643
- cone_df_live .sd_up ,
644
- color = 'red' , alpha = 0.30 )
645
-
646
658
ax .axhline (1.0 , linestyle = '--' , color = 'black' , lw = 2 )
647
659
ax .set_ylabel ('Cumulative returns' )
648
660
ax .set_title ('Cumulative Returns' )
0 commit comments