1use frenchrs::{
2 Carhart4Factor, FamaFrench3Factor, FamaFrench5Factor, IVOLAnalysis, TrackingErrorAnalysis, CAPM,
3};
4use greeners::CovarianceType;
5use ndarray::array;
6
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8 println!("\n{}", "=".repeat(80));
9 println!("ANALYSIS OF RISCO: IVOL & TRACKING ERROR");
10 println!("{}", "=".repeat(80));
11
12 let fund = array![
16 0.058, 0.042, -0.018, 0.082, 0.045, -0.032, 0.068, 0.052, -0.015, 0.062, 0.041, 0.075
17 ];
18 let market = array![
19 0.042, 0.030, -0.015, 0.060, 0.033, -0.025, 0.050, 0.038, -0.012, 0.045, 0.032, 0.055
20 ];
21 let smb = array![
22 0.008, -0.003, 0.012, 0.005, -0.007, 0.015, 0.002, -0.005, 0.010, 0.003, -0.004, 0.006
23 ];
24 let hml = array![
25 0.005, 0.008, -0.010, 0.012, 0.003, -0.006, 0.009, 0.004, -0.008, 0.011, 0.002, 0.007
26 ];
27 let mom = array![
28 0.012, 0.008, -0.015, 0.018, 0.010, -0.012, 0.015, 0.009, -0.010, 0.013, 0.007, 0.014
29 ];
30 let rmw = array![
31 0.006, 0.004, -0.005, 0.008, 0.003, -0.004, 0.007, 0.005, -0.003, 0.006, 0.004, 0.007
32 ];
33 let cma = array![
34 0.003, -0.002, 0.005, 0.002, -0.003, 0.004, 0.003, -0.002, 0.004, 0.003, -0.001, 0.003
35 ];
36
37 let rf = 0.03 / 12.0;
38
39 println!("\nEstimatesndthe models...\n");
43
44 let capm = CAPM::fit(&fund, &market, rf, CovarianceType::HC3)?;
45 let ff3 = FamaFrench3Factor::fit(&fund, &market, &smb, &hml, rf, CovarianceType::HC3)?;
46 let carhart = Carhart4Factor::fit(&fund, &market, &smb, &hml, &mom, rf, CovarianceType::HC3)?;
47 let ff5 = FamaFrench5Factor::fit(
48 &fund,
49 &market,
50 &smb,
51 &hml,
52 &rmw,
53 &cma,
54 rf,
55 CovarianceType::HC3,
56 )?;
57
58 println!("{}", "=".repeat(80));
62 println!("COMPARAÇÃO DE IVOL (IDIOSYNCRATIC VOLATILITY)");
63 println!("{}", "=".repeat(80));
64
65 let ivol_capm = IVOLAnalysis::from_residuals(&capm.residuals)?;
66 let ivol_ff3 = IVOLAnalysis::from_residuals(&ff3.residuals)?;
67 let ivol_carhart = IVOLAnalysis::from_residuals(&carhart.residuals)?;
68 let ivol_ff5 = IVOLAnalysis::from_residuals(&ff5.residuals)?;
69
70 println!(
71 "\n{:<20} {:>15} {:>20} {:>20}",
72 "Model", "IVOL", "IVOL Anual (Monthly)", "Classification"
73 );
74 println!("{}", "-".repeat(80));
75
76 println!(
77 "{:<20} {:>14.4}% {:>19.2}% {:>20}",
78 "CAPM",
79 ivol_capm.ivol * 100.0,
80 ivol_capm.ivol_annualized_monthly * 100.0,
81 ivol_capm.ivol_classification()
82 );
83
84 println!(
85 "{:<20} {:>14.4}% {:>19.2}% {:>20}",
86 "FF3",
87 ivol_ff3.ivol * 100.0,
88 ivol_ff3.ivol_annualized_monthly * 100.0,
89 ivol_ff3.ivol_classification()
90 );
91
92 println!(
93 "{:<20} {:>14.4}% {:>19.2}% {:>20}",
94 "Carhart",
95 ivol_carhart.ivol * 100.0,
96 ivol_carhart.ivol_annualized_monthly * 100.0,
97 ivol_carhart.ivol_classification()
98 );
99
100 println!(
101 "{:<20} {:>14.4}% {:>19.2}% {:>20}",
102 "FF5",
103 ivol_ff5.ivol * 100.0,
104 ivol_ff5.ivol_annualized_monthly * 100.0,
105 ivol_ff5.ivol_classification()
106 );
107
108 println!("\n{}", "-".repeat(80));
109 println!("INTERPRETATION:");
110 println!("• IVOL diminui à medida que adicionamos factors to the model");
111 println!("• Menor IVOL indica que the model explica better the returns");
112 println!("• IVOL representa the risk diversificável/específico of the asset");
113
114 println!("\n{}", "=".repeat(80));
118 println!("COMPARAÇÃO DE TRACKING ERROR");
119 println!("{}", "=".repeat(80));
120
121 let te_capm =
122 TrackingErrorAnalysis::new(&fund, &capm.fitted_values, capm.alpha, capm.r_squared)?;
123 let te_ff3 = TrackingErrorAnalysis::new(&fund, &ff3.fitted_values, ff3.alpha, ff3.r_squared)?;
124 let te_carhart = TrackingErrorAnalysis::new(
125 &fund,
126 &carhart.fitted_values,
127 carhart.alpha,
128 carhart.r_squared,
129 )?;
130 let te_ff5 = TrackingErrorAnalysis::new(&fund, &ff5.fitted_values, ff5.alpha, ff5.r_squared)?;
131
132 println!(
133 "\n{:<20} {:>12} {:>18} {:>18} {:>15}",
134 "Model", "TE", "TE Anual (Monthly)", "Info Ratio", "Classification"
135 );
136 println!("{}", "-".repeat(80));
137
138 println!(
139 "{:<20} {:>11.4}% {:>17.2}% {:>18.4} {:>15}",
140 "CAPM",
141 te_capm.tracking_error * 100.0,
142 te_capm.tracking_error_annualized_monthly * 100.0,
143 te_capm.information_ratio,
144 te_capm.te_classification()
145 );
146
147 println!(
148 "{:<20} {:>11.4}% {:>17.2}% {:>18.4} {:>15}",
149 "FF3",
150 te_ff3.tracking_error * 100.0,
151 te_ff3.tracking_error_annualized_monthly * 100.0,
152 te_ff3.information_ratio,
153 te_ff3.te_classification()
154 );
155
156 println!(
157 "{:<20} {:>11.4}% {:>17.2}% {:>18.4} {:>15}",
158 "Carhart",
159 te_carhart.tracking_error * 100.0,
160 te_carhart.tracking_error_annualized_monthly * 100.0,
161 te_carhart.information_ratio,
162 te_carhart.te_classification()
163 );
164
165 println!(
166 "{:<20} {:>11.4}% {:>17.2}% {:>18.4} {:>15}",
167 "FF5",
168 te_ff5.tracking_error * 100.0,
169 te_ff5.tracking_error_annualized_monthly * 100.0,
170 te_ff5.information_ratio,
171 te_ff5.te_classification()
172 );
173
174 println!("\n{}", "-".repeat(80));
175 println!("INTERPRETATION:");
176 println!("• Tracking Error measures o how much o fundo desvia of the model");
177 println!("• Information Ratio = Alpha / Tracking Error");
178 println!("• IR > 0.5 é considerado bom, > 1.0 é excelente");
179
180 println!("\n{}", "=".repeat(80));
184 println!("ANALYSIS OFTALHADA: CAPM");
185 println!("{}", "=".repeat(80));
186
187 println!("{}", ivol_capm);
188 println!("{}", te_capm);
189
190 println!("\n{}", "=".repeat(80));
194 println!("ANALYSIS OFTALHADA: FAMA-FRENCH 5 FACTOR");
195 println!("{}", "=".repeat(80));
196
197 println!("{}", ivol_ff5);
198 println!("{}", te_ff5);
199
200 println!("\n{}", "=".repeat(80));
204 println!("RESIDUAL STATISTICS");
205 println!("{}", "=".repeat(80));
206
207 println!(
208 "\n{:<20} {:>12} {:>12} {:>12} {:>12}",
209 "Model", "Mean", "Skewness", "Kurtosis", "Normal?"
210 );
211 println!("{}", "-".repeat(80));
212
213 println!(
214 "{:<20} {:>12.6} {:>12.4} {:>12.4} {:>12}",
215 "CAPM",
216 ivol_capm.residual_mean,
217 ivol_capm.residual_skewness,
218 ivol_capm.residual_kurtosis,
219 if ivol_capm.is_residuals_normal(0.05) {
220 "SIM"
221 } else {
222 "NÃO"
223 }
224 );
225
226 println!(
227 "{:<20} {:>12.6} {:>12.4} {:>12.4} {:>12}",
228 "FF3",
229 ivol_ff3.residual_mean,
230 ivol_ff3.residual_skewness,
231 ivol_ff3.residual_kurtosis,
232 if ivol_ff3.is_residuals_normal(0.05) {
233 "SIM"
234 } else {
235 "NÃO"
236 }
237 );
238
239 println!(
240 "{:<20} {:>12.6} {:>12.4} {:>12.4} {:>12}",
241 "Carhart",
242 ivol_carhart.residual_mean,
243 ivol_carhart.residual_skewness,
244 ivol_carhart.residual_kurtosis,
245 if ivol_carhart.is_residuals_normal(0.05) {
246 "SIM"
247 } else {
248 "NÃO"
249 }
250 );
251
252 println!(
253 "{:<20} {:>12.6} {:>12.4} {:>12.4} {:>12}",
254 "FF5",
255 ivol_ff5.residual_mean,
256 ivol_ff5.residual_skewness,
257 ivol_ff5.residual_kurtosis,
258 if ivol_ff5.is_residuals_normal(0.05) {
259 "SIM"
260 } else {
261 "NÃO"
262 }
263 );
264
265 println!("\n{}", "-".repeat(80));
266 println!("INTERPRETATION:");
267 println!("• Mean of the residuals should be próxima of zero");
268 println!("• Skewness measures assimetria (0 = simétrico)");
269 println!("• Kurtosis measures caudas pesadas (0 = normal)");
270 println!("• Test of normalidade: Jarque-Bera a 5%");
271
272 println!("\n{}", "=".repeat(80));
276 println!("FIT QUALITY");
277 println!("{}", "=".repeat(80));
278
279 println!(
280 "\n{:<20} {:>12} {:>12} {:>12} {:>12}",
281 "Model", "R²", "Correlação", "RMSE", "MAE"
282 );
283 println!("{}", "-".repeat(80));
284
285 println!(
286 "{:<20} {:>12.4} {:>12.4} {:>12.6} {:>12.6}",
287 "CAPM", te_capm.r_squared, te_capm.correlation, te_capm.rmse, te_capm.mae
288 );
289
290 println!(
291 "{:<20} {:>12.4} {:>12.4} {:>12.6} {:>12.6}",
292 "FF3", te_ff3.r_squared, te_ff3.correlation, te_ff3.rmse, te_ff3.mae
293 );
294
295 println!(
296 "{:<20} {:>12.4} {:>12.4} {:>12.6} {:>12.6}",
297 "Carhart", te_carhart.r_squared, te_carhart.correlation, te_carhart.rmse, te_carhart.mae
298 );
299
300 println!(
301 "{:<20} {:>12.4} {:>12.4} {:>12.6} {:>12.6}",
302 "FF5", te_ff5.r_squared, te_ff5.correlation, te_ff5.rmse, te_ff5.mae
303 );
304
305 println!("\n{}", "-".repeat(80));
306 println!("INTERPRETATION:");
307 println!("• R² = proportion of the variesnce explieach");
308 println!("• Correlação = relação linear between obbevado e prevthis");
309 println!("• RMSE = erro quadrático médio");
310 println!("• MAE = erro absoluto médio");
311
312 println!("\n{}", "=".repeat(80));
316 println!("CONCLUSÕES");
317 println!("{}", "=".repeat(80));
318
319 println!("\n1. EVOLUÇÃO DO IVOL:");
320 println!(
321 " • CAPM: {:.2}% (annualized)",
322 ivol_capm.ivol_annualized_monthly * 100.0
323 );
324 println!(
325 " • FF5: {:.2}% (annualized)",
326 ivol_ff5.ivol_annualized_monthly * 100.0
327 );
328 println!(
329 " • Redução: {:.2} pontos percentuais",
330 (ivol_capm.ivol_annualized_monthly - ivol_ff5.ivol_annualized_monthly) * 100.0
331 );
332
333 println!("\n2. INFORMATION RATIO:");
334 println!(" • CAPM: {:.4}", te_capm.information_ratio);
335 println!(" • FF5: {:.4}", te_ff5.information_ratio);
336 println!(
337 " • Melhor model por IR: {}",
338 if te_ff5.information_ratio.abs() > te_capm.information_ratio.abs() {
339 "FF5"
340 } else {
341 "CAPM"
342 }
343 );
344
345 println!("\n3. DISTRIBUIÇÃO Dthe residuals:");
346 println!(
347 " • Models with residuals normore: {}",
348 [
349 ("CAPM", ivol_capm.is_residuals_normal(0.05)),
350 ("FF3", ivol_ff3.is_residuals_normal(0.05)),
351 ("Carhart", ivol_carhart.is_residuals_normal(0.05)),
352 ("FF5", ivol_ff5.is_residuals_normal(0.05))
353 ]
354 .iter()
355 .filter(|(_, is_normal)| *is_normal)
356 .map(|(name, _)| *name)
357 .collect::<Vec<&str>>()
358 .join(", ")
359 );
360
361 println!("\n4. RECOMENDAÇÃO:");
362 if ivol_ff5.ivol < ivol_capm.ivol * 0.8 {
363 println!(" • FF5 reduz significantly o IVOL (>20%)");
364 println!(" • Recomenda-if usar FF5 for better explicação of the returns");
365 } else {
366 println!(" • Ganho marginal with models multi-fatoriais");
367 println!(" • CAPM can be suficiente for this asset");
368 }
369
370 println!("\n{}", "=".repeat(80));
371
372 Ok(())
373}