14
14
]
15
15
16
16
17
- def plot_quantile_returns ( mean_ret ):
17
+ def plot_quantile_and_cumulative_returns ( factor_data , mean_ret ):
18
18
"""
19
19
install plotly extension first:
20
20
https://plot.ly/python/getting-started/
@@ -31,84 +31,59 @@ def plot_quantile_returns(mean_ret):
31
31
x = quantiles
32
32
factors = mean_ret .columns .levels [0 ]
33
33
periods = mean_ret .columns .levels [1 ]
34
- rows = math .ceil (len (factors ) / 2 )
34
+ rows = math .ceil (len (factors ))
35
35
cols = 2
36
36
37
37
colors = dict (zip (periods , cycle (DEFAULT_COLORS )))
38
- styles = {
38
+ quantile_styles = {
39
39
period : {'name' : period , 'legendgroup' : period ,
40
40
'hovertemplate' : '<b>Quantile</b>:%{x}<br>'
41
41
'<b>Return</b>: %{y:.3f}%±%{error_y.array:.3f}%' ,
42
42
'marker' : {'color' : colors [period ]}}
43
43
for period in periods
44
44
}
45
+ cumulative_styles = {
46
+ period : {'name' : period , 'mode' : 'lines' , 'legendgroup' : period , 'showlegend' : False ,
47
+ 'hovertemplate' : '<b>Date</b>:%{x}<br>'
48
+ '<b>Return</b>: %{y:.3f}x' ,
49
+ 'marker' : {'color' : colors [period ]}}
50
+ for period in periods
51
+ }
45
52
46
53
fig = go .Figure ()
47
54
fig = subplots .make_subplots (
48
55
rows = rows , cols = 2 ,
49
- subplot_titles = factors ,
56
+ vertical_spacing = 0.03 ,
57
+ horizontal_spacing = 0.06 ,
58
+ subplot_titles = ['Quantile Return' , 'Portfolio cumulative returns' ],
50
59
)
51
60
52
61
for i , factor in enumerate (factors ):
53
- row = int (i / cols ) + 1
54
- col = i % cols + 1
62
+ row = i + 1
63
+ weight_col = (factor , 'factor_weight' )
64
+ weighted = factor_data ['Returns' ].multiply (factor_data [weight_col ], axis = 0 )
65
+ factor_return = weighted .groupby (level = 'date' ).sum ()
55
66
for j , period in enumerate (periods ):
56
67
y = mean_ret .loc [:, (factor , period , 'mean' )] * 100
57
68
err_y = mean_ret .loc [:, (factor , period , 'sem' )] * 100
58
69
fig .add_trace (go .Bar (
59
70
x = x , y = y , error_y = dict (type = 'data' , array = err_y , thickness = 0.2 ),
60
- ** styles [period ]
61
- ), row = row , col = col )
62
- styles [period ]['showlegend' ] = False
63
- fig .update_xaxes (title_text = "factor quantile" , type = "category" , row = row , col = col )
64
-
65
- fig .update_layout (height = 400 * rows , barmode = 'group' , bargap = 0.5 ,
66
- title_text = "Mean return by quantile" )
67
- fig .show ()
68
-
69
-
70
- def plot_cumulative_return (factor_data ):
71
- import plotly .graph_objects as go
72
- import plotly .subplots as subplots
73
-
74
- factors = list (factor_data .columns .levels [0 ])
75
- factors .remove ('Demeaned' )
76
- factors .remove ('Returns' )
77
- periods = factor_data ['Returns' ].columns
78
-
79
- rows = math .ceil (len (factors ) / 2 )
80
- cols = 2
81
-
82
- fig = go .Figure ()
83
- fig = subplots .make_subplots (
84
- rows = rows , cols = 2 ,
85
- subplot_titles = factors ,
86
- )
87
-
88
- colors = dict (zip (periods , cycle (DEFAULT_COLORS )))
89
- styles = {
90
- period : {'name' : period , 'mode' : 'lines' , 'legendgroup' : period ,
91
- 'marker' : {'color' : colors [period ]}}
92
- for period in periods
93
- }
94
-
95
- for i , factor in enumerate (factors ):
96
- row = int (i / cols ) + 1
97
- col = i % cols + 1
98
- weight_col = (factor , 'factor_weight' )
99
- weighted = factor_data ['Returns' ].multiply (factor_data [weight_col ], axis = 0 )
100
- factor_return = weighted .groupby (level = 'date' ).sum ()
101
- for period in periods :
102
- cumret = factor_return [period ].resample ('b' + period ).mean ().dropna ()
103
- cumret = (cumret + 1 ).cumprod ()
104
- fig .add_trace (go .Scatter (x = cumret .index , y = cumret .values , ** styles [period ]),
105
- row = row , col = col )
106
- styles [period ]['showlegend' ] = False
107
-
108
- fig .add_shape (go .layout .Shape (type = "line" , y0 = 1 , y1 = 1 ,
109
- x0 = cumret .index [0 ], x1 = cumret .index [- 1 ],
110
- line = dict (width = 1 )),
111
- row = row , col = col )
112
-
113
- fig .update_layout (height = 400 * rows , title_text = "Portfolio cumulative return" )
71
+ ** quantile_styles [period ]
72
+ ), row = row , col = 1 )
73
+ quantile_styles [period ]['showlegend' ] = False
74
+ fig .update_xaxes (type = "category" , row = row , col = 1 )
75
+ fig .update_yaxes (title_text = factor , row = row , col = 1 , ticksuffix = '%' )
76
+
77
+ cum_ret = factor_return [period ].resample ('b' + period ).mean ().dropna ()
78
+ cum_ret = (cum_ret + 1 ).cumprod ()
79
+ fig .add_trace (go .Scatter (
80
+ x = cum_ret .index , y = cum_ret .values , ** cumulative_styles [period ]
81
+ ), row = row , col = 2 )
82
+
83
+ fig .add_shape (go .layout .Shape (
84
+ type = "line" , line = dict (width = 1 ),
85
+ y0 = 1 , y1 = 1 , x0 = cum_ret .index [0 ], x1 = cum_ret .index [- 1 ],
86
+ ), row = row , col = 2 )
87
+
88
+ fig .update_layout (height = 300 * rows , barmode = 'group' , bargap = 0.5 , margin = {'t' : 50 })
114
89
fig .show ()
0 commit comments