Lifecycle portfolio allocation framework inspired by Choi et al.
Project description
lifecycle-allocation
A Python library implementing a practical lifecycle portfolio choice framework inspired by Choi et al. It combines human capital analysis with visual analytics to produce data-driven stock/bond allocation recommendations.
Why This Matters
Most portfolio allocation "rules" are single-variable heuristics: 60/40, 100-minus-age, target-date funds. They ignore the biggest asset most people own -- their future earning power. A 30-year-old software engineer with $100k in savings and 35 years of income ahead is in a fundamentally different position than a 30-year-old retiree with the same $100k.
This library takes a balance-sheet view of your finances. Your investable portfolio is only part of your total wealth. Future earnings (human capital) act like a bond-like asset, and accounting for them changes how much stock risk you should take. The result is a theoretically grounded, personalized allocation that evolves naturally over your lifecycle -- no arbitrary rules required.
Features
- Core allocation engine -- Merton-style optimal risky share adjusted for human capital
- 4 income models -- flat, constant-growth, age-profile, and CSV-based
- Strategy comparison -- benchmark against 60/40, 100-minus-age, and target-date funds
- Visualization suite -- balance sheet waterfall, glide paths, sensitivity tornado, heatmaps
- CLI interface -- generate full reports from YAML/JSON profiles
- YAML/JSON profiles -- declarative investor configuration
- Leverage support -- two-tier borrowing rate model with configurable constraints
- Mortality adjustment -- survival probability discounting for human capital
Install
pip install lifecycle-allocation
For development:
git clone https://github.com/engineerinvestor/lifecycle-allocation.git
cd lifecycle-allocation
pip install -e ".[dev]"
Requires Python 3.10+.
Quick Start (Python)
from lifecycle_allocation import (
InvestorProfile,
MarketAssumptions,
recommended_stock_share,
compare_strategies,
)
profile = InvestorProfile(
age=30,
retirement_age=67,
investable_wealth=100_000,
after_tax_income=70_000,
risk_tolerance=5,
)
market = MarketAssumptions(mu=0.05, r=0.02, sigma=0.18)
result = recommended_stock_share(profile, market)
print(f"Recommended stock allocation: {result.alpha_recommended:.1%}")
print(f"Human capital: ${result.human_capital:,.0f}")
print(result.explain)
# Compare against heuristic strategies
df = compare_strategies(profile, market)
print(df.to_string(index=False))
Quick Start (CLI)
lifecycle-allocation alloc \
--profile examples/profiles/young_saver.yaml \
--out ./output \
--report
This produces allocation.json, summary.md, and charts in output/charts/.
How It Works
- Compute a baseline risky share (Merton-style):
alpha* = (mu - r) / (gamma * sigma^2) - Estimate human capital H as the present value of future earnings + retirement benefits, discounted by survival probability and a term structure
- Adjust:
alpha = alpha* x (1 + H/W), clamped to [0, 1] (or [0, L_max] with leverage)
Young workers with high H/W ratios get higher equity allocations. As you age and accumulate financial wealth, H shrinks relative to W and the allocation naturally declines -- producing a lifecycle glide path from first principles rather than arbitrary rules.
Example Output
| Archetype | Age | Income | Wealth | H/W Ratio | Recommended Equity |
|---|---|---|---|---|---|
| Young saver | 30 | $70k | $100k | ~15x | ~90%+ |
| Mid-career | 45 | $120k | $500k | ~4x | ~65% |
| Near-retirement | 60 | $90k | $1.2M | ~0.5x | ~40% |
Values depend on market assumptions and risk tolerance. These are illustrative.
Tutorial
Explore the interactive tutorial notebook for a guided walkthrough:
Or run locally:
jupyter notebook examples/notebooks/tutorial.ipynb
Documentation
Full documentation is available at engineerinvestor.github.io/lifecycle-allocation.
Roadmap
| Version | Milestone |
|---|---|
| v0.1 | Core allocation engine, CLI, YAML profiles, strategy comparison, charts |
| v0.5 | Monte Carlo simulation, CRRA utility evaluation, Social Security modeling |
| v1.0 | Full documentation, tax-aware optimization, couples modeling |
Contributing
Contributions are welcome! See CONTRIBUTING.md for development setup, code style, and PR guidelines.
Citation
If you use this library in academic work, please cite both the underlying research and the software:
@techreport{choi2025practical,
title={Practical Finance: An Approximate Solution to Lifecycle Portfolio Choice},
author={Choi, James J. and Liu, Canyao and Liu, Pengcheng},
year={2025},
institution={National Bureau of Economic Research},
type={Working Paper},
number={34166},
doi={10.3386/w34166},
url={https://www.nber.org/papers/w34166}
}
@software{engineerinvestor2025lifecycle,
title={lifecycle-allocation: A Lifecycle Portfolio Choice Framework},
author={{Engineer Investor}},
year={2025},
url={https://github.com/engineerinvestor/lifecycle-allocation},
version={0.1.0},
license={MIT}
}
Disclaimer
This library is for education and research purposes only. It is not investment advice. The authors are not financial advisors. Consult a qualified professional before making investment decisions. Past performance and model outputs do not guarantee future results.
License
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 lifecycle_allocation-0.1.0.tar.gz.
File metadata
- Download URL: lifecycle_allocation-0.1.0.tar.gz
- Upload date:
- Size: 24.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
124202a93a5a7641f9c7b6141ba694e407df26a15665212e17cf63cde16bc0f8
|
|
| MD5 |
e5a573f6016afd79ae68fc68aed2f1a8
|
|
| BLAKE2b-256 |
5209bdc439e5aeb6b5159011e9609272cdc1e6dbb6e914fcfadde9e6bffe463d
|
File details
Details for the file lifecycle_allocation-0.1.0-py3-none-any.whl.
File metadata
- Download URL: lifecycle_allocation-0.1.0-py3-none-any.whl
- Upload date:
- Size: 20.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
713b34cd9f0c9a4d23d114f0d29d5969c721db23c7530a093c0e7ceffa17a5b9
|
|
| MD5 |
88740b8d4d2a5a0d7ccd1f59ba97db83
|
|
| BLAKE2b-256 |
33500aceff455be280582e5a8dfd65b930a7f513ea6567255266aba5249fe6b5
|