Skip to content

Latest commit

Β 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

MacroPulse β€” Multi-Factor Black-Litterman Wealth Portfolio Optimiser

A live, interactive quantitative portfolio construction engine built on the Black-Litterman framework. Integrates Ledoit-Wolf covariance shrinkage with real-time market data to generate institutional-grade, macro-aware asset allocations across South African and global equity markets.

Live App Python License


πŸš€ Live Demo

β†’ Open the Live Dashboard

The dashboard runs a full Black-Litterman optimisation pipeline in real time:

  • Fetches live market data from Yahoo Finance on every run
  • Computes a Ledoit-Wolf shrinkage covariance matrix
  • Extracts market-implied equilibrium returns via reverse optimisation
  • Blends market consensus with your active investment views
  • Optimises for maximum Sharpe Ratio under configurable constraints
  • Generates a full institutional performance report with 9 risk metrics

The Business Problem

Retail and institutional wealth clients require customised portfolios that maximise risk-adjusted returns while respecting individual risk appetites. Traditional Markowitz mean-variance optimisation fails in practice because it is acutely sensitive to historical return estimates β€” small changes in input assumptions produce wildly unstable and unrealistic portfolio weights.

This project solves that problem by implementing the Black-Litterman model, which anchors portfolio construction to a stable market equilibrium prior and allows a portfolio manager to inject explicit, confidence-weighted views on top of that prior. The result is a portfolio that is simultaneously grounded in market consensus and responsive to active investment views β€” exactly the kind of model used by quantitative teams at firms like Goldman Sachs, where Black-Litterman was originally developed in 1990.


Why Black-Litterman

Standard mean-variance optimisation has three well-documented failure modes:

Problem Effect BL Solution
Noisy historical return estimates Extreme, unstable weights Uses market-implied equilibrium returns as prior
Garbage-in-garbage-out Small input changes β†’ massive weight changes Bayesian blending stabilises the posterior
Ignores market consensus Portfolio disconnected from reality Reverse optimisation extracts collective market wisdom
No mechanism for views Cannot incorporate active research P/Q/Omega framework encodes manager views formally

Dashboard Features

The live dashboard exposes every model parameter as an interactive control:

Sidebar Controls

  • Historical window β€” configurable start and end date
  • Risk-free rate β€” adjustable for different rate environments (SARB repo rate or US 10Y)
  • Maximum weight per asset β€” controls concentration vs diversification
  • Benchmark weights β€” adjust the market-cap prior for each of the 10 assets
  • Active views β€” set return expectations and confidence for three manager views

Output Panels

  • KPI metrics bar β€” Annualised Return, Sharpe, Max Drawdown, Sortino, Volatility
  • Cumulative growth chart β€” BL Optimised vs Equal Weight vs Market-Cap benchmark
  • Portfolio weights donut chart β€” with Sharpe Ratio displayed at centre
  • Drawdown over time β€” both portfolios overlaid
  • Prior vs Posterior returns β€” how your views shifted the market consensus
  • Full metrics table β€” all 9 institutional metrics across all 3 portfolios
  • Monthly returns heatmap β€” full year-by-month breakdown

Project Architecture

The project is structured as four research notebooks and a live application layer:

Step 1: Data Preparation          β†’ notebooks/01_data_preparation.ipynb
    └── Live Yahoo Finance data
    └── Ledoit-Wolf covariance matrix
    └── Correlation heatmap

Step 2: Market-Implied Returns    β†’ notebooks/02_implied_returns.ipynb
    └── Risk aversion coefficient (Ξ΄)
    └── Reverse optimisation: Ξ  = Ξ΄ Β· Ξ£ Β· w_mkt

Step 3: BL Optimisation          β†’ notebooks/03_bl_optimisation.ipynb
    └── Manager views (P matrix, Q vector, Omega)
    └── BL posterior expected returns
    └── Efficient Frontier: max Sharpe, long-only, ≀20% per asset

Step 4: Performance Report        β†’ notebooks/04_performance_report.ipynb
    └── Backtest vs benchmarks
    └── Drawdown, Sortino, Calmar, IR, VaR, CVaR
    └── Monthly returns heatmap

Live Dashboard                    β†’ app.py + engine/
    └── All of the above, live, interactive, deployed

Methodology

Step 1 β€” Data Preparation & Ledoit-Wolf Covariance

Daily closing prices are downloaded live via Yahoo Finance for a blended universe of South African Top 40 constituents and global ETFs. Missing values caused by non-overlapping trading calendars (JSE vs NYSE) are forward-filled using the last known price.

The covariance matrix is estimated using Ledoit-Wolf shrinkage rather than the standard sample covariance. Ledoit-Wolf computes a convex combination of the sample covariance and a structured target:

Ξ£_shrunk = (1 - Ξ±) Β· Ξ£_sample + Ξ± Β· Ξ£_target

where Ξ± is determined analytically to minimise expected out-of-sample estimation error. This produces a covariance matrix that is better conditioned and more stable across market regimes.

Step 2 β€” Reverse Optimisation (Market-Implied Returns)

Rather than using historical average returns as expected return inputs, the model applies reverse optimisation to extract the returns implied by the current market-cap benchmark weights:

Ξ  = Ξ΄ Β· Ξ£ Β· w_market

Where:

  • Ξ  β€” vector of market-implied equilibrium returns
  • Ξ΄ β€” market risk aversion: Ξ΄ = (E[Rm] - Rf) / σ²m
  • Ξ£ β€” Ledoit-Wolf covariance matrix
  • w_market β€” benchmark weights (configurable in sidebar)

Step 3 β€” Black-Litterman Views and Optimisation

Three active views are encoded using the standard Black-Litterman framework:

View Type Assets Default Return Default Confidence
SA Financials vs SA Tech Relative FSR.JO vs NPN.JO +2.0% 60%
Gold vs Long Bonds Relative GLD vs TLT +1.5% 50%
Emerging Markets Absolute EEM +5.0% 40%

All view parameters are adjustable in real time via the dashboard sidebar.

The Black-Litterman formula produces posterior expected returns:

ΞΌ_BL = [(τΣ)⁻¹ + PᡀΩ⁻¹P]⁻¹ [(τΣ)⁻¹Π + PᡀΩ⁻¹Q]

These are fed into an Efficient Frontier optimiser targeting maximum Sharpe Ratio under long-only and maximum weight constraints, with L2 regularisation to prevent corner solutions.

Step 4 β€” Performance Reporting

Nine institutional metrics computed across three portfolios:

Metric Definition
Sharpe Ratio Return per unit of total volatility
Sortino Ratio Return per unit of downside volatility only
Calmar Ratio Annualised return Γ· maximum drawdown
Information Ratio Consistency of active return vs benchmark
Max Drawdown Worst peak-to-trough loss in the period
VaR 95% Daily loss not exceeded on 95% of trading days
CVaR 95% Average loss on the worst 5% of trading days

Asset Universe

Asset Ticker Role
Naspers NPN.JO SA Tech / Consumer
FirstRand FSR.JO SA Financials
Anglo American AGL.JO SA Resources
Sasol SOL.JO SA Energy
Shoprite SHP.JO SA Consumer Staples
S&P 500 ETF SPY Global Equities
Nasdaq 100 ETF QQQ Global Technology
Emerging Markets ETF EEM EM Diversification
Gold ETF GLD Inflation Hedge
US Long Bonds ETF TLT Fixed Income

Tech Stack

Category Library Purpose
Data collection yfinance Live daily prices β€” JSE and global
Data manipulation pandas, numpy DataFrames, matrix algebra
Covariance estimation PyPortfolioOpt Ledoit-Wolf shrinkage
Portfolio optimisation PyPortfolioOpt Black-Litterman, Efficient Frontier
Statistical analysis scipy Return distribution, skewness, kurtosis
Visualisation plotly Interactive charts in dashboard
Dashboard streamlit Live web application
Macroeconomic data fredapi Risk-free rate, yield data

Run Locally

Prerequisites

  • Python 3.8 or higher

Installation

git clone https://github.com/Fikilesondach/MacroPulse-Black-Litterman-Wealth-Portfolio-Optimiser.git
cd MacroPulse-Black-Litterman-Wealth-Portfolio-Optimiser/MacroPulse_BL
pip install -r requirements.txt

Run the Dashboard

streamlit run app.py

Opens at http://localhost:8501. Configure parameters in the sidebar and click Run Optimisation.

Note on data caching: The first run downloads live data from Yahoo Finance (~20 seconds). All subsequent runs within the same hour use cached data and are near-instant. If Yahoo Finance rate limits your IP, wait 15 minutes before retrying.

Run the Research Notebooks

jupyter notebook

Run notebooks in order: 01 β†’ 02 β†’ 03 β†’ 04.


Project Structure

MacroPulse_BL/
β”‚
β”œβ”€β”€ app.py                          # Live Streamlit dashboard
β”‚
β”œβ”€β”€ engine/                         # Modular model layer
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ data.py                     # Live data fetching with retry + caching
β”‚   β”œβ”€β”€ implied_returns.py          # Reverse optimisation
β”‚   β”œβ”€β”€ optimiser.py                # Black-Litterman + Efficient Frontier
β”‚   └── metrics.py                  # 9 institutional performance metrics
β”‚
β”œβ”€β”€ notebooks/
β”‚   β”œβ”€β”€ 01_data_preparation.ipynb
β”‚   β”œβ”€β”€ 02_implied_returns.ipynb
β”‚   β”œβ”€β”€ 03_bl_optimisation.ipynb
β”‚   └── 04_performance_report.ipynb
β”‚
└── requirements.txt

Key Concepts Explained

Why not just use historical average returns? Historical returns are extremely noisy. A single strong or weak year dramatically shifts the average, causing the optimiser to concentrate heavily in recently lucky assets. The Black-Litterman prior anchors to market equilibrium, which is far more stable.

What does market-implied return actually mean? The return that makes the current benchmark weights mathematically optimal under our risk model. It represents the market's embedded consensus β€” not any individual's forecast.

Why Ledoit-Wolf instead of the standard covariance matrix? The standard sample covariance overfits to the specific sample period. During COVID (March 2020), correlations spiked toward 1.0 across all assets. Ledoit-Wolf shrinkage regularises this, producing a more stable out-of-sample estimate.

Why use Sortino instead of just Sharpe? Sharpe penalises all volatility β€” both upside and downside. Wealth clients do not object to rising sharply. They object to falling sharply. Sortino isolates downside deviation, making it more appropriate for client-facing portfolio management.


Real-World Applications

Wealth Management β€” Systematic portfolio construction for HNW clients with explicit risk constraints and customisable manager views per client segment.

Bank Treasury / ALM β€” Equity exposure management and tactical allocation, with macro overlay from rate and inflation signals.

Pension / Insurance Funds β€” Liability-aware allocation where BL views can encode actuarial assumptions about long-run economic conditions.

Quantitative Research β€” Foundation for factor-based extensions incorporating momentum, value, quality, and low-volatility signals as structured views.

Risk Management β€” Drawdown, VaR, and CVaR outputs serve as early warning indicators for portfolio stress, informing hedging and position sizing decisions.


Roadmap

  • SARB repo rate integration for South African risk-free rate
  • FRED macro overlay β€” rate hike and inflation regime shading on charts
  • Rolling re-optimisation to simulate quarterly rebalancing
  • Factor model extension β€” momentum, value, quality as additional views
  • Efficient frontier visualisation β€” full risk-return curve
  • Monte Carlo simulation for forward-looking scenario analysis
  • Paid data provider integration (Polygon.io) for production reliability

Known Limitations

Yahoo Finance Rate Limiting The live engine uses Yahoo Finance's free API. Yahoo Finance rate limits IPs that make frequent requests. If you encounter a rate limit error, wait 10–15 minutes and try again. Subsequent runs within the same session are cached for 1 hour. A production deployment would use a paid provider such as Bloomberg, Refinitiv, or Polygon.io.


Disclaimer

This project is built for educational and portfolio demonstration purposes. It does not constitute financial advice. Past performance simulated in backtests does not guarantee future results. All investment decisions should be made in consultation with a qualified financial adviser.


Built with Python Β· Powered by PyPortfolioOpt Β· Data via Yahoo Finance Β· Deployed on Streamlit Cloud

About

Built a Bayesian portfolio construction engine using Ledoit-Wolf shrinkage covariance estimation, market-implied equilibrium returns, and confidence-weighted macro views to optimise institutional-grade asset allocation.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages