Updated the project documents with the pportfolio updates for fundtracker

This commit is contained in:
Rufus King 2026-08-19 15:21:34 -04:00
parent 6db1f35392
commit 80dc04a679
2 changed files with 60 additions and 2 deletions

View file

@ -1,5 +1,5 @@
# KingDezigns Network Documentation Index # KingDezigns Network Documentation Index
Last Updated: 2026-08-17 Last Updated: 2026-08-19
This index provides a structured overview of all VLANs, servers, and firewall policies in the KingDezigns network. This index provides a structured overview of all VLANs, servers, and firewall policies in the KingDezigns network.
Each section links to its own dedicated Markdown file for clarity and modularity. Each section links to its own dedicated Markdown file for clarity and modularity.
@ -183,6 +183,8 @@ A menu-driven launcher script provides one-click access to run scripts on PLEX32
- **Synaplan AI integration** (added 2026-07-18, not yet functional): Nextcloud connector app installed, but the required self-hosted Synaplan backend stack (PHP/MariaDB/Qdrant/optional Ollama) has not been deployed. Treat as a future project, not a bug — see `server_nas08.md` for details before troubleshooting. - **Synaplan AI integration** (added 2026-07-18, not yet functional): Nextcloud connector app installed, but the required self-hosted Synaplan backend stack (PHP/MariaDB/Qdrant/optional Ollama) has not been deployed. Treat as a future project, not a bug — see `server_nas08.md` for details before troubleshooting.
- **Forgejo self-hosted Git server** (added 2026-07-26): Deployed on NAS16 (192.168.150.40) via Docker Compose — chosen over Gitea for non-profit/community governance (Codeberg e.V.). Public web UI at `https://forgejo.kingdezigns.com/` (proxied via NPM on HAS), SSH git access on port 2222 (LAN-only, no external port-forward). Provides version control with in-browser file editing/history for the `07 - Projects` folder on NAS16's share, organized as one Forgejo org per host (`NAS08`, `NAS16`, `PLEX32`) with a `<hostname>-scripts` repo in each. Data (`/opt/forgejo/data`) folded into the existing `nas16-backup.sh` job. A companion `forgejo_autosync.sh` script (not yet scheduled as of this writing) auto-commits/pushes any uncommitted changes on a timer as a safety net alongside normal manual commits. Full setup, config, and troubleshooting history in `server_forgejo.md`. - **Forgejo self-hosted Git server** (added 2026-07-26): Deployed on NAS16 (192.168.150.40) via Docker Compose — chosen over Gitea for non-profit/community governance (Codeberg e.V.). Public web UI at `https://forgejo.kingdezigns.com/` (proxied via NPM on HAS), SSH git access on port 2222 (LAN-only, no external port-forward). Provides version control with in-browser file editing/history for the `07 - Projects` folder on NAS16's share, organized as one Forgejo org per host (`NAS08`, `NAS16`, `PLEX32`) with a `<hostname>-scripts` repo in each. Data (`/opt/forgejo/data`) folded into the existing `nas16-backup.sh` job. A companion `forgejo_autosync.sh` script (not yet scheduled as of this writing) auto-commits/pushes any uncommitted changes on a timer as a safety net alongside normal manual commits. Full setup, config, and troubleshooting history in `server_forgejo.md`.
- **Fund Tracker multi-profile mutual fund/stock tracker** (added 2026-08-17): Deployed on NAS16 (192.168.150.40) via Docker Compose — FastAPI + PostgreSQL, containers `fundtracker_api`/`fundtracker_db`. Public web UI at `https://funds.kingdezigns.com/` (proxied via NPM on HAS, Force SSL). Core design: security data (ticker, price history, group classification) is shared across all profiles and synced once regardless of how many profiles track it; each profile's holdings, cost basis, and watchlist membership are private. Scoring built from quality (risk-adjusted), opportunity (quality + genuine price dip, requires both a return z-score AND a drawdown-from-high to agree), combined (harmonic mean of quality+opportunity), and rebound (peer-relative "usually near the top of its group, currently collapsed" detector, reconstructed retroactively from existing price history) scores. Market data via `yfinance` + `curl_cffi` Chrome-impersonation session — required to avoid Yahoo Finance's bot-detection blocking, and a real fix (not a sandbox artifact). Session-cookie auth (httponly, bcrypt), admin role with anonymized cross-profile Census and versioned legal-document (Terms of Use + Investment Disclaimer) publishing — a version bump immediately requires every profile to re-accept. Daily sync + personalized-per-profile email report runs via an in-process APScheduler inside the container at 06:00 UTC, **not** an OMV Scheduled Job. Full architecture, scoring methodology, deployment pattern (code-only changes = folder swap + restart; dependency changes = rebuild; compose/env changes = `up -d`; structural DB migrations require a `pg_dump` backup first), and known-issue history in `server_fundtracker.md`. - **Fund Tracker multi-profile mutual fund/stock tracker** (added 2026-08-17): Deployed on NAS16 (192.168.150.40) via Docker Compose — FastAPI + PostgreSQL, containers `fundtracker_api`/`fundtracker_db`. Public web UI at `https://funds.kingdezigns.com/` (proxied via NPM on HAS, Force SSL). Core design: security data (ticker, price history, group classification) is shared across all profiles and synced once regardless of how many profiles track it; each profile's holdings, cost basis, and watchlist membership are private. Scoring built from quality (risk-adjusted), opportunity (quality + genuine price dip, requires both a return z-score AND a drawdown-from-high to agree), combined (harmonic mean of quality+opportunity), and rebound (peer-relative "usually near the top of its group, currently collapsed" detector, reconstructed retroactively from existing price history) scores. Market data via `yfinance` + `curl_cffi` Chrome-impersonation session — required to avoid Yahoo Finance's bot-detection blocking, and a real fix (not a sandbox artifact). Session-cookie auth (httponly, bcrypt), admin role with anonymized cross-profile Census and versioned legal-document (Terms of Use + Investment Disclaimer) publishing — a version bump immediately requires every profile to re-accept. Daily sync + personalized-per-profile email report runs via an in-process APScheduler inside the container at 06:00 UTC, **not** an OMV Scheduled Job. Full architecture, scoring methodology, deployment pattern (code-only changes = folder swap + restart; dependency changes = rebuild; compose/env changes = `up -d`; structural DB migrations require a `pg_dump` backup first), and known-issue history in `server_fundtracker.md`.
- **Fund Tracker — Portfolio/transaction tracking system documented 2026-08-19** (built in an earlier session, previously undocumented in this project): a buy-lot/sale/split register sits alongside the scoring system — FIFO/LIFO/Specific-ID sale matching, split handling that only touches lots active before the split date, a `GET /portfolio/register` endpoint, and edit/delete with lock-on-sale-allocation protection (deleting the sale restores shares and unlocks the lot). Full endpoint list, data model, and known deployment gotchas now in `server_fundtracker.md`'s dedicated Portfolio section.
- **Fund Tracker Portfolio UI — three-level collapsible register (2026-08-19):** the Portfolio page now renders **Ticker (collapsed by default) → Buy Lot → Sale(s)**, instead of showing every buy lot for every ticker at once. Frontend-only change to `portfolio.html`; deployed via scp to a `/tmp` scratch path → `sudo cp` into the root-owned `/Kingdezignsnas16/fundtracker/` project dir → `sudo docker restart fundtracker_api` (no rebuild needed for a static-file-only change, consistent with the documented code-only deployment pattern). Full detail in `server_fundtracker.md`.
--- ---
@ -203,6 +205,7 @@ Additional files may be added for:
- **STOCKPROXY hardening** — not yet stress-tested at full ~50-ticker scale for Yahoo-side rate limiting; revisit if widespread simultaneous lookup failures are observed - **STOCKPROXY hardening** — not yet stress-tested at full ~50-ticker scale for Yahoo-side rate limiting; revisit if widespread simultaneous lookup failures are observed
- **Fund Tracker — pending feature work** (as of 2026-08-17, not yet built): self-service user account page (email update, relocated change-password), stronger primary-admin delete/demote protection, Eastern-time display throughout (currently UTC), always-visible logout control in the sidebar (currently inconsistent across pages per user report), confirmation of cookie/session behavior over the new HTTPS domain, fully-qualified-domain links everywhere (email + in-app) instead of the internal IP, public self-service signup with an admin on/off toggle and disclaimer acceptance, and open questions on geo-blocking / additional security hardening / anti-infiltration monitoring / PII collection policy. See the dedicated handoff summary provided at the end of the deployment conversation before starting this work. - **Fund Tracker — pending feature work** (as of 2026-08-17, not yet built): self-service user account page (email update, relocated change-password), stronger primary-admin delete/demote protection, Eastern-time display throughout (currently UTC), always-visible logout control in the sidebar (currently inconsistent across pages per user report), confirmation of cookie/session behavior over the new HTTPS domain, fully-qualified-domain links everywhere (email + in-app) instead of the internal IP, public self-service signup with an admin on/off toggle and disclaimer acceptance, and open questions on geo-blocking / additional security hardening / anti-infiltration monitoring / PII collection policy. See the dedicated handoff summary provided at the end of the deployment conversation before starting this work.
- **Fund Tracker — pre-existing documentation gap fixed 2026-08-17:** the Forgejo proxy host (`192.168.150.40:3000`) was missing from `server_homeassistant.md`'s Current Proxy Hosts table despite Forgejo being deployed and documented since 2026-07-26 — added along with the new Fund Tracker entry. - **Fund Tracker — pre-existing documentation gap fixed 2026-08-17:** the Forgejo proxy host (`192.168.150.40:3000`) was missing from `server_homeassistant.md`'s Current Proxy Hosts table despite Forgejo being deployed and documented since 2026-07-26 — added along with the new Fund Tracker entry.
- **Fund Tracker — Portfolio system testing ongoing (as of 2026-08-19):** the buy-lot/sale/split register is working but not yet exhaustively tested. Per the project's own handoff notes, remaining test cases include: purchases entered out of chronological order, sales entered against multiple lots, partial vs. complete sales, sale deletion, editing an unmatched lot, splits entered after transactions exist, reverse splits, purchases made after a historical split, lots fully sold before a split, and confirming one ticker's sales don't affect another ticker's lots. Continue from the current known-good state rather than rebuilding — see the Portfolio section of `server_fundtracker.md`.
--- ---

View file

@ -4,7 +4,7 @@ Containers: **fundtracker_api**, **fundtracker_db**
IP Address: **192.168.150.40** (shares NAS16's host IP) IP Address: **192.168.150.40** (shares NAS16's host IP)
VLAN: **50 — Lab / Servers** VLAN: **50 — Lab / Servers**
Last Updated: 2026-08-17 Last Updated: 2026-08-19
--- ---
@ -113,6 +113,58 @@ Publishing a new version (`Admin → Accounts → Legal documents`) **immediatel
--- ---
## 💼 Portfolio / Transaction Tracking (added, undated prior session — first documented here 2026-08-19)
In addition to the shared-security screening/scoring system described above, Fund Tracker also includes a full **buy-lot/sale/split transaction register** per profile — this had been built in an earlier session but was never previously written up in this file. It lives alongside (not instead of) the scheduler, pricing/NAV sync, auth, admin functionality, settings, and watchlist behavior documented elsewhere in this file.
### Data model
- **Buy lots** — each purchase is its own row: original quantity, remaining quantity, buy price, buy fees, notes/source. Selling from a lot reduces its remaining quantity but the lot itself is never deleted.
- **Sales** — recorded as their own rows, linked to the buy lot(s) they draw down via **sale allocations**, so the relationship between a sale and the lot(s) it consumed is preserved.
- **Matching methods** — FIFO, LIFO, and Specific Lot Identification (user picks exact per-lot quantities, validated to sum to the total sold) are all supported at time of sale.
- **Lot status** — `ACTIVE` (untouched), `PARTIAL` (some shares sold, some remain), `CLOSED` (fully sold) — a closed lot stays visible in the register rather than disappearing, preserving full history.
- **Stock splits / reverse splits** — a recorded split only adjusts share count and per-share cost basis on lots that were **active and purchased before** the split date. Lots purchased on/after the split date, and lots already fully sold before the split date, are left untouched.
### Key endpoints
| Endpoint | Purpose |
|---|---|
| `GET /portfolio/holdings` | Active holdings dashboard view |
| `GET/POST /portfolio/lots/{ticker}` | List / create buy lots for a ticker |
| `PATCH /portfolio/lots/{lot_id}` | Edit a buy lot — only allowed while it has zero sale allocations (see locking, below) |
| `DELETE /portfolio/lots/{lot_id}` | Delete a buy lot (same zero-allocation restriction) |
| `GET/POST /portfolio/sales` | List / record sales (FIFO/LIFO/Specific ID) |
| `DELETE /portfolio/sales/{sale_id}` | Delete a sale — **restores** the allocated quantity back to the affected buy lot(s), which also unlocks them for editing |
| `GET/POST /portfolio/splits` | List / apply stock splits |
| `GET /portfolio/register` | The full buy-lot register: each lot with its associated sale allocations, current price, unrealized G/L, realized G/L, and status — this is what the Portfolio page renders |
| `POST /portfolio/import/csv` | Bulk import buys/sells from pasted CSV text |
### Editing & locking
A buy lot can be edited or deleted only while it has **no** sale allocations against it — once a sale references a lot, that lot locks (shown as 🔒 in the UI) to protect the historical/tax record. The recovery path is to delete the associated sale first (which restores the shares to the lot), then edit/delete the now-unlocked lot.
### Portfolio UI — three-level collapsible hierarchy (updated 2026-08-19)
The Portfolio page (`/dashboard/portfolio.html`) renders the register as **Ticker → Buy Lot → Sale(s)**, both levels collapsible:
- **Ticker rows** — one row per ticker held, **collapsed by default** on page load. Clicking the row's twistie (▶/▼) expands it to reveal that ticker's individual buy lots. This was the 2026-08-19 change — previously every buy lot for every ticker rendered at once, making the register long and hard to scan.
- **Buy lot rows** — nested under their ticker once expanded. Each lot that has associated sales shows its own twistie (e.g. `▶ 2`) which expands to reveal the sale row(s) consumed from that specific lot. This twistie already existed prior to 2026-08-19 and was left unchanged.
- Expanding a ticker does not auto-expand its lots' sale twisties, and collapsing a ticker hides its lots' sale rows regardless of their own expand/collapse state — the two levels are independent, but a lot's sales can only ever be visible while its parent ticker is also expanded.
- Implementation notes: both expand states are tracked client-side only (`_expandedTickers` and `_expanded` JS `Set`s in `portfolio.html`), not persisted to the backend or across page reloads. Toggling is done via direct DOM show/hide rather than a full table re-render, so toggling one ticker/lot does not disturb the expand state of others.
- **Deployment note:** this was a frontend-only change to `api/app/static/portfolio.html` — no router, schema, or model changes. Deployed via the standard code-only pattern: file copied to a `/tmp` scratch path via `scp`, moved into `/Kingdezignsnas16/fundtracker/api/app/static/` with `sudo cp` (the project directory is root-owned), then `sudo docker restart fundtracker_api` (plain restart is sufficient since `app/` is bind-mounted — no rebuild needed for a static-file-only change).
### Known deployment gotchas (from earlier portfolio build work — kept here to avoid repeating)
- `models.py` has previously broken from using `Decimal` or `CheckConstraint` without importing them — always verify imports after any model change before restarting the container.
- Files transferred from a stale `/tmp` copy have previously overwritten newer code (e.g. an old portfolio router missing the `register` endpoint) — always confirm the source file being copied is the actual latest version, not a leftover scratch copy.
- `BuyLotEdit` (in `schemas.py`) has previously been referenced by `portfolio.py` before being defined — a reminder that schema and router changes need to land together, not the router first.
- General rule reaffirmed: after any deploy, check container startup logs, hit `/health`, then explicitly test the specific endpoint/UI behavior that changed — don't assume a clean restart means the change is live and correct.
### Admin — user account email management
Two admin endpoints exist for managing account emails, separate from the Census/legal-document admin features described above:
```
PATCH /admin/users/{id}/email
GET /admin/users/{id}/email-changes
```
Email changes are logged in an audit trail that requires a reason at time of change.
---
## 🐳 Docker Configuration ## 🐳 Docker Configuration
Managed via OMV → Services → Compose, project `fundtracker`, at `/Kingdezignsnas16/fundtracker/`. Managed via OMV → Services → Compose, project `fundtracker`, at `/Kingdezignsnas16/fundtracker/`.
@ -169,6 +221,9 @@ Two tiers, by risk:
- Market data via `yfinance` + `curl_cffi` (Chrome impersonation) — required workaround for Yahoo's bot detection, not optional. - Market data via `yfinance` + `curl_cffi` (Chrome impersonation) — required workaround for Yahoo's bot detection, not optional.
- Deployment: code-only changes = folder swap + `docker restart`; dependency changes = rebuild; compose/env changes = `up -d`. Structural DB migrations require a `pg_dump` backup first. - Deployment: code-only changes = folder swap + `docker restart`; dependency changes = rebuild; compose/env changes = `up -d`. Structural DB migrations require a `pg_dump` backup first.
- `COOKIE_SECURE=true` and `PUBLIC_BASE_URL=https://funds.kingdezigns.com` must both be set now that this is publicly exposed — leaving `COOKIE_SECURE=false` on a public HTTPS deployment would send credentials in cleartext. - `COOKIE_SECURE=true` and `PUBLIC_BASE_URL=https://funds.kingdezigns.com` must both be set now that this is publicly exposed — leaving `COOKIE_SECURE=false` on a public HTTPS deployment would send credentials in cleartext.
- **Portfolio / transaction tracking (documented here 2026-08-19, built in an earlier session):** a full buy-lot/sale/split register sits alongside the scoring/screening system — buy lots (with remaining-qty tracking), sales (FIFO/LIFO/Specific ID matching via `/portfolio/sales`), and splits (`/portfolio/splits`, only affects lots active and purchased before the split date). `GET /portfolio/register` returns the full lot+sale view the Portfolio page renders. Lots lock against edit/delete once a sale references them — deleting the sale restores shares and unlocks the lot. CSV bulk import via `/portfolio/import/csv`. Full endpoint list and locking rules in the dedicated section above.
- **Portfolio UI updated 2026-08-19:** the register now renders as a three-level collapsible hierarchy — **Ticker (collapsed by default) → Buy Lot → Sale(s)** — instead of showing every lot for every ticker at once. Frontend-only change to `portfolio.html`, deployed via the standard code-only pattern (scp to `/tmp` → `sudo cp` into the root-owned project dir → `sudo docker restart fundtracker_api`, no rebuild required).
- **Known portfolio deployment gotchas** (missing `Decimal`/`CheckConstraint` imports in `models.py`, stale `/tmp` copies overwriting newer router code, schema/router ordering) are logged in the Portfolio section above — check there before re-diagnosing the same class of failure.
--- ---