You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

330 lines
14 KiB

<!DOCTYPE html>
<html lang="en">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Using Ledger for YNAB-like envelope budgeting</title>
<meta name="description" content="Bye bye Elbank">
<link rel="shortcut icon" type="image/png" href="/favicon.png"/>
<link href="|Gentium+Book+Basic|Lato" rel="stylesheet">
<link rel="stylesheet" href="/assets/main.css">
<link rel="stylesheet" href="/css/cafe.css">
<link rel="canonical" href="">
<link rel="alternate" type="application/rss+xml" title="Emacs café" href="/feed.xml">
<header class="site-header" role="banner">
<div class="wrapper">
<a class="site-title" href="/"><img src="/img/emacscafe.png"/>Emacs café</a>
<nav class="site-nav">
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger">
<span class="menu-icon">
<svg viewBox="0 0 18 15" width="18px" height="15px">
<path fill="#424242" d="M18,1.484c0,0.82-0.665,1.484-1.484,1.484H1.484C0.665,2.969,0,2.304,0,1.484l0,0C0,0.665,0.665,0,1.484,0 h15.031C17.335,0,18,0.665,18,1.484L18,1.484z"/>
<path fill="#424242" d="M18,7.516C18,8.335,17.335,9,16.516,9H1.484C0.665,9,0,8.335,0,7.516l0,0c0-0.82,0.665-1.484,1.484-1.484 h15.031C17.335,6.031,18,6.696,18,7.516L18,7.516z"/>
<path fill="#424242" d="M18,13.516C18,14.335,17.335,15,16.516,15H1.484C0.665,15,0,14.335,0,13.516l0,0 c0-0.82,0.665-1.484,1.484-1.484h15.031C17.335,12.031,18,12.696,18,13.516L18,13.516z"/>
<div class="trigger">
<a class="page-link" href="/about/">About Emacs café</a>
<main class="page-content" aria-label="Content">
<div class="wrapper">
<article class="post" itemscope itemtype="">
<header class="post-header">
<h1 class="post-title" itemprop="name headline">Using Ledger for YNAB-like envelope budgeting</h1>
<p class="post-meta">
<time datetime="2018-06-12T21:20:00+02:00" itemprop="datePublished">
Jun 12, 2018
<span itemprop="author" itemscope itemtype=""><span itemprop="name">Nicolas Petton</span></span>
<div class="post-content" itemprop="articleBody">
<h1 id="bye-bye-elbank">Bye bye Elbank</h1>
<p>I have to start this post with this: I will not be actively maintaining
<a href="">Elbank</a> anymore, simply because I
switched back to <a href="">Ledger</a>. If someone wants to
take over, please contact me!</p>
<p>The main reason for switching is budgeting. While Elbank was a cool experiment,
it is not an accounting software, and inherently lacks support for powerful
<p>When I started working on Elbank as a replacement for Ledger, I was looking for
a reporting tool within Emacs that would fetch bank transactions automatically,
so I wouldn’t have to enter transactions by hand (this is a seriously tedious
task, and I grew tired of doing it after roughly two years, and finally gave up).</p>
<p>Since then, I learned about ledger-autosync and boobank, which I use to sync my
bank statements with Ledger (more about that in another post).</p>
<h1 id="ynabs-way-of-budgeting">YNAB’s way of budgeting</h1>
<p>I only came across <a href="">YNAB</a> recently. While I won’t use their
software (being a non-free web application, and, you know… there’s no <code class="highlighter-rouge">M-x
ynab</code>), I think that the principles behind it are really appealing for personal
budgeting. I encourage you to <a href="">read more about
it</a> (or grab a <a href="">copy of the
book</a>, it’s great), but here’s
the idea.</p>
<p><strong>Budget every euro</strong>: Quite simple once you get it. Every single Euro you have
should be in a budget envelope. You should assign a job to every Euro you
earn (that’s called
<a href="">zero-based</a>, <a href="">envelope
<p><strong>Embrace your true expenses</strong>: Plan for larger and less frequent expenses, so
when a yearly bill arrives, or your car breaks down, you’ll be covered.</p>
<p><strong>Roll with the punches</strong>: Address overspending as it happens by taking money
overspent from another envelope. As long as you keep budgeting, you’re
<p><strong>Age your money</strong>: Spend less than you earn, so your money stays in the bank
account longer. As you do that, the age of your money will grow, and once
you reach the goal of spending money that is at least one month old, you
won’t worry about that next bill.</p>
<h1 id="implementation-in-ledger">Implementation in Ledger</h1>
<p>I assume that you are familiar with Ledger, but if not I recommend reading its
<a href="">introduction</a>
and <a href="">tutorial</a>.</p>
<p>The implementation in Ledger uses plain double-entry accounting. I took most of
it from
<a href="">Sacha</a>, with
some minor differences.</p>
<h2 id="budgeting-new-money">Budgeting new money</h2>
<p>After each income transaction, I budget the new money:</p>
<div class="highlighter-rouge"><pre class="highlight"><code>2018-06-12 Employer
Assets:Bank:Checking 1600.00 EUR
Income:Salary -1600.00 EUR
2018-06-12 Budget
[Assets:Budget:Food] 400.00 EUR
[Assets:Budget:Rent] 600.00 EUR
[Assets:Budget:Utilities] 600.00 EUR
[Equity:Budget] -1600.00 EUR
<p>Did you notice the square brackets around the accounts of the budget
transaction? It’s a feature Ledger calls <a href="">virtual
postings</a>. These
postings are not considered real, and won’t be present in any report that uses
the <code class="highlighter-rouge">--real</code> flag. This is exactly what we want, since it’s a budget allocation
and not a “real” transaction. Therefore we’ll use the <code class="highlighter-rouge">--real</code> flag for all
reports except for our budget report.</p>
<h2 id="automatically-crediting-budget-accounts-when-spending-money">Automatically crediting budget accounts when spending money</h2>
<p>Next, we need to credit the budget accounts each time we spend money. Ledger
has another neat feature called <a href="">automated
for this:</p>
<div class="highlighter-rouge"><pre class="highlight"><code>= /Expenses/
[Assets:Budget:Unbudgeted] -1.0
[Equity:Budget] 1.0
= /Expenses:Food/
[Assets:Budget:Food] -1.0
[Assets:Budget:Unbudgeted] 1.0
= /Expenses:Rent/
[Assets:Budget:Rent] -1.0
[Assets:Budget:Unbudgeted] 1.0
= /Expenses:Utilities/
[Assets:Budget:Utilities] -1.0
[Assets:Budget:Unbudgeted] 1.0
<p>Every expense is taken out of the <code class="highlighter-rouge">Assets:Budget:Unbudgeted</code> account by default.</p>
<p>This forces me to budget properly, as <code class="highlighter-rouge">Assets:Budget:Unbudgeted</code> should always
be 0 (if it is not the case I immediately know that there is something wrong
going on).</p>
<p>All other automatic transactions take money out of the
<code class="highlighter-rouge">Assets:Budget:Unbudgeted</code> account instead of <code class="highlighter-rouge">Equity:Budget</code> account.</p>
<h2 id="a-budget-report">A Budget report</h2>
<p>This is the final piece of the puzzle. Here’s the budget report command:</p>
<div class="highlighter-rouge"><pre class="highlight"><code>ledger --empty -S -T -f ledger.dat bal ^assets:budget
<p>If we have the following transactions:</p>
<div class="highlighter-rouge"><pre class="highlight"><code>2018/06/12 Groceries store
Expenses:Food 123.00 EUR
2018/06/12 Landlord
Expenses:Rent 600.00 EUR
2018/06/12 Internet provider
Expenses:Utilities:Internet 40.00 EUR
<p>Here’s what the report looks like:</p>
<div class="highlighter-rouge"><pre class="highlight"><code> 837.00 EUR Assets:Budget
560.00 EUR Utilities
277.00 EUR Food
0 Rent
0 Unbudgeted
837.00 EUR
<h1 id="conclusion">Conclusion</h1>
<p>Ledger is amazingly powerful, and provides a great framework for YNAB-like
budgeting. In a future post I’ll explain how I automatically import my bank
transactions using a mix of <code class="highlighter-rouge">ledger-autosync</code> and <code class="highlighter-rouge">weboob</code>.</p>
<div id="disqus_thread"></div>
var disqus_shortname = 'emacs-cafe';
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//' + disqus_shortname + '';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
<noscript>Please enable JavaScript to view the <a href="" rel="nofollow">comments powered by Disqus.</a></noscript>
<footer class="site-footer">
<div class="wrapper">
<h2 class="footer-heading">Emacs café</h2>
<div class="footer-col-wrapper">
<div class="footer-col footer-col-1">
<ul class="contact-list">
Emacs café
<li><a href=""></a></li>
<div class="footer-col footer-col-2">
<ul class="social-media-list">
<a href=""><span class="icon icon--github"><svg viewBox="0 0 16 16" width="16px" height="16px"><path fill="#828282" d="M7.999,0.431c-4.285,0-7.76,3.474-7.76,7.761 c0,3.428,2.223,6.337,5.307,7.363c0.388,0.071,0.53-0.168,0.53-0.374c0-0.184-0.007-0.672-0.01-1.32 c-2.159,0.469-2.614-1.04-2.614-1.04c-0.353-0.896-0.862-1.135-0.862-1.135c-0.705-0.481,0.053-0.472,0.053-0.472 c0.779,0.055,1.189,0.8,1.189,0.8c0.692,1.186,1.816,0.843,2.258,0.645c0.071-0.502,0.271-0.843,0.493-1.037 C4.86,11.425,3.049,10.76,3.049,7.786c0-0.847,0.302-1.54,0.799-2.082C3.768,5.507,3.501,4.718,3.924,3.65 c0,0,0.652-0.209,2.134,0.796C6.677,4.273,7.34,4.187,8,4.184c0.659,0.003,1.323,0.089,1.943,0.261 c1.482-1.004,2.132-0.796,2.132-0.796c0.423,1.068,0.157,1.857,0.077,2.054c0.497,0.542,0.798,1.235,0.798,2.082 c0,2.981-1.814,3.637-3.543,3.829c0.279,0.24,0.527,0.713,0.527,1.437c0,1.037-0.01,1.874-0.01,2.129 c0,0.208,0.14,0.449,0.534,0.373c3.081-1.028,5.302-3.935,5.302-7.362C15.76,3.906,12.285,0.431,7.999,0.431z"/></svg>
</span><span class="username">NicolasPetton</span></a>
<a href=""><span class="icon icon--twitter"><svg viewBox="0 0 16 16" width="16px" height="16px"><path fill="#828282" d="M15.969,3.058c-0.586,0.26-1.217,0.436-1.878,0.515c0.675-0.405,1.194-1.045,1.438-1.809c-0.632,0.375-1.332,0.647-2.076,0.793c-0.596-0.636-1.446-1.033-2.387-1.033c-1.806,0-3.27,1.464-3.27,3.27 c0,0.256,0.029,0.506,0.085,0.745C5.163,5.404,2.753,4.102,1.14,2.124C0.859,2.607,0.698,3.168,0.698,3.767 c0,1.134,0.577,2.135,1.455,2.722C1.616,6.472,1.112,6.325,0.671,6.08c0,0.014,0,0.027,0,0.041c0,1.584,1.127,2.906,2.623,3.206 C3.02,9.402,2.731,9.442,2.433,9.442c-0.211,0-0.416-0.021-0.615-0.059c0.416,1.299,1.624,2.245,3.055,2.271 c-1.119,0.877-2.529,1.4-4.061,1.4c-0.264,0-0.524-0.015-0.78-0.046c1.447,0.928,3.166,1.469,5.013,1.469 c6.015,0,9.304-4.983,9.304-9.304c0-0.142-0.003-0.283-0.009-0.423C14.976,4.29,15.531,3.714,15.969,3.058z"/></svg>
</span><span class="username">NicolasPetton</span></a>
<div class="footer-col footer-col-3">
<p>A blog about Emacs, mostly focused on JavaScript development, by Nicolas Petton.