MarkoWizard
A modern Python library for Markowitz portfolio optimization and analysis.
Previously known as Diversificador. The original portfolio-analysis web app built with Dash is no longer maintained, but it is preserved on the
dash-deprecatedbranch for reference.
Features
- Markowitz Mean-Variance Optimization — Compute the efficient frontier using
scipy.optimize - Capital Allocation Line — Mix risky portfolios with risk-free assets
- Visualization — Plotly-based charts for efficient frontier, allocation pie, CAL, correlation heatmaps, and price timelines
- Data Fetching — Optional convenience functions for downloading market data via yfinance
- Web Application — FastAPI backend with a dark-themed interactive frontend
Installation
pip install markowizard
That's everything the library needs: optimization (scipy), market-data
fetching (yfinance), and visualization (plotly). No optional extras.
Quick Start (Library)
from markowizard import MarkowitzOptimizer, CapitalAllocator
from markowizard.data import fetch_prices, compute_monthly_returns
from markowizard.visualization import efficiency_frontier_plot
# Fetch prices and compute monthly returns (decimal form, e.g. 0.01 = 1%)...
prices = fetch_prices(["AAPL", "MSFT", "GOOGL", "SPY"], period="5y")
returns = compute_monthly_returns(prices)
# ...or bring your own returns DataFrame (assets as columns).
# Optimize
optimizer = MarkowitzOptimizer(returns)
portfolios = optimizer.optimize()
# Compute Sharpe ratios (provide monthly risk-free rate)
risk_free_rate = 0.005 # 0.5% per month
portfolios = optimizer.compute_sharpe(risk_free_rate)
# Plot the efficient frontier
fig = efficiency_frontier_plot(portfolios, highlight_portfolio=50)
fig.show()
# Best portfolio (maximum Sharpe ratio)
best = optimizer.max_sharpe_portfolio()
print(best)
# Capital allocation line
allocator = CapitalAllocator(best, risk_free_rate)
cal_points = allocator.capital_allocation_line(steps=21)
Web Application
An interactive web UI (FastAPI + a dark-themed frontend) lives in backend/ and
frontend/. It is not part of the PyPI package — run it from the container
image or a clone.
Using Docker
docker run -p 8000:8000 ghcr.io/outliersanalytics/markowizard:latest
From a clone
git clone https://github.com/OutliersAnalytics/MarkoWizard
cd MarkoWizard
uv run --with-requirements backend/requirements.txt uvicorn backend.main:app --port 8000
Open http://localhost:8000 — the app auto-submits with default tickers on load.
It exposes a single endpoint, POST /api/analyze:
{
"tickers": ["AAPL", "MSFT", "GOOGL", "SPY"],
"period": "5y",
"risk_free_rate": 0.005
}
which returns the efficient frontier, max-Sharpe portfolio, capital-allocation-line points, and correlation matrix as JSON. The frontend renders the charts.
API Reference
markowizard (top-level)
| Export | Description |
|---|---|
MarkowitzOptimizer |
Efficient frontier optimization (from core) |
CapitalAllocator |
Risk-free asset allocation (from allocation) |
__version__ |
Package version string |
markowizard.core
MarkowitzOptimizer
class MarkowitzOptimizer:
def __init__(self, returns: pd.DataFrame) -> None
def optimize(self) -> pd.DataFrame
def compute_sharpe(self, risk_free_rate: float) -> pd.DataFrame
def max_sharpe_portfolio(self) -> pd.Series
Constants: COL_RETURN = "Expected Return", COL_RISK = "Risk", COL_SHARPE = "Sharpe", COL_RISK_FREE = "Risk-Free"
Parameters:
returns: DataFrame where each column is an asset and each row is a time period. Values must be in decimal form (e.g., 0.01 = 1%).
optimize() computes the efficient frontier by solving 100 quadratic programming problems with varying risk-aversion parameters. Uses warm-starting: each iteration's solution seeds the next.
compute_sharpe(risk_free_rate) adds a Sharpe column. risk_free_rate must match the period of returns (e.g., monthly).
max_sharpe_portfolio() returns the tangency portfolio row.
MarkowitzOptimizer.portfolios DataFrame columns
| Column | Description |
|---|---|
| (ticker columns) | Asset weights (sum to 1, all >= 0) |
Expected Return |
Expected portfolio return |
Risk |
Portfolio standard deviation (risk) |
Sharpe |
Sharpe ratio (after compute_sharpe()) |
markowizard.allocation
CapitalAllocator
class CapitalAllocator:
def __init__(self, portfolio: pd.Series | Mapping, risk_free_rate: float) -> None
@staticmethod
def weigh_risk_free(value: float, risk_free_value: float, p: float) -> float
def capital_allocation_line(self, steps: int = 21) -> list[dict]
def final_allocation(self, p: float) -> dict[str, float]
def expected_returns(self, p: float) -> tuple[float, float]
capital_allocation_line() returns points along the CAL, each with keys p, expected_return, risk, and label.
final_allocation(p) returns asset weights including Risk-Free (risk-free portion).
markowizard.visualization
| Function | Returns | Description |
|---|---|---|
efficiency_frontier_plot(portfolios, highlight_portfolio=0) |
Figure |
Scatter plot of expected return vs risk |
allocation_pie(portfolio) |
Figure |
Pie chart of asset weights |
capital_allocation_line_plot(cal_points, highlight_point=0) |
Figure |
CAL risk-return trade-off |
correlation_timeline(prices, ticker_a, ticker_b=None) |
Figure |
Price history (single or normalized dual) |
correlation_heatmap(corr_matrix) |
Figure |
Correlation matrix heatmap |
All visualization functions return Plotly Figure objects — call .show() to display.
markowizard.data
| Function | Returns | Description |
|---|---|---|
fetch_prices(tickers, period="5y", auto_adjust=True) |
pd.DataFrame |
Historical close prices from Yahoo Finance |
compute_monthly_returns(prices) |
pd.DataFrame |
Monthly returns from daily close prices |
Modules
| Module | Description |
|---|---|
core |
MarkowitzOptimizer — efficient frontier optimization |
allocation |
CapitalAllocator — risk-free asset allocation |
visualization |
Plotly chart functions (efficient frontier, pie, CAL, correlation) |
data |
Market-data fetching and monthly-return helpers (yfinance) |
The web application (backend/, frontend/) is kept in the repo but is not
part of the installable package — see Web Application.
Development
See CONTRIBUTING.md for setup instructions and contribution guidelines.
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file markowizard-0.1.0.tar.gz.
File metadata
- Download URL: markowizard-0.1.0.tar.gz
- Upload date:
- Size: 15.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4d77e0d505a32945f74606b2e3308c76d140fd63b93206bb9659ba568a722185
|
|
| MD5 |
f866363b2082e34b9f94c3c4341dc479
|
|
| BLAKE2b-256 |
0a0df1413cf23b3a539098d297720d995adbd9be20dbe2a381be1814c8956d31
|
Provenance
The following attestation bundles were made for markowizard-0.1.0.tar.gz:
Publisher:
python-publish.yml on OutliersAnalytics/MarkoWizard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
markowizard-0.1.0.tar.gz -
Subject digest:
4d77e0d505a32945f74606b2e3308c76d140fd63b93206bb9659ba568a722185 - Sigstore transparency entry: 2784623541
- Sigstore integration time:
-
Permalink:
OutliersAnalytics/MarkoWizard@5233605ec2fe460e04746efdeff8e14008417290 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/OutliersAnalytics
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@5233605ec2fe460e04746efdeff8e14008417290 -
Trigger Event:
push
-
Statement type:
File details
Details for the file markowizard-0.1.0-py3-none-any.whl.
File metadata
- Download URL: markowizard-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a66612f1af25076710b96a17bd623ea62d2c5372dd2b108d67cca909603801ae
|
|
| MD5 |
d655aa699db7dd853c4999c13f642288
|
|
| BLAKE2b-256 |
b4bac6ea9c4d4a3f64b44fae45facefd21b3b7d890309e6e829fdc4b4fec7589
|
Provenance
The following attestation bundles were made for markowizard-0.1.0-py3-none-any.whl:
Publisher:
python-publish.yml on OutliersAnalytics/MarkoWizard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
markowizard-0.1.0-py3-none-any.whl -
Subject digest:
a66612f1af25076710b96a17bd623ea62d2c5372dd2b108d67cca909603801ae - Sigstore transparency entry: 2784623582
- Sigstore integration time:
-
Permalink:
OutliersAnalytics/MarkoWizard@5233605ec2fe460e04746efdeff8e14008417290 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/OutliersAnalytics
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@5233605ec2fe460e04746efdeff8e14008417290 -
Trigger Event:
push
-
Statement type: