Skip to content

Repository files navigation

eToro Account Statement Summariser

A single-command Python CLI that turns an eToro Excel account statement into a clean per-metric performance summary in the terminal and a CSV alongside the input file.

CI CodeQL SonarCloud Monthly Dependency Refresh License: MIT Python 3.11+

What it is

etoro_statement is a small, focused command-line utility for retail eToro users. eToro's exported account statement is a multi-sheet Excel workbook. This tool ignores the noise and pulls only the aggregates that matter (deposits, withdrawals, realised gains, dividends, fees, equity), then prints a coloured, sectioned Rich table and writes the same numbers to a _summary.csv next to the source file for further analysis.

It does one thing, in one script (etoro_summary.py, about 300 lines), with no configuration and no persistent state.

Pipeline

flowchart TD
    A[eToro .xlsx statement] --> B[pandas read_excel]
    B --> C{Two sheets}
    C -->|Account Summary| D[deposits, withdrawals,<br/>ending equities]
    C -->|Financial Summary| E[trade P/L, dividends,<br/>other income, fees]
    D --> F[metrics dict]
    E --> F
    F --> G[calculate_roi]
    G --> H[Rich table<br/>4 sections]
    F --> I[pandas to_csv]
    H --> J[terminal]
    I --> K[&lt;input&gt;_summary.csv]
Loading

Features

Grounded in etoro_summary.py:

  • Reads the Account Summary and Financial Summary sheets from an eToro-exported .xlsx.
  • Extracts 11 metrics: total deposits, total withdrawals, net investment, realised gains, dividend income, other income, total expenses and fees, net realised profit, current realised equity, current unrealised equity, unrealised profit.
  • Computes Return on Investment as net_realized_profit / abs(net_investment) (percentage, with sign).
  • Robust to eToro's shifting column names: tries four variants of the amount column ("Amount\r\n in (USD)", "Amount in (USD)", "Amount\nin (USD)", "Amount (USD)") and falls back to any column containing both Amount and USD.
  • Terminal output uses rich with four sections (Investment, Realized, Unrealized, Performance); positive values render green, negative red.
  • Persists the same metrics to a CSV named <input>_summary.csv in the same directory as the source workbook.
  • Exits with a clear error on missing input or unloadable file.
  • If no amount column can be matched at all, it prints ERROR: Could not find amount column in Financial Summary sheet but still exits 0 and still writes the CSV, with every Financial Summary metric left at zero. Check that line before trusting the numbers.

Example output

Rich-formatted eToro financial summary table in the terminal

The CSV counterpart holds the same rows as Metric,Value pairs.

Requirements

  • Python 3.11 or newer (CI runs on 3.12).
  • uv for dependency management.

Runtime dependencies (pinned in uv.lock):

  • pandas (3.x)
  • openpyxl (3.1+)
  • rich (15.x)
  • tabulate (0.10+, declared but not imported by the code)

Installation

git clone https://github.com/weirdapps/etoro_statement.git
cd etoro_statement
uv sync --frozen

uv sync --frozen installs the exact versions in uv.lock. Drop --frozen if you want uv to re-resolve.

Usage

Point the script at any eToro-exported .xlsx account statement:

uv run python etoro_summary.py path/to/etoro-account-statement.xlsx

The tool will:

  1. Load the Account Summary and Financial Summary sheets.
  2. Print a Rich-formatted summary table to the terminal.
  3. Write path/to/etoro-account-statement_summary.csv next to the input.

No sample workbook is committed. .gitignore excludes *.xlsx, *.xls and *.csv, so a real statement and its generated summary stay out of this public repository. Export your own statement from eToro and pass its path.

How eToro's data maps to the output sections

Section Metrics Source in the workbook
Investment Deposits, Withdrawals, Net Investment Account Summary rows Deposits, Withdrawals
Realized Realized Gains, Dividend Income, Other Income, Expenses and Fees, Net Realized Profit, Current Realized Equity Financial Summary (rows with Profit or Loss, Dividend, fee, charge, plus Account Summary row Ending Realized Equity)
Unrealized Unrealized Profit, Current Unrealized Equity Account Summary row Ending Unrealized Equity, minus realised equity
Performance Return on Investment Computed: net_realized_profit / abs(net_investment)

Categorisation of Financial Summary rows follows the rules in process_etoro_statement:

  • Any row whose Name contains Profit or Loss with a positive amount is added to Realized Gains.
  • Any row whose Name contains Dividend is added to Dividend Income.
  • Any row whose Name contains fee or charge, or any other negative amount not already categorised, is added to Total Expenses and Fees (stored as a positive number, subtracted from profit at the end).
  • Any remaining positive amount is added to Other Income.

Development

Install dev tools and run the checks:

uv sync --frozen
uv run ruff check .
uv run ruff format --check .
uv run pytest

The test suite (tests/test_etoro_summary.py) covers ROI edge cases (positive, negative, zero investment, zero profit), the shape of the metrics dictionary, table construction on synthetic data, and the missing-file error path.

Optionally install the pre-commit hooks (.pre-commit-config.yaml), which add mypy, gitleaks, yamllint and markdownlint on top of ruff. None of these four run in CI.

uv run --with pre-commit pre-commit install

Continuous Integration

All workflows run against the master branch.

Workflow File Trigger
CI (ruff + pytest) ci.yml push and PR to master
CodeQL codeql.yml push, PR, weekly (Mon 06:00 UTC)
SonarCloud sonarcloud.yml push, PR, manual (skipped if SONAR_TOKEN unset)
Monthly Dependency Refresh deps-refresh.yml 6th of each month, 04:23 UTC (opens PR with a fresh uv.lock)
Dependabot auto-merge dependabot-auto-merge.yml Dependabot PRs. A thin caller that delegates to the shared reusable workflow weirdapps/shared-workflows/.github/workflows/dependabot-auto-merge.yml@main

The caller passes no inputs, so the shared workflow's defaults apply: patch and minor updates auto-merge once the PR's own checks are green, a standalone major stays open for review, and a grouped update auto-merges even when its aggregate level is major.

Project layout

etoro_statement/
├── etoro_summary.py            # entire application
├── tests/
│   └── test_etoro_summary.py   # 11 tests across 5 classes
├── example_output.png          # screenshot of the Rich table
├── pyproject.toml              # deps + ruff/mypy/pytest config
├── uv.lock                     # pinned dependency graph
├── .pre-commit-config.yaml     # ruff, mypy, gitleaks, yamllint, markdownlint
└── .github/workflows/          # CI, CodeQL, SonarCloud, deps refresh, Dependabot auto-merge

Security

Report vulnerabilities per SECURITY.md. Do not open a public issue for security reports.

License

MIT. Copyright (c) 2026 Dimitris Plessas.

About

eToro account statement processor — extracts ROI, dividends, and performance metrics from Excel exports

Topics

Resources

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages