https://github.com/ckaznable/kabu
https://github.com/ckaznable/kabu
Last synced: about 2 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/ckaznable/kabu
- Owner: ckaznable
- Created: 2024-02-07T02:25:11.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2026-05-05T02:52:40.000Z (3 months ago)
- Last Synced: 2026-05-05T04:36:41.510Z (3 months ago)
- Language: Rust
- Size: 156 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Kabu
Personal stock portfolio tracker. Tracks holdings, fetches live prices (global stocks via Finnhub, Taiwan stocks via TWSE/TPEX public feed), and extracts transactions from PDF financial documents using Gemini LLM.
## Architecture
```
kabu/
├── server/ — Axum HTTP server (API + static frontend)
├── updater/ — One-shot price updater binary (for K8s CronJob)
├── shared/ — Shared library (DB, models, config)
├── typegen/ — TypeScript type generator (Rust → TS via specta)
├── frontend/ — Vue 3 + Vite SPA
└── migrations/ — SQL schema
```
## Configuration
Copy `config.example.toml` to `config.toml` and fill in your API keys:
```bash
cp config.example.toml config.toml
```
### config.toml
| Section | Field | Description |
|------------|-----------------|-----------------------------------------------------|
| `server` | `port` | HTTP server port (default: `3000`) |
| `server` | `database_url` | SQLite connection string (default: `sqlite:kabu.db`)|
| `finnhub` | `api_key` | Finnhub API key (direct value) |
| `finnhub` | `api_key_env` | Or: env var name to read Finnhub key from |
| `gemini` | `api_key` | Gemini API key (direct value) |
| `gemini` | `api_key_env` | Or: env var name to read Gemini key from |
| `gemini` | `model` | Gemini model name (default: `gemini-2.5-flash-lite`)|
| `exchange_rate` | `api_key` | ExchangeRate-API key (direct value) |
| `exchange_rate` | `api_key_env` | Or: env var name for ExchangeRate-API key |
| `exchange_rate` | `base` | Base currency code (default: `USD`) |
| `exchange_rate` | `currencies`| Target currency codes to store |
| `pdf` | `password` | PDF decryption password (direct value) |
| `pdf` | `password_env` | Or: env var name to read PDF password from |
For each secret, you can either:
- Set the value directly in the config file, or
- Set the `_env` variant to the name of an environment variable (e.g. `FINNHUB_API_KEY`), and export that variable in your shell
Config file path can be overridden with the `KABU_CONFIG` env var.
### Required API Keys
- **Finnhub** — Get a free key at https://finnhub.io/
- **Gemini** — Get a key at https://aistudio.google.com/apikey
- **ExchangeRate-API** — Get a key at https://www.exchangerate-api.com/
### Taiwan Stock Symbols
- Taiwan stocks are auto-routed to TWSE/TPEX source when symbol is numeric (e.g. `2330`) or ends with `.TW` / `.TWO`.
- This source does not require an extra API key.
## Development
### Prerequisites
- Rust (stable, edition 2024)
- Node.js 18+
- Make
### Using Make
```bash
make # Build everything (frontend + server + updater)
make dev # Build frontend, then run server
make dev-frontend # Vite dev server on http://localhost:5173 (proxies /api to :3000)
make dev-server # Run Rust server directly
make typegen # Regenerate TypeScript types from Rust models
make lint # Run oxlint on frontend
make clean # Remove all build artifacts
```
### Manual
```bash
# Backend
cargo build
cargo run -p kabu-server
cargo run -p kabu-updater # One-shot: fetches latest prices then exits
# Frontend
cd frontend
npm install
npm run dev
npm run build # Production build → frontend/dist/
```
### Type Generation
Shared types between Rust and TypeScript are generated with specta:
```bash
make typegen # Outputs frontend/src/api/types.ts
```
## Production / K8s
Build two separate binaries for deployment:
```bash
make build
# or
cargo build --release -p kabu-server
cargo build --release -p kabu-updater
```
- `kabu-server` — serves the API and frontend static files from `frontend/dist/`
- `kabu-updater` — one-shot binary, fetches prices for all tracked stocks then exits. Schedule with K8s CronJob for post-market updates.
Both binaries read from `config.toml` (or the path in `KABU_CONFIG`).
### AArch64 Container Build
For an `arm64` image build on an `amd64` machine, use the helper script:
```bash
./scripts/build-kabu-aarch64.sh
```
Defaults:
- package: `kabu-server`
- image tag: `kabu-server:aarch64`
- `TARGET_TRIPLE=aarch64-unknown-linux-gnu`
Examples:
```bash
# Build the server image
./scripts/build-kabu-aarch64.sh
# Build the updater image
./scripts/build-kabu-aarch64.sh kabu-updater
# Override the output image tag
./scripts/build-kabu-aarch64.sh kabu-server registry.example.com/kabu-server:latest
```
Requirements:
- `cargo`
- `rustup`
- `cargo-zigbuild`
- `zig`
- `podman`
- `npm` (only for `kabu-server`, to produce `frontend/dist`)
## API Endpoints
| Method | Path | Description |
|----------|--------------------------|---------------------------------------------|
| `GET` | `/api/stocks` | List all tracked stocks |
| `POST` | `/api/stocks` | Add a stock to track |
| `GET` | `/api/stocks/{id}` | Get stock details |
| `PUT` | `/api/stocks/{id}` | Update stock quantity / cost |
| `DELETE` | `/api/stocks/{id}` | Remove a stock |
| `GET` | `/api/portfolio/summary` | Portfolio overview with prices |
| `POST` | `/api/pdf/upload` | Upload PDF, extract transactions via Gemini |
| `GET` | `/api/transactions` | List extracted transactions |