A personal tool to track and analyze the performance of an equity portfolio, benchmarked against the S&P 500 Total Return index.
Built in Python with pandas and numpy. Input data comes from Fineco bank statements, market prices from Yahoo Finance.
This project was developed with the support of AI tools such as Google AI and Claude AI, primarily for debugging, resolving implementation issues, and improving code structure.
AI was used as a learning and productivity assistant, not as a substitute for understanding. Every suggestion was carefully reviewed, tested, and adapted to fit the logic of the system. This process helped in:
Debugging complex issues in data pipelines and calculations Understanding edge cases in portfolio accounting and return computation Improving code modularity and overall architecture Strengthening Python proficiency, especially with pandas and numerical computations
Rather than relying on generated code blindly, I focused on understanding the flow, financial logic, and mathematical foundations behind each component — including return calculations (TWR, XIRR), risk metrics (volatility, Sharpe, beta), and attribution logic.
Prior to this, I had built an equity analytics project that included Monte Carlo simulation, Sharpe ratio, and beta calculations on NIFTY 50 stocks using ~63K data points over 5 years.
This project goes significantly deeper by moving from market-level analysis to real portfolio-level analytics.
Key advancements in this project: End-to-end portfolio engine (cash flows, positions, returns), Benchmark comparison against S&P 500 Total Return, Accurate handling of external cash flows (TWR vs MWR/XIRR), Rolling metrics to analyze performance across time, Per-ticker contribution & attribution analysis, Real-world data integration (broker statements, FX conversion, missing data handling).
This project helped me develop a much deeper understanding of:
Portfolio return methodologies (TWR vs MWR), Risk and performance measurement in real-world scenarios, Data pipeline design for financial systems, Handling imperfect financial data (missing prices, timing assumptions, FX), Translating financial theory into production-like code.
Overall, this project represents a shift from theoretical analytics to practical, real-world portfolio analysis, combining finance, data science, and software engineering.
portfolio_analytics/
│
├── data/ ← Input files (do not share publicly)
│ ├── Lista_Titoli.xlsx ← Transaction history exported from Fineco
│ ├── isin_ticker.csv ← ISIN → Yahoo Finance ticker mapping
│ └── external_cashflows.csv ← External deposits and withdrawals
│
├── cache/ ← Prices downloaded from Yahoo (regenerable)
│ ├── prices_close.parquet
│ └── fx_to_eur.parquet
│
├── outputs/ ← Files generated by the program
│ ├── report_metrics.json ← Full-period performance metrics
│ ├── report_daily.csv ← Daily time series
│ ├── report_rolling.csv ← Rolling metrics on 180-day window
│ ├── reconcile_lastday_by_ticker.csv ← Current holdings by ticker
│ ├── contrib_daily.csv ← Daily contribution by ticker
│ ├── contrib_total.csv ← Total contribution by ticker
│ ├── contrib_by_year.csv ← Yearly contribution by ticker
│ └── charts/
│ ├── equity_port_vs_sp500tr.png ← Equity curve
│ ├── drawdown_portfolio.png ← Drawdown
│ ├── contrib_total_top10.png ← Top 10 contributors
│ ├── rolling_sharpe.png ← Rolling Sharpe ratio
│ ├── rolling_vol.png ← Rolling volatility
│ └── rolling_beta.png ← Rolling beta
│
└── src/ ← Source code
├── main.py ← Main entry point
├── config.py ← Constants and paths
├── io_fineco.py ← Fineco file parsing
├── market_data.py ← Price and FX downloads from Yahoo Finance
├── portfolio_engine.py ← Positions, cash and return calculations
├── attribution.py ← Per-ticker performance attribution
├── metrics.py ← Risk/return metrics including rolling
└── viz.py ← Chart generation
data/isin_ticker.csv — maps each security from its ISIN to its Yahoo Finance ticker:
isin,ticker
IE0031442068,IUSA.MI
US0378331005,AAPL
To find the correct ticker: go to finance.yahoo.com, search by security name or ISIN, and use the symbol shown.
data/external_cashflows.csv — every deposit or withdrawal on the account:
date,amount_eur,type
2020-09-16,3000,DEPOSIT
2021-03-01,2000,DEPOSIT
2022-01-10,-500,WITHDRAWAL
Deposits = positive numbers. Withdrawals = negative numbers.
The type column is optional but recommended for readability.
Timing note: the analysis starts from the first trade in the Fineco ledger, not from the first deposit. Funds deposited before the first trade are automatically carried forward to the first available trading day.
data/Lista_Titoli.xlsx — exported directly from Fineco:
Go to "Dossier Titoli" → "Movimenti" → export to Excel.
python -m src.mainrm cache/prices_close.parquet cache/fx_to_eur.parquet
python -m src.mainAll performance metrics computed over the full historical period.
Metrics computed on a rolling 180-trading-day window (~9 months). Contains:
rolling_sharpe_180d— Sharpe ratio over the last 180 daysrolling_vol_port_180d— portfolio volatility over the last 180 daysrolling_vol_bench_180d— benchmark volatility over the last 180 daysrolling_beta_180d— portfolio beta vs S&P 500 over the last 180 days
One row per trading day with:
mv_total_eur— total portfolio value (assets + cash)mv_assets_eur— asset value only (excluding cash)cash_eur— available cash balanceexternal_flows_eur— deposits/withdrawals on that dayport_ret/bench_ret— daily return for portfolio and benchmarkport_eq/bench_eq— equity curve (indexed to 1.0)
A snapshot of the portfolio on the last available trading day. Compare against your Fineco balance to verify data accuracy.
Per-ticker contribution to total return, year by year.
is_partial_year = True flags incomplete years (e.g. current year):
the figure shown is YTD, not annualized.
| Metric | What it measures |
|---|---|
port_ann_return |
Portfolio CAGR. Compounded average yearly growth rate. |
bench_ann_return |
Same calculation for the S&P 500 Total Return benchmark. |
xirr_mwr |
Return from the investor's perspective, accounting for deposit timing. |
| Metric | What it measures |
|---|---|
port_ann_vol |
Annualized volatility. How much daily returns fluctuate. |
port_max_drawdown |
Largest peak-to-trough loss across the entire history. |
| Metric | What it measures | Good if... |
|---|---|---|
port_sharpe |
Excess return divided by total volatility. | > 1.0 |
port_sortino |
Like Sharpe but penalizes only downside volatility. | > 1.0 |
port_calmar |
Annualized return divided by absolute max drawdown. | > 1.0 |
| Metric | What it measures |
|---|---|
tracking_error |
How much daily returns deviate from the S&P 500. |
information_ratio |
Active return divided by tracking error. Negative = underperforming benchmark. |
| Metric | What it measures |
|---|---|
capm_beta |
Market sensitivity. Beta 0.67 = portfolio moves ~6.7% for every 10% market move. |
capm_alpha_annual |
Return above what the beta level would predict. |
capm_r2 |
Share of portfolio movement explained by the S&P 500. |
Rolling metrics show how the risk/return profile has changed over time, rather than collapsing everything into a single number for the full period.
| Metric | What it shows |
|---|---|
rolling_sharpe_180d |
Periods of strong/weak risk-adjusted performance. |
rolling_vol_port_180d |
High/low volatility regimes in the portfolio. |
rolling_vol_bench_180d — |
S&P 500 volatility over the same period (for comparison). |
rolling_beta_180d |
Whether the portfolio is becoming more or less market-sensitive over time. |
The program follows a linear 14-step pipeline (see main.py):
Input (Fineco + CSV files)
↓
Yahoo Finance price download
↓
Trading calendar construction
↓
Cash flow alignment
(pre-first-trade deposits carried forward to first trading day)
↓
Currency conversion → EUR
↓
Portfolio Engine
├── Daily positions (shares held per ticker per day)
├── Daily cash (trades + external deposits)
└── Market value + TWR returns
↓
Full-period performance metrics
↓
Rolling metrics (180-day window)
↓
Per-ticker attribution
↓
Charts and output files
Forward fill capped at 5 days: missing prices are propagated for a maximum of 5 days. Beyond that threshold, the value becomes NaN to flag the issue explicitly.
Cash flow timing assumption: external deposits are assumed to arrive at end of day, the standard convention when intraday timing is unavailable.
Pre-calendar deposits: funds deposited before the first trade are automatically accumulated and assigned to the first available trading day.
Dividends: cash dividends increase the portfolio's cash balance. If Fineco reinvests them automatically they appear as BUY transactions — both cases are handled correctly.
pip install pandas numpy scipy yfinance matplotlib openpyxl pyarrowThis project is designed as a continuously evolving system. The following enhancements are planned to improve scalability, robustness, and real-world usability.
Replace flat files (.csv, .xlsx, .parquet) with a structured database: SQLite (local lightweight setup), PostgreSQL (production-ready),
Build a proper data schema for: Transactions (ledger), Market prices, FX rates, Portfolio snapshots, Enable incremental updates instead of full reprocessing, Add versioning for historical data consistency.
Automate data ingestion from broker statements: Scheduled jobs (daily/weekly refresh), Auto-detection of new transactions.
Replace manual Yahoo Finance pulls with: Cached API layer, Retry + validation mechanisms Introduce logging and monitoring for pipeline reliability
Extend support beyond Fineco to: Zerodha / Groww / Interactive Brokers Add new asset classes: Mutual funds / ETFs (full support) Bonds and fixed income instruments Crypto assets (via exchange APIs) Standardize a universal transaction schema across brokers
Add advanced analytics: Factor exposure (Fama-French factors) Sector allocation & drift tracking Performance attribution (allocation vs selection effect) Risk enhancements: Value at Risk (VaR) / Conditional VaR Stress testing (market crash scenarios) Benchmark flexibility: Custom benchmarks (NIFTY, sector indices, blended benchmarks)
Optimize heavy computations using: Vectorization improvements in pandas/numpy Optional use of Polars for faster data processing Introduce caching layers for: Computed metrics Intermediate datasets Parallelize data fetching and processing where applicable 6. User Interface & Visualization Build an interactive dashboard using: Streamlit or Dash Features: Portfolio overview (P&L, allocation, drawdown) Interactive charts (zoom, filter by ticker/time) Real-time metric updates Export reports in user-friendly formats (PDF / HTML dashboards)
Extend the system into a backtesting engine: Strategy simulation on historical data Comparison of multiple strategies vs actual portfolio Add support for: Rebalancing strategies Dollar-cost averaging simulations Factor-based portfolios
Add comprehensive unit and integration tests: Portfolio engine validation Cash flow and return calculations Introduce CI/CD pipeline: Automated testing on every commit Improve documentation: Function-level docstrings Type validation and stricter schemas (e.g., pydantic)
Expose core functionality via a REST API: Portfolio metrics endpoint Time-series data endpoint Enable integration with: Frontend dashboards External applications
Add configuration management (env-based configs) Implement robust error handling and alerts Secure handling of sensitive data (API keys, financial data) Prepare for deployment: Docker containerization Cloud deployment (AWS/GCP) Long-Term Vision
The long-term goal is to evolve this project from a personal analytics tool into a modular portfolio analytics platform, capable of:
Handling institutional-grade data pipelines Supporting multiple users and portfolios Providing real-time analytics and insights Bridging the gap between finance theory and production systems