2026-03-25 15:36:05 +01:00
2026-03-25 15:36:05 +01:00
2026-03-25 15:36:05 +01:00
2026-03-24 11:17:08 +01:00
2026-03-24 11:17:08 +01:00
2026-03-24 11:17:08 +01:00
2026-03-25 15:36:05 +01:00
2026-03-25 15:36:05 +01:00
2026-03-24 12:49:55 +01:00

Portfolio Engine

A lightweight portfolio and financial data backend written in Go. Tracks companies, currencies, and revenue reports across configurable time periods — replacing spreadsheets with a proper database, REST API, and interactive shell.

Features

  • SQLite database with foreign key enforcement
  • REST API on port 8080
  • Interactive shell for managing data without an HTTP client
  • Revenue tracking by period (quarterly, half-year, full year)
  • Revenue broken down by custom categories (product, location, segment...)
  • Health endpoint with DB latency, memory, and uptime stats

Project Structure

.
├── main.go                      # Entry point — HTTP routes + shell loop
├── app.db                       # SQLite database (auto-created on first run)
├── build.sh                     # Build script
├── go.mod / go.sum
└── internal/
    ├── database/
    │   └── main.go              # Schema init (InitDB) — all CREATE TABLE statements
    ├── handlers/
    │   ├── main.go              # HealthHandler
    │   ├── currency.go          # AddCurrencyHandler, GetCurrenciesHandler
    │   └── revenue.go           # AddRevenueEntryHandler, GetRevenueReportHandler, GetRevenueSumHandler
    ├── model/
    │   ├── company.go           # Company struct + SQL (InsertCompany, GetAllCompanies)
    │   ├── currency.go          # Currency struct + SQL (InsertCurrency, GetAllCurrencies)
    │   ├── periode.go           # Period struct + helpers (QuarterPeriod, HalfYearPeriod, FullYearPeriod)
    │   └── revenue.go           # Revenue, RevenueReport, RevSum structs + SQL
    ├── service/
    │   ├── main.go              # Service wiring
    │   ├── company.go           # Company business logic
    │   ├── currency.go          # Currency business logic
    │   └── revenue.go           # Revenue aggregation logic
    └── shell/
        ├── company.go           # add-company, list-companies
        ├── currency.go          # add-currency, list-currency
        └── revenue.go           # add-revenue, list-revenue, sum-revenue

Getting Started

Prerequisites

  • Go 1.21+
  • GCC (required for SQLite CGO bindings)
    • Ubuntu/Debian: sudo apt install gcc
    • macOS: comes with Xcode Command Line Tools

Install & Run

git clone git@git.samantha42.xyz:samantha/Portifolio-Engine.git
cd Portifolio-Engine
go mod download
go run main.go

Or build first:

chmod +x build.sh
./build.sh
./Portifolio

On startup:

Connected to SQLite database
Tables ready
Server running on :8080

Shell ready. Type 'help' for commands.
>

The HTTP server and shell run concurrently — the API is live while you type shell commands.


REST API

GET /health

Returns server status, DB connection info, memory usage, and uptime.

curl http://localhost:8080/health
{
  "status": "ok",
  "uptime": "4m32s",
  "database": {
    "status": "ok",
    "latency": "121µs",
    "open_connections": 1,
    "in_use": 0,
    "idle": 1
  },
  "memory": {
    "alloc_mb": 2.31,
    "sys_mb": 9.44,
    "num_gc": 3
  },
  "go_version": "go1.22.0",
  "goroutines": 4
}

Returns 503 with "status": "degraded" if the database is unreachable.


POST /add/company

curl -X POST http://localhost:8080/add/company \
  -H "Content-Type: application/json" \
  -d '{"name":"Novo Nordisk","shares_outstanding":4442064180,"price":251.00,"currency_id":1}'

GET /companies

curl http://localhost:8080/companies

POST /add/currency

curl -X POST http://localhost:8080/add/currency \
  -H "Content-Type: application/json" \
  -d '{"code":"DKK","name":"Danish Krone"}'

GET /currencies

curl http://localhost:8080/currencies

POST /add/revenue/entry

Add a single revenue line to a report. The period is created automatically if it doesn't exist.

curl -X POST http://localhost:8080/add/revenue/entry \
  -H "Content-Type: application/json" \
  -d '{
    "company_id":  1,
    "currency_id": 1,
    "period_type": "Q",
    "year":        2025,
    "index":       1,
    "category":    "product",
    "label":       "iPhone",
    "value":       69143
  }'

period_type is one of:

Value Meaning index range
Q Quarter 14
H Half-year 12
Y Full year 1

GET /revenue/report

Get all revenue entries for a company and period.

curl "http://localhost:8080/revenue/report?company_id=1&period_type=Q&year=2025&index=1"

GET /revenue/sum

Sum all revenue entries for a company across all periods of a given type in a year.

curl "http://localhost:8080/revenue/sum?company_id=1&period_type=Q&year=2025"

Shell Commands

Command Description
add-company Add a new company interactively
list-companies List all companies with price and share count
add-currency Add a new currency interactively
list-currency List all currencies with their IDs
add-revenue Add a revenue entry interactively
list-revenue List revenue entries for a company/period
sum-revenue Sum revenue across all periods in a year
help Show all available commands
exit Quit

Example session

> add-currency
  Code (e.g. DKK): DKK
  Name (e.g. Danish Krone): Danish Krone
  ✓ Currency 'Danish Krone' (DKK) added with ID 1

> add-company
  Name: Novo Nordisk
  Shares outstanding: 4442064180
  Price: 251.00
  Currency ID: 1
  ✓ Company 'Novo Nordisk' added.

> add-revenue
  Company ID: 1
  Currency ID: 1
  Period type (Q/H/Y): Q
  Year: 2025
  Index (Q: 1-4 | H: 1-2 | Y: 1): 1
  Category (product/location/total): product
  Label (e.g. iPhone, Americas): Diabetes Care
  Value: 54200
  ✓ Revenue entry added: product / Diabetes Care = 54200.00 (Q1 2025)

> sum-revenue
  Company ID: 1
  Period type to sum (Q/H/Y): Q
  Year: 2025

  Revenue Sum — FY2025
  Total: 54200.00

  By Category:
    product         54200.00

  By Label:
    Diabetes Care   54200.00

> list-companies
  ID    NAME                 CURRENCY   PRICE           SHARES
  ------------------------------------------------------------
  1     Novo Nordisk         DKK        251.00          4442064180

Database Schema

CREATE TABLE currencies (
    id   INTEGER PRIMARY KEY AUTOINCREMENT,
    code TEXT    NOT NULL UNIQUE,
    name TEXT    NOT NULL
);

CREATE TABLE companies (
    id                 INTEGER PRIMARY KEY AUTOINCREMENT,
    name               TEXT    NOT NULL UNIQUE,
    shares_outstanding INTEGER NOT NULL,
    price              REAL    NOT NULL,
    currency_id        INTEGER NOT NULL,
    FOREIGN KEY (currency_id) REFERENCES currencies(id)
);

CREATE TABLE periods (
    id         INTEGER PRIMARY KEY AUTOINCREMENT,
    type       TEXT    NOT NULL CHECK(type IN ('Q', 'H', 'Y')),
    year       INTEGER NOT NULL,
    idx        INTEGER NOT NULL,
    start_date TEXT    NOT NULL,
    end_date   TEXT    NOT NULL,
    UNIQUE(type, year, idx)
);

CREATE TABLE revenue_reports (
    id         INTEGER PRIMARY KEY AUTOINCREMENT,
    company_id INTEGER NOT NULL,
    period_id  INTEGER NOT NULL,
    FOREIGN KEY (company_id) REFERENCES companies(id),
    FOREIGN KEY (period_id)  REFERENCES periods(id),
    UNIQUE(company_id, period_id)
);

CREATE TABLE revenue_entries (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    report_id   INTEGER NOT NULL,
    currency_id INTEGER NOT NULL,
    category    TEXT    NOT NULL,
    label       TEXT    NOT NULL,
    value       REAL    NOT NULL,
    FOREIGN KEY (report_id)   REFERENCES revenue_reports(id),
    FOREIGN KEY (currency_id) REFERENCES currencies(id)
);

CREATE TABLE category_defs (
    id         INTEGER PRIMARY KEY AUTOINCREMENT,
    company_id INTEGER NOT NULL,
    name       TEXT    NOT NULL,
    FOREIGN KEY (company_id) REFERENCES companies(id),
    UNIQUE(company_id, name)
);

CREATE TABLE category_labels (
    id              INTEGER PRIMARY KEY AUTOINCREMENT,
    category_def_id INTEGER NOT NULL,
    label           TEXT    NOT NULL,
    FOREIGN KEY (category_def_id) REFERENCES category_defs(id),
    UNIQUE(category_def_id, label)
);

Roadmap

  • GET /company/{id}/revenue — full revenue history for a company
  • Price update endpoint
  • Market cap calculation (price × shares outstanding)
  • Multi-currency conversion
  • Frontend dashboard
Description
No description provided
Readme 113 MiB
Languages
Go 49.1%
HTML 29.3%
CSS 21.6%