Skip to content

Plotting functionality for reports built with the Ledger CLI tool.

License

Notifications You must be signed in to change notification settings

Qkessler/ledger-plot

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ledger-plot

ledger-plot is a CLI written in Rust to plot relevant data for your ledger reports and overall balance. It can be used to get a quick glance at your finances on an arbitrary time range, your wealth growth or other relevant reports from just a simple .ledger file.

From a .ledger file, we’ll parse the transactions and process the data for you.

Implementation notes

What I’m thinking is whether we actually want to be parsing all the transactions ourselves or we can offload some of the work to the actual ledger CLI, which already has most of the “reports” that we are trying to build generated. We could then parse the results and use that to build our graphs. Probably something to consider.

Let’s see if there are Rust bindings for Ledger CLI. There aren’t any Rust bindings to the CLI. We have to define the different use cases that I want to implement and then decide whether we want to do them manually with the parsed Transactions, or move forward with offloading the work to Ledger CLI.

Plus, let’s talk caching. We can know from the next iteration whether a file being passed was modified or not. If it wasn’t modified, there isn’t a reason to actually reparse everything, if that saved x amount of ms. Nevertheless, probably it’s an early optimization and we should focus on getting the thing done first.

Here are the use cases I’m thinking about:

  1. Time series plot: Display the transaction amounts over time, giving insights into the spending or income patterns of the individual. This plot can help identify any trends, seasonality, or periodic patterns.
  2. Categorical bar plot: Analyze the distribution of transaction categories (e.g., groceries, bills, entertainment) to understand the individual’s spending habits and identify any high or low spending areas. We can have x amount of colors to show differences through the months of spending.
  3. Pie chart or donut chart: Highlight the percentage distribution of transactions in different categories, providing a visual breakdown of spending across different expense types.
  4. Monthly/Yearly summary plot: Summarize the total expenses or income for each month or year, allowing the individual to see their financial performance over time.
  5. Portfolio Value Over Time: Plot the value of their investment portfolio over time, including both stocks and real estate. This plot helps visualize the growth and fluctuations in their overall wealth.
  6. Asset Allocation Pie Chart: Display the allocation of their wealth across different asset classes, such as stocks, real estate, bonds, or cash. This visual representation helps individuals understand the diversification and concentration of their portfolio.
  7. Cash Flow Statement: Showcase the inflows and outflows of money over time, including investment contributions, dividends, rental income, expenses, and mortgage payments. This plot allows individuals to track their overall cash flow and identify areas of improvement.
  8. Return on Investment (ROI) Comparison: Compare the ROI of different investment assets over different time periods. This plot aids in evaluating the effectiveness of various wealth-building strategies and identifying the most successful investments.

We are not passing the copy to Transaction, but rather owning the value itself, creating our accounts hashmap, which is the one that will provide all the info we need when doing the queries for building the graphs. I’m thinking whether in this case it makes sense to have a temporary DB for the user. We would take time to build the DB, but be fast to query it. I don’t think it’s worth the hassle, probably it’s not going to take less than 500ms which we benchmarked today. I’m thinking how we can optimize the saving and storing further.

For now, let’s focus on Getting It Done, we want to have something that works and then move on to the next thing. We want the community to use it, so we should optimize for the user. If we feel the user experience is bad when loading a file directly, we could have a command to cache and others to query, which could be closer to the user experience.

Account over time: Time series plot

It seems like Plotters does not yet have a console backend, but we have WASM, we have other stuff we can try. Nevertheless, I want to play with the console support, since it seems like something I would probably use, more than having a REVIEW Axium web server running and updating from the files in a directory, although definitely an interesting idea.

Ref: https://github.com/plotters-rs/plotters/blob/master/plotters/examples/console.rs. Let’s play with it.

Now we should think that we must have the time series data directly, and the date for each of the transactions. I’m thinking what would be the most efficient way to store the data, based on what should be accessed in what order.

  1. If we access the info for an account more, rather than accessing for all accounts, we should store as the partition key, the account str.
  2. On the other hand, if we have more reports using all the accounts at the same time, we should probably store the dates as the partition key, as we might need to have all the transactions (regardless of their account) for a specific time range.

All reports seem in relation with the date, hence, why I’m going to be searching for a datastructure that considers dates, let’s see what’s out there.

Transactions ergonomy

What I see is happening with the Transactions object is that it takes the Postings, which is not great, because we don’t really know the “impact” on the accounts from it. Let’s see some examples.

Transaction { comment: None, date: 2023-11-03, effective_date: None, status: Some(Cleared), code: None, description: "Liquidacion De Las Tarjetas De Credito Del Contrato 0049 6773 502 1909957", postings: [Posting { account: "Assets:Checking", reality: Real, amount: Some(PostingAmount { amount: Amount { quantity: -1.99, commodity: Commodity { name: "€", position: Right } }, lot_price: None, price: None }), balance: None, status: None, comment: None }, Posting { account: "Expenses:Food:Social", reality: Real, amount: None, balance: None, status: None, comment: None }] }

What we know from the postings, is that each of the accounts is impacted by X, and since Ledger by nature is a 0 sum reporting system, if we are in the red, and any posting does not have an amount associated with it, we make sure the remaining sum is 0. So for that scenario, the could would do:

  • Assets:Checking, -1.99, €
  • Expenses:Food:Social, 0 - (- 1.99) = 1.99, €

With that, we need another data structure to hold the information for each of the accounts, in real time. We want to have the data aggregated by day, month, year, but that can also be computed in real time. Basically, we want to make sure we have all the actual postings per account.

That sounds like a HashMap, where we could have the account name as the key, and a Set as the value. TODO Would that take too much mem when running the program if we have a big data set? We would need to check.

Benchmarks

First iteration, just parsing the files. It seems like parsing a 5000 line file we take 10ms.

hyperfine -N --warmup 3 './target/release/ledger-plot -f /tmp/current-copy.ledger'
Benchmark 1: ./target/release/ledger-plot -f /tmp/current-copy.ledger
  Time (mean ± σ):      10.0 ms ±   1.4 ms    [User: 4.2 ms, System: 2.0 ms]
  Range (min … max):     7.0 ms …  13.7 ms    254 runs

What happened here. Can we speed it up? Probs we can be async when adding transactions, careful with race conditions though.

❯ hyperfine -N --warmup 3 './target/release/ledger-plot -f /tmp/current-copy.ledger'
Benchmark 1: ./target/release/ledger-plot -f /tmp/current-copy.ledger
  Time (mean ± σ):     529.2 ms ±   4.1 ms    [User: 14.0 ms, System: 6.8 ms]
  Range (min … max):   523.9 ms … 538.4 ms    10 runs
./target/release/ledger-plot -f $(fd --absolute-path --base-directory ~/Documents/finances/years ledger) ~/Documents/finances/current.ledger

About

Plotting functionality for reports built with the Ledger CLI tool.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages