Wealth Pulse is web frontend for a ledger journal file. The ledger journal file is based on the command line Ledger journal file format and features double-entry accounting for personal finance tracking.
Short-term: provide better looking reports and charts via a web frontend.
Long-term: provide better reporting on investments.
I met my "must have" goals for this project in May 2015, and have been using it with my ledger file exclusively since. That said, it may not meet your needs, as it has been catered for my personal usage.
- React 0.9.0-rc1
- Underscore 1.6.0
- Backbone 1.1.1 (routing only)
- D3 3.0.0
- jQuery 2.1.0
- jQuery Hotkeys
- Bootstrap 2.3.1
How to Build
This project uses paket for .NET dependency management, so you need to run the following commands before building:
.paket\paket.bootstrapper.exe .paket\paket.exe restore
How to Run
LEDGER_FILEenvironment variable that points to your ledger file.
Optional: Setup a
WEALTH_PULSE_CONFIG_FILEenvironment variable that points to your Wealth Pulse config file.
Optional: Setup a
WEALTH_PULSE_PRICES_FILEenvironment variable that points to where you'd like Wealth Pulse to store symbol prices.
Server & Browser method:
Open a browser to
npm installto download dependencies
npm startto start Electron & server
Wealth Pulse Config File
Wealth Pulse will scrape Google Finance for prices of stocks & mutual funds.
To utilize this feature, define the
variable as the path to a config file.
Within the config file, you can define a mapping of commodity symbol to Google Finance search string:
SC [symbol] [search string]
SC AAPL aapl SC "INI220" MUTF_CA:INI220
The first will search for stock prices for Apple, the second will search for mutual fund prices for the Tangerine Balanced Portfolio.
Command Bar Supported Commands
You can use the '/' hotkey to reach the command bar.
Balance: bal [accounts-to-include] [parameters] Register: reg [accounts-to-include] [parameters] Net Worth: nw
:exclude [accounts-to-exclude] :period [this month|last month] :since [yyyy/mm/dd] :upto [yyyy/mm/dd] :title [report title] :convert Applies to balance report only. Converts commodities into their current value based on available price information.
Notes on Commodities
I've taken the approach recommended by Penny for tracking commodities. Follow the link for some great documentation. In my case though, I'm still using a subset the Ledger syntax, instead of different syntax, as Penny does.
So a purchase of a mutual fund might look like:
2015-04-12 * Bank of Olympia - Golden Fund Assets:Investments:Olympia:GoldenFund 59.5856 "OGF387" Assets:Savings $-1,272.73 Basis:OGF387:2013-04-01 -59.5856 "OGF387" Basis:OGF387:2013-04-01 $1,272.73 P 2015-04-12 "OGF387" $21.36
A sale of the same mutual fund would look like:
2015-05-12 * Bank of Olympia - Golden Fund sale Assets:Savings $1,430.05 Assets:Investments:Olympia:GoldenFund -59.5856 "OGF387" Basis:OGF387:2013-04-01 $-1,272.73 Basis:OGF387:2013-04-01 59.5856 "OGF387" Income:Investments:Olympia:GoldenFund $-157.32 P 2015-05-12 "OGF387" $24.00
Phase 1: Basic Reporting
- Replace the ledger bal and reg commandline options with a web interface.
- Provide some basic reporting like net worth, income vs expenses, ...
- See http://bugsplat.info/static/stan-demo-report.html for some examples
Parsing Ledger File
- Basic / optimistic parsing of ledger file
- Autobalance transactions
Initial Static Balance Reports:
- Assets vs Liabilities, ie Net Worth
- Income Statement (current & previous month)
- Net Worth chart
Parsing Ledger File
- Ensure transactions balance (if not autobalanced)
- Combine balance sheet & income report into single balance report with parameters
Net Worth Chart
- Make it a separate page/report
- Generate "networth" chart from the command bar
- Convert all existing reports to render dynamically instead of a static
- Get barebones nancy working
- map /, /balancesheet, /currentincomestatement, /previousincomestatement to current pages
- Switch to client-side framework
- Setup JSON services
- Setup static file services
- Create client-side app
- Setup command bar
- Highlight active page on navlist
- Register report with parameters (ie accounts, date range)
- build register report generator function
- create register report template
- link up to command bar
- link to from balance reports
- Preserve file order for transactions and entries within transactions but
output in reverse so most recent is on top
- Need to do sorting at the end so that running total makes sense
- Preserve file order for transactions and entries within transactions but output in reverse so most recent is on top
- Accounts Payable vs Accounts Receivable
- Dynamically list non-zero accounts with balance in navlist. Link to register report
- Watch ledger file and reload on change
- Handle situation where file cannot be parsed
- How to use / setup
Phase 2: Commodities
- Add support for investments (ie, multiple commodities) on balance and register reports
- Provide "real" and "book value" lines on the Net Worth chart
2014/06/11: I've prototyped it out enough now that I think what I want to do is make sure I can get current prices for commodities and then for the balance sheet, it would look something like:
Assets:Investments $realvalue $basis Investment1 $realvalue $basis num_units price price_date Investment2 $realvalue $basis num_units price price_date Investment3 $realvalue $basis num_units price price_date TOTAL $realvalue $basis
That would be the eventual goal. That assumes that I'm either going to merge units/book value entries in the program or update the ledger file. In the mean time, I'll have to keep the "units" excluded from the main balance report. This way I can avoid having to propogate up the account hierarchy all the different commodities.
Prototype using @@ or @ notation for commodity transactions, as per Ledger file spec. I'm not sure if I want to use Ledger's way of doing this, so going to set up the logic for the balance report and see how it feels with a sample file.
- What do I want the balance report to look like with investments?
- Update backend functions to handle multiple commodities
- Update frontend to display all commodities for balance
- Identify commodities in ledger file
- Commodity, date of first use, date when balance becomes 0
- Fetch prices from internet, cache and store
- Fetch prices from first date commodity appears in ledger file until 0 balance reached
- Store prices in a local cache (and in memory while app is running)
- For leaf accounts, calculate/provide book value, real value, number of
units, price, price date (may drop price date)
- Lookup price in price DB based on end date
- For parent accounts & total, calculate/provide book value and real value
- Update front end
- Omit commodity-related columns if "real value" and "book value" columns are the same for the whole report (based on query parameters)
Turns out I don't like using the @@ and @ notation. Recently found Penny, a ledger-inspired app that handles commodities much more cleanly and a lot closer to how I've been handling them in my own ledger file. This milestone will be about modifying Wealth Pulse to behave a bit more like Penny than Ledger.
Instead of using the @@/@ notation that Ledger supports/recommends, I will use Basis accounts the same way Penny documentation recommends. I will probably also have to write the "selloff" function.
One thing I will have to decide is if I will parse prices out of the ledger file
at all. Gut feeling right now is that I should. I think the
should only have downloaded prices, and the ledger file should only have
prices that were manually input.
I will keep track of two price DBs in memory: the downloaded prices and the ledger file prices. When performing a price lookup, check the downloaded prices file first, and the ledger file if no price exists.
- Update Entry type -- will only have one amount, no commodity field
- Price on SymbolPrice record should be an Amount, not just a decimal
- Symbol type should be value + quoted or format
- Rename Entry to Posting
- Remove EntryType field from Posting (no longer supporting "virtual" accounts)
- Move price DBs into Journal record
- Remove multiple commodity parsing logic. ie. remove @@ and @ options
- Parse price lines from journal file
- Get initial parsing working
- Should fix parsing up to be more precise (amount MUST have symbol)
- Create price db from parsed prices
- Get line numbers for headers, postings, prices
- Add unit tests for parsers
- Rework post-parse processing
- Fix pricedb parsing and serialization
- Use FParsec parser instead of regex
- Price should be an Amount with a symbol
- Pricedb and Journal should use the same parser combinator
- Parse config file using FParsec
- Review & restore commented code in symbolprices.cs and journalservice.cs
- Update logic for calculating basis and real value for commodities
- Need query function for latest price as of date (check
.pricedbthen ledger prices)
- Make ":convert" a report option
Net Worth Report
- Provide "real value" and "basis value" lines
- Use of commodities within file
- Configuration file
- Scraping Google Finance for prices
- Review identifySymbolUsage
- Review and enable price fetching logic
- Handle multiple commodities
- Handle Multiple commodities
- Can I get rid of the list comprehension?
- Can I clean it up so the balance query function is just sub-function calls?
- Clean up computeCommodityValues (get rid of side-effects)
- Use electron to make a bundled app instead of requiring an always-running server process with a web browser pointed at it
- Do proof-of-concept with Electron pointed to current server -- it works!
- Turn it into an Electron app instead of accessing through a browser:
- Launch F# server at startup
- Launch electron browser window pointed to WP server
- Update documentation with launch instructions (npm start in dev)
- Delay launching browser window until server ready
- Retrieve port (or full address) from server output
- Determine correct path to wealth pulse server depending on:
- dev mode vs bundled app
- platform (OS X vs Windows)
- Icon and rename and change 'Electron' app name to 'WealthPulse'
- Ultimately abandoned this approach -- the overhead of Electron just isn't worth it
- Setup CI
- Switch from nuget to Paket
- Retry after delay if fetching prices fails (happens if no internet is available) instead of waiting a full day to retry
- Review all types for consistency
- Symbol Price: Hard-coded line number to -1 -- feels like a hack
- Convert/update project to .NET 6 (Core)
- Replace NancyFX
- Replace TravisCI (it's dead) -- maybe Appveyor too?
- Convert unit tests to xUnit + Unquote or Expecto?
- Add a real logger
- Use npm/FAKE/grunt/gulp automation?
- Building/bundling a shippable "app"
- OS X:
- Copy F# app to
- Windows: TBD
- OS X:
- Building/bundling a shippable "app"
- Google Finance went RIP a few years ago (sigh), so I need to redo this
- Only write to
.pricedbif new prices were found
- When writing to
.pricedb, write to temp file first, then replace
.pricedbfile (avoid clobbering a file if app exits during write)
- Consider using Akka.net actors?
- Update the UI with one of:
- Latest React (with React Router instead of Backbone router)
- Use bower or npm to retrieve dependencies, rather than including sources for all dependencies in the git repo
- Update to Bootstrap from v2 to v3 (or v4...)
- Display indicator when ajax call is happening
- Combine reports and payables / receivables into one dict?
- Include a list of account levels field on Posting?
- How am I actually using Account & AccountLineage in the app?
- Also change Account to a Subaccount list and Subaccount = String
- Add unit tests
- Break functions into smaller units?
- Use LINQ?
- Balance: Can I improve the entry filtering code?
- Income Statement: Should be able to pull up income statement for any month
- Add a dropdown for picking period?
New Report Ideas
- Overall portfolio return and per investment
- Expected T3s/T5s to receive for last year (ie had distribution)
- I should be able to get this with a register report query
- Average in last 3 months, in last year
- Burn rate - using last 3 months expenses average, how long until savings is gone?
- Top Expenses over last period
- Income Statement chart (monthly, over time)
Parsing Ledger File
- Skipping the comment lines during parsing would simplify processing (since first thing we do is drop them)
- Transform post-processing to a pipeline that deals with one transaction at a time (completely), or does things in parallel
- Improve error reporting during parsing and balance checking
- Validate accounts based on a master list? To prevent typos and whatnot...
Command Bar Enhancements
- Add parameters: :payee :excludepayee :uncleared or :cleared
- Add fault tolerance to parameter parsing
- Clean up and improve date/period parsing Additions for period: yyyy, last year, this year
- Autocomplete hints (bootstrap typeahead)
- Configurable nav list
- Configurable default report
- Handle outstanding payees with multiple commodity amounts a bit nicer (renders poorly right now)
- Write the equivalent of Penny's sell-off command
- Script to convert pasted transactions from bank into commodity transactions
- Let server launch on any port?
- Server should handle SIGTERM signal gracefully