A local-first, keyboard-driven Rust TUI for portfolio tracking.
pulse is an in-progress finance and investment tracker focused on:
- stocks / ETFs
- crypto spot
- cash / FX balances
- custom manual assets
- SQLite-backed local persistence
- ledger-first transaction tracking
- projection-based positions, cash, and P&L
This project is under active development. The current build already supports:
- SQLite migrations and local database bootstrap
- entities, accounts, and currency pockets
- manual assets
- deposits, withdrawals, fees, interest, dividends
- buys and sells
- transaction void / soft delete / revisions
- cash transfers between accounts
- FX conversion inside an account
- manual quote entry
- manual FX rate entry
- projection-backed dashboard, positions, cash/FX, transactions, reports, and activity log screens
- Rust
- ratatui
- crossterm
- rusqlite
- rust_decimal
git clone <your-repo-url>
cd pulseYou can run without a config file, but the easiest setup is:
cp pulse.example.toml pulse.toml
mkdir -p data backupsThe example config stores the SQLite database at:
./data/pulse.db
pulse looks for config in this order:
./pulse.toml- your default OS config directory
- built-in defaults
cargo runcargo testOptional:
cargo checkWhen the TUI opens:
?toggle help:enter command modeqquitrrebuild projections + reload
Command mode supports both direct commands and bare-command forms:
- type a full command like
:buy 1 AAPL 2 185.50 CAD 0 "starter position" - or type a bare command like
:buy,:sell, or:depositto open a modal form - inside forms, use
Tabor arrow keys to move, type to edit,Enterto submit, andEscto cancel - use
:help <command>or:<command> helpfor command-specific usage
Navigation:
gddashboardgeentitiesgaaccountsgppositionsgttransactionsgxcash / FXgrreportsgssettingsglactivity log
Paste these commands one by one in command mode:
:entity new "Personal" person CAD
:account new 1 "Brokerage" Taxable CAD "Manual"
:pocket new 1 CAD "Main CAD" default
:pocket new 1 USD "USD Pocket"
:asset new AAPL USD equity "Apple Inc"
:deposit 1 CAD 1000 "initial funding"
:fx 1 CAD 200 USD 150 2 "convert cash"
:buy 1 AAPL 2 185 CAD 0 "starter position"
:price AAPL USD 210.50 "manual mark"
:fxrate USD CAD 1.35
Then inspect:
gpfor positionsgxfor cash / FX balancesgrfor valuation-backed report totals
You can either enter the full command directly, or type a bare command keyword to open a form for many workflows.
Examples:
:buyopens a buy form:sellopens a sell form:depositopens a deposit form:accountopens an account-creation form:help buyor:buy helpshows command-specific help
:entity new <name> [person|household|corporation|trust|custom] [reporting_currency]
:account new <entity_id> <name> [AccountType] [reporting_currency] [Institution]
:pocket new <account_id> <currency> [Label] [default]
:asset new <ticker> [quote_currency] [asset_type] [Name]
:price <ticker> <currency> <price> [note]
:fxrate <base_currency> <quote_currency> <rate>
:deposit <account_id> <currency> <amount> [note]
:withdraw <account_id> <currency> <amount> [note]
:fee <account_id> <currency> <amount> [note]
:interest <account_id> <currency> <amount> [note]
:dividend <account_id> <ticker> <currency> <amount> [note]
:transfer <from_account_id> <to_account_id> <currency> <amount> [note]
:fx <account_id> <from_currency> <from_amount> <to_currency> <to_amount> [fee] [note]
:buy <account_id> <ticker> <qty> <unit_price> <settlement_currency> [fee] [note]
:sell <account_id> <ticker> <qty> <unit_price> <settlement_currency> [fee] [note]
:void <transaction_id> [reason]
:delete <transaction_id> [reason]
:revisions <transaction_id>
:help
- local-first
- ledger-first
- transactions are the source of truth
- projection tables are derived and rebuildable
- weighted average cost basis for v1
- offline-friendly behavior with cached/manual market data
Planned or incomplete areas include:
- provider-backed quote / FX refresh
- richer revision inspection UI
- benchmark tracking
- broader reporting
- transaction edit flows
- import/export improvements
Project notes and planning artifacts live in:
notes/
Example config:
pulse.example.toml
See LICENSE.