### Setup CVaR Optimization Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/14_ConditionalMaximumLoss.ipynb Sets up the necessary matrices and parameters for Conditional Value at Risk (CVaR) optimization, including bounds and a return target. ```python # Set upper and lower bounds (same as for CML) G_cvar = np.vstack((-np.identity(I), np.identity(I))) h_cvar = np.hstack((np.zeros(I), np.full(I, individual_upper_bounds))) opt = ft.MeanCVaR(cum_pnl[:, :, -1], G_cvar, h_cvar, alpha=alpha, options={'demean': False}) ``` -------------------------------- ### Install fortitudo.tech via pip Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/docs/build/html/installation.html Use this command to install or upgrade the fortitudo.tech package directly using pip. ```bash pip install -U fortitudo.tech ``` -------------------------------- ### Setup Optimization Constraints Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/5_DerivativesFramework.ipynb Defines constraints and bounds for portfolio optimization. This setup is typically used before calculating efficient portfolios. ```python v = np.hstack((np.ones(I), [put_90, put_95, put_atmf, call_atmf, call_105, call_110])) G = np.vstack((np.eye(len(v)), -np.eye(len(v)))) options_bounds = 0.5 * np.ones(6) h = np.hstack((0.25 * np.ones(I), options_bounds, np.zeros(I), options_bounds)) ``` -------------------------------- ### Setup Optimization Matrices Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/14_ConditionalMaximumLoss.ipynb Configures the matrices A, b, G, h, and c for a linear programming problem, optionally including a return target constraint. ```python G[-I:, :I] = np.identity(I) h[-I:, 0] = upper_bound # Optional return target if return_target is not None: expected_returns = p.T @ cum_pnl[:, :, -1] expected_return_row = np.zeros((1, num_vars)) expected_return_row[0, :I] = expected_returns G = np.vstack((G, -expected_return_row)) h = np.vstack((h, np.array([[-return_target]]))) return A, b, G, h, c ``` -------------------------------- ### Create and activate conda environment Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/docs/build/html/installation.html Recommended steps to create a conda environment named 'fortitudo.tech' with essential scientific packages before installing the fortitudo.tech package. ```bash conda create -n fortitudo.tech -c conda-forge python scipy pandas matplotlib cvxopt ``` ```bash conda activate fortitudo.tech ``` -------------------------------- ### Parameter Uncertainty Simulation Setup Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/10_DerivPortOpt_ParamUncertainty.ipynb Configures parameters for simulating parameter uncertainty, including the number of efficient frontiers, portfolios, and sample size for estimation. Sets a random seed for reproducibility. ```python # Parameter uncertainty specification B = 100 # Number of efficient frontiers P = 5 # Number of portfolios used to span the efficient frontiers pf_index = 2 N = 100 # Sample size for parameter estimation np.random.seed(3) # Fix results return_sim = np.random.multivariate_normal(means, covariance_matrix, (N, B)) ``` -------------------------------- ### Install fortitudo.tech within conda environment Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/docs/build/html/installation.html After activating the conda environment, use pip to install the fortitudo.tech package. ```bash pip install fortitudo.tech ``` -------------------------------- ### Initialize CVaR Optimizer Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/13_HighDimensionalCVaR.ipynb Initializes the MeanCVaR optimizer with simulated stock returns and long-only constraints. This setup is used for subsequent portfolio optimization. ```python G = -np.eye(I_stocks) h = np.zeros(I_stocks) cvar_opt = ft.MeanCVaR(stock_returns_1m, G, h) ``` -------------------------------- ### Create Conda Environment Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/docs/build/html/contributing.html Use this command to create a conda environment from the provided requirements file, which is the recommended way to start contributing to the code. ```bash conda env create --file requirements.yml ``` -------------------------------- ### Compute CVaR for Multiple Portfolios Source: https://context7.com/fortitudo-tech/fortitudo.tech/llms.txt Computes the CVaR for multiple portfolios simultaneously. This example calculates CVaR for an equal-weight portfolio and a portfolio concentrated in 'Gov & MBS'. ```python import numpy as np import fortitudo.tech as ft R_df = ft.load_pnl() R = R_df.values S, I = R.shape p = np.ones((S, 1)) / S # Multiple portfolios at once e_multi = np.hstack((np.ones((I, 1)) / I, # equal weight np.eye(I)[:, 0:1])) # 100% Gov & MBS cvar_multi = ft.portfolio_cvar(e_multi, R, p) print(cvar_multi.shape) # (1, 2) print(np.round(cvar_multi * 100, 2)) ``` -------------------------------- ### Create Cash Flows for Bonds Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/7_RiskFactorViews.ipynb Defines the cash flows for government and credit bonds. This is a setup step for pricing these instruments. ```python # Create cash flows for the bonds gov_cf = 3 * np.ones(10) gov_cf[-1] = gov_cf[-1] + 100 cr_cf = 3 + gov_cf ``` -------------------------------- ### Import Libraries for CVaR Optimization Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/13_HighDimensionalCVaR.ipynb Imports necessary libraries including numpy for numerical operations, yfinance for data fetching, fortitudo.tech for CVaR optimization, and matplotlib for plotting. This setup is required before performing any optimization tasks. ```python import numpy as np import yfinance as yf import fortitudo.tech as ft # for CVaR optimization and Entropy Pooling import matplotlib.pyplot as plt from time import time ``` -------------------------------- ### Initialize and Optimize Posterior Portfolios Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/5_DerivativesFramework.ipynb Initializes MeanCVaR and MeanVariance objects for posterior distributions and calculates efficient portfolios for a given confidence level. ```python cvar_opt_post = ft.MeanCVaR(R, G, h, v=v, p=q, alpha=alpha) mean_post = stats_post['Mean'].values cov_post = ft.covariance_matrix(R, q).values var_opt_post = ft.MeanVariance(mean_post, cov_post, G, h, v=v) port_cvar_post = cvar_opt_post.efficient_portfolio(0.05) port_var_post = var_opt_post.efficient_portfolio(0.05) post_results = np.hstack((port_cvar_post, port_var_post)) ``` -------------------------------- ### Import Libraries Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/2_EntropyPooling.ipynb Imports necessary libraries for numerical operations and the fortitudo.tech library. ```python import numpy as np import fortitudo.tech as ft ``` -------------------------------- ### Initialize MeanCVaR Object and Optimize Portfolios Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/1_MeanCVaR_EntropyPooling.ipynb Initializes the MeanCVaR optimization object with P&L data, constraints, and prior probability. It then computes the efficient portfolio and a portfolio targeting a specific return. ```python R = R_df.values ft.cvar_options['demean'] = False cvar_opt = ft.MeanCVaR(R, G=G_pf, h=h_pf, p=p) w_min = cvar_opt.efficient_portfolio() w_target = cvar_opt.efficient_portfolio(return_target=0.05) ``` -------------------------------- ### Load risk factor simulation data Source: https://context7.com/fortitudo-tech/fortitudo.tech/llms.txt Loads a risk factor simulation DataFrame. This is used for risk-factor-based P&L attribution and stress-testing examples. ```python import fortitudo.tech as ft rf = ft.load_risk_factors() print(rf.shape) print(rf.head()) ``` -------------------------------- ### Import Libraries for Optimization Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/3_MeanCVaR_MeanVariance.ipynb Imports necessary libraries for financial analysis and optimization, including NumPy, Pandas, Fortitudo, PyPortfolioOpt, and time. ```python import numpy as np import pandas as pd import fortitudo.tech as ft from pypfopt.efficient_frontier import EfficientCVaR from time import time ``` -------------------------------- ### Import Libraries for Portfolio Optimization Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/9_PortfolioOpt_ParamUncertainty.ipynb Imports necessary libraries including numpy, pandas, fortitudo.tech, matplotlib, seaborn, scipy, cvxopt, and copy for portfolio optimization tasks. ```python import numpy as np import pandas as pd import fortitudo.tech as ft import matplotlib.pyplot as plt import seaborn as sns from scipy import stats from cvxopt import matrix, solvers from copy import copy ``` -------------------------------- ### Load bundled P&L simulation data Source: https://context7.com/fortitudo-tech/fortitudo.tech/llms.txt Loads a bundled P&L simulation DataFrame. This dataset is used in most library examples. It covers 10 asset classes. ```python import fortitudo.tech as ft R_df = ft.load_pnl() print(R_df.shape) # (S, 10) print(R_df.columns.tolist()) # ['Gov & MBS', 'Corp IG', 'Corp HY', 'EM Debt', 'DM Equity', # 'EM Equity', 'Private Equity', 'Infrastructure', 'Real Estate', 'Hedge Funds'] print(R_df.head(3)) ``` -------------------------------- ### Download and Prepare Stock Data Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/12_NormalDistributionMyth.ipynb Downloads historical closing prices for selected ETFs and the S&P 500. Renames columns for better readability and prints the number of observations. ```python tickers = [ 'XLB', 'XLE', 'XLF', 'XLI', 'XLK', 'XLP', 'XLU', 'XLV', 'XLY', '^GSPC'] data = yf.download(tickers, start='1998-12-22', end='2025-06-04')['Close'] names_dict = { 'XLB': 'Materials', 'XLE': 'Energy', 'XLF': 'Financial', 'XLI': 'Industrial', 'XLK': 'Technology', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLV': 'Health Care', 'XLY': 'Consumer Discretionary', '^GSPC': 'S&P 500'} data = data.rename(columns=names_dict) print(f'The number of daily observations is {len(data)}.') ``` -------------------------------- ### Visualize Prior and Posterior Distributions Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/2_SequentialEntropyPooling.ipynb Compares the prior and posterior return distributions for S&P 500 and STOXX 50 using KDE plots, highlighting the impact of Entropy Pooling. ```python sns.kdeplot(x=R_df.iloc[:, 0]) sns.kdeplot(x=R_df.iloc[:, 0], weights=q[:, 0]) plt.title('S&P 500 return distributions') plt.legend(['Prior', 'Posterior']) plt.show() sns.kdeplot(x=R_df.iloc[:, 1]) sns.kdeplot(x=R_df.iloc[:, 1], weights=q[:, 0]) plt.title('STOXX 50 return distributions') plt.legend(['Prior', 'Posterior']) plt.show() ``` -------------------------------- ### Set CVaR Options Globally Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/docs/build/html/_sources/documentation.rst.txt Control parameters for the CVaR risk measure can be set globally using the cvar_options dictionary. This example shows how to disable demean or adjust the R_scalar. ```python import fortitudo.tech as ft ft.cvar_options['demean'] = False ft.cvar_options['R_scalar'] = 10000 ``` -------------------------------- ### Initialize Simulation Parameters Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/13_HighDimensionalCVaR.ipynb Sets the number of scenarios (S) to 10,000, the simulation horizon (H) to 21 trading days, and the number of assets (I) based on the dimensions of the log returns. It also sets a random seed for reproducibility. ```python # Simulate historical observations S = 10000 H = 21 I = log_returns.shape[-1] np.random.seed(1) ``` -------------------------------- ### Download and Prepare Financial Data Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/13_HighDimensionalCVaR.ipynb Downloads historical closing prices for a list of tickers using yfinance. It renames columns to more descriptive names and prints the number of daily observations. ```python tickers = [ 'XLB', 'XLE', 'XLF', 'XLI', 'XLK', 'XLP', 'XLU', 'XLV', 'XLY', '^GSPC', '^VIX'] data = yf.download(tickers, start='1998-12-22', end='2025-11-15')['Close'] names_dict = { 'XLB': 'Materials', 'XLE': 'Energy', 'XLF': 'Financial', 'XLI': 'Industrial', 'XLK': 'Technology', 'XLP': 'Consumer Staples', 'XLU': 'Utilities', 'XLV': 'Health Care', 'XLY': 'Consumer Discretionary', '^GSPC': 'S&P 500', } data = data.rename(columns=names_dict) print(f'The number of daily observations is {len(data)}.') ``` -------------------------------- ### Load SDE-based time series simulation data Source: https://context7.com/fortitudo-tech/fortitudo.tech/llms.txt Loads a time series simulation based on Stochastic Differential Equations (SDEs). Suitable for option pricing and volatility surface examples. ```python import fortitudo.tech as ft ts = ft.load_time_series() print(ts.shape) # (T, N) — includes implied vol surface columns print(ts.head()) ``` -------------------------------- ### Define Entropy Pooling Constraints Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/13_HighDimensionalCVaR.ipynb Sets up constraints for Entropy Pooling to adjust scenario probabilities. This example increases the expected return of the S&P 500 index by 10% relative to its prior mean. ```python # Entropy Pooling S&P 500 volatility increase for non-uniform scenario probabilities example A_ep = np.vstack((np.ones((1, S)), return_sim_1m[:, -1][np.newaxis, :])) b_ep = np.array([[1], [np.mean(return_sim_1m[:, -1]) * 1.1]]) # 10% relative increase in the S&P 500 expected return q = ft.entropy_pooling(np.ones((S, 1)) / S, A_ep, b_ep) ``` -------------------------------- ### Import Libraries Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/7_RiskFactorViews.ipynb Imports necessary libraries for numerical operations, plotting, and the fortitudo.tech library. ```python import numpy as np import seaborn as sns import fortitudo.tech as ft import matplotlib.pyplot as plt ``` -------------------------------- ### European Option Pricing and Verification Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/4_TimeSeries.ipynb Calculates and verifies prices for European call and put options using the Fortitudo Tech library. Demonstrates ATMF pricing, out-of-the-money pricing, and put-call parity. ```python # Extract parameters i = 0 S_0 = time_series['Equity Index'][i] r = time_series['1y'][i] / 100 q = 0 T = 1 F = ft.forward(S_0, r, q, T) sigma = time_series['1y100'][i] / 100 # Price ATMF options call = ft.call_option(F, F, sigma, r, T) put = ft.put_option(F, F, sigma, r, T) # Verify that ATMF prices are the same print(f'Difference between ATMF call and put price is {call - put} (must be equal to 0).') # Price options with strike 105% relative to forward value sigma_105 = time_series['1y105'][i] / 100 call_105 = ft.call_option(F, 1.05 * F, sigma_105, r, T) put_105 = ft.put_option(F, 1.05 * F, sigma_105, r, T) # Verify that call price < put price print(f'Call price is {np.round(call_105, 2)} and put price is {np.round(put_105, 2)}.') # Verify call price with put-call parity call_105_parity = put_105 + time_series['Equity Index'][i] - np.exp(-r * T) * 1.05 * F print(f'Call price using put-call parity is {np.round(call_105_parity, 2)}.') ``` -------------------------------- ### Perform Multi-Horizon Normality Tests Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/12_NormalDistributionMyth.ipynb Iterates through different test start days and applies D'Agostino K-squared, Shapiro-Wilk, and Anderson-Darling tests to monthly, quarterly, and yearly log returns. Requires data to be preprocessed into respective return series. ```python for test in range(remainder_days): k2[test, :, 0] = normaltest(log_returns_monthly[test:-(remainder_days-test):horizons[1]]).pvalue k2[test, :, 1] = normaltest(log_returns_quarterly[test:-(remainder_days-test):horizons[2]]).pvalue k2[test, :, 2] = normaltest(log_returns_yearly[test:-(remainder_days-test):horizons[3]]).pvalue shapiro_p[test, :, 0] = shapiro(log_returns_monthly[test:-(remainder_days-test):horizons[1]], axis=0).pvalue shapiro_p[test, :, 1] = shapiro(log_returns_quarterly[test:-(remainder_days-test):horizons[2]], axis=0).pvalue shapiro_p[test, :, 2] = shapiro(log_returns_yearly[test:-(remainder_days-test):horizons[3]], axis=0).pvalue for i in range(I): anderson_statistic[test, i, 0] = anderson(log_returns_monthly[test:-(remainder_days-test):horizons[1], i]).statistic anderson_statistic[test, i, 1] = anderson(log_returns_quarterly[test:-(remainder_days-test):horizons[2], i]).statistic anderson_statistic[test, i, 2] = anderson(log_returns_yearly[test:-(remainder_days-test):horizons[3], i]).statistic anderson_5p_critical_value = anderson(log_returns_monthly[test:-(remainder_days-test):horizons[3], i]).critical_values[2] ``` -------------------------------- ### Compare Target Return Portfolios (Prior vs. Posterior) Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/1_MeanCVaR_EntropyPooling.ipynb Compares the asset allocations for portfolios targeting a 5% return, generated using prior probabilities versus posterior probabilities. The allocations are presented as percentages, rounded to one decimal place. ```python target_return_pfs = pd.DataFrame( data=np.hstack((w_target, w_target_post)), index=instrument_names, columns=['Prior', 'Posterior']) display(np.round(target_return_pfs * 100, 1)) ``` -------------------------------- ### Sector and S&P 500 Financial Data Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/12_NormalDistributionMyth.ipynb This data represents financial metrics for different sectors and the S&P 500, often used to analyze deviations from normal distribution assumptions in finance. No specific setup or imports are required to view this data. ```text Utilities 0.028973 1.222527 -0.051895 14.083450 Health Care 0.030854 1.130872 -0.216691 11.779079 Consumer Discretionary 0.036583 1.439790 -0.347540 9.531116 S&P 500 0.024079 1.227349 -0.331202 13.208427 ``` -------------------------------- ### Download and Prepare Market Data Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/2_SequentialEntropyPooling.ipynb Downloads historical adjusted closing prices for S&P 500 and STOXX 50, fills missing values, and renames columns. ```python tickers = ['^GSPC', '^STOXX50E'] data = yf.download(tickers, start='2007-03-30', end='2024-02-24')['Adj Close'] data.bfill(inplace=True) column_names = ['S&P 500', 'STOXX 50'] data.columns = column_names ``` -------------------------------- ### Calculate and Display Performance Overview Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/6_SimulationExpDecay.ipynb Calculates and displays a summary overview of the optimized portfolios, including mean return, volatility, Value at Risk (VaR), and Conditional Value at Risk (CVaR), based on the exponentially decaying probabilities. ```python means2 = p.T @ pf_pnls2.values vols2 = ft.portfolio_vol(exp_results, R, p=p) vars2 = ft.portfolio_var(exp_results, R, p=p) cvars2 = ft.portfolio_cvar(exp_results, R, p=p) overview2 = np.vstack((means2, vols2, vars2, cvars2)) display(np.round(pd.DataFrame( 100 * overview2, index=['Mean', 'Vol', 'VaR', 'CVaR'], columns=pf_perfs_df.columns), 3)) ``` -------------------------------- ### Initialize MeanCVaR with Options Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/docs/build/html/_sources/documentation.rst.txt Instantiate the MeanCVaR class with custom options for P&L simulation scaling and demeaning. Adjust 'R_scalar' for P&L simulations with different scales. ```python opt = ft.MeanCVaR(R, G, h, A, b, options={'demean': False, 'R_scalar': 10000}) ``` -------------------------------- ### Define Portfolio Exposure and Compute P&L Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/8_CausalPredictiveFramework.ipynb Sets up portfolio exposures and calculates the portfolio's Profit and Loss (P&L) using provided return data and exposure vectors. Verifies that portfolio weights sum to 1. ```python # Hacky put relative value extraction from 7_RiskFactorViews.ipynb atmf_put_3m = 0.06580262150126014 # Define a vector of relative exposures exposure_equity = 0.30 exposure_put = 0.30 weight_put = 0.30 * atmf_put_3m exposure_gov = 0.5 exposure_credit = 1 - exposure_equity - weight_put - exposure_gov e = np.array([[exposure_equity], [exposure_gov], [exposure_credit], [exposure_put]]) # Verify that weights sum to 1 print(f'Portfolio weight sum is: {np.sum(np.sum(e[0:3]) + weight_put)}') # Compute portfolio P&L pnl_pf = (100 * R[:, -4:] @ e)[:, 0] ``` -------------------------------- ### Implement C0 Market View with Entropy Pooling Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/14_ConditionalMaximumLoss.ipynb Sets up and applies a C0 market view using Sequential Entropy Pooling (SeqEP). This view focuses on the mean return of the S&P 500. Verifies the posterior mean value. ```python R = cum_pnl[:, :, -1] # C0 views mean_row = R[:, -1][np.newaxis] # S&P 500 A0 = np.vstack((np.ones((1, S)), mean_row)) snp_mean_view = sim_stats_1m['Mean'].values[-1] * 0.9 / 100 b0 = np.array([[1.], [snp_mean_view]]) q0 = ft.entropy_pooling(p, A0, b0) ``` ```python # Verify that the mean view is satisfied stats0 = ft.simulation_moments(pd.DataFrame(R, columns=data.columns[:-1]), q0) print(f'Difference between mean view and C0 posterior mean value is {stats0['Mean'].values[-1] - snp_mean_view}') means0 = stats0['Mean'].values ``` -------------------------------- ### Import Libraries for Portfolio Optimization Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/14_ConditionalMaximumLoss.ipynb Imports necessary libraries including numpy, pandas, yfinance, and fortitudo.tech for financial data manipulation and optimization tasks. ```python import numpy as np import pandas as pd import yfinance as yf import fortitudo.tech as ft from scipy.optimize import linprog from time import time from typing import Tuple ``` -------------------------------- ### Import Libraries Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/11_TimeStateResampling.ipynb Imports necessary libraries for numerical operations, data manipulation, plotting, and the fortitudo.tech library. ```python import numpy as np import pandas as pd import seaborn as sns import fortitudo.tech as ft import matplotlib.pyplot as plt ``` -------------------------------- ### Initialize CVaR Optimization Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/14_ConditionalMaximumLoss.ipynb Initializes the MeanCVaR optimizer for posterior optimization, using provided cumulative profit and loss data, and CVaR related matrices. ```python opt_post = ft.MeanCVaR( cum_pnl[:, :, -1], G_cvar, h_cvar, p=q1, alpha=alpha, options={'demean': False}) ``` -------------------------------- ### Exposure Stacking Portfolio Aggregation Source: https://context7.com/fortitudo-tech/fortitudo.tech/llms.txt Demonstrates L-fold Exposure Stacking for portfolio aggregation, comparing it with traditional resampling. Requires numpy, pandas, and fortitudo.tech. ```python import numpy as np import fortitudo.tech as ft instrument_names, means, cov_matrix = ft.load_parameters() I = len(instrument_names) B = 200 # number of resampled frontiers P = 9 # portfolios per frontier np.random.seed(42) # Generate resampled frontier portfolios G = -np.eye(I); h = np.zeros(I) frontier_samples = np.full((I, P, B), np.nan) for b in range(B): sample_returns = np.random.multivariate_normal(means, cov_matrix, 100) sample_means = np.mean(sample_returns, axis=0) sample_cov = np.cov(sample_returns, rowvar=False) try: mv = ft.MeanVariance(sample_means, sample_cov, G, h) frontier_samples[:, :, b] = mv.efficient_frontier(P) except Exception: frontier_samples[:, :, b] = frontier_samples[:, :, max(b-1, 0)] # Middle portfolio index pf_idx = 4 sample_pfs = frontier_samples[:, pf_idx, :] # shape (I, B) # Traditional resampling w_resampled = np.mean(sample_pfs, axis=1) # Exposure Stacking (2-fold, 5-fold) w_es2 = ft.exposure_stacking(L=2, sample_portfolios=sample_pfs) w_es5 = ft.exposure_stacking(L=5, sample_portfolios=sample_pfs) import pandas as pd comparison = pd.DataFrame( np.round(np.vstack((w_resampled, w_es2, w_es5)) * 100, 2).T, index=instrument_names, columns=['Resampled', 'ES 2-fold', 'ES 5-fold']) print(comparison) ``` -------------------------------- ### Load and Display Time Series Data Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/4_TimeSeries.ipynb Loads the time series data using the fortitudo.tech package and displays the first 10 observations. ```python # Load the time series data and print some observations time_series = ft.load_time_series() time_series.head(10) ``` -------------------------------- ### Compare Minimum Risk Portfolios (Prior vs. Posterior) Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/1_MeanCVaR_EntropyPooling.ipynb Compares the asset allocations for the minimum risk portfolio generated using prior probabilities versus posterior probabilities. The results are displayed as percentages, rounded to one decimal place. ```python instrument_names = R_df.columns min_risk_pfs = pd.DataFrame( data=np.hstack((w_min, w_min_post)), index=instrument_names, columns=['Prior', 'Posterior']) display(np.round(min_risk_pfs * 100, 1)) ``` -------------------------------- ### Load Time Series Data Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/11_TimeStateResampling.ipynb Loads simulated time series data using the fortitudo.tech library. This is the initial step before computing stationary transformations. ```python # Load the simulated time series data time_series = ft.load_time_series() ``` -------------------------------- ### Calculate Log Returns for Different Horizons Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/12_NormalDistributionMyth.ipynb Computes daily log returns from the closing prices for various investment horizons (daily, monthly, quarterly, yearly). ```python horizons = [1, 21, 63, 252] log_prices = np.log(data.values) log_returns_daily = log_prices[horizons[0]:] - log_prices[:-horizons[0]] log_returns_monthly = log_prices[horizons[1]:] - log_prices[:-horizons[1]] log_returns_quarterly = log_prices[horizons[2]:] - log_prices[:-horizons[2]] log_returns_yearly = log_prices[horizons[3]:] - log_prices[:-horizons[3]] ``` -------------------------------- ### Import Libraries for Market Views Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/8_CausalPredictiveFramework.ipynb Imports necessary libraries including numpy, pandas, seaborn, matplotlib, and the fortitudo.tech library for financial analysis. ```python import numpy as np import pandas as pd import seaborn as sns import fortitudo.tech as ft import matplotlib.pyplot as plt from itertools import product ``` -------------------------------- ### Visualize Prior and Posterior Distributions Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/1_MeanCVaR_EntropyPooling.ipynb Generates a Kernel Density Estimate (KDE) plot to visualize the prior and posterior return distributions for a specific asset class, 'Private Equity'. This helps in understanding the impact of the incorporated views. ```python # Visualize Private Equity prior and posterior sns.kdeplot(x=R_df['Private Equity']) sns.kdeplot(x=R_df['Private Equity'], weights=q[:, 0]) plt.title('Private Equity return distribution') plt.legend(['Prior', 'Posterior']) plt.show() ``` -------------------------------- ### Calculate Daily Returns Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/2_SequentialEntropyPooling.ipynb Calculates the daily percentage returns from the adjusted closing prices. ```python R_df = pd.DataFrame( 100 * (data.iloc[1:].values / data.iloc[0:-1].values - 1), columns=column_names) ``` -------------------------------- ### Prepare State Probabilities and Vectors for Simulation Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/13_HighDimensionalCVaR.ipynb Combines the state-specific probability vectors (q_low, q_mid, q_high) into a single matrix 'states_prob'. It also creates a 'states_vector' that maps indices to their corresponding volatility states (0 for low, 1 for mid, 2 for high). ```python # Create state probabilities and vectors states_prob = np.hstack((q_low / np.sum(q_low), q_mid / np.sum(q_mid), q_high / np.sum(q_high))) states_vector = 0 * low_vol_indices + 1 * mid_vol_indices + 2 * high_vol_indices ``` -------------------------------- ### Load and Process Portfolio Parameters Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/9_PortfolioOpt_ParamUncertainty.ipynb Loads instrument names, expected returns (means), and the covariance matrix. Calculates volatilities and the correlation matrix from the covariance matrix. ```python # Load instrument info instrument_names, means, covariance_matrix = ft.load_parameters() vols = np.sqrt(np.diag(covariance_matrix)) correlation_matrix = np.diag(vols**-1) @ covariance_matrix @ np.diag(vols**-1) ``` -------------------------------- ### Calculate and Print Portfolio Return Target Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/14_ConditionalMaximumLoss.ipynb Calculates the expected returns for a portfolio with uniform probabilities and prints the resulting portfolio return target. ```python p = np.full((S, 1), 1.) / S # Uniform probabilities individual_upper_bounds = 0.25 expected_returns = p.T @ cum_pnl[:, :, -1] return_target = np.mean(expected_returns) print(f'The portfolio return target is {return_target * 100:.4f}%') ``` -------------------------------- ### GNU General Public License Information Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/2_EntropyPooling.ipynb This section provides the copyright and licensing information for the Fortitudo Technologies software, adhering to the terms of the GNU General Public License. ```python # fortitudo.tech - Novel Investment Technologies. # Copyright (C) 2021-2026 Fortitudo Technologies. # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . ``` -------------------------------- ### Compute Entropy Pooling Posterior Probabilities and Verify Views Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/7_RiskFactorViews.ipynb Calculates the posterior probabilities 'q' using Entropy Pooling and verifies that the implemented views on the 1m zero-coupon rate and 1m implied volatility are satisfied by the posterior mean. ```python # Entropy Pooling posterior prob q = ft.entropy_pooling(np.ones((S, 1)) / S, A, b) # Compute posterior and verify that views are satisfied mean_post = q.T @ R print(mean_post[0, 0] - b[1, 0]) print(mean_post[0, vol_index2] - b[2, 0]) ``` -------------------------------- ### Plot Posterior Optimization Results Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/5_DerivativesFramework.ipynb Generates KDE plots for the P&L of portfolios optimized using posterior distributions, comparing the two optimization strategies. ```python pfs_pnl_post = pnl @ post_results pfs_pnl_post.columns = portfolio_names sns.kdeplot(x=pfs_pnl_post.values[:, 0], weights=q[:, 0]) sns.kdeplot(x=pfs_pnl_post.values[:, 1], weights=q[:, 0]) plt.title('Posterior optimization') plt.legend(portfolio_names) plt.xlabel('Portfolio return') plt.xlim([-0.2, 0.5]) plt.show() ``` -------------------------------- ### Timing Mean-CVaR and Mean-Variance Portfolios Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/3_MeanCVaR_MeanVariance.ipynb This code times the computation of portfolios using different optimization methods: pypfopt's EfficientCVaR, non-demeaned P&L CVaR, demeaned P&L CVaR, and Mean-Variance. It prints the time taken for each method. ```python # Comparison with pypfopt target_return = np.mean(R @ frontier_var, axis=0)[0] eff_cvar = EfficientCVaR(means_sim, R) start_time = time() port = eff_cvar.efficient_return(target_return) print(f'pypfopt portfolio computed in {np.round(time() - start_time, 2)} seconds.') start_time = time() port2 = opt_cvar_mean.efficient_portfolio(target_return) print(f'Non-demeaned P&L portfolio computed in {np.round(time() - start_time, 2)} seconds.') start_time = time() port3 = opt_cvar.efficient_portfolio(target_return) print(f'Demeaned P&L portfolio computed in {np.round(time() - start_time, 2)} seconds.') port4 = opt_var.efficient_portfolio(target_return) ``` -------------------------------- ### Display Downloaded Data Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/12_NormalDistributionMyth.ipynb Displays the first few rows of the downloaded stock price data, showing tickers and their corresponding closing prices over time. ```python data ``` -------------------------------- ### Simulate Portfolio Returns Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/13_HighDimensionalCVaR.ipynb Simulates portfolio returns based on historical data and states. This is used to generate realistic return scenarios for further analysis. ```python sim_indices = resampling(S, H, states_vector[-1], states_prob, states_vector) return_sim_1m = np.exp(np.cumsum(log_returns[sim_indices], axis=1))[:, -1, :] - 1 ``` -------------------------------- ### Visualize Mean Estimate Distributions Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/9_PortfolioOpt_ParamUncertainty.ipynb Visualizes the probability density functions of the true return and estimated mean returns for a specific asset (DM equity) under different sample sizes (N=10, 50, 100). Shows how the distribution of the mean estimate tightens around the true mean with more samples. ```python # Visualize mean uncertainty for some different sample sizes x_min = means[4] - 4 * vols[4] x_max = means[4] + 4 * vols[4] x = np.linspace(x_min, x_max, 500) plt.plot(x, stats.norm.pdf(x, means[4], vols[4])) plt.plot(x, stats.norm.pdf(x, means[4], vols[4] / np.sqrt(10))) plt.plot(x, stats.norm.pdf(x, means[4], vols[4] / np.sqrt(50))) plt.plot(x, stats.norm.pdf(x, means[4], vols[4] / np.sqrt(100))) plt.legend(['Return', 'Mean estimate N=10', 'Mean estimate N=50', 'Mean estimate N=100']) plt.title('DM equity return and mean estimate distributions') plt.ylabel('Probability density') plt.xlabel('Return') plt.show() ``` -------------------------------- ### Implement C1 Market View with Entropy Pooling Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/14_ConditionalMaximumLoss.ipynb Defines and applies a C1 market view using Sequential Entropy Pooling (SeqEP), incorporating views on mean returns and the variance of the Technology sector. Verifies the posterior mean and volatility. ```python means0 = q0.T @ R vol_row0 = (R[:, 4] - means0[0, 4])[np.newaxis]**2 # Technology mean_rows = R[:, [4, 9]].T A1 = np.vstack((np.ones((1, S)), mean_rows, vol_row0)) tech_variance_view = (1.1 * sim_stats_1m['Volatility'].values[4] / 100)**2 b1 = np.array([[1.], [means0[0, 4]], [means0[0, 9]], [tech_variance_view]]) q1 = ft.entropy_pooling(p, A1, b1) ``` ```python # Verify that the mean and volatility views are satisfied stats1 = ft.simulation_moments(pd.DataFrame(R, columns=data.columns[:-1]), q1) print(f'Difference between volatility view and posterior is {stats1['Volatility'].values[4] - np.sqrt(tech_variance_view)}') ``` -------------------------------- ### Plot Daily Return Distributions Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/6_SimulationExpDecay.ipynb Visualizes the probability density of daily returns for all assets using Kernel Density Estimation. ```python sns.kdeplot(R) plt.xlim([-0.075, 0.075]) plt.title('Daily return distributions') plt.show() ``` -------------------------------- ### Analyze Statistical Moments of Daily Returns Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/12_NormalDistributionMyth.ipynb Calculates and displays the mean, volatility, skewness, and kurtosis of the daily log returns, scaled by 100, using the `simulation_moments` function from the `fortitudo.tech` library. ```python stats = ft.simulation_moments(100 * log_returns_daily) stats.index = data.columns stats ``` -------------------------------- ### Calculate Exposure Values Over Samples Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/9_PortfolioOpt_ParamUncertainty.ipynb Initializes an array to store exposure values and then iterates through a range of samples to calculate and store the mean of frontier portfolios for specific exposures. ```python exposure_re_vals = np.full((4, B), np.nan) for b in range(B): re_pf = np.mean(frontier_mean[:, pf_index, 0:b+1], axis=1) exposure_re_vals[:, b] = re_pf[-4:] ``` -------------------------------- ### Displaying Optimization Results Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/3_MeanCVaR_MeanVariance.ipynb This code compares the resulting portfolio weights from pypfopt, non-demeaned CVaR, demeaned CVaR, and Variance optimizations. The results are presented in a pandas DataFrame for easy comparison. ```python # Compare results port_values = np.array([value for value in port.values()])[:, np.newaxis] pf_res = pd.DataFrame( np.round(100 * np.hstack((port_values, port2, port3, port4)), 2), index=instrument_names, columns=['Pypfopt', 'Non-demeaned', 'Demeaned', 'Variance']) display(pf_res) ``` -------------------------------- ### Exposure Stacking with 500 Samples Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/9_PortfolioOpt_ParamUncertainty.ipynb Performs exposure stacking with an increased number of samples (500) to evaluate its effect on portfolio risk and return. Assumes 'exposure_stacking', 'frontier_mean', and 'risk_return' functions are defined. ```python num_samples = 500 exposure_2_500 = exposure_stacking(2, frontier_mean, num_samples) exposure_5_500 = exposure_stacking(5, frontier_mean, num_samples) exposure_B_500 = exposure_stacking(B, frontier_mean, num_samples) re_pf_500 = np.mean(frontier_mean[:, pf_index, :num_samples], axis=1) es_2_rr_500 = risk_return(exposure_2_500) es_2_rar_500 = es_2_rr_500[1, :] / es_2_rr_500[0, :] es_5_rr_500 = risk_return(exposure_5_500) es_5_rar_500 = es_5_rr_500[1, :] / es_5_rr_500[0, :] es_B_rr_500 = risk_return(exposure_B_500) es_B_rar_500 = es_B_rr_500[1, :] / es_B_rr_500[0, :] re_rr_500 = risk_return(re_pf_500) re_rar_500 = re_rr_500[1, :] / re_rr_500[0, :] ``` -------------------------------- ### Load PNL and Calculate Prior Statistics Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/10_DerivPortOpt_ParamUncertainty.ipynb Loads historical P&L data and computes initial statistical moments (Mean, Volatility, Skewness, Kurtosis). ```python # Load instrument info and pnl pnl = ft.load_pnl() prior_stats = ft.simulation_moments(pnl) np.round(prior_stats, 3) ``` -------------------------------- ### Simulate and Plot Portfolio Performance Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/6_SimulationExpDecay.ipynb Simulates the historical performance of the optimized portfolios (CVaR and Variance) with daily rebalancing and plots the cumulative product of daily returns. This visualizes the historical effectiveness of the strategies. ```python # Historical performance with daily rebal pf_pnls2 = R @ exp_results_df sns.kdeplot(pf_pnls2) plt.xlim([-0.075, 0.075]) pf_perfs2 = np.cumprod(np.vstack((np.ones((1, 2)), pf_pnls2.values + 1)), axis=0) pf_perfs_df2 = pd.DataFrame(pf_perfs2, index=data.index, columns=hist_results_df.columns) pf_perfs_df2.plot() plt.show() ``` -------------------------------- ### Compute Efficient Frontiers with Mean-CVaR and Mean-Variance Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/3_MeanCVaR_MeanVariance.ipynb Initializes optimization objects for Mean-CVaR and Mean-Variance, then computes and times the efficient frontiers for a specified number of portfolios. This code is used to generate the performance metrics shown in the output. ```python opt_cvar = ft.MeanCVaR(R, G, h) opt_cvar_mean = ft.MeanCVaR(R, G, h, options={'demean': False}) opt_var = ft.MeanVariance(means_sim, covariances_sim, G, h) num_portfolios = 9 start_time = time() frontier_var = opt_var.efficient_frontier(num_portfolios) print(f'Variance efficient frontier with {I} instruments and {num_portfolios} portfolios' + f' computed in {np.round(time() - start_time, 2)} seconds.') start_time = time() frontier_cvar = opt_cvar.efficient_frontier(num_portfolios) print(f'Demeaned CVaR efficient frontier with {S} scenarios, {I} instruments, and {num_portfolios} portfolios' + f' computed in {np.round(time() - start_time, 2)} seconds.') start_time = time() frontier_cvar_mean = opt_cvar_mean.efficient_frontier(num_portfolios) print(f'Non-demeaned CVaR efficient frontier with {S} scenarios, {I} instruments, and {num_portfolios} portfolios' + f' computed in {np.round(time() - start_time, 2)} seconds.') ``` -------------------------------- ### Exposure Stacking with 100 Samples Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/9_PortfolioOpt_ParamUncertainty.ipynb Performs exposure stacking with a reduced number of samples (100) to assess its impact on portfolio risk and return. Assumes 'exposure_stacking', 'frontier_mean', and 'risk_return' functions are defined. ```python num_samples = 100 exposure_2_100 = exposure_stacking(2, frontier_mean, num_samples) exposure_5_100 = exposure_stacking(5, frontier_mean, num_samples) exposure_B_100 = exposure_stacking(B, frontier_mean, num_samples) re_pf_100 = np.mean(frontier_mean[:, pf_index, :num_samples], axis=1) es_2_rr_100 = risk_return(exposure_2_100) es_2_rar_100 = es_2_rr_100[1, :] / es_2_rr_100[0, :] es_5_rr_100 = risk_return(exposure_5_100) es_5_rar_100 = es_5_rr_100[1, :] / es_5_rr_100[0, :] es_B_rr_100 = risk_return(exposure_B_100) es_B_rar_100 = es_B_rr_100[1, :] / es_B_rr_100[0, :] re_rr_100 = risk_return(re_pf_100) re_rar_100 = re_rr_100[1, :] / re_rr_100[0, :] ``` -------------------------------- ### Exposure Stacking Function Implementation Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/9_PortfolioOpt_ParamUncertainty.ipynb Implements the Exposure Stacking method for portfolio optimization. This function partitions samples and uses quadratic programming to find optimal weights. ```python # Left in order to align with the walkthrough video https://youtu.be/P2vtg_A-JsY. # In practice, we recommend that you use the ft.exposure_stacking function. def exposure_stacking(L, frontier, num_samples): """Computes the L-fold Exposure Stacking. Partitions the first num_samples samples from the frontier into L sets of equal size and computes the L-fold Exposure Stacking. Args: L: Number of partition sets. frontier: Resampled exposures frontier with shape (I, P, B). num_samples: Number of samples to include. Returns: Exposure Stacking portfolio. """ partition_size = num_samples // L # If L does not divide num_bootstraps "remainder data" is not used. M = frontier[:, pf_index, :num_samples].T P = np.zeros((num_samples, num_samples)) q = np.zeros((num_samples, 1)) for l in range(L): K_l = np.arange(l * partition_size, (l + 1) * partition_size) M_l = copy(M) M_l[K_l, :] = 0 P = P + M_l @ M_l.T sum_exposures_K_l = np.sum(frontier[:, pf_index, K_l], axis=1) q = q + (M_l @ sum_exposures_K_l)[:, np.newaxis] P = matrix(2 * partition_size * P) q = matrix(-2 * q) # Weights must sum to one A = matrix(np.ones((1, num_samples))) b = matrix(np.array([[1.]])) # Weights must be nonnegative G = matrix(-np.identity(num_samples)) h = matrix(np.zeros((num_samples, 1))) sol = solvers.qp(P, q, G, h, A, b) w = sol['x'] exposure = np.squeeze(M.T @ w) return exposure ``` -------------------------------- ### Specify Entropy Pooling Views (Mean and Variance) Source: https://github.com/fortitudo-tech/fortitudo.tech/blob/main/examples/11_TimeStateResampling.ipynb Sets up the matrices and vectors for Entropy Pooling views. This includes constraints for the sum of probabilities, the mean of implied volatility, and the second moment (variance plus mean squared) for each state. ```python # Specify left hand sides for Entropy Pooling views A = np.vstack((np.ones((1, T_tilde)), imp_vol_1m)) # probabilities sum to 1 and mean b_low = np.array(([[1.], [mu_low]])) b_mid = np.array(([[1.], [mu_mid]])) b_high = np.array(([[1.], [mu_high]])) G = imp_vol_1m[:, np.newaxis].T ** 2 h_low = np.array([[sigma_low ** 2 + mu_low ** 2]]) h_mid = np.array([[sigma_mid ** 2 + mu_mid ** 2]]) h_high = np.array([[sigma_high ** 2 + mu_high ** 2]]) ```