Skip to content

Repository files navigation

Long-only Portfolio Research

A compact, reproducible research pipeline that converts dated return forecasts into long-only allocations and backtests them against daily prices. The project is designed for method review: inputs are explicit, execution timing is conservative by default, and synthetic data can exercise the entire workflow without proprietary datasets.

What it does

  1. Aligns forecast dates to the next available trading day.
  2. Estimates covariance from prices strictly before each rebalance.
  3. Solves a fully invested, long-only maximum-Sharpe allocation, with a minimum-variance fallback.
  4. Expands rebalance weights over the trading calendar.
  5. Runs a T+1 backtest with optional turnover-based transaction costs.
  6. Exports daily P&L, summary metrics, and an equity chart.

Quick start

Requires Python 3.10 or newer.

python -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt

python examples/generate_synthetic_data.py
python portfolio_optimizer.py \
  --selections examples/synthetic_data/selections.csv \
  --prices examples/synthetic_data/prices.csv \
  --output results/portfolio_weights.csv \
  --lookback 252

python backtest.py \
  --prices examples/synthetic_data/prices.csv \
  --weights results/portfolio_weights.csv \
  --transaction-cost-bps 5 \
  --output-dir results

The final command writes:

  • backtest_pnl.csv: gross return, turnover, cost, net return, and NAV by day
  • backtest_metrics.csv: CAGR, annualized volatility, Sharpe ratio, cumulative return, and maximum drawdown
  • backtest_equity.png: growth of one dollar, with optional SPX/QQQ overlays

Input schemas

Forecast selections:

Column Meaning
trade_date Forecast/rebalance date
tic Security identifier
predicted_return Expected return used by the optimizer

Daily prices use date (or datadate), tic, and one of adj_close, adjcp, close, prccd, or price. If both prccd and ajexdi are present, the backtester uses prccd / ajexdi.

Weights use trade_date, tic, and weights. Each date is normalized to one, and negative or zero-total portfolios are rejected.

Method and safeguards

  • Covariance estimation excludes the rebalance day's close.
  • T+1 execution is the default, reducing same-day look-ahead risk. --same-day is available only for an intentional comparison.
  • Missing held-security prices and missing selected tickers raise errors instead of being converted into zero returns.
  • Duplicate date/ticker rows follow a documented last-observation-wins rule.
  • --transaction-cost-bps charges half-turnover at a linear per-side rate; initial deployment is not charged.
  • Annual return is a geometric CAGR rather than arithmetic daily mean multiplied by 252.

Important limitations

This is an offline research framework, not a trading system or investment recommendation. Results remain sensitive to forecast construction, survivorship bias, delistings, corporate-action quality, stale prices, liquidity, market impact, taxes, benchmark definitions, and the simplified transaction-cost model. The optimizer can also concentrate heavily when expected returns or covariance estimates are unstable. Validate all source data and assumptions before interpreting results.

No market data or trained model artifacts are included. The example generator creates fictional tickers and deterministic synthetic prices.

Tests

python -m unittest discover -s tests -v

The suite covers duplicate observations, covariance dimensions, long-only constraints, date alignment, weight normalization, T+1 execution, missing-price failures, transaction costs, and geometric performance metrics. GitHub Actions runs it on Python 3.10 and 3.13.

Project origin

This repository is a cleaned, standalone reconstruction of the author's portfolio-optimization and backtesting work from a graduate financial data science project. See NOTICE for the publication boundary.

License

Code is released under the MIT License. Data providers may impose separate terms on any datasets you supply.

About

Reproducible long-only portfolio optimization and T+1 backtesting with synthetic data and tested safeguards.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages