GrahamScreener
07Watchlist And Portfolio

Watchlist and Portfolio Guide

Watchlist

Adding a Ticker

Via UI: Navigate to any stock page (e.g., /stock/AAPL) and click the star icon. Or use the watchlist page's add form.

Via API:

curl -X POST http://localhost:3000/api/watchlist \
  -H "Content-Type: application/json" \
  -d '{
    "ticker": "CBA.AX",
    "thesis": "Big Four bank with best cost-to-income ratio",
    "targetBuyPrice": 95,
    "stopLoss": 80,
    "positionSizePct": 6
  }'

Field Descriptions

FieldTypeRequiredDescription
tickerstringYesYahoo Finance ticker with exchange suffix (e.g., CBA.AX, AAPL)
thesisstringNoYour investment thesis — why you're watching this stock
targetBuyPricenumberNoThe price at which you'd consider buying
stopLossnumberNoThe price at which you'd exit to limit losses
positionSizePctnumberNoTarget portfolio allocation (0–100%)

All optional fields default to null. The exchange is auto-detected from the ticker suffix.

Removing a Ticker

Via UI: Click the delete button on the watchlist page, or click the star icon on the stock page to untoggle.

Via API:

curl -X DELETE "http://localhost:3000/api/watchlist?ticker=CBA.AX"

Updating a Ticker

POST to the same endpoint with the same ticker — it upserts (updates if exists, inserts if new).

Portfolio

Recording a Trade

Via UI: Navigate to /portfolio and use the trade entry form.

Via API:

curl -X POST http://localhost:3000/api/portfolio \
  -H "Content-Type: application/json" \
  -d '{
    "ticker": "AAPL",
    "side": "BUY",
    "quantity": 10,
    "price": 175.50,
    "fees": 9.95,
    "tradeDate": 1704067200000,
    "notes": "Initial position — value entry"
  }'

Field Descriptions

FieldTypeRequiredDescription
tickerstringYesYahoo Finance ticker
side"BUY" or "SELL"YesTrade direction
quantitynumberYesNumber of shares (must be positive)
pricenumberYesPrice per share (must be positive)
feesnumberNoBrokerage/commission fees (default 0)
tradeDatenumberYesUnix milliseconds timestamp of the trade date
notesstringNoFree-text notes about the trade

FIFO Cost-Basis Explanation

GrahamScreener uses First-In-First-Out (FIFO) accounting. When you sell shares, the oldest purchased lots are consumed first.

Code path: src/lib/portfolio.tsbuildPositionsFIFO(trades)

Worked Example

#DateSideQtyPriceFees
12024-01-01BUY10$100$10
22024-03-15BUY5$120$5
32024-06-01SELL8$140$8

After Trade 3 (SELL 8 @ $140):

FIFO consumes the oldest lots first:

  • Lot 1: 10 shares @ $100 → sell 8 of these
  • Realised P&L = 8 × ($140 − $100) = $320

Remaining open lots:

  • 2 shares from Lot 1 @ $100
  • 5 shares from Lot 2 @ $120

Position summary:

  • Shares held: 7
  • Cost basis: (2 × $100) + (5 × $120) = $800
  • Average cost: $800 / 7 = $114.29
  • Realised P&L: $320
  • Total fees: $10 + $5 + $8 = $23

What FIFO Does Not Handle

  • Tax-lot picking: Cannot select specific lots to sell (e.g., highest-cost first for tax optimisation)
  • Average-cost method: Some jurisdictions allow averaging; GrahamScreener uses FIFO only
  • Wash-sale rules: No tracking of 30-day wash-sale windows (US IRS requirement)
  • Currency conversion: Mixed-currency positions are summed in their native currencies

Benchmark Comparison

Each exchange maps to a benchmark index for relative performance:

ExchangeBenchmarkTickerWhy
ASXS&P/ASX 200^AXJOBroadest liquid Australian index
BSEBSE SENSEX^BSESNIndia's flagship 30-stock index
NSENIFTY 50^NSEIIndia's broadest large-cap index
USS&P 500^GSPCStandard US large-cap benchmark

Code path: src/lib/universe/index.tsBENCHMARK constant

Note: Benchmark comparison charting is planned for v2. Currently the benchmarks are defined but not rendered in the portfolio UI.

Exporting / Backing Up the SQLite File

The entire database is a single file: data/valuelens.db.

To back up:

cp data/valuelens.db data/valuelens-backup-$(date +%Y%m%d).db

To export trades as JSON:

curl http://localhost:3000/api/portfolio | python3 -m json.tool > trades.json

To export watchlist as JSON:

curl http://localhost:3000/api/watchlist | python3 -m json.tool > watchlist.json

To inspect with sqlite3 CLI:

sqlite3 data/valuelens.db
.tables
SELECT * FROM watchlist;
SELECT * FROM portfolio_trades;
.quit

Last updated: 2026-05-09 by Claude Cowork