Case study · Web app

Yatırım Aklı

An AI-assisted portfolio tracker that explains what the numbers mean.

Portfolio trackers show numbers but not what they mean: a beginner sees a profit figure and has no idea how risky the portfolio is or how concentrated it has become.

Yatırım Aklı calculates returns and risk in code and has Gemini turn those metrics into a plain-language report. It began as a demo with known problems; I audited it, logged 42 issues and fixed them in order. The backend now passes 612 automated tests.

  • 612 backend tests passing in CI, on a real PostgreSQL
  • 42 logged issues, audited and fixed in order
  • ~0.6 s until the first words of a streamed chat answer
Year
2026
Role
Developer, end to end
Team
Built during my internship at Amasya Şeker
Built with
  • Python
  • FastAPI
  • React
  • PostgreSQL
  • Docker
  • Google Gemini
Yatırım Aklı's landing page: the headline “See your portfolio's real state on one screen” above a dashboard preview.

01

The challenge

An investor who holds a dozen stocks across two currencies cannot easily tell how much of the portfolio sits in one sector or how volatile it is. Spreadsheets give the figures; they do not give the reading.

It was hard because the data is messy. Exchanges close on different days, Borsa Istanbul symbols need a suffix, London prices come in pence, positions are held in different currencies, and the free price source is slow and rate limited. An AI report is only worth reading if the numbers underneath are right.

Constraints

  • Gemini’s free plan limits how many requests the app can make
  • The app gives no investment advice, only readings of the user’s own data

02

My part

  1. Designed and built the whole application: a FastAPI backend, a React interface and a PostgreSQL database, packaged with Docker Compose.

  2. Audited the first version, logged every problem as a numbered issue and fixed them in dependency order, starting with a test base.

  3. Built the finance engine with a time-weighted return, a covariance-based volatility, the Sharpe ratio and price series aligned by date across exchanges.

  4. Added the Gemini assistant with portfolio and risk reports, a what-if simulator, rebalancing suggestions and a chat that streams over Server-Sent Events.

  5. Secured sign-in with short-lived access tokens kept in memory and rotating refresh tokens in an httpOnly cookie, plus rate limits.

  6. Made price alerts fire from a periodic server task, and gave the interface light and dark themes, Turkish and English, and checked text contrast.

The dashboard with total value, daily change, unrealised and realised profit, an allocation donut and a performance curve against BIST-100.
The dashboard, with the assistant always one click away.

03

Key decisions

  1. One Gemini call per request, not a swarm of agents

    Chose
    Every AI feature makes a single call that receives the portfolio and its calculated metrics.
    Why
    The first description spoke of several cooperating agents, but the code never did that. I deleted the agent classes and corrected the README and the interface to match what the app really does. A single call is faster, cheaper and easier to debug.
  2. Calculate on the server, let the model only interpret

    Chose
    Returns, volatility, Sharpe ratio, allocation and profit and loss are computed in code and handed to the model as facts.
    Why
    A language model should not do arithmetic, and a wrong figure in a report erodes trust faster than anything else. If Gemini fails, the user sees an honest error; no error text is ever saved as a report.
  3. A time-weighted return instead of a simple one

    Chose
    Performance is measured as a time-weighted return from the trade history and daily closes, the method the GIPS standard uses.
    Why
    The old chart counted every purchase as a gain. For the same portfolio it showed +236%, while the time-weighted return is −3.74%.
The AI insights page with a calculated risk score of 3.6 out of 10 beside a full Gemini portfolio report.
The risk score is calculated in code; Gemini only writes the reading around it.

04

Results

The application works end to end: portfolios and trades, profit and loss across currencies, performance against an index, risk metrics, alerts and the Gemini reports. In CI the backend passes 612 tests on a real PostgreSQL, the interface passes 38 component tests and 44 logic tests, and ESLint reports nothing.

It is still a prototype and not deployed yet. The production setup with HTTPS is ready, notifications stay inside the app, and currency moves are not yet part of profit and loss for foreign positions. These limits are written down in the repository.

  • The full source is private; a public showcase repository holds the documentation, screenshots and selected code samples.
  • The accessibility check covered both themes and both languages; all 8 findings were fixed.