Skip to main content

A Python package for financial modeling and reporting

Project description

pyproforma

A Python library for building financial models — a code-first alternative to Excel for pro formas, projections, and structured financial tables.

pip install pyproforma

Why

Spreadsheets are the default tool for financial modeling, but they have real problems: no version control, no testing, formulas hidden inside cells, and no easy way to generate the same model for multiple scenarios. pyproforma is designed for analysts who want the benefits of code — reproducibility, testability, version history — without giving up the tabular output that finance people actually use.


How it works

Define a model by subclassing ProformaModel and declaring line items as class attributes. Instantiate it with a list of periods and the library calculates everything.

from pyproforma import ProformaModel, FixedLine, FormulaLine, Assumption

class IncomeStatement(ProformaModel):
    growth_rate = Assumption(value=0.10)

    revenue = FixedLine(
        values={2024: 500_000, 2025: 550_000, 2026: 605_000},
        label="Revenue",
        tags=["operating"],
    )
    cogs = FormulaLine(
        formula=lambda li, t: li.revenue[t] * 0.55,
        label="Cost of Goods Sold",
        tags=["operating"],
    )
    gross_profit = FormulaLine(
        formula=lambda li, t: li.revenue[t] - li.cogs[t],
        label="Gross Profit",
    )
    tax_expense = FormulaLine(
        formula=lambda li, t: li.gross_profit[t] * 0.21,
        label="Tax Expense",
    )
    net_income = FormulaLine(
        formula=lambda li, t: li.gross_profit[t] - li.tax_expense[t],
        label="Net Income",
    )

model = IncomeStatement(periods=[2024, 2025, 2026])

Access results directly:

model["net_income"][2025]   # 218_130.0
model["gross_profit"][2024] # 225_000.0

Tables

Generate formatted tables for display or export. Tables are the primary output — they render to HTML (for Jupyter notebooks), Excel, or a pandas DataFrame.

# All line items
model.tables.line_items().show()

# Single item with period-over-period analysis
model.tables.line_item("net_income", include_percent_change=True).show()

# Show what feeds into a calculated line item
model.tables.precedents("net_income").show()

# Custom layout using a template
from pyproforma import Format
from pyproforma.tables import HeaderRow, LabelRow, ItemRow

table = model.tables.from_template([
    HeaderRow(background_color="lightblue"),
    LabelRow(label="Income Statement"),
    ItemRow(name="revenue", value_format=Format.THOUSANDS_K),
    ItemRow(name="cogs",    value_format=Format.THOUSANDS_K, bottom_border="single"),
    ItemRow(name="gross_profit", bold=True, value_format=Format.THOUSANDS_K),
])
table.show()

Example table

Export to Excel with formatting preserved:

model.tables.line_items().to_excel("income_statement.xlsx")

Scenarios and comparisons

A model is just a class — create multiple instances with different inputs to compare scenarios.

class FlexModel(ProformaModel):
    cogs_rate = Assumption(value=0.55)
    revenue = FixedLine(values={2024: 500_000, 2025: 550_000})
    cogs = FormulaLine(formula=lambda li, t: li.revenue[t] * li.cogs_rate)
    gross_profit = FormulaLine(formula=lambda li, t: li.revenue[t] - li.cogs[t])

base = FlexModel(periods=[2024, 2025])

Use ModelComparison to diff two model instances:

from pyproforma import ModelComparison

# (requires two separately instantiated models with different inputs)
comparison = ModelComparison(base, upside)
comparison.diff("gross_profit")

Time-series formulas

Formulas receive the full model namespace and the current period t. Reference prior periods with t-1:

# Compound growth from a seeded base year
revenue = FormulaLine(
    formula=lambda li, t: li.revenue[t-1] * (1 + li.growth_rate),
    values={2024: 500_000},  # seed value for first period
    label="Revenue",
)

Tags

Tag line items to group them flexibly without fixed categories:

revenue = FixedLine(values={...}, tags=["operating", "top_line"])
other_income = FixedLine(values={...}, tags=["operating"])

# Sum all items tagged "operating" in a formula
ebit = FormulaLine(formula=lambda li, t: li.tag["operating"][t])

# Or in a table
from pyproforma.tables import TagTotalRow
table = model.tables.from_template([
    HeaderRow(),
    ItemRow(name="revenue"),
    ItemRow(name="other_income"),
    TagTotalRow(tag="operating", label="Total Operating"),
])

Number formatting

A Format class provides named format constants that flow through to both HTML and Excel output:

from pyproforma import Format

ItemRow(name="revenue",    value_format=Format.THOUSANDS_K)   # 500K
ItemRow(name="margin",     value_format=Format.PERCENT_ONE_DECIMAL)  # 45.0%
ItemRow(name="net_income", value_format=Format.CURRENCY_NO_DECIMALS) # $218,130

Custom formats via NumberFormatSpec:

from pyproforma import NumberFormatSpec

fmt = NumberFormatSpec(decimals=1, scale="millions", suffix="M")
# 500_000 → "0.5M"

Installation

pip install pyproforma

Requires Python 3.9+. Dependencies: pandas, openpyxl.


Status

This project is in active development. The core modeling and table export features are stable. Charts, extended documentation, and additional row types are on the roadmap. Feedback welcome — open an issue or reach out directly.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyproforma-0.2.4.tar.gz (69.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyproforma-0.2.4-py3-none-any.whl (86.6 kB view details)

Uploaded Python 3

File details

Details for the file pyproforma-0.2.4.tar.gz.

File metadata

  • Download URL: pyproforma-0.2.4.tar.gz
  • Upload date:
  • Size: 69.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for pyproforma-0.2.4.tar.gz
Algorithm Hash digest
SHA256 25eff78d82def6ffb438bdff2c904e5c2a4adda072aee9a40ba7a0e27c864b9b
MD5 c054e293687c124407f2476b223a0834
BLAKE2b-256 979dfef18e98dbaa6ab89343a7afed38bdce51f3ded1b3d509638077daa3364b

See more details on using hashes here.

File details

Details for the file pyproforma-0.2.4-py3-none-any.whl.

File metadata

  • Download URL: pyproforma-0.2.4-py3-none-any.whl
  • Upload date:
  • Size: 86.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for pyproforma-0.2.4-py3-none-any.whl
Algorithm Hash digest
SHA256 8bd8e645bb7e5582372c94a84de3805ddf3ebd78227d94e9d7957061be9778fe
MD5 560217b757005e33ea796450d6e52652
BLAKE2b-256 95e3f58aae78a581a658948daa2b24db70ab18101384229850e1431896fd7ea9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page