Stateless, high-performance Mean-Variance portfolio optimization engine.
Project description
Frontier
Stateless, high-performance Mean-Variance portfolio optimization — as a Python library and a deployable FastAPI service.
Frontier implements Modern Portfolio Theory (MPT) as a library-first package. A quant analyst can import frontier and pass in any returns data — no internet connection required. Optionally, install the [data] extra to pull live prices from Yahoo Finance, or the [server] extra to expose the entire engine as a production-ready REST API.
Features
| Feature | Description |
|---|---|
| Mean-Variance Optimization | Markowitz SLSQP solver with long-only, fully-invested constraints |
| Maximum Sharpe Portfolio | Finds the exact portfolio maximizing risk-adjusted return for any risk-free rate |
| Efficient Frontier Generation | Returns 20 optimized (volatility, return) coordinates for charting |
| FastAPI Service Layer | Two production endpoints — pure-math and ticker-driven — with full CORS and OpenAPI docs |
| Full Type Coverage | py.typed marker shipped; all public functions annotated; MyPy-clean |
| Modular Extras | Install only what you need: core math, data adapter, or HTTP server |
| PyPI Distribution | pip install frontier-quant — no build step required |
| 92% Test Coverage | Offline unit tests, mocked API tests, and optional live network tests |
Installation
Frontier uses optional extras so you install only the dependencies your workflow requires.
Core Library (pure math — no network, no server)
pip install frontier-quant
Installs: numpy, scipy, pydantic.
Use this when you already have your own return data and want the optimizer only.
With the Data Adapter
pip install frontier-quant[data]
Adds: yfinance, pandas.
Use this to fetch live adjusted-close prices from Yahoo Finance.
With the FastAPI Server
pip install frontier-quant[server]
Adds: fastapi, uvicorn.
Use this to run Frontier as a local or containerized REST API.
Everything
pip install frontier-quant[all]
Installs all production extras (data + server). Ideal for full deployments.
Development Setup
git clone https://github.com/Pratham-Mishra225/frontier-api.git
cd frontier-api
python -m venv venv
# Windows
venv\Scripts\activate
# macOS / Linux
source venv/bin/activate
pip install -e ".[dev,data,server]"
Requires Python 3.10, 3.11, or 3.12. CI runs all three.
Quick Start
Option A — Pure Library (bring your own data)
from frontier import optimize
# Provide pre-calculated daily log or percentage returns
returns = {
"AAPL": [0.010, 0.005, -0.002, 0.015, 0.008, 0.003, -0.001, 0.012],
"MSFT": [0.008, 0.010, 0.000, -0.005, 0.012, 0.007, 0.002, 0.009],
"GOOG": [-0.010, 0.020, 0.010, -0.015, 0.005, 0.001, 0.018, -0.003],
}
result = optimize(returns, risk_free_rate=0.04)
print("Max Sharpe weights:")
for ticker, weight in result["optimal_portfolio"]["weights"].items():
print(f" {ticker}: {weight:.1%}")
print(f"\nExpected annual return : {result['optimal_portfolio']['expected_annual_return']:.1%}")
print(f"Annual volatility : {result['optimal_portfolio']['annual_volatility']:.1%}")
print(f"Sharpe ratio : {result['optimal_portfolio']['sharpe_ratio']:.4f}")
print(f"Frontier points : {len(result['frontier_curve'])}")
Expected output:
Max Sharpe weights:
AAPL: 0.0%
GOOG: 100.0%
MSFT: 0.0%
Expected annual return : 168.0%
Annual volatility : 68.5%
Sharpe ratio : 2.3939
Frontier points : 20
Note: toy data (8 observations) produces unrealistic numbers. Use 252+ daily observations or real data for meaningful results.
Option B — With Yahoo Finance Data Adapter
pip install frontier-quant[data]
from frontier import fetch_data, optimize
# Fetches 3 years of adjusted-close prices and computes daily returns
returns = fetch_data(["AAPL", "MSFT", "GOOG"], lookback_years=3)
result = optimize(returns, risk_free_rate=0.04)
print("Optimal weights:")
for ticker, w in result["optimal_portfolio"]["weights"].items():
print(f" {ticker}: {w:.1%}")
Option C — As a REST API
pip install frontier-quant[server]
uvicorn frontier.api.main:app --reload
Then in another terminal:
curl -X POST http://localhost:8000/v1/optimize \
-H "Content-Type: application/json" \
-d '{
"returns": {
"AAPL": [0.010, 0.005, -0.002, 0.015, 0.008],
"MSFT": [0.008, 0.010, 0.000, -0.005, 0.012]
},
"risk_free_rate": 0.04
}'
Visit http://localhost:8000/docs for the interactive Swagger UI.
Example Output
A real optimization over 3 years of daily data for AAPL / MSFT / GOOG returns a response like:
{
"status": "success",
"metadata": {},
"optimal_portfolio": {
"sharpe_ratio": 1.2534,
"expected_annual_return": 0.3841,
"annual_volatility": 0.2748,
"weights": {
"AAPL": 0.1200,
"GOOG": 0.8800,
"MSFT": 0.0000
}
},
"frontier_curve": [
{ "volatility": 0.2134, "return_rate": 0.1523 },
{ "volatility": 0.2198, "return_rate": 0.1733 },
"... 18 more points ..."
]
}
Architecture Overview
Frontier is designed around a strict one-way dependency rule:
frontier.models ←── frontier.core ←── frontier.adapters ←── frontier.api
(schemas) (optimizer) (yfinance) (FastAPI)
No inner layer may import from an outer layer.
| Module | Responsibility | Key Dependencies |
|---|---|---|
frontier.models |
Pydantic request/response schemas | pydantic |
frontier.core |
Pure numerical optimization engine | numpy, scipy |
frontier.adapters |
Yahoo Finance data retrieval + cleaning | yfinance, pandas |
frontier.api |
FastAPI HTTP endpoints + CORS | fastapi, uvicorn |
frontier.exceptions |
Typed exception hierarchy | — |
frontier.core is intentionally isolated. It knows nothing about the internet, tickers, or HTTP. It receives a Dict[str, List[float]] and returns a structured dict. This makes it trivially testable, embeddable, and replaceable.
Project Structure
frontier-api/
├── pyproject.toml # Build config, extras, tool settings
├── README.md
├── LICENSE
├── docs/
│ ├── index.md # Documentation hub
│ ├── installation.md # Detailed install guide
│ ├── quickstart.md # First steps and examples
│ ├── api-reference.md # Full public API documentation
│ ├── architecture.md # Design decisions and dependency flow
│ └── examples.md # Copy-paste runnable examples
├── tests/
│ ├── conftest.py # Shared fixtures and markers
│ ├── test_optimizer.py # Core optimizer unit tests
│ ├── test_adapters.py # yfinance adapter tests (mocked)
│ ├── test_api.py # FastAPI endpoint tests (fully offline)
│ ├── test_exceptions.py # Exception hierarchy tests
│ └── test_full_workflow.py # Live integration tests (@network)
└── src/
└── frontier/
├── __init__.py # Public API: optimize, fetch_data
├── py.typed # PEP 561 marker
├── exceptions.py # FrontierError, ConvergenceError, ...
├── core/
│ └── optimizer.py # optimize_portfolio, portfolio_performance
├── adapters/
│ └── yfinance_client.py # fetch_historical_returns
├── models/
│ └── schemas.py # OptimizeRequest, OptimizeResponse, ...
└── api/
└── main.py # FastAPI app, /v1/optimize, /v1/optimize_from_tickers
Development
Set Up Environment
python -m venv venv
venv\Scripts\activate # Windows
# or: source venv/bin/activate # macOS / Linux
pip install -e ".[dev,data,server]"
Run Tests
# Fast offline suite (CI mode — skips live Yahoo Finance calls)
pytest -m "not network"
# Full suite including live network tests
pytest
# With coverage report
pytest --cov=frontier --cov-report=term-missing -m "not network"
# A single module
pytest tests/test_optimizer.py -v
Lint and Type-Check
# Ruff — linting and formatting
ruff check .
ruff format .
# MyPy — static type checking
mypy src
Run the Dev Server
uvicorn frontier.api.main:app --reload
Open http://localhost:8000/docs for Swagger UI or http://localhost:8000/redoc for ReDoc.
CI/CD
GitHub Actions runs on every push to main and every pull request.
Workflow (.github/workflows/ci.yml):
- Matrix: Python 3.10, 3.11, 3.12 — each in its own job
- Install:
pip install -e ".[dev,server,data]"— all extras - Ruff: linting (
ruff check .) - MyPy: static typing (
mypy src) - Tests: offline suite (
pytest -m "not network")
Live Yahoo Finance tests are marked @pytest.mark.network and excluded from CI. Run them locally with pytest (no -m flag).
API Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/health |
None | Liveness probe |
POST |
/v1/optimize |
None | Pure-math endpoint — bring your own returns |
POST |
/v1/optimize_from_tickers |
None | Convenience endpoint — pass tickers, get results |
Interactive docs automatically available at /docs (Swagger) and /redoc (ReDoc).
See docs/api-reference.md for full parameter and response documentation.
Roadmap
v0.1 ✅ Released
- Mean-variance optimization engine (SLSQP)
- Maximum Sharpe portfolio
- Efficient frontier (20 points)
- Yahoo Finance data adapter
- FastAPI service with two endpoints
- 92% test coverage
- MyPy + Ruff clean
- CI on Python 3.10 / 3.11 / 3.12
- TestPyPI distribution
v0.2 — Planned
- Minimum volatility portfolio endpoint
- Portfolio analytics (Sortino ratio, max drawdown, beta)
- Frontier visualization helpers (matplotlib / plotly)
- Portfolio comparison (multiple strategies side-by-side)
v0.3 — Planned
- Risk parity (equal-risk-contribution) optimization
- Black-Litterman model
- Custom return priors
- Extended lookback with data caching
v1.0 — Target
- Stable, versioned public API with deprecation policy
- Full Sphinx / MkDocs documentation site
- PyPI production release
- Docker image for the FastAPI service
Contributing
Contributions are welcome.
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Implement changes with tests
- Ensure
ruff check .,mypy src, andpytest -m "not network"all pass - Open a pull request against
main
Please ensure:
- All public functions have type annotations
- New features include unit tests
- Existing test coverage does not decrease
- No internal implementation details leak into the public API
License
MIT License. See LICENSE for details.
Disclaimer
This software is provided for educational and research purposes only. It does not constitute financial advice, investment recommendations, or guarantees of future performance. Users are solely responsible for their own investment decisions.
Author
Pratham Mishra
Computer Engineering Student · Quantitative Finance Enthusiast · Open Source Developer
Project details
Release history Release notifications | RSS feed
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 frontier_quant-0.1.1.tar.gz.
File metadata
- Download URL: frontier_quant-0.1.1.tar.gz
- Upload date:
- Size: 24.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
909c8aab601296451beec9f6adcc3c22bb7a7b5ea2508d994396f552a0d7f8bc
|
|
| MD5 |
6afaca58834845ed9d08abbf425b3e88
|
|
| BLAKE2b-256 |
dbf59eefd4066dca6d995448747defa653fb6da3869fc1f45296646d575ce7dd
|
File details
Details for the file frontier_quant-0.1.1-py3-none-any.whl.
File metadata
- Download URL: frontier_quant-0.1.1-py3-none-any.whl
- Upload date:
- Size: 15.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9846fde43e70f7738f658e9d5dd012017ff8be147f7c45fe42afa613300948c8
|
|
| MD5 |
754495b0877b2d914683ebd8127a6566
|
|
| BLAKE2b-256 |
1fac4900f2a943c319328b4e82669601908463a2719d100b12f516be95480bd8
|