| """ |
| LaTeX formula generation module using sympy. |
| |
| Handles: |
| - Generating symbolic mathematical formulas |
| - Creating LaTeX representations for all calculations |
| - Detailed variance expansion with smart truncation |
| - Both symbolic and numerical formula variants |
| """ |
|
|
| from typing import Dict, List, Tuple |
| import pandas as pd |
| import numpy as np |
| from sympy import symbols, Matrix, sqrt, latex |
|
|
|
|
| def generate_weight_formulas( |
| weights: Dict[str, float], |
| amounts: Dict[str, float] |
| ) -> Tuple[str, str]: |
| """ |
| Generate weight calculation formulas. |
| |
| Returns both symbolic and numerical versions. |
| |
| Args: |
| weights: Calculated weights {ticker: weight} |
| amounts: Original amounts {ticker: amount} |
| |
| Returns: |
| Tuple of (symbolic_latex, numerical_latex) |
| """ |
| tickers = list(weights.keys()) |
| total = sum(amounts.values()) |
|
|
| |
| symbolic = r"w_i = \frac{\text{amount}_i}{\sum_j \text{amount}_j}" |
|
|
| |
| numerical_lines = [] |
| for ticker in tickers: |
| amt = amounts[ticker] |
| wt = weights[ticker] |
| line = f"w_{{{ticker}}} = \\frac{{{amt:.2f}}}{{{total:.2f}}} = {wt:.4f}" |
| numerical_lines.append(line) |
|
|
| numerical = "\\begin{aligned}\n" |
| numerical += " \\\\\n".join(numerical_lines) |
| numerical += "\n\\end{aligned}" |
|
|
| return symbolic, numerical |
|
|
|
|
| def generate_covariance_matrix_latex( |
| cov_matrix: pd.DataFrame, |
| annualized: bool = True |
| ) -> str: |
| """ |
| Generate LaTeX representation of covariance matrix. |
| |
| Args: |
| cov_matrix: Covariance matrix DataFrame |
| annualized: Whether to show annualized values |
| |
| Returns: |
| LaTeX string for the matrix |
| """ |
| tickers = list(cov_matrix.columns) |
| n = len(tickers) |
|
|
| |
| if annualized: |
| cov_values = cov_matrix.values * 252 |
| else: |
| cov_values = cov_matrix.values |
|
|
| |
| latex_str = r"\Sigma = \begin{bmatrix}" + "\n" |
|
|
| for i in range(n): |
| row_values = [] |
| for j in range(n): |
| value = cov_values[i, j] |
| row_values.append(f"{value:.6f}") |
| latex_str += " & ".join(row_values) |
| if i < n - 1: |
| latex_str += r" \\" + "\n" |
|
|
| latex_str += "\n" + r"\end{bmatrix}" |
|
|
| return latex_str |
|
|
|
|
| def generate_variance_formula_symbolic(tickers: List[str]) -> str: |
| """ |
| Generate symbolic variance formula using matrix notation. |
| |
| Formula: σ²_p = w^T × Σ × w |
| |
| Args: |
| tickers: List of ticker symbols |
| |
| Returns: |
| LaTeX string for symbolic variance formula |
| """ |
| |
| matrix_form = r"\sigma_p^2 = \mathbf{w}^T \Sigma \mathbf{w}" |
|
|
| |
| expanded_form = r"\sigma_p^2 = \sum_{i=1}^{n} \sum_{j=1}^{n} w_i w_j \sigma_{ij}" |
|
|
| |
| latex_str = "\\begin{aligned}\n" |
| latex_str += matrix_form + r" \\" + "\n" |
| latex_str += expanded_form + "\n" |
| latex_str += "\\end{aligned}" |
|
|
| return latex_str |
|
|
|
|
| def generate_variance_formula_expanded( |
| weights: Dict[str, float], |
| cov_matrix: pd.DataFrame, |
| variance_breakdown: List[Tuple[str, str, float, float, float, float]], |
| smart_truncation: bool = True, |
| truncation_threshold: int = 4 |
| ) -> str: |
| """ |
| Generate detailed variance expansion showing all terms. |
| |
| This is the most complex formula generation function. |
| |
| Shows: |
| 1. Symbolic expansion term by term |
| 2. Numerical substitution |
| 3. Intermediate calculations |
| 4. Final result |
| |
| With smart truncation: shows first 3-4 terms + "..." + last 2 terms for readability |
| |
| Args: |
| weights: Portfolio weights |
| cov_matrix: Covariance matrix |
| variance_breakdown: List of (ticker_i, ticker_j, w_i, w_j, cov_ij, contribution) |
| smart_truncation: Whether to truncate long expansions |
| truncation_threshold: Number of tickers before truncation kicks in |
| |
| Returns: |
| LaTeX string with full variance expansion |
| """ |
| tickers = list(weights.keys()) |
| n = len(tickers) |
|
|
| |
| should_truncate = smart_truncation and n >= truncation_threshold |
|
|
| |
| symbolic_terms = [] |
| for ticker_i, ticker_j, w_i, w_j, cov_ij, contrib in variance_breakdown: |
| if ticker_i == ticker_j: |
| |
| term = f"w_{{{ticker_i}}}^2 \\sigma_{{{ticker_i}{ticker_j}}}" |
| else: |
| |
| term = f"w_{{{ticker_i}}} w_{{{ticker_j}}} \\sigma_{{{ticker_i}{ticker_j}}}" |
| symbolic_terms.append(term) |
|
|
| |
| numerical_terms = [] |
| for ticker_i, ticker_j, w_i, w_j, cov_ij, contrib in variance_breakdown: |
| if ticker_i == ticker_j: |
| |
| num = f"({w_i:.4f})^2 \\times {cov_ij:.6f}" |
| else: |
| |
| num = f"({w_i:.4f}) \\times ({w_j:.4f}) \\times {cov_ij:.6f}" |
| numerical_terms.append(num) |
|
|
| |
| intermediate_values = [f"{contrib:.6f}" for (_, _, _, _, _, contrib) in variance_breakdown] |
|
|
| |
| total_variance = sum(contrib for (_, _, _, _, _, contrib) in variance_breakdown) |
|
|
| |
| if should_truncate: |
| |
| num_show_start = 3 |
| num_show_end = 2 |
|
|
| symbolic_display = ( |
| symbolic_terms[:num_show_start] |
| + [r"\cdots"] |
| + symbolic_terms[-num_show_end:] |
| ) |
|
|
| numerical_display = ( |
| numerical_terms[:num_show_start] |
| + [r"\cdots"] |
| + numerical_terms[-num_show_end:] |
| ) |
|
|
| intermediate_display = ( |
| intermediate_values[:num_show_start] |
| + [r"\cdots"] |
| + intermediate_values[-num_show_end:] |
| ) |
| else: |
| symbolic_display = symbolic_terms |
| numerical_display = numerical_terms |
| intermediate_display = intermediate_values |
|
|
| |
| latex_str = "\\begin{aligned}\n" |
|
|
| |
| latex_str += r"\sigma_p^2 &= " + " + ".join(symbolic_display) + r" \\" + "\n" |
|
|
| |
| latex_str += r" &= " + " + ".join(numerical_display) + r" \\" + "\n" |
|
|
| |
| latex_str += r" &= " + " + ".join(intermediate_display) + r" \\" + "\n" |
|
|
| |
| latex_str += f" &= {total_variance:.6f}\n" |
|
|
| latex_str += "\\end{aligned}" |
|
|
| return latex_str |
|
|
|
|
| def generate_variance_formula_expanded_full( |
| weights: Dict[str, float], |
| cov_matrix: pd.DataFrame, |
| variance_breakdown: List[Tuple[str, str, float, float, float, float]] |
| ) -> str: |
| """ |
| Generate FULL variance expansion without truncation. |
| |
| Use this for "Show all terms" toggle. |
| |
| Args: |
| weights: Portfolio weights |
| cov_matrix: Covariance matrix |
| variance_breakdown: List of (ticker_i, ticker_j, w_i, w_j, cov_ij, contribution) |
| |
| Returns: |
| LaTeX string with complete variance expansion |
| """ |
| |
| return generate_variance_formula_expanded( |
| weights, |
| cov_matrix, |
| variance_breakdown, |
| smart_truncation=False |
| ) |
|
|
|
|
| def generate_volatility_formulas( |
| variance: float, |
| volatility: float |
| ) -> Tuple[str, str]: |
| """ |
| Generate volatility calculation formulas. |
| |
| Returns both symbolic and numerical versions. |
| |
| Args: |
| variance: Calculated portfolio variance |
| volatility: Calculated portfolio volatility |
| |
| Returns: |
| Tuple of (symbolic_latex, numerical_latex) |
| """ |
| |
| symbolic = r"\sigma_p = \sqrt{\sigma_p^2}" |
|
|
| |
| numerical = f"\\sigma_p = \\sqrt{{{variance:.6f}}} = {volatility:.6f} = {volatility*100:.2f}\\%" |
|
|
| return symbolic, numerical |
|
|
|
|
| def generate_correlation_matrix_latex(cov_matrix: pd.DataFrame) -> str: |
| """ |
| Generate correlation matrix from covariance matrix. |
| |
| Correlation: ρ_ij = σ_ij / (σ_i × σ_j) |
| |
| Args: |
| cov_matrix: Covariance matrix |
| |
| Returns: |
| LaTeX string for correlation matrix |
| """ |
| |
| std_devs = np.sqrt(np.diag(cov_matrix)) |
| corr_matrix = cov_matrix / np.outer(std_devs, std_devs) |
|
|
| tickers = list(cov_matrix.columns) |
| n = len(tickers) |
|
|
| |
| latex_str = r"\text{Correlation Matrix} = \begin{bmatrix}" + "\n" |
|
|
| for i in range(n): |
| row_values = [] |
| for j in range(n): |
| value = corr_matrix.iloc[i, j] |
| row_values.append(f"{value:.4f}") |
| latex_str += " & ".join(row_values) |
| if i < n - 1: |
| latex_str += r" \\" + "\n" |
|
|
| latex_str += "\n" + r"\end{bmatrix}" |
|
|
| return latex_str |
|
|
|
|
| def generate_all_formulas( |
| amounts: Dict[str, float], |
| weights: Dict[str, float], |
| cov_matrix: pd.DataFrame, |
| variance: float, |
| volatility: float, |
| variance_breakdown: List[Tuple[str, str, float, float, float, float]] |
| ) -> Dict[str, str]: |
| """ |
| Generate all LaTeX formulas for the portfolio analysis. |
| |
| This is the orchestrator function that generates all formula variants. |
| |
| Args: |
| amounts: Portfolio amounts {ticker: amount} |
| weights: Portfolio weights {ticker: weight} |
| cov_matrix: Covariance matrix |
| variance: Portfolio variance |
| volatility: Portfolio volatility |
| variance_breakdown: Detailed variance breakdown |
| |
| Returns: |
| Dictionary of LaTeX strings: |
| { |
| 'weights_symbolic': str, |
| 'weights_numerical': str, |
| 'covariance_matrix': str, |
| 'correlation_matrix': str, |
| 'variance_symbolic': str, |
| 'variance_expanded': str, |
| 'variance_expanded_full': str, |
| 'volatility_symbolic': str, |
| 'volatility_numerical': str |
| } |
| """ |
| tickers = list(weights.keys()) |
|
|
| |
| weights_symbolic, weights_numerical = generate_weight_formulas(weights, amounts) |
|
|
| covariance_matrix = generate_covariance_matrix_latex(cov_matrix, annualized=True) |
|
|
| correlation_matrix = generate_correlation_matrix_latex(cov_matrix) |
|
|
| variance_symbolic = generate_variance_formula_symbolic(tickers) |
|
|
| variance_expanded = generate_variance_formula_expanded( |
| weights, |
| cov_matrix, |
| variance_breakdown, |
| smart_truncation=True |
| ) |
|
|
| variance_expanded_full = generate_variance_formula_expanded_full( |
| weights, |
| cov_matrix, |
| variance_breakdown |
| ) |
|
|
| volatility_symbolic, volatility_numerical = generate_volatility_formulas( |
| variance, |
| volatility |
| ) |
|
|
| return { |
| 'weights_symbolic': weights_symbolic, |
| 'weights_numerical': weights_numerical, |
| 'covariance_matrix': covariance_matrix, |
| 'correlation_matrix': correlation_matrix, |
| 'variance_symbolic': variance_symbolic, |
| 'variance_expanded': variance_expanded, |
| 'variance_expanded_full': variance_expanded_full, |
| 'volatility_symbolic': volatility_symbolic, |
| 'volatility_numerical': volatility_numerical, |
| } |
|
|