Sync

One command refreshes the outside data firma depends on: live stock prices from Finnhub and historical FX rates from FRED. Because the scope is derived from the data you've already logged, firma asks each API only for what is missing.

By default, this syncs prices for your active holdings and FX history for the five supported currencies: KRW, JPY, EUR, CNY, and GBP. Both happen in one run.
$firma sync
◇  Updated 4 stocks
◇  Synced 9525 FX rates across 5 currencies
Requires both finnhub-key (for prices) and fred-key (for FX history). Run firma config set finnhub-key <key> and firma config set fred-key <key> first.
firma never asks the APIs for more than it needs. The sync scope is computed from the local SQLite database:
📈
Prices, based on active holdings
Only tickers with a non-zero net position, computed from your transactions, are fetched from Finnhub. Sell a position down to 0 and it disappears from the next sync. If you hold 4 stocks, sync makes 4 Finnhub calls, not 400.
💱
FX history, based on your earliest entry
On the first run, firma scans transactions, balances, and flow entries to find your earliest date, then backfills FX rates from there through today, usually in about 5 FRED calls and 10 seconds. After that, it only fetches dates beyond your latest cached rate, which is usually sub-second.
This means you should add your data first, then sync. If the database is empty, sync has nothing to work from and does nothing.
This refreshes FX history only. It's useful when you import an older transaction that predates the current FX cache, or when you want updated historical rates without re-fetching prices.
$firma sync fx
◇  Synced 9525 FX rates across 5 currencies
Same incremental logic. Pulls from FRED via the corresponding daily series (DEXKOUS, DEXJPUS, DEXCHUS, DEXUSEU, DEXUSUK) and stores uniform direction (foreign per 1 USD) in ~/.firma/firma.db fx_rates table.
Without a historical FX cache, past balances shown in a non-USD currency use today's exchange rate. That means "what was my net worth in KRW in 2018?" gets the wrong answer. Firma converts each entry using the FX rate from its own date, so historical figures reflect what you actually had at that time.
Anything tagged with a date uses the historical rate. Anything that represents current value, such as portfolio market value, the brief's MACRO TODAY section, or FX impact since yesterday, stays on live rates from open.er-api.com.
After the initial setup, the morning ritual is two commands or one Claude prompt:
$firma sync && firma brief
Or, in Claude:
Claude Desktop · firma MCP connected

You

Sync everything and give me today's brief.

Claude

sync_prices ✓   sync_fx_rates ✓  get_brief

Portfolio is up +$1,820 (+1.1%) today. NVDA leads at +4.3%. Macro is risk-on (5/5), stress low (4/100). MSFT earnings hits Apr 29. Anything you want to dig into?

MIT License