Stock Vesting in Beancount

Martin Blais, June 2015


This document explains the vesting of restricted stock units in Beancount, by way of an example. This example may not exactly match your situation, but enough detail is provided that you should be able to adapt it for your own particular differences.

A working example file can be found here to follow along with this text.

Restricted Stock Compensation

Many technology companies offer their employees incentive compensation in the form of “grants” (or “awards”) of “restricted stock units” (RSU), which is essentially a promise for the “release” to you of actual shares in the future. The stock is “restricted” in the sense that you cannot access it—you only receive it when it “vests”, and this happens based on a schedule. Typically, you are promised a fixed number of shares that vest every quarter or every month over a period of 3 or 4 years. If you leave the company, your remaining unvested shares are lost.

One way you can view these RSUs is as an asset, a receivable that arrives regularly over time. These RSUs are essentially compensation denominated in the currency of the company’s shares itself. We want to track the unraveling of these unvested units, and correctly account for their conversion to real stock with a cost basis and including whatever taxes were paid upon vesting.

Tracking Awards


First we want to define some commodities. In this example, I work for “Hooli Inc.” and will eventually receive shares of that company (valued in US dollars):

1990-12-02 commodity HOOL
  name: "Common shares of Hooli Inc."
  quote: USD

We will also want to track the amount of unvested shares:

2013-01-28 commodity HOOL.UNVEST
  name: "Unvested shares of Hooli from awards."

Accounts for Awards

Grants received is income. I use “Income:US:Hooli” as the root for all income accounts from Hooli, but in particular, I define an account for the awards, which contains units of unvested stock:

2013-01-28 open Income:US:Hooli:Awards        HOOL.UNVEST

When the stock vests, we will need to book the other side of this income somewhere, so we define an expenses account to count how much stock has been vested over a period of time:

2014-01-28 open Expenses:Hooli:Vested         HOOL.UNVEST

Receiving Awards

When you receive a new award (this may occur every year, for example, some people call this a “stock refresh”), you receive it as income and deposit it into a fresh new account, used to track this particular award:

2014-04-02 * "Award S0012345"
  Income:US:Hooli:Awards                -1680 HOOL.UNVEST
  Assets:US:Hooli:Unvested:S0012345      1680 HOOL.UNVEST

2014-04-02 open Assets:US:Hooli:Unvested:S0012345

You may have multiple active awards at the same time. It’s nice to have a separate account per award, as it offers a natural way to list their contents and when the award expires, you can close the account—the list of open award accounts gives you the list of outstanding & actively vesting awards. In this example I used the number of the award (#S0012345) as the sub-account name. It’s useful to use the number as the statements typically include it.

I like to keep all the awards in a small dedicated section.

Vesting Events

Then I have a different section that contains all the transactions that follow a vesting event.


First, when we vest stock, it’s a taxable income event. The cash value for the stock needs an Income account:

2013-04-04 open Income:US:Hooli:RSU

Taxes paid should be on the annual expenses accounts you should have defined somewhere else to account for that year’s taxes (this is covered elsewhere in the cookbook):

2015-01-01 open Expenses:Taxes:TY2015:US:StateNY
2015-01-01 open Expenses:Taxes:TY2015:US:Federal
2015-01-01 open Expenses:Taxes:TY2015:US:SocSec
2015-01-01 open Expenses:Taxes:TY2015:US:SDI
2015-01-01 open Expenses:Taxes:TY2015:US:Medicare
2015-01-01 open Expenses:Taxes:TY2015:US:CityNYC

After paying taxes on the received income, the remaining cash is deposited in a limbo account before getting converted:

2013-01-28 open Assets:US:Hooli:RSURefund

Also, in another section we should have an account for the brokerage which holds and manages the shares for you:

2013-04-04 open Assets:US:Schwab:HOOL

Generally you don’t have a choice for this broker because the company you work normally makes an arrangement with an external firm in order to administer the restricted stock program. Typical firms doing this type of administration are Morgan Stanley, Salomon Smith Barney, Schwab, even E*Trade. In the example we’ll use a Schwab account.

And we also need some sort of checking account to receive cash in lieu of fractional shares. I’ll assume a Bank of America account in the example:

2001-01-01 open Assets:US:BofA:Checking


First, the vesting events themselves:

2015-05-27 * "Vesting Event - S0012345 - HOOL" #award-S0012345 ^392f97dd62d0
  doc: "2015-02-13.hooli.38745783.pdf"
  Income:US:Hooli:RSU                    -4597.95 USD
  Expenses:Taxes:TY2015:US:Medicare         66.68 USD
  Expenses:Taxes:TY2015:US:Federal        1149.48 USD
  Expenses:Taxes:TY2015:US:CityNYC         195.42 USD
  Expenses:Taxes:TY2015:US:SDI               0.00 USD
  Expenses:Taxes:TY2015:US:StateNY         442.32 USD
  Expenses:Taxes:TY2015:US:SocSec          285.08 USD
  Assets:US:Hooli:RSURefund               2458.97 USD

This corresponds line-by-line to a payroll stub that I receive each vesting event for each award. Since all the RSU awards at Hooli vest on the same day of the month, this means that I have clusters of these, one for each award (in the example file I show two awards).

Some observations are in order:

  • The value of the Income posting consists of the number of shares vested times the FMV of the stock. In this case, that is 35 shares ⨉ $131.37/share = $4597.95. I just write the dollar amount because it is provided to me on the pay stub.

  • Receiving vested stock is a taxable income event, and Hooli automatically withholds taxes and pays them to the government on its employees’ behalf. These are the Expenses:Taxes accounts corresponding to your W-2.

  • Finally, I don’t directly deposit the converted shares to the brokerage account; this is because the remainder of cash after paying tax expenses is not necessarily a round number of shares. Therefore, I used a limbo account (Assets:US:Hooli:RSURefund) and decouple the receipt from the conversion.

This way, each transaction corresponds exactly to one pay stub. It makes it easier to enter the data.

Also note that I used a unique link (^392f97dd62d0) to group all the transactions for a particular vesting event. You could also use tags if you prefer.

Conversion to Actual Stock

Now we’re ready to convert the remaining cash to stock units. This happens in the brokerage firm and you should see the new shares in your brokerage account. The brokerage will typically issue a “stock release report” statement for each vesting event for each award, with the details necessary to make the conversion, namely, the actual number of shares converted from cash and the cost basis (the FMV on the vesting day):

2015-05-25 * "Conversion into shares" ^392f97dd62d0
  Assets:US:Schwab:HOOL                   18 HOOL {131.3700 USD}
  Assets:US:Hooli:Unvested:S0012345      -35 HOOL.UNVEST
  Expenses:Hooli:Vested                   35 HOOL.UNVEST

The first two postings deposit the shares and subtract the dollar value from the limbo account where the vesting transaction left the remaining cash. You should make sure to use the specific FMV price provided by the statement and not an approximate price, because this has tax consequences later on. Note that you cannot buy fractional shares, so the cost of the rounded amount of shares (18) will leave some remaining cash in the limbo account.

The last two postings deduct from the balance of unvested shares and I “receive” an expenses; that expenses account basically counts how many shares were vested over a particular time period.

Here again you will probably have one of these conversions for each stock grant you have. I enter them separately, so that one statement matches one transaction. This is a good rule to follow.

Refund for Fractions

After all the conversion events have moved cash out of the limbo account, it is left the fractional remainders from all the conversions. In my case, this remainder is refunded by Hooli 3-4 weeks after vesting as a single separate pay stub that includes all the remainders (it even lists each of them separately). I enter this as a transaction as well:

2015-06-13 * "HOOLI INC       PAYROLL" ^392f97dd62d0
  doc: "2015-02-13.hooli.38745783.pdf"
  Assets:US:Hooli:RSURefund            -94.31 USD
  Assets:US:Hooli:RSURefund             -2.88 USD ; (For second award in example)
  Assets:US:BofA:Checking               97.19 USD

After the fractions have been paid the limbo account should be empty. I verify this claim using a balance assertion:

2015-06-14 balance Assets:US:Hooli:RSURefund  0 USD

This provides me with some sense that the numbers are right.

Organizing your Input

I like to put all the vesting events together in my input file; this makes them much easier to update and reconcile, especially with multiple awards. For example, with two awards I would have multiple chunks of transactions like this, separated with 4-5 empty lines to delineate them:

2015-05-27 * "Vesting Event - S0012345 - HOOL" #award-S0012345 ^392f97dd62d0
  doc: "2015-02-13.hooli.38745783.pdf"
  Income:US:Hooli:RSU                    -4597.95 USD
  Assets:US:Hooli:RSURefund               2458.97 USD
  Expenses:Taxes:TY2015:US:Medicare         66.68 USD
  Expenses:Taxes:TY2015:US:Federal        1149.48 USD
  Expenses:Taxes:TY2015:US:CityNYC         195.42 USD
  Expenses:Taxes:TY2015:US:SDI               0.00 USD
  Expenses:Taxes:TY2015:US:StateNY         442.32 USD
  Expenses:Taxes:TY2015:US:SocSec          285.08 USD

2015-05-27 * "Vesting Event - C123456 - HOOL" #award-C123456 ^392f97dd62d0
  doc: "2015-02-13.hooli.38745783.pdf"
  Income:US:Hooli:RSU                    -1970.55 USD
  Assets:US:Hooli:RSURefund               1053.84 USD
  Expenses:Taxes:TY2015:US:Medicare         28.58 USD
  Expenses:Taxes:TY2015:US:Federal         492.63 USD
  Expenses:Taxes:TY2015:US:CityNYC          83.75 USD
  Expenses:Taxes:TY2015:US:SDI               0.00 USD
  Expenses:Taxes:TY2015:US:StateNY         189.57 USD
  Expenses:Taxes:TY2015:US:SocSec          122.18 USD

2015-05-25 * "Conversion into shares" ^392f97dd62d0
  Assets:US:Schwab:HOOL                        18 HOOL {131.3700 USD}
  Assets:US:Hooli:Unvested:S0012345           -35 HOOL.UNVEST
  Expenses:Hooli:Vested                        35 HOOL.UNVEST

2015-05-25 * "Conversion into shares" ^392f97dd62d0
  Assets:US:Schwab:HOOL                         9 HOOL {131.3700 USD}
  Assets:US:Hooli:Unvested:C123456            -15 HOOL.UNVEST
  Expenses:Hooli:Vested                        15 HOOL.UNVEST

2015-06-13 * "HOOLI INC       PAYROLL" ^392f97dd62d0
  doc: "2015-02-13.hooli.38745783.pdf"
  Assets:US:Hooli:RSURefund                -94.30 USD
  Assets:US:Hooli:RSURefund                 -2.88 USD
  Assets:US:BofA:Checking                   97.18 USD

2015-02-14 balance Assets:US:Hooli:RSURefund    0 USD

Unvested Shares

Asserting Unvested Balances

Finally, you may occasionally want to assert the number of unvested shares. I like to do this semi-annually, for example. The brokerage company that handles the RSUs for Hooli should be able to list how many unvested shares of each award remain, so it’s as simple as looking it up on a website:

2015-06-04 balance Assets:US:Hooli:Unvested:S0012345  1645 HOOL.UNVEST
2015-06-04 balance Assets:US:Hooli:Unvested:C123456    705 HOOL.UNVEST

Pricing Unvested Shares

You can also put a price on the unvested shares in order to estimate the unvested dollar amount. You should use a fictional currency for this, because we want to avoid a situation where a balance sheet is produced that includes these unvested assets as regular dollars:

2015-06-02 price HOOL.UNVEST               132.4300 USD.UNVEST

At the time of this writing, the bean-web interface does not convert the units if they are not held at cost, but using the SQL query interface or writing a custom script you should be able to produce those numbers:

$ bean-query examples/vesting/vesting.beancount

beancount> select account, sum(convert(position, 'USD.UNVEST')) as unvested 
           where account ~ 'Unvested' group by account;

             account                     unvested       
--------------------------------- ----------------------
Assets:US:Hooli:Unvested:S0012345 217847.3500 USD.UNVEST
Assets:US:Hooli:Unvested:C123456   93363.1500 USD.UNVEST

Selling Vested Stock

After each vesting event, the stock is left in your brokerage account. Selling this stock proceeds just as in any other trading transaction (see Trading with Beancount for full details). For example, selling the shares from the example would look something like this:

2015-09-10 * "Selling shares"
  Assets:US:Schwab:HOOL        -26 HOOL {131.3700 USD} @ 138.23 USD
  Assets:US:Schwab:Cash    3593.98 USD

Here you can see why it matters that the cost basis you used on the conversion event is the correct one: You will have to pay taxes on the difference (in Income:US:Schwab:Gains). In this example the taxable difference is (138.23 - 131.37) dollars per share.

I like to keep all the brokerage transactions in a separate section of my document, where other transactions related to the brokerage occur, such as fees, dividends and transfers.


This is a simple example that is modeled after how technology companies deal with this type of compensation. It is by no means comprehensive, and some of the details will necessarily vary in your situation. In particular, it does not explain how to deal with options (ISOs). My hope is that there is enough meat in this document to allow you to extrapolate and adapt to your particular situation. If you get stuck, please reach out on the mailing-list.