2026-03-27 10:14:43 +01:00
2026-03-27 10:14:43 +01:00
2026-03-26 08:39:42 +01:00
2026-03-27 10:14:43 +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-27 10:14:43 +01:00
2026-03-27 10:14:43 +01:00
2026-03-25 16:54:46 +01:00

Portfolio Engine

A lightweight portfolio and financial data backend written in Go. Tracks companies, currencies, revenue reports, and trades 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...)
  • Trade tracking (stocks, options, currency trades)
  • 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
    │   └── trade.go             # AddTradeHandler, GetTradeListHandler
    ├── 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
    │   └── trade.go             # Trade struct, TradeType, TradeProduct enums + 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 /trade/add

Add a new trade. product and type are integer enums (see tables below).

curl -X POST http://localhost:8080/trade/add \
  -H "Content-Type: application/json" \
  -d '{
    "ticker_id":   1,
    "currency":    "DKK",
    "shares":      100,
    "product":     0,
    "type":        false,
    "price":       251.00,
    "date":        "2025-03-01T00:00:00Z"
  }'

product values:

Value Meaning
0 StockTrade
1 OptionTradeCall
2 OptionTradePut
3 CurrencyTrade

type values:

Value Meaning
false Buy
true Sell

GET /trade/list

Returns all trades as a JSON array.

curl http://localhost:8080/trade/list
[
  {
    "Ticker":   { "id": 1, "name": "Novo Nordisk", ... },
    "Shares":   100,
    "Product":  0,
    "Type":     false,
    "Price":    251.00,
    "Currency": { "id": 1, "code": "DKK", "name": "Danish Krone" },
    "Date":     "2025-03-01T00:00:00Z"
  }
]

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

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 trades (
    id          INTEGER  PRIMARY KEY AUTOINCREMENT,
    company_id  INTEGER  NOT NULL,
    currency_id INTEGER  NOT NULL,
    shares      INTEGER  NOT NULL,
    product     INTEGER  NOT NULL CHECK(product IN (0, 1, 2, 3)),
    type        INTEGER  NOT NULL CHECK(type IN (0, 1)),
    price       REAL     NOT NULL,
    traded_at   DATETIME NOT NULL
);

Roadmap

  • GET /company/{id}/revenue — full revenue history for a company
  • GET /trade/list?ticker_id=1 — filter trades by 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%