Giter Club home page Giter Club logo

beancount-ynab5's Introduction

beancount-ynab5

Import YNAB5 transactions from the cloud into beancount, a plaintext accounting system.

Table of Contents

What it does.

beancount-ynab5 will import reconciled transactions from your cloud-based YNAB5 budget into a beancount file. This allows you to use YNAB5 for its budgeting, for automatically pulling transactions from various US financial institutions, and for its mobile app. But you can also sync it to beancount, which has support for multiple currencies, investments, and other things.

Why only reconciled transactions? This importer doesn't handle updating a transaction that you've already imported. By waiting until the transaction is reconciled we (hopefully) avoid situations where we import something that then later gets updated.

Requirements.

You must have one of these installed. You don't need both. If you have aiohttp installed, then the importer will make parallel requests to YNAB, which speeds things up somewhat.

The command line options --enable-async-fetch and --disable-async-fetch will allow you to override the default behaviour.

Running it.

When you run the script it will output beancount transactions to stdout. You probably want to tee it or redirect it to a file.

> python ynab.py --ynab-token FAKE4642488831a79ea13cb291570eae186d9
2019-10-04 * "Starting Balance" 
    ynab-id: "b79660e1-6554-4dae-97be-9bf7b04764f3"
    Assets:Checking                                    100000000 VND
    Income:Internal-Master-Category:Inflows

2019-10-04 * "Starting Balance" 
    ynab-id: "b8bdf6d5-85fa-4e50-b0a9-d6579b9cb9ae"
    Assets:Wallet                                              0 VND
    Income:Internal-Master-Category:Inflows

2019-10-06 * "Blammo" 
    ynab-id: "08c9f5c0-c764-4c89-bc82-9437556ce316"
    Assets:Checking                                      -100000 VND
    Assets:Wallet                                          75000 VND ; to wallet
    Expenses:Immediate-Obligations:Transportation          25000 VND ; 

In order to get access to your YNAB budget you need to have a Personal Access Token and provide it to the importer on the command line with the --ynab-token option.

Notes on running it.

You don't actually have to specify a beancount file, it is optional. If no beancount file is provided, this is equivalent to an empty beancount file.

If you have multiple budgets in YNAB, you must specify which one to use. If you only have a single budget, then this parameter can be omitted.

Get a Personal Access Token for your YNAB account.

Instructions on how to do this are on YNAB's API website.

The short version: Go to "My Account" then click on "Developer Settings".

Write down the very long token somewhere. You'll need it later.

YNAB Rate Limits.

YNAB enforces rate limits on its API.

An access token may be used for up to 200 requests per hour. The limit is reset every clock hour. So, if an access token is used at 12:30 PM and for 199 more requests up to 12:45 PM and then hits the limit, any additional requests will be forbidden until 1:00 PM. At 1:00 PM you would have the full 200 requests allowed again, until 2:00 PM.

It shouldn't be an issue in normal usage -- each run of the importer results in 4 HTTP accesses, so you can run it 50 times an hour -- but just FYI.

Mapping accounts between YNAB and beancount.

The importer needs to map accounts & budget categories between YNAB and beancount. You have two options.

  1. Rely on the default mapping algorithm.
  2. Add metadata to the beancount file explicitly mapping accounts.

The default algorithm.

  1. Any punctuation in the YNAB account name is removed. So Rent/Mortgage would become RentMortgage
  2. Any spaces get turned into a hyphen. So Car Insurance would become Car-Insurance
  3. All YNAB accounts are given the Assets prefix.
  4. All YNAB budget categories are given the Expenses prefix.
  5. Budget groups are used as an additional level of hierarchy.

In the default budget category list that means Rent & Mortgage in the group Immediate Obligations becomes Expenses:Immediate-Obligations:RentMortgage.

Here are a few other examples of the transformation:

  1. Expenses:Immediate-Obligations:RentMortgage (Rent & Mortgage)
  2. Expenses:True-Expenses:RentersHome-Insurance (Renter's/Home Insurance)
  3. Expenses:Immediate-Obligations:Interest--Fees (Interest & Fees)

Add a ynab-id to your beancount accounts.

It is also possible to perform an explicit mapping. You might do this if you don't like the default algorithm or if you want beancount and YNAB to have different structures & naming.

All you need to do is add a ynab-id metadata to any account with the value of the UUID of the YNAB account you want it to map to.

2016-01-01 open Expenses:Monthly:Phone
    ynab-id: "9a2fb967-974a-4040-a584-0234d1de7abb"

You can mix & match the two approaches. You can add ynab-id to some accounts but rely on the default algorithm for other accounts.

How do you get that UUID? By using the importer's --list-ynab-ids mode.

> python ynab.py --token $TOKEN my.beanfile --list-ynab-ids
2e092108-4065-44a3-875e-db77fc2bc48f Just for Fun:Dining Out
                                     Expenses:Everyday:Restaurants
[...repeats...]

This will list the UUID, the YNAB account, and the associated beancount account (if any). If there is no beancount account associated with that UUID yet then (none) will be displayed instead, as in this example:

> python ynab.py --token $TOKEN my.beanfile --list-ynab-ids
f7aa4b9e-7fa4-4294-a5ec-ded5a54f5ff2 Just for Fun:Fun Money
                                     (none)

Skipping 'Starting Balance' statements in YNAB.

YNAB will generate Starting Balance statements that are, essentially, income from nowhere.

2019-10-04 * "Starting Balance" 
    ynab-id: "b79660e1-6554-4dae-97be-9bf7b04764f3"
    Assets:Checking                                    5,000 USD
    Income:Internal-Master-Category:Inflows

In many cases, you won't want to import these transaction. You may have an existing beancount history that accounts for the current value of the account. This transaction would double the value of the account and throw it out of balance.

You can skip these "Starting Balance" statements during the import by specifying the command line option --skip-starting-balances.

Income.

The biggest disconnect between YNAB and beancount is how they handle income. YNAB doesn't differentiate between different sources of income. They are all just "income". Salary, interest, dividends...they are all just income as far as YNAB is concerned.

That means the importer will place all income into a single category. By default it will all go into Income:Internal-Master-Category:Inflows.

At the moment, the importer doesn't do anything smart to help you out. If you don't want all of your income to go into a single category, then you will need to manually edit the resulting import statements.

Reconciling.

When you reconcile in YNAB, if your cleared balance and your working balance don't match up you can select "Create Adjustment & Finish". If you do this, YNAB will generate a transaction that will cause your cleared balance and working balance to match up. The transaction will look something like this:

2019-10-07 * "Reconciliation Balance Adjustment" "Entered automatically by YNAB"
    ynab-id: "76972f80-1374-48ec-b28d-8b309900f976"
    Assets:Wallet                                        -12 USD
    Income:Internal-Master-Category:Inflows

In particular, notice that the transaction is generated against your income. This is unlikely to be what you want to happen if you are using beancount.

A better course is to create an additional transaction that will make the cleared balance and working balance match. For instance, create a transaction in YNAB that assigns $12 to a "Miscellaneous" expense.

You can also automatically remap this to another account by using the --balance-adjustment-account command line option.

YNAB's Off Budget (aka Tracking) Accounts.

YNAB allows you to have Tracking accounts, that aren't part of its normal budgeting workflow. These are useful for things like Mortgages or Certificates of Deposit. Some transactions in Tracking accounts will only have a single "leg". This is a problem because beancount's double-entry bookkeeping requires everything to have (at least) two legs.

In this scenario, we don't have enough information to generate a valid beancount entry. A warning will be displayed on-screen, giving you some information about where to locate the problem. Additionally, the imported data will fail bean-check with a "Transaction does not balance" error.

You will need to manually edit the imported data and fix this.

TODOs.

  • YNAB comes with several special accounts. I've only seen the Inflows one used. So I'm not sure what's up with the others.
    • Internal Master Category:Inflows
    • Internal Master Category:Deferred Income SubCategory
    • Internal Master Category:Uncategorized
  • YNAB creates a special category for credit card payments (e.g. Credit Card Payments:Timo Mastercard). I haven't looked into how exactly that works.
  • Implement something automagic for Inflows? A way to map based on payee to different beancount Income accounts?

beancount-ynab5's People

Contributors

hoostus avatar adamgibbins avatar

Stargazers

 avatar Vince Vu avatar Andrew Kail avatar Bernhard Bauer avatar Matt Payne avatar Jacinto S. avatar Billy Hardy avatar  avatar Khanh Nguyen avatar Josh D avatar  avatar

Watchers

 avatar  avatar James Cloos avatar

beancount-ynab5's Issues

Request: Get transactions by account_id

Hi,

Wondering if I can make a request to get transactions by specifying an account_id instead of getting all transactions for all accounts.

Use-case: I only use this for on_budget accounts and want to exclude tracking accounts.

Thanks

Strange Errors

Just tried the script but got some strange error messages which looks like server side issues. Any hints?

Traceback (most recent call last):
File "ynab.py", line 325, in
budget, ynab_accounts, ynab_category_groups, ynab_categories, ynab_transactions = fetcher(args.ynab_token, args.budget, args.since)
File "ynab.py", line 241, in get_ynab_data_async
budget, accounts, category_groups, categories, transactions = loop.run_until_complete(future) File "/usr/lib/python3.7/asyncio/base_events.py", line 584, in run_until_complete
return future.result()
File "ynab.py", line 216, in run
budget = budget_from_json(budget_name, d['data']['budgets'])
File "ynab.py", line 59, in budget_from_json
b = json_budgets.values()[0]
AttributeError: 'list' object has no attribute 'values'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.