A local Rust app for stock portfolio optimization and historical tests. It estimates returns and covariance from supplied prices, then finds stock weights within a volatility budget and holding limit.
The default universe combines the stocks held by SPY with current Nasdaq-100 constituents. You can also use your own stock symbols, a price CSV, or synthetic demo data. SPY and QQQ provide comparisons and do not enter the stock allocation.
Portfolio Lab is a desktop app. It opens in its own window and needs no browser, Python, or Docker. The pages come from inside the program, so the app opens no network port.
CI builds the programs for each change. Open the latest successful CI run, then download an artifact under Artifacts. GitHub asks you to sign in for artifacts and keeps them for 90 days.
| System | Artifact | Start the app |
|---|---|---|
| Windows 10 or 11, 64-bit | portfolio-lab-Windows |
Extract the ZIP. Double-click Portfolio Lab.exe. |
| Linux, 64-bit, with WebKitGTK 4.1 | portfolio-lab-Linux |
Extract the ZIP, then portfolio-lab-linux-x86_64.tar.gz. Run ./portfolio-lab. |
Each artifact also contains the command-line program efficient-frontier. The Windows app uses the Microsoft Edge WebView2 Runtime, which Windows 11 includes. The Linux app uses the system's WebKitGTK 4.1: on Debian or Ubuntu, install libwebkit2gtk-4.1-0; on Arch, install webkit2gtk-4.1. The programs have no code signature, so Windows SmartScreen can show a warning for a downloaded file. Select More info, then Run anyway.
Install Rust 1.90 or newer with rustup. Rust also needs a C compiler and linker, and Linux needs the WebKitGTK 4.1 files for the window. The table gives the tools for each system. Then download and extract the repository.
| System | Install first | Start the app |
|---|---|---|
| Windows | Microsoft C++ build tools with "Desktop development with C++". The rustup installer offers them. | Open Start Portfolio Lab.bat. |
| macOS | Xcode Command Line Tools. Run xcode-select --install. |
Open Start Portfolio Lab.command. |
| Linux | A C compiler and WebKitGTK 4.1. Debian or Ubuntu: sudo apt install build-essential libwebkit2gtk-4.1-dev. Arch: sudo pacman -S --needed base-devel webkit2gtk-4.1. |
Run bash run.sh from the project folder. |
The first start compiles the app. It needs internet access and takes a few minutes. Later starts are fast. Close the window to stop the app. Demo and CSV studies work offline after the first build.
For a Git checkout:
git clone https://github.com/sammcentee/efficientfrontier.git
cd efficientfrontier
bash run.shSee the user guide for setup errors and platform limits.
- Read the model equations on the Efficient Frontier Analysis for US Equities opening page. Open Settings to choose stocks and history, or select the Demo stock source.
- Set Annual volatility budget and Holding limit in the two number fields. The holding limit defaults to 10%.
- Select Build portfolio.
- Inspect the latest stock holdings and their fitted frontier. Then compare in-sample and out-of-sample results, and the later replay with SPY and QQQ.
- Select Export, then choose where to save the offline report and exact input/result CSVs.
The app opens in a dark theme. The sun button beside Settings changes to the light theme, and the app keeps your choice. Exported reports use the light theme.
The app starts the volatility budget at QQQ's latest one-year annualized volatility, with its price date shown. This reference is separate from the stock fit window. Your edits take precedence. If the reference is unavailable, the app labels its 20% manual fallback.
A 10% holding limit needs at least ten eligible stocks. The six-asset synthetic demo uses a 20% limit. An infeasible budget or holding limit stops the calculation.
The optimizer maximizes the estimated annual arithmetic mean return. Weights are long-only, sum to 100%, and obey the holding limit. The volatility budget limits the covariance-based estimate, not future volatility or losses. The objective is not CAGR or maximum Sharpe. The R comparison explains the original method, its return-formula error, and the corrected stock-only results.
The app fixes these assumptions:
| Assumption | Value |
|---|---|
| Initial training / later test | First 70% / final 30% of return observations |
| Walk-forward tests | Fits on the first 50%, 60%, 70%, 80%, and 90% of returns. Each holds through the next tenth only |
| Holding rule | Fit once on the initial sample, buy once, then let weights drift |
| Execution | Close after the last price used in the fit |
| Trading fees | 0.10% of the initial purchase. No later trades or terminal sale |
| Covariance | Ordinary sample covariance, without shrinkage |
The first test interval stays in cash at zero interest. All replay paths then buy once and hold. The model omits taxes, currency conversion, and market impact. The method reference gives the equations and accounting.
Overview starts with the latest stock holdings, followed by their fitted frontier, then an in-sample and out-of-sample comparison. Company cards show names, weights, and a short business description from current Yahoo Finance profiles when available. CSV and demo studies stay offline. Five walk-forward tests fit the model on earlier stock prices only, then hold that allocation through a later window that the fit did not see. Each window shows the same allocation on its own fit dates (in sample) and on the later dates (out of sample). The growth chart, replay metrics, and exact return reconciliation follow. Open Settings from the results navigation, or Data & method for coverage and trades.
The latest fit includes the replay dates. The replay did not use the latest holdings. The fit chooses one allocation using all supplied history. The replay buys one allocation from the initial training sample, then holds it through the later prices without rebalancing. The two panels use different dates, weights, and fee treatment.
| Result | Meaning |
|---|---|
| Fitted annual mean | Average return from a fit's history, using its target weights, before fees |
| Fitted volatility | Sample volatility from the fit's dates and target weights; the budget applies to this estimate |
| Fitted frontier | Best estimated stock returns across risk levels, with SPY/QQQ as separate reference points |
| In sample | Buy-and-hold result of an allocation on the same dates that chose it, measured from the purchase close |
| Out of sample | Buy-and-hold result of an allocation on later dates that its fit did not use, measured from the purchase close |
| Historical replay | Net performance of the initial allocation, bought once and held through later prices |
| Latest holdings | A new fit using all supplied history; these weights did not produce the earlier test |
Equal-weight stocks, SPY, and QQQ also buy once and hold. The equal-weight reference starts with equal weights but lets them drift. Within the replay, all comparisons use the same dates and fee rate, but their risks can differ. A benchmark with higher mean return and lower volatility beat the stock rule in that replay, even if the latest fitted portfolio sits above it on the fitted frontier chart.
The app displays typeset equations for the optimizer, volatility budget, buy-and-hold accounting, and performance metrics. The introduction appears beside the controls. Fitted moments describe the starting weights. They do not describe a historical buy-and-hold path.
The test status describes coverage, realized risk, and comparison results. It is not proof of future outperformance. Current member lists and complete-history requirements favor survivors. Prior review of the test dates also limits their value as new evidence.
Run the app's model without the window:
# Offline demo
cargo run --release --bin efficient-frontier -- --target-volatility .20 --max-weight .20
# Supplied stock prices, with at least ten eligible stocks
cargo run --release --bin efficient-frontier -- --csv data/prices.csv --target-volatility .20The first run compiles the CLI. You can also build it one time with cargo build --release. Then run ./target/release/efficient-frontier with the same flags. On Windows, the program is target\release\efficient-frontier.exe.
Output goes to results/latest/. Use --output to preserve separate studies. Reports contain the input prices. Keep private data in ignored data/ or private/ folders.
- User guide: inputs, settings, reports, and separate legacy tools.
- Model and accounting: optimization, trade timing, fees, and metrics.
- Benchmarks: SPY/QQQ comparisons and their limits.
- Contributing: development setup and checks.
- Validation record: dated checks and limitations.
The original Efficient Frontier v1.12.R and spy_holdings.ods remain historical files. R is not required. The Rust version replaced the Python release. Git history keeps the last Python release at commit 2e5f21c. Earlier findings remain in the stock study and research archive.
Code and documentation use the MIT License. Third-party terms still apply. The software license does not grant rights to download or redistribute market data.