Skip to content
vp.Ven Petrov
WorkWritingAboutAt a glance
Inside the financial lab

What actually runs when you press refresh?

The interface is React and TypeScript. The calculations run in Python, and FastAPI exposes them over HTTP. A saved result lets the page work when the service is unavailable.

Report explorer The original income bridge
  1. Start with reviewed source data

    The figures come from the public Novartis report. Selected tables were transcribed into JSON and checked against the report. Each fact retains its period, unit, accounting basis and source page. This version does not automatically extract or understand an arbitrary PDF.

  2. Validate and calculate in Python

    The backend checks the shape and meaning of the input, including expense signs, compatible periods and complete financial lines. Ordinary Python arithmetic produces the changes, margins, shares and reconciliation checks. An LLM does not calculate these numbers.

  3. Expose the calculation through FastAPI

    backend/app.py defines the HTTP routes. GET /api/review builds the focused quarterly review; GET /api/report builds the wider explorer dataset. These routes call the Python functions on the server and return JSON.

  4. Let the interface ask different questions

    The React page displays the returned dataset as charts and tables. Period changes, product sorting and source searches happen in the browser. Refreshing makes a new request to the Python service. The page labels whether it is showing that response or a saved calculation.

  5. Try inputs that should fail

    The test suite changes signs, periods, units and totals on purpose. A test passes when the calculation rejects the bad input. Saved-output checks also compare a fresh calculation with the version shipped in the site. These tests check this dataset and its rules, not the financial accuracy of every possible document.

The route is real

This excerpt is read directly from the backend source when the site is built. FastAPI registers the URL; build_review() does the work.

@application.get("/api/review")
    def review():
        report = build_review()
        report["model"] = current_model_status()
        return report

The service’s interactive API documentation lists the routes and lets you inspect responses. FastAPI generates this documentation from its route definitions and declared schemas. The financial response itself is checked by the pipeline and tests.

A check that matters

It is not enough for the changes to add up. Both original income statements must also reconcile. Otherwise, the same error in both years could cancel out and leave a convincing movement chart.

    income, sales = by_id["operating_income"], by_id["net_sales"]
    delta = income["current"] - income["prior"]
    residual = delta - sum(c["delta"] for c in contributions)
    # Reject an offsetting error that reconciles deltas but breaks both P&L totals.
    for period in ("prior", "current"):
        if sum(by_id[key][period] for key in CONTRIBUTION_IDS) != income[period]:
            raise ValueError(f"{period} income-statement total does not reconcile")
    if residual:
        raise ValueError("the income movement does not reconcile")

This is the actual check used in the focused quarterly review. The wider report applies the same idea to income, cash, balance-sheet totals and the company’s non-IFRS reconciliations.

Why keep a saved result?

The portfolio should still be readable when its Python service is unavailable. The saved JSON is a generated artifact from the same code, with source identifiers and validation results. It is a fallback, not evidence that a request ran successfully. That is why the mode stays visible.

What I delegated, and what I check

I used Codex to help build this example and independent agents to check its figures and claims. The source choices, expense signs, reconciliation rules and displayed results are the parts I need to review. Generating the code does not settle those decisions.

The optional OpenRouter adapter is disabled. Its limited task would be selecting from approved statements. It has not been evaluated with live model calls. The working explorer is a financial-data application, and it does not pretend to be an autonomous analyst.

Follow one number through the system

On page 17, fourth-quarter net sales are 13,336 USD million for 2025 and 13,153 for 2024. In data/novartis-q4-2025.json, the net_sales record keeps both values, their unit and the income-statement source reference.

Python subtracts the prior value from the current value: 13,336 − 13,153 = +183. That becomes the net-sales contribution in the operating-income bridge. FastAPI returns the result, and React draws the bar. The other six contributions bring the total movement to +86 USD million.

One failure check changes an expense to a positive number. Another changes both operating-income totals by the same amount. The first catches a sign error; the second catches an error that a check of the net movement alone would miss.

Inspect or run it yourself

Python source, data and tests Live report response

The source package includes setup instructions and pinned dependencies. The public site runs the Python service as a Vercel Function. For local development, the same application can run with Uvicorn. The data comes from the published report.

Ven PetrovSoftware engineer · Barcelona
GitHub ↗LinkedIn ↗Email ↗

Find a project or a note

17 results

Transfutur ERP · ExperienceERP for transport and logisticsAs the sole engineer, I built an ERP from zero for transport and logistics. It supported roughly €5–6 million in annual invoicing across two offices, and I kept it running for about a year.Financial review lab · Project storyExploring a financial report with Python and FastAPII started with one income change, then opened up the same 54-page report: products, regions, cash flow, core adjustments and a dataset whose figures lead back to the source.Crude-oil narrative tracker · Project storyCrude-oil research from global news and market dataI wanted help following global events that could matter for crude oil. The prototype combines news research with calculated market and position values, while keeping recent assessments separate from the longer numeric history.MNQ time-series research · ExperimentTime-series prediction experimentsAn exploration of high-resolution market data: choosing what to acquire, checking its limits, comparing time intervals and testing whether a prediction survived later data and the cost of acting on it.NQ/MNQ backtest engine · Technical noteA missing closing bar left the simulator’s short position openThe strategy reset for a new day while a shared counter still counted yesterday’s short. A small replay shows why closing the position has to happen before clearing its state.Small-cap edge research · Project storyTurning small-cap trading ideas into repeatable researchI asked Claude Code for a fresh research system that could test named price patterns, compare later periods and carry its methodology into new sessions. One candidate shows how features, entry timing and costs fit together.Gadgeon Europe applications · ExperienceConnected-device dashboards and mobile apps at Gadgeon EuropeI worked on Ionic and Angular applications that brought several APIs into dashboards for energy monitoring and connected-device control, with releases and maintenance across multiple client versions.ctxforge · Project storyctxforge: discovering what to build before codingA context discovery framework that asks an LLM to act like a senior engineering interviewer: understand the idea, uncover requirements and agree the scope before an agent starts building.Sociedades Inactivas enquiries · Technical noteSaving an enquiry before trying to send its emailOn the Sociedades Inactivas catalogue, a visitor can ask about a company or send a general enquiry. The form separates saving that request from delivering its notification, including when the visitor retries.Astro local-services website · Project storyA local-services site assembled from city, service and FAQ recordsThe Astro site describes flooring and other local services across different cities. Shared templates check availability, read the matching content and select one FAQ set for both visitors and structured data.3D Spaces website migration · Project storyMoving 3D Spaces to Next.js while its email stayed on the old serverI rebuilt the virtual-tour website with AI coding collaborators, preserving its content and URLs. The cutover also had to protect the mailbox, enquiry flow and existing analytics history.Capture CRM · Project storyConnecting classified listings to contacts in a small CRMA Chrome extension and Next.js dashboard bring classified listings, platform profiles and contact history together. Saving a listing again can add the person who was missing from an earlier import.Product-description comparison bench · ExperimentComparing one model call with separate vision and writing stepsA small product-description tool lets me inspect the visual analysis before the copy is written, change the model used for each step and compare request measurements.Between Us · Project storyBetween Us: why I kept the first take and widened the shotI compared dialogue takes, asked for wider shots and more physical acting, then changed the workflow when its studio limited the scenes I wanted to make.Restaurant-service voice agent · ExperimentDesigning a restaurant-service call around the real offerI worked through a Spanish outbound agent prompt, corrected a free visit to the actual paid service and compared how two drafts handled transfers and bookings.Vapi and Telnyx voice integration · Technical noteThe Vapi agent could speak, but it could not hear me clearlyMy Telnyx number connected to the assistant, but the caller audio was distorted. Three codec tests recovered the connection without fixing that original problem.Warloop: Echoes of Solmara · Project storyWarloop: turning generated character art into a playable prototypeGood-looking character images became tiny figures, a backwards walk and misplaced bosses in the game. I worked through the gap between generated art and playable behavior.
esc to close · Tab to move through results