Skip to main content

Forecasting Future Transactions

Build a separate, validated Beancount scenario ledger with future-dated income, expenses, and loan payments.

Put future-dated transactions in a separate scenario ledger to project cash, expenses, and debt in your financial reports. This walkthrough uses ordinary Beancount entries and a fixed planning period. Your realized books stay separate from your assumptions.

Compatibility, checked September 7, 2026: the examples were tested with stock Beancount 3.2.3, beanquery 0.2.0, and Fava 1.30.16. That Fava release does not ship a forecast plugin. Beancount.io's custom source does contain a fava.plugins.forecast implementation; that is a different runtime, and source availability does not establish the version deployed to your account. The portable workflow below needs no extension.

Step 1: Enable the Forecast Plugin

Skip plugin activation for this workflow. Adding plugin "fava.plugins.forecast" to the tested stock installation produces an import error. For general plugin setup, see the plugin guide; a module must actually be installed before a ledger can load it.

Prerequisites: a local Python environment with beancount==3.2.3, beanquery==0.2.0, and fava==1.30.16 installed. Follow the pinned Fava setup to create that environment. Save all three files below in the same directory, and run the commands there.

Create books.beancount with this complete example snapshot. Its as-of date is December 31, 2023, after the opening transaction. Checking holds 10,000.00 USD and the outstanding car-loan principal is 14,400.00 USD. Equity balances this simplified opening snapshot.

; books.beancount — realized balances as of 2023-12-31
option "operating_currency" "USD"
2023-12-31 open Assets:Checking USD
2023-12-31 open Liabilities:Auto-Loan USD
2023-12-31 open Equity:Opening-Balances USD
2023-12-31 open Income:Salary USD
2023-12-31 open Expenses:Housing:Rent USD
2023-12-31 open Expenses:Food:Groceries USD
2023-12-31 open Expenses:Health:Gym USD
2023-12-31 open Expenses:Insurance:Auto USD
2023-12-31 open Expenses:Transportation:Interest USD
 
2023-12-31 * "Opening snapshot"
  Assets:Checking                  10000.00 USD
  Liabilities:Auto-Loan            -14400.00 USD
  Equity:Opening-Balances           4400.00 USD

Create scenario.beancount as the planning entry point. Its two includes load the realized snapshot and the assumptions you will write next. Beancount reads the include paths relative to this file.

; scenario.beancount — planning entry point
option "title" "January 2024 planning scenario"
option "operating_currency" "USD"
include "books.beancount"
include "scenario-2024-01.beancount"

Never include scenario.beancount or scenario-2024-01.beancount from books.beancount. The realized entry point must not load planned transactions, even after their dates pass.

Step 2: Create a Forecast Transaction

Create scenario-2024-01.beancount with this first entry. Append the four later scenario blocks to that same file once each. These are contextual transaction blocks: their account declarations come from books.beancount, and you validate them through scenario.beancount.

2024-01-01 ! "Scenario: January rent" #forecast
  Expenses:Housing:Rent              2500.00 USD
  Assets:Checking                  -2500.00 USD

The ! flag marks a transaction for review; #forecast is an ordinary transaction tag. Neither creates recurrences or excludes a transaction from reports. The separate entry point controls inclusion. Use this tag only on planned entries so that you can identify them during review.

Understanding the Forecast Syntax

Every dated entry represents one occurrence. Narration text such as [MONTHLY] has no recurrence effect in stock Beancount. The older UNTIL, REPEAT, and SKIP instructions belong to a custom plugin convention, not the core language. This workflow expresses their intended dates explicitly.

The scenario covers January 1–31, 2024, inclusive. All planned dates are after the fixed as-of date and before February 1. These historical dates make the example reproducible; nothing depends on today's date. When adapting it, choose a new as-of date and finite horizon before adding entries.

Frequency

Write one transaction for each due date inside the planning period. The complete January example uses these rules:

ItemScheduleDates included in January 2024
RentMonthly, on the 1stJanuary 1
GroceriesEvery 7 days, starting January 1January 1, 8, 15, 22, 29
SalaryEvery 14 days, starting January 5January 5, 19
GymMonthly, on the 15thJanuary 15
Loan paymentMonthly, on the 25thJanuary 25
Car insuranceEvery 3 calendar months, starting January 15January 15; next payment April 15 is outside this horizon

For other daily or yearly plans, enumerate the actual due dates in the same way. Set a policy for dates such as the 31st or February 29 before extending a schedule. Do not silently move them or assume every month has the same number of days.

Conditions (Optional)

1. Set an end date explicitly

Suppose the gym membership ends on December 31, 2024. Its January occurrence is below. The January scenario stops at January 31, so it contains no February–December entries. If you extend the horizon, write each later payment through December 15 and stop there.

2024-01-15 ! "Scenario: gym membership" #forecast
  Expenses:Health:Gym                  80.00 USD
  Assets:Checking                    -80.00 USD

2. Limit occurrences and separate loan principal from interest

Suppose the car loan has 36 remaining payments. Include only the first payment in this January horizon. For this hypothetical installment, assume its 450.00 USD cash payment consists of 400.00 USD principal and 50.00 USD interest. These are scenario assumptions; no later installments are generated.

2024-01-25 ! "Scenario: car loan payment 1 of 36" #forecast
  Liabilities:Auto-Loan               400.00 USD
  Expenses:Transportation:Interest     50.00 USD
  Assets:Checking                   -450.00 USD

The positive liability posting reduces the debt balance from −14,400.00 USD to −14,000.00 USD. Cash falls by 450.00 USD, and only 50.00 USD is an expense. The postings balance because 400.00 + 50.00 - 450.00 = 0. A negative liability posting would increase the debt.

Do not repeat the same principal/interest split for all 36 payments without a supporting schedule. Future interest and the final payment can differ. Use the lender's payment schedule when extending this example; see amortization for the distinction between expense recognition and debt repayment.

3. Write irregular intervals as actual dates

For a biweekly salary starting January 5, the January dates are January 5 and January 19. The next occurrence is February 2, outside this scenario. Biweekly means every 14 days, not twice per calendar month.

2024-01-05 ! "Scenario: salary" #forecast
  Assets:Checking                   3000.00 USD
  Income:Salary                    -3000.00 USD
 
2024-01-19 ! "Scenario: salary" #forecast
  Assets:Checking                   3000.00 USD
  Income:Salary                    -3000.00 USD

Practical Example: A Monthly Budget

Complete your monthly budget by appending the groceries and car-insurance entries below to scenario-2024-01.beancount. Together with rent, gym, the loan payment, and both salary entries above, this produces 11 planned transactions.

2024-01-01 ! "Scenario: groceries" #forecast
  Expenses:Food:Groceries             150.00 USD
  Assets:Checking                   -150.00 USD
 
2024-01-08 ! "Scenario: groceries" #forecast
  Expenses:Food:Groceries             150.00 USD
  Assets:Checking                   -150.00 USD
 
2024-01-15 ! "Scenario: groceries" #forecast
  Expenses:Food:Groceries             150.00 USD
  Assets:Checking                   -150.00 USD
 
2024-01-22 ! "Scenario: groceries" #forecast
  Expenses:Food:Groceries             150.00 USD
  Assets:Checking                   -150.00 USD
 
2024-01-29 ! "Scenario: groceries" #forecast
  Expenses:Food:Groceries             150.00 USD
  Assets:Checking                   -150.00 USD
 
2024-01-15 ! "Scenario: quarterly car insurance" #forecast
  Expenses:Insurance:Auto             450.00 USD
  Assets:Checking                   -450.00 USD

Validate both entry points, then query their closing balances. date < 2024-02-01 includes all January postings. The books query uses the fixed as-of boundary date < 2024-01-01.

bea --file books.beancount check
bea --file scenario.beancount check
bea --file books.beancount query "SELECT account, sum(position) WHERE date < 2024-01-01 GROUP BY account ORDER BY account"
bea --file scenario.beancount query "SELECT account, sum(position) WHERE date < 2024-02-01 GROUP BY account ORDER BY account"

Both bea check commands exit 0 when clean; errors are listed with a non-zero exit. Reconcile the query results against these independent calculations:

ResultRealized books, December 31January scenario, January 31
Checking10,000.00 USD10,000 + 6,000 − 2,500 − 750 − 80 − 450 − 450 = 11,770.00 USD
Loan balance−14,400.00 USD−14,400 + 400 = −14,000.00 USD
Salary income posting0.00 USD−6,000.00 USD
Total expense postings0.00 USD2,500 + 750 + 80 + 450 + 50 = 3,830.00 USD

January's projected profit is 2,170.00 USD: 6,000 income less 3,830 expenses. Checking increases by 1,770.00 USD because the additional 400 principal repayment reduces debt rather than profit.

Open the scenario in stock Fava:

fava scenario.beancount

Set Fava's Time filter to 2024-01. Its time filtering carries prior asset and liability balances into the period and removes later entries. Check the balance sheet for 11,770.00 USD cash and 14,000.00 USD outstanding debt. The income statement should show 2,170.00 USD projected profit. Opening books.beancount instead gives the realized snapshot.

Troubleshooting & Best Practices

  • Import error for the old plugin: remove the activation line when using this portable workflow. A stock Fava installation does not provide Beancount.io's custom modules.
  • Unknown account or missing include file: save all three files in the same directory, copy the opens, and validate scenario.beancount rather than the scenario fragment alone.
  • Forecasts missing from Fava: load scenario.beancount and set the Time filter to 2024-01. A tag or narration does not generate entries.
  • Unexpected balances: compare the 11 planned dates and amounts with the table. Loading an occurrence twice doubles its financial effect, even when every transaction balances.
  • Keep forecasts separate: ordinary flags, tags, and future dates do not prevent entries from affecting an unfiltered report. Use books.beancount for actual results. When a payment happens, record its actual amount there and remove the corresponding assumption from your next scenario to avoid counting it twice.
  • Review the horizon: preserve a dated snapshot when saving a scenario. Extending its end date means adding specific occurrences and checking loan splits, cancellation dates, and rounding again.

Source: https://beancount.io/docs/Tips/forecast-plugin