Skip to main content

risk_analysis/
risk_analysis.rs

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    // ========================================================================
13    // DADOS SIMULADOS
14    // ========================================================================
15    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    // ========================================================================
40    // ESTIMAÇÃO DOS MODELOS
41    // ========================================================================
42    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    // ========================================================================
59    // ANALYSIS OF IVOL
60    // ========================================================================
61    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    // ========================================================================
115    // ANALYSIS OF TRACKING ERROR
116    // ========================================================================
117    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    // ========================================================================
181    // ANALYSIS OFTALHADA: CAPM
182    // ========================================================================
183    println!("\n{}", "=".repeat(80));
184    println!("ANALYSIS OFTALHADA: CAPM");
185    println!("{}", "=".repeat(80));
186
187    println!("{}", ivol_capm);
188    println!("{}", te_capm);
189
190    // ========================================================================
191    // ANALYSIS OFTALHADA: FAMA-FRENCH 5 FACTOR
192    // ========================================================================
193    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    // ========================================================================
201    // STATISTICS COMPARATIVAS Dthe residuals
202    // ========================================================================
203    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    // ========================================================================
273    // METRICS DE FIT QUALITY
274    // ========================================================================
275    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    // ========================================================================
313    // CONCLUSÕES
314    // ========================================================================
315    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}