riskfolio-lib 7.2.0__cp313-cp313-macosx_10_13_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,692 @@
1
+ """""" #
2
+
3
+ """
4
+ Copyright (c) 2020-2026, Dany Cajas
5
+ All rights reserved.
6
+ This work is licensed under BSD 3-Clause "New" or "Revised" License.
7
+ License available at https://github.com/dcajasn/Riskfolio-Lib/blob/master/LICENSE.txt
8
+ """
9
+
10
+ import pandas as pd
11
+ import matplotlib.pyplot as plt
12
+ from matplotlib.gridspec import GridSpec
13
+ from xlsxwriter.utility import xl_range_abs, xl_range, xl_rowcol_to_cell, xl_col_to_name
14
+ import datetime
15
+ import riskfolio.src.PlotFunctions as plf
16
+ import riskfolio.src.RiskFunctions as rk
17
+
18
+
19
+ __all__ = [
20
+ "jupyter_report",
21
+ "excel_report",
22
+ ]
23
+
24
+
25
+ __LICENSE__ = """Copyright (c) 2020-2024, Dany Cajas
26
+ All rights reserved.
27
+
28
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
29
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
30
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
31
+ * Neither the name of Riskfolio-Lib nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
32
+
33
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."""
34
+
35
+
36
+ def jupyter_report(
37
+ returns,
38
+ w,
39
+ rm="MV",
40
+ rf=0,
41
+ alpha=0.05,
42
+ a_sim=100,
43
+ beta=None,
44
+ b_sim=None,
45
+ kappa=0.30,
46
+ solver="CLARABEL",
47
+ percentage=False,
48
+ erc_line=True,
49
+ color="tab:blue",
50
+ erc_linecolor="r",
51
+ others=0.05,
52
+ nrow=25,
53
+ cmap="tab20",
54
+ height=6,
55
+ width=14,
56
+ t_factor=252,
57
+ ini_days=1,
58
+ days_per_year=252,
59
+ bins=50,
60
+ ):
61
+ r"""
62
+ Create a matplotlib report with useful information to analyze risk and
63
+ profitability of investment portfolios.
64
+
65
+ Parameters
66
+ ----------
67
+ returns : DataFrame of shape (n_samples, n_assets), optional
68
+ Assets returns DataFrame, where n_samples is the number of
69
+ observations and n_assets is the number of assets.
70
+ w : DataFrame or Series of shape (n_assets, 1)
71
+ Portfolio weights, where n_assets is the number of assets.
72
+ rm : str, optional
73
+ Risk measure used to estimate risk contribution.
74
+ The default is 'MV'. Possible values are:
75
+
76
+ - 'MV': Standard Deviation.
77
+ - 'KT': Square Root Kurtosis.
78
+ - 'MAD': Mean Absolute Deviation.
79
+ - 'GMD': Gini Mean Difference.
80
+ - 'MSV': Semi Standard Deviation.
81
+ - 'SKT': Square Root Semi Kurtosis.
82
+ - 'FLPM': First Lower Partial Moment (Omega Ratio).
83
+ - 'SLPM': Second Lower Partial Moment (Sortino Ratio).
84
+ - 'CVaR': Conditional Value at Risk.
85
+ - 'TG': Tail Gini.
86
+ - 'EVaR': Entropic Value at Risk.
87
+ - 'RLVaR': Relativistc Value at Risk.
88
+ - 'WR': Worst Realization (Minimax).
89
+ - 'CVRG': CVaR range of returns.
90
+ - 'TGRG': Tail Gini range of returns.
91
+ - 'RG': Range of returns.
92
+ - 'MDD': Maximum Drawdown of uncompounded cumulative returns (Calmar Ratio).
93
+ - 'ADD': Average Drawdown of uncompounded cumulative returns.
94
+ - 'DaR': Drawdown at Risk of uncompounded cumulative returns.
95
+ - 'CDaR': Conditional Drawdown at Risk of uncompounded cumulative returns.
96
+ - 'EDaR': Entropic Drawdown at Risk of uncompounded cumulative returns.
97
+ - 'RLDaR': Relativistic Drawdown at Risk of uncompounded cumulative returns.
98
+ - 'UCI': Ulcer Index of uncompounded cumulative returns.
99
+
100
+ rf : float, optional
101
+ Risk free rate or minimum acceptable return. The default is 0.
102
+ alpha : float, optional
103
+ Significance level of VaR, CVaR, Tail Gini, EVaR, RLVaR, CDaR, EDaR and RLDaR. The default is 0.05.
104
+ The default is 0.05.
105
+ a_sim : float, optional
106
+ Number of CVaRs used to approximate Tail Gini of losses. The default is 100.
107
+ beta : float, optional
108
+ Significance level of CVaR and Tail Gini of gains. If None it duplicates alpha value.
109
+ The default is None.
110
+ b_sim : float, optional
111
+ Number of CVaRs used to approximate Tail Gini of gains. If None it duplicates a_sim value.
112
+ The default is None.
113
+ kappa : float, optional
114
+ Deformation parameter of RLVaR and RLDaR, must be between 0 and 1. The default is 0.30.
115
+ solver: str, optional
116
+ Solver available for CVXPY that supports power cone programming and exponential cone programming.
117
+ Used to calculate EVaR, EDaR, RLVaR and RLDaR. The default value is 'CLARABEL'.
118
+ percentage : bool, optional
119
+ If risk contribution per asset is expressed as percentage or as a value. The default is False.
120
+ erc_line : bool, optional
121
+ If equal risk contribution line is plotted.
122
+ The default is False.
123
+ color : str, optional
124
+ Color used to plot each asset risk contribution.
125
+ The default is 'tab:blue'.
126
+ erc_linecolor : str, optional
127
+ Color used to plot equal risk contribution line.
128
+ The default is 'r'.
129
+ others : float, optional
130
+ Percentage of others section. The default is 0.05.
131
+ nrow : int, optional
132
+ Number of rows of the legend. The default is 25.
133
+ cmap : cmap, optional
134
+ Color scale used to plot each asset weight.
135
+ The default is 'tab20'.
136
+ height : float, optional
137
+ Average height of charts in the image in inches. The default is 6.
138
+ width : float, optional
139
+ Width of the image in inches. The default is 14.
140
+ t_factor : float, optional
141
+ Factor used to annualize expected return and expected risks for
142
+ risk measures based on returns (not drawdowns). The default is 252.
143
+
144
+ .. math::
145
+
146
+ \begin{align}
147
+ \text{Annualized Return} & = \text{Return} \, \times \, \text{t_factor} \\
148
+ \text{Annualized Risk} & = \text{Risk} \, \times \, \sqrt{\text{t_factor}}
149
+ \end{align}
150
+
151
+ ini_days : float, optional
152
+ If provided, it is the number of days of compounding for first return.
153
+ It is used to calculate Compound Annual Growth Rate (CAGR). This value
154
+ depend on assumptions used in t_factor, for example if data is monthly
155
+ you can use 21 (252 days per year) or 30 (360 days per year). The
156
+ default is 1 for daily returns.
157
+ days_per_year: float, optional
158
+ Days per year assumption. It is used to calculate Compound Annual
159
+ Growth Rate (CAGR). Default value is 252 trading days per year.
160
+ bins : float, optional
161
+ Number of bins of the histogram. The default is 50.
162
+
163
+ Raises
164
+ ------
165
+ ValueError
166
+ When the value cannot be calculated.
167
+
168
+ Returns
169
+ -------
170
+ ax : matplotlib axis of size (6,1)
171
+ Returns the Axes object with the plot for further tweaking.
172
+
173
+ Example
174
+ -------
175
+ ::
176
+
177
+ ax = rp.jupyter_report(returns,
178
+ w,
179
+ rm='MV',
180
+ rf=0,
181
+ alpha=0.05,
182
+ height=6,
183
+ width=14,
184
+ others=0.05,
185
+ nrow=25)
186
+
187
+ .. image:: images/Report_1.png
188
+ .. image:: images/Report_2.png
189
+ .. image:: images/Report_3.png
190
+ .. image:: images/Report_4.png
191
+
192
+ """
193
+
194
+ cov = returns.cov()
195
+
196
+ fig, ax = plt.subplots(figsize=(width, height * 6))
197
+ ax.axis("off")
198
+
199
+ gs0 = GridSpec(5, 1, figure=fig, height_ratios=[2, 1.5, 1, 2, 1])
200
+ gs00 = gs0[0].subgridspec(1, 1)
201
+ ax0 = fig.add_subplot(gs00[0, 0])
202
+
203
+ gs01 = gs0[1].subgridspec(1, 1)
204
+ ax1 = fig.add_subplot(gs01[0, 0])
205
+
206
+ gs02 = gs0[2].subgridspec(1, 1)
207
+ ax2 = fig.add_subplot(gs02[0, 0])
208
+
209
+ gs03 = gs0[3].subgridspec(1, 1)
210
+ ax3 = fig.add_subplot(gs03[0, 0])
211
+
212
+ gs04 = gs0[4].subgridspec(1, 1)
213
+ ax4 = fig.add_subplot(gs04[0, 0])
214
+
215
+ ax0 = plf.plot_table(
216
+ returns,
217
+ w,
218
+ MAR=rf,
219
+ alpha=alpha,
220
+ a_sim=a_sim,
221
+ kappa=kappa,
222
+ solver=solver,
223
+ t_factor=t_factor,
224
+ ini_days=ini_days,
225
+ days_per_year=days_per_year,
226
+ ax=ax0,
227
+ )
228
+
229
+ ax1 = plf.plot_pie(
230
+ w=w,
231
+ title="Portfolio Composition",
232
+ others=others,
233
+ nrow=nrow,
234
+ cmap=cmap,
235
+ ax=ax1,
236
+ )
237
+
238
+ ax2 = plf.plot_hist(
239
+ returns=returns,
240
+ w=w,
241
+ alpha=alpha,
242
+ a_sim=a_sim,
243
+ kappa=kappa,
244
+ solver=solver,
245
+ bins=bins,
246
+ ax=ax2,
247
+ )
248
+
249
+ ax3 = plf.plot_drawdown(
250
+ returns=returns, w=w, alpha=alpha, kappa=kappa, solver=solver, ax=ax3
251
+ )
252
+
253
+ ax4 = plf.plot_risk_con(
254
+ w=w,
255
+ cov=cov,
256
+ returns=returns,
257
+ rm=rm,
258
+ rf=rf,
259
+ alpha=alpha,
260
+ a_sim=a_sim,
261
+ beta=beta,
262
+ b_sim=b_sim,
263
+ kappa=kappa,
264
+ solver=solver,
265
+ t_factor=t_factor,
266
+ percentage=percentage,
267
+ erc_line=erc_line,
268
+ color=color,
269
+ erc_linecolor=erc_linecolor,
270
+ ax=ax4,
271
+ )
272
+
273
+ year = str(datetime.datetime.now().year)
274
+
275
+ title = "Riskfolio-Lib Report"
276
+ subtitle = "Copyright (c) 2020-" + year + ", Dany Cajas. All rights reserved."
277
+
278
+ fig.suptitle(title, fontsize="xx-large", y=1.011, fontweight="bold")
279
+ ax0.set_title(subtitle, fontsize="large", ha="center", pad=10)
280
+
281
+ return ax
282
+
283
+
284
+ def excel_report(
285
+ returns,
286
+ w,
287
+ rf=0,
288
+ alpha=0.05,
289
+ solver="CLARABEL",
290
+ t_factor=252,
291
+ ini_days=1,
292
+ days_per_year=252,
293
+ name="report",
294
+ ):
295
+ r"""
296
+ Create an Excel report (with formulas) with useful information to analyze
297
+ risk and profitability of investment portfolios.
298
+
299
+ Parameters
300
+ ----------
301
+ returns : DataFrame of shape (n_samples, n_assets), optional
302
+ Assets returns DataFrame, where n_samples is the number of
303
+ observations and n_assets is the number of assets.
304
+ w : DataFrame or Series of shape (n_assets, 1)
305
+ Portfolio weights, where n_assets is the number of assets.
306
+ rf : float, optional
307
+ Risk free rate or minimum acceptable return. The default is 0.
308
+ alpha : float, optional
309
+ Significance level of VaR, CVaR, EVaR, DaR and CDaR.
310
+ The default is 0.05.
311
+ solver: str, optional
312
+ Solver available for CVXPY that supports exponential cone programming.
313
+ Used to calculate EVaR and EDaR. The default value is 'CLARABEL'.
314
+ t_factor : float, optional
315
+ Factor used to annualize expected return and expected risks for
316
+ risk measures based on returns (not drawdowns). The default is 252.
317
+
318
+ .. math::
319
+
320
+ \begin{align}
321
+ \text{Annualized Return} & = \text{Return} \, \times \, \text{t_factor} \\
322
+ \text{Annualized Risk} & = \text{Risk} \, \times \, \sqrt{\text{t_factor}}
323
+ \end{align}
324
+
325
+ ini_days : float, optional
326
+ If provided, it is the number of days of compounding for first return.
327
+ It is used to calculate Compound Annual Growth Rate (CAGR). This value
328
+ depend on assumptions used in t_factor, for example if data is monthly
329
+ you can use 21 (252 days per year) or 30 (360 days per year). The
330
+ default is 1 for daily returns.
331
+ days_per_year: float, optional
332
+ Days per year assumption. It is used to calculate Compound Annual
333
+ Growth Rate (CAGR). Default value is 252 trading days per year.
334
+ name : str, optional
335
+ Name or name with path where the Excel report will be saved. If no
336
+ path is provided the report will be saved in the same path of
337
+ current file.
338
+
339
+ Raises
340
+ ------
341
+ ValueError
342
+ When the report cannot be built.
343
+
344
+ Example
345
+ -------
346
+ ::
347
+
348
+ rp.excel_report(returns,
349
+ w,
350
+ rf=0,
351
+ alpha=0.05,
352
+ t_factor=252,
353
+ ini_days=1,
354
+ days_per_year=252,
355
+ name="report")
356
+
357
+ .. image:: images/Excel.png
358
+
359
+ """
360
+ n1 = w.shape[0]
361
+ n2 = returns.shape[0]
362
+
363
+ portfolios = w.columns.tolist()
364
+ returns.index = returns.index.tz_localize(None)
365
+ dates = returns.index.tolist()
366
+ year = str(datetime.datetime.now().year)
367
+ days = (returns.index[-1] - returns.index[0]).days + ini_days
368
+
369
+ # Create a Pandas Excel writer using XlsxWriter as the engine.
370
+ writer = pd.ExcelWriter(name + ".xlsx", engine="xlsxwriter")
371
+
372
+ # Convert the dataframe to an XlsxWriter Excel object.
373
+ w.to_excel(writer, sheet_name="Resume", startrow=36, startcol=0)
374
+ returns.to_excel(writer, sheet_name="Returns", index_label=["Date"])
375
+
376
+ # Get the xlsxwriter objects from the dataframe writer object.
377
+ workbook = writer.book
378
+ worksheet1 = writer.sheets["Resume"]
379
+ worksheet2 = writer.sheets["Returns"]
380
+ worksheet3 = workbook.add_worksheet("Portfolios")
381
+ worksheet4 = workbook.add_worksheet("Absdev")
382
+ worksheet5 = workbook.add_worksheet("CumRet")
383
+ worksheet6 = workbook.add_worksheet("Drawdown")
384
+ worksheet7 = workbook.add_worksheet("devBelowTarget")
385
+ worksheet8 = workbook.add_worksheet("devBelowMean")
386
+
387
+ worksheet1.hide_gridlines(2)
388
+ worksheet2.hide_gridlines(2)
389
+ worksheet3.hide_gridlines(2)
390
+ worksheet4.hide_gridlines(2)
391
+ worksheet5.hide_gridlines(2)
392
+ worksheet6.hide_gridlines(2)
393
+ worksheet7.hide_gridlines(2)
394
+ worksheet8.hide_gridlines(2)
395
+
396
+ # Cell Formats
397
+ cell_format1 = workbook.add_format({"bold": True, "border": True})
398
+ cell_format2 = workbook.add_format({"bold": True, "font_size": 28, "right": True})
399
+ cell_format3 = workbook.add_format({"num_format": "0.0000%"})
400
+ cell_format4 = workbook.add_format({"num_format": "0.0000%", "border": True})
401
+ cell_format5 = workbook.add_format({"num_format": "yyyy-mm-dd", "bold": True})
402
+ cell_format6 = workbook.add_format({"num_format": "0.0000", "border": True})
403
+ cell_format7 = workbook.add_format(
404
+ {"num_format": "yyyy-mm-dd", "bold": True, "border": True}
405
+ )
406
+ cell_format8 = workbook.add_format({"num_format": "0,000", "border": True})
407
+
408
+ cols = xl_col_to_name(1) + ":" + xl_col_to_name(n2)
409
+ worksheet1.set_column(cols, 11, cell_format3)
410
+ worksheet2.set_column(cols, 9, cell_format3)
411
+
412
+ worksheet2.write(0, 0, "Date", cell_format1)
413
+ worksheet3.write(0, 0, "Date", cell_format1)
414
+ worksheet4.write(0, 0, "Date", cell_format1)
415
+ worksheet5.write(0, 0, "Date", cell_format1)
416
+ worksheet6.write(0, 0, "Date", cell_format1)
417
+ worksheet7.write(0, 0, "Date", cell_format1)
418
+ worksheet8.write(0, 0, "Date", cell_format1)
419
+
420
+ worksheet1.set_column("A:A", 36)
421
+ worksheet2.set_column("A:A", 10, cell_format5)
422
+ worksheet3.set_column("A:A", 10, cell_format5)
423
+ worksheet4.set_column("A:A", 10, cell_format5)
424
+ worksheet5.set_column("A:A", 10, cell_format5)
425
+ worksheet6.set_column("A:A", 10, cell_format5)
426
+ worksheet7.set_column("A:A", 10, cell_format5)
427
+ worksheet8.set_column("A:A", 10, cell_format5)
428
+
429
+ for i in range(0, n2):
430
+ r = xl_rowcol_to_cell(i + 1, 0)
431
+ formula = "=Returns!" + r + ""
432
+ worksheet2.write(i + 1, 0, dates[i], cell_format7)
433
+ worksheet3.write_formula(i + 1, 0, formula, cell_format7)
434
+ worksheet4.write_formula(i + 1, 0, formula, cell_format7)
435
+ worksheet5.write_formula(i + 1, 0, formula, cell_format7)
436
+ worksheet6.write_formula(i + 1, 0, formula, cell_format7)
437
+ worksheet7.write_formula(i + 1, 0, formula, cell_format7)
438
+ worksheet8.write_formula(i + 1, 0, formula, cell_format7)
439
+
440
+ labels_1 = [
441
+ "",
442
+ "",
443
+ "",
444
+ "",
445
+ "Profitability and Other Inputs",
446
+ "Total Days in DataBase",
447
+ "Mean Return (1)",
448
+ "Compound Annual Growth Rate (CAGR)",
449
+ "Minimum Acceptable Return (MAR) (1)",
450
+ "Alpha",
451
+ "",
452
+ "Risk Measures based on Returns",
453
+ "Standard Deviation (2)",
454
+ "Mean Absolute Deviation (MAD) (2)",
455
+ "Semi Standard Deviation (2)",
456
+ "First Lower Partial Moment (FLPM) (2)",
457
+ "Second Lower Partial Moment (SLPM) (2)",
458
+ "Value at Risk (VaR) (2)",
459
+ "Conditional Value at Risk (CVaR) (2)",
460
+ "Entropic Value at Risk (EVaR) (2)",
461
+ "Worst Realization (2)",
462
+ "Skewness",
463
+ "Kurtosis",
464
+ "",
465
+ "Risk Measures based on Drawdowns (3)",
466
+ "Ulcer Index (UCI)",
467
+ "Average Drawdown (ADD)",
468
+ "Drawdown at Risk (DaR)",
469
+ "Conditional Drawdown at Risk (CDaR)",
470
+ "Entropic Drawdown at Risk (CDaR)",
471
+ "Max Drawdown (MDD)",
472
+ ]
473
+
474
+ for i in range(0, len(labels_1)):
475
+ if labels_1[i] != "":
476
+ worksheet1.write(i, 0, labels_1[i], cell_format1)
477
+
478
+ for i in range(0, len(portfolios)):
479
+ a = "Portfolio " + str(i + 1)
480
+ worksheet1.write(3, 1 + i, a, cell_format1)
481
+ worksheet1.write(36, 1 + i, a, cell_format1)
482
+ worksheet3.write(0, 1 + i, a, cell_format1)
483
+ worksheet4.write(0, 1 + i, a, cell_format1)
484
+ worksheet5.write(0, 1 + i, a, cell_format1)
485
+ worksheet6.write(0, 1 + i, a, cell_format1)
486
+ worksheet7.write(0, 1 + i, a, cell_format1)
487
+ worksheet8.write(0, 1 + i, a, cell_format1)
488
+
489
+ for j in range(0, len(portfolios)):
490
+ r_0 = xl_rowcol_to_cell(8, 1 + j) # MAR cell
491
+ r_1 = xl_range_abs(37, 1 + j, 36 + n1, 1 + j)
492
+ r_2 = xl_range_abs(1, 1 + j, n2, 1 + j)
493
+ for i in range(0, n2):
494
+ r_3 = xl_range(i + 1, 1, i + 1, n1)
495
+ r_4 = xl_rowcol_to_cell(i + 1, 1 + j)
496
+ r_5 = xl_range_abs(1, 1 + j, i + 1, 1 + j)
497
+ formula1 = "{=MMULT(" + "Returns!" + r_3 + ",Resume!" + r_1 + ")}"
498
+ formula2 = "=ABS(Portfolios!" + r_4 + "-AVERAGE(Portfolios!" + r_2 + "))"
499
+ formula3 = "=SUM(Portfolios!" + r_5 + ")"
500
+ formula4 = "=MAX(CumRet!" + r_5 + ")-CumRet!" + r_4
501
+ formula5 = (
502
+ "=MAX(Resume!"
503
+ + r_0
504
+ + "/ "
505
+ + str(t_factor)
506
+ + "-Portfolios!"
507
+ + r_4
508
+ + ", 0)"
509
+ )
510
+ formula6 = "=MAX(AVERAGE(Portfolios!" + r_2 + ")-Portfolios!" + r_4 + ", 0)"
511
+ worksheet3.write_formula(i + 1, 1 + j, formula1, cell_format3)
512
+ worksheet4.write_formula(i + 1, 1 + j, formula2, cell_format3)
513
+ worksheet5.write_formula(i + 1, 1 + j, formula3, cell_format3)
514
+ worksheet6.write_formula(i + 1, 1 + j, formula4, cell_format3)
515
+ worksheet7.write_formula(i + 1, 1 + j, formula5, cell_format3)
516
+ worksheet8.write_formula(i + 1, 1 + j, formula6, cell_format3)
517
+
518
+ r_6 = xl_rowcol_to_cell(9, 1 + j) # Alpha cell
519
+ r_7 = xl_rowcol_to_cell(17, 1 + j) # Value at Risk cell
520
+ AVG = "=AVERAGE(Portfolios!" + r_2 + ") * " + str(t_factor) + ""
521
+ CUM = (
522
+ "{=PRODUCT(1 + Portfolios!"
523
+ + r_2
524
+ + ")^("
525
+ + str(days_per_year)
526
+ + "/"
527
+ + str(days)
528
+ + ")-1}"
529
+ )
530
+ STDEV = "=STDEV(Portfolios!" + r_2 + ") * SQRT(" + str(t_factor) + ")"
531
+ MAD = "=AVERAGE(Absdev!" + r_2 + ") * SQRT(" + str(t_factor) + ")"
532
+ ALPHA = "=" + str(alpha)
533
+ VaR = (
534
+ "=-SMALL(Portfolios!"
535
+ + r_2
536
+ + ",ROUNDUP(COUNT(Portfolios!"
537
+ + r_2
538
+ + ")*"
539
+ + r_6
540
+ + ",0)) * SQRT("
541
+ + str(t_factor)
542
+ + ")"
543
+ )
544
+ CVaR = (
545
+ "=-((SUMIF(Portfolios!"
546
+ + r_2
547
+ + ',"<="&(-'
548
+ + r_7
549
+ + "/SQRT("
550
+ + str(t_factor)
551
+ + ")),Portfolios!"
552
+ + r_2
553
+ + ")"
554
+ )
555
+ CVaR += (
556
+ "-ROUNDUP(COUNT(Portfolios!"
557
+ + r_2
558
+ + ")*"
559
+ + r_6
560
+ + ",0)*(-"
561
+ + r_7
562
+ + "/SQRT("
563
+ + str(t_factor)
564
+ + ")))/(COUNT(Portfolios!"
565
+ + r_2
566
+ + ")*"
567
+ + r_6
568
+ + ")-"
569
+ + r_7
570
+ + "/SQRT("
571
+ + str(t_factor)
572
+ + ")) * SQRT("
573
+ + str(t_factor)
574
+ + ")"
575
+ )
576
+ EVaR = (
577
+ "="
578
+ + str(rk.EVaR_Hist(returns @ w.iloc[:, j], alpha=alpha, solver=solver)[0])
579
+ + " * SQRT("
580
+ + str(t_factor)
581
+ + ")"
582
+ )
583
+ WR = "=-MIN(Portfolios!" + r_2 + ") * SQRT(" + str(t_factor) + ")"
584
+ MDD = "=MAX(Drawdown!" + r_2 + ")"
585
+ ADD = "=AVERAGE(Drawdown!" + r_2 + ")"
586
+ DaR = (
587
+ "=+LARGE(Drawdown!"
588
+ + r_2
589
+ + ",ROUNDUP(COUNT(Drawdown!"
590
+ + r_2
591
+ + ")*"
592
+ + r_6
593
+ + ",0))"
594
+ )
595
+ CDaR = (
596
+ "=((SUMIF(Drawdown!" + r_2 + ',">="&' + DaR[2:] + ",Drawdown!" + r_2 + ")"
597
+ )
598
+ CDaR += (
599
+ "-ROUNDUP(COUNT(Drawdown!"
600
+ + r_2
601
+ + ")*"
602
+ + r_6
603
+ + ",0)*"
604
+ + DaR[2:]
605
+ + ")/(COUNT(Drawdown!"
606
+ + r_2
607
+ + ")*"
608
+ + r_6
609
+ + ")+"
610
+ + DaR[2:]
611
+ + ")"
612
+ )
613
+ EDaR = "=" + str(rk.EDaR_Abs(returns @ w.iloc[:, j], alpha=alpha)[0])
614
+ UCI = "=SQRT(SUMSQ(Drawdown!" + r_2 + ")/COUNT(Drawdown!" + r_2 + "))"
615
+ MAR = "=" + str(rf)
616
+ FLPM = "=AVERAGE(devBelowTarget!" + r_2 + ") * SQRT(" + str(t_factor) + ")"
617
+ SLPM = (
618
+ "=SQRT(SUMSQ(devBelowTarget!"
619
+ + r_2
620
+ + ")/(COUNT(devBelowTarget!"
621
+ + r_2
622
+ + ") - 1))"
623
+ + " * SQRT("
624
+ + str(t_factor)
625
+ + ")"
626
+ )
627
+ SDEV = (
628
+ "=SQRT(SUMSQ(devBelowMean!"
629
+ + r_2
630
+ + ")/(COUNT(devBelowMean!"
631
+ + r_2
632
+ + ") - 1))"
633
+ + " * SQRT("
634
+ + str(t_factor)
635
+ + ")"
636
+ )
637
+ SKEW = "=SKEW(Portfolios!" + r_2 + ")"
638
+ KURT = "=KURT(Portfolios!" + r_2 + ")"
639
+
640
+ labels_2 = [
641
+ "",
642
+ "",
643
+ "",
644
+ "",
645
+ "",
646
+ str(days),
647
+ AVG,
648
+ CUM,
649
+ MAR,
650
+ ALPHA,
651
+ "",
652
+ "",
653
+ STDEV,
654
+ MAD,
655
+ SDEV,
656
+ FLPM,
657
+ SLPM,
658
+ VaR,
659
+ CVaR,
660
+ EVaR,
661
+ WR,
662
+ SKEW,
663
+ KURT,
664
+ "",
665
+ "",
666
+ UCI,
667
+ ADD,
668
+ DaR,
669
+ CDaR,
670
+ EDaR,
671
+ MDD,
672
+ ]
673
+
674
+ for i in range(0, len(labels_2)):
675
+ if labels_1[i] in ["Skewness", "Kurtosis"]:
676
+ worksheet1.write_formula(i, 1 + j, labels_2[i], cell_format6)
677
+ elif labels_1[i] in ["Total Days in DataBase"]:
678
+ worksheet1.write_formula(i, 1 + j, labels_2[i], cell_format8)
679
+ elif labels_2[i] != "":
680
+ worksheet1.write_formula(i, 1 + j, labels_2[i], cell_format4)
681
+
682
+ merge_format = workbook.add_format({"align": "Left", "valign": "vjustify"})
683
+ merge_format.set_text_wrap()
684
+ worksheet1.set_row(1, 215)
685
+ worksheet1.merge_range("A2:K2", __LICENSE__.replace("2021", year), merge_format)
686
+ worksheet1.write(2, 0, "https://github.com/dcajasn/Riskfolio-Lib")
687
+ worksheet1.write(31, 0, "(1) Annualized, multiplied by " + str(t_factor))
688
+ worksheet1.write(32, 0, "(2) Annualized, multiplied by √" + str(t_factor))
689
+ worksheet1.write(33, 0, "(3) Based on uncompounded cumulated returns")
690
+ worksheet1.write(0, 0, "Riskfolio-Lib Report", cell_format2)
691
+
692
+ workbook.close()