Перейти к основному содержимому

Как Python-скрипты автоматизируют Beancount и Fava

Beancount и Fava остаются скриптуемыми: используйте Python для автоматизации отчетов, балансов и пользовательских рабочих процессов в вашей главной книге.

Beancount (инструмент учёта методом двойной записи на основе простого текста) и Fava (его веб-интерфейс) обладают высокой расширяемостью и поддерживают написание скриптов. Их архитектура позволяет автоматизировать финансовые задачи, создавать пользовательские отчёты и настраивать оповещения с помощью Python-скриптов. По словам одного пользователя, «мне очень нравится, что мои данные хранятся в таком удобном формате, и что я могу автоматизировать всё, что душе угодно. Лучшего API, чем файл на диске, не существует; с ним легко интегрироваться». Это руководство проведёт вас через создание автоматизированных рабочих процессов — от простой автоматизации для начинающих до продвинутых плагинов Fava.

Изучите живой пример журнала:

Открыть Example Ledger в новой вкладке

Начало работы с командой bea​

Прежде чем писать какой-либо Python-код, проверьте, справляется ли уже с задачей команда bea. Она проверяет журнал, выполняет запросы BQL, формирует четыре финансовых отчёта и импортирует банковские выписки, а глобальный флаг --json превращает каждый из этих результатов в разбираемый конверт, который ваша оболочка может передать в jq. Её коды возврата — это контракт, по которому ветвится запланированная задача, так что cron или CI вообще не нужен скрипт-загрузчик. См. автоматизация бухгалтерского учёта с помощью bea, чтобы узнать о разрешении целей, конверте и ветвлении по кодам возврата, и возвращайтесь сюда, когда вам понадобится пользовательское вычисление, которое CLI не предоставляет.

Начало работы: запуск Beancount как Python-скрипта​

Для приведённых ниже пользовательских Python-скриптов установите библиотеки для скриптов (pip install beancount beanquery beangulp). Рабочие процессы команды bea используют управляемый движок; следуйте краткому руководству по CLI, чтобы его установить. Поскольку Beancount написан на Python, вы можете использовать его как библиотеку в собственных скриптах. Приведённые ниже скрипты выполнялись с Beancount 3.2.3, beanquery 0.2.0 и beangulp 0.2.0. Общий подход таков:

  • Загрузите ваш журнал Beancount: используйте загрузчик Beancount, чтобы разобрать файл .beancount в объекты Python. Например:

    from beancount import loader
    entries, errors, options = loader.load_file("myledger.beancount")
    if errors:
        for error in errors:
            print(error)
        raise SystemExit(1)

    Загрузчик возвращает записи и ошибки вместе. Несбалансированный или недействительный файл всё равно возвращает записи, поэтому проверьте errors и остановитесь, прежде чем доверять данным. Теперь все ваши счета, транзакции и балансы доступны в коде.

  • Используйте язык запросов Beancount (BQL): вместо ручного перебора вы можете выполнять SQL-подобные запросы к данным. Запросы находятся в отдельном пакете beanquery. В Beancount 3.2.3 нет модуля beancount.query. Например, чтобы получить общие расходы по месяцам, подключите загруженные записи и выполните запрос напрямую:

    import beanquery
     
    conn = beanquery.connect("beancount:", entries=entries, errors=errors, options=options)
    cur = conn.execute(
        "SELECT year, month, sum(position) WHERE account ~ 'Expenses' GROUP BY year, month"
    )
    for row in cur.fetchall():
        print(row)

    Это использует beanquery для агрегирования данных. Это тот же движок, что стоит за bea query, но здесь вы вызываете его в скрипте. Это избавляет от необходимости вызывать внешнюю команду в цикле.

  • Настройте структуру проекта: организуйте ваши скрипты рядом с журналом. Распространённая структура — иметь каталоги для импортёров (для получения/разбора внешних данных), отчётов или запросов (для скриптов анализа) и документов (для хранения скачанных выписок). Например, один пользователь хранит:

    • importers/ – пользовательские скрипты импорта на Python (с тестами),
    • queries/ – скрипты для генерации отчётов (запускаются через python3 queries/...),
    • documents/ – скачанные банковские CSV/PDF, организованные по счетам.

С такой структурой вы можете запускать скрипты вручную (например, python3 queries/cash_flow.py) или планировать их (через cron или планировщик задач) для автоматизации рабочего процесса.

Автоматизация задач сверки​

Сверка означает проверку того, что ваш журнал соответствует внешним записям (банковским выпискам, отчётам по кредитным картам и т. д.). Журнал Beancount в простом тексте и Python API позволяют автоматизировать большую часть этого процесса.

Импорт и сопоставление транзакций (Начинающим)​

Для начинающих рекомендуемый подход — использовать импортёры из отдельного пакета beangulp. Beancount 3 удалил модуль ingest из v2 и его команду extract. Вы пишете небольшой Python-класс, наследующий beangulp.Importer, для разбора заданного формата (CSV, OFX, PDF и т. д.) и создания транзакций. Зарегистрируйте его в коротком скрипте ingest, затем запустите через bea ingest в управляемом движке:

  • Напишите импортёр (Python-класс с методами identify(), account() и extract()) для CSV-формата вашего банка.
  • Добавьте скрипт ingest, который регистрирует ваши импортёры. bea ingest выполняет команды identify, extract и archive скрипта. Например, один рабочий процесс запускает extract для всех файлов в ~/Downloads и выводит транзакции во временный файл.
  • Вручную просмотрите и скопируйте транзакции из временного файла в ваш основной журнал, затем запустите bea check, чтобы убедиться, что балансы сходятся.

Минимальный пример: файл statement.csv со столбцами date,description,amount, разбираемый этим импортёром (checking_importer.py):

import csv
import datetime
from beancount.core import data
from beancount.core.amount import Amount
from beancount.core.number import D
import beangulp
 
 
class CheckingImporter(beangulp.Importer):
    def identify(self, filepath: str) -> bool:
        return filepath.endswith("statement.csv")
 
    def account(self, filepath: str) -> str:
        return "Assets:Bank:Checking"
 
    def extract(self, filepath: str, existing):
        entries = []
        with open(filepath, newline="") as f:
            for row in csv.DictReader(f):
                date = datetime.date.fromisoformat(row["date"])
                amount = Amount(D(row["amount"]), "USD")
                meta = data.new_metadata(filepath, 0)
                entries.append(
                    data.Transaction(
                        meta, date, "*", None, row["description"],
                        data.EMPTY_SET, data.EMPTY_SET, [
                            data.Posting("Expenses:Food:Groceries", amount,
                                         None, None, None, None),
                            data.Posting("Assets:Bank:Checking",
                                         Amount(-amount.number, "USD"),
                                         None, None, None, None),
                        ]))
        return entries

Скрипт ingest (ingest.py) подключает его:

from checking_importer import CheckingImporter
from beangulp import Ingest
 
ingest = Ingest([CheckingImporter()])
 
if __name__ == "__main__":
    ingest()

Запустите его для скачанного файла. Для локального CSV учётные данные не нужны. Сначала установите системную библиотеку libmagic. Команда однократного включения скачивает Beangulp в управляемый движок:

bea engine enable beangulp
bea ingest identify --config ingest.py statement.csv
bea ingest extract --config ingest.py statement.csv -o new.beancount

identify сообщает checking_importer.CheckingImporter для файла. extract записывает транзакции в формате Beancount:

2024-01-08 * "Grocery Store"
  Expenses:Food:Groceries   120.00 USD
  Assets:Bank:Checking     -120.00 USD

Просмотрите new.beancount, скопируйте записи в ваш основной журнал и запустите bea check.

Пропустите импортёр для разового случая

Вам не нужно писать импортёр, чтобы преобразовать одну выписку. Вставьте файл в конвертер CSV в Beancount или используйте OFX и QIF в Beancount для загрузок .ofx, .qfx и .qif. Оба работают полностью в вашем браузере, поэтому выписка никогда не покидает вашу машину.

Хотя этот процесс всё ещё включает этап проверки, большая часть рутинной работы по разбору и форматированию записей автоматизирована. Скрипты-импортёры также могут автоматически назначать категории и даже устанавливать утверждения о балансе (утверждения об ожидаемых балансах), чтобы выявлять расхождения. Например, после импорта у вас может появиться строка вида 2025-04-30 balance Assets:Bank:Checking 1234.56 USD, которая утверждает конечный баланс. Когда вы запускаете bea check, Beancount проверит, что все эти утверждения о балансе верны, и отметит любые ошибки, если транзакции отсутствуют или дублируются. Это лучшая практика: автоматически генерируйте утверждения о балансе для каждого периода выписки, чтобы компьютер сам находил несверенные расхождения.

Пользовательские скрипты сверки (средний уровень)​

Для большего контроля вы можете написать пользовательский Python-скрипт для сравнения списка транзакций банка (CSV или через API) с записями вашего журнала:

  1. Прочитайте внешние данные: разберите CSV-файл банка с помощью модуля Python csv (или Pandas). Нормализуйте данные в список транзакций, каждая из которых содержит дату, сумму и описание.
  2. Загрузите транзакции журнала: используйте loader.load_file, как показано ранее, чтобы получить все записи журнала. Отфильтруйте этот список по интересующему счёту (например, вашему расчётному счёту) и, возможно, по диапазону дат выписки.
  3. Сравните и найдите несоответствия:
  • Для каждой внешней транзакции проверьте, существует ли идентичная запись в журнале (сопоставление по дате и сумме, возможно, по описанию). Если не найдено, отметьте её как «новую» и, возможно, выведите её как транзакцию в формате Beancount для вашей проверки.
  • И наоборот, определите любые записи журнала по этому счёту, которые не появляются во внешнем источнике — это могут быть ошибки ввода данных или транзакции, которые ещё не прошли через банк.
  1. Выведите результаты: напечатайте отчёт или создайте новый фрагмент .beancount с отсутствующими транзакциями.

В качестве примера, скрипт сообщества под названием reconcile.py делает именно это: получив файл Beancount и входной CSV, он выводит список новых транзакций, которые следует импортировать, а также любые существующие проводки в журнале, которых нет во входных данных (возможный признак неверной классификации). С таким скриптом ежемесячная сверка может сводиться к его запуску и добавлению предложенных транзакций в журнал. Один пользователь Beancount отмечает, что он «проводит процесс сверки по всем счетам каждый месяц» и использует растущую коллекцию Python-кода, чтобы устранить большую часть ручной работы по импорту и сверке данных.

Совет: во время сверки используйте инструменты Beancount для точности:

  • Используйте утверждения о балансе, как упоминалось, для автоматических проверок балансов счетов.
  • При необходимости используйте директиву pad, которая может автоматически вставлять балансирующие записи для небольших различий округления (используйте с осторожностью).
  • Пишите модульные тесты для вашего импортёра или логики сверки (Beancount предоставляет вспомогательные средства для тестирования). Например, один рабочий процесс включал взятие образца CSV, написание падающих тестов с ожидаемыми транзакциями, затем реализацию импортёра до прохождения всех тестов. Это гарантирует корректную работу вашего скрипта импорта для различных случаев.

Создание пользовательских отчетов и сводок​

Хотя Fava предоставляет множество стандартных отчётов (отчёт о прибылях и убытках, балансовый отчёт и т. д.), вы можете создавать пользовательские отчёты с помощью скриптов. Они могут варьироваться от простого вывода в консоль до богато отформатированных файлов или графиков.

Запрос данных для отчетов (Начинающие)​

На базовом уровне вы можете использовать язык запросов Beancount (BQL), чтобы получить сводные данные и вывести или сохранить их. Например:

  • Сводка денежных потоков: используйте запрос для вычисления чистого денежного потока. «Денежный поток» можно определить как изменение баланса определённых счетов за период. С помощью BQL вы можете сделать:

    SELECT year, month, sum(position)
    WHERE account ~ 'Income' OR account ~ 'Expenses'
    GROUP BY year, month

    Это суммирует все проводки доходов и расходов по месяцам. Фильтруйте с помощью ~ и регулярного выражения: LIKE — это синтаксическая ошибка в beanquery 0.2.0. Проводки содержат position, а не amount. Каждая строка содержит один Inventory, поэтому каждая валюта указывается отдельно, а не конвертируется. Доходы приходят отрицательными, а расходы положительными. Вы можете выполнить это через bea query или через Python API beanquery, показанный ранее, а затем отформатировать результат.

  • Отчёт о расходах по категориям: запросите общие расходы по категориям:

    SELECT account, sum(position)
    WHERE account ~ 'Expenses'
    GROUP BY account
    ORDER BY sum(position) ASC

    Это даёт таблицу расходов по категориям. Каждый итог — это Inventory в исходной валюте. Не оборачивайте агрегат в round(): функции round(inventory, int) не существует, поэтому round(sum(position), 2) не компилируется. Вы можете выполнять несколько запросов в скрипте и выводить результаты в виде текста, CSV или даже JSON для дальнейшей обработки.

Один пользователь счёл «тривиальным» анализ финансовых данных с помощью Fava или скриптов, отметив, что он использует один Python-скрипт для извлечения данных из Beancount через язык запросов, а затем помещает их в Pandas DataFrame для подготовки пользовательского отчёта. Например, вы можете получить месячные итоги с помощью запроса, а затем использовать Pandas/Matplotlib, чтобы построить график денежного потока во времени. Комбинация BQL и библиотек для работы с данными позволяет создавать отчёты за пределами того, что Fava предлагает по умолчанию.

Продвинутые отчеты (графики, производительность и др.)​

Для более сложных задач ваши скрипты могут вычислять такие показатели, как доходность инвестиций, или создавать визуальные результаты:

  • Доходность инвестиций (IRR/XIRR): поскольку ваш журнал содержит все денежные потоки (покупки, продажи, дивиденды), вы можете рассчитать нормы доходности портфеля. Например, вы можете написать скрипт, который отфильтровывает транзакции ваших инвестиционных счетов, а затем вычисляет внутреннюю норму доходности. Существуют библиотеки (или формулы) для вычисления IRR по данным о денежных потоках. Некоторые разработанные сообществом расширения Fava (например, PortfolioSummary или fava_investor) делают именно это, вычисляя IRR и другие метрики для инвестиционных портфелей. Как скрипт, вы можете применить функцию IRR (из NumPy или свою собственную) к ряду взносов/изъятий плюс конечную стоимость.

  • Многопериодные или пользовательские метрики: хотите отчёт о вашей норме сбережений (отношение сбережений к доходу) за каждый месяц? Python-скрипт может загрузить журнал, просуммировать все счета доходов и все счета расходов, затем вычислить сбережения = доходы - расходы и процент. Это может вывести красивую таблицу или даже сгенерировать HTML/Markdown-отчёт для ваших записей.

  • Визуализация: вы можете генерировать графики вне Fava. Например, используйте matplotlib или altair в скрипте, чтобы создать график чистой стоимости во времени на основе данных журнала. Поскольку журнал содержит все исторические балансы (или вы можете накопить их, перебирая записи), вы можете строить графики временных рядов. Сохраняйте эти графики как изображения или интерактивный HTML. (Если вы предпочитаете визуализацию внутри приложения, см. раздел о расширениях Fava ниже, чтобы добавить графики внутри Fava.)

Варианты вывода: решите, как доставлять отчёт:

  • Для разового анализа может быть достаточно вывода на экран или сохранения в файл CSV/Excel.
  • Для дашбордов рассмотрите возможность генерации HTML-файла с данными (возможно, используя библиотеку шаблонов, например Jinja2, или даже просто написав Markdown), который вы можете открыть в браузере.
  • Вы также можете интегрироваться с Jupyter Notebooks для интерактивной среды отчётности, хотя это больше для исследования, чем для автоматизации.

Триггеры оповещений из вашей бухгалтерской книги​

Ещё одно мощное применение автоматизированных рабочих процессов — настройка оповещений на основе условий в ваших финансовых данных. Поскольку ваш журнал регулярно обновляется (и может включать будущие даты, например предстоящие счета или бюджеты), вы можете сканировать его скриптом и получать уведомления о важных событиях.

Предупреждения о низком балансе счета​

Чтобы избежать овердрафтов или поддерживать минимальный баланс, вам может понадобиться оповещение, если какой-либо счёт (например, расчётный или сберегательный) опускается ниже порога. Вот как это можно реализовать:

  1. Определите текущие балансы: после загрузки entries через загрузчик вычислите последний баланс интересующих счетов. Это можно сделать, агрегируя проводки или используя запрос. Например, используйте запрос BQL для баланса конкретного счёта:

    SELECT sum(position) WHERE account = 'Assets:Bank:Checking'

    Это возвращает текущий баланс этого счёта (сумму всех его проводок). В качестве альтернативы используйте внутренние функции Beancount для построения балансового отчёта. Например:

    from beancount.core import realization
    tree = realization.realize(entries)
    acct = realization.get_or_create(tree, "Assets:Bank:Checking")
    balance = acct.balance  # an Inventory of commodities

    Передавайте только записи: второй параметр — это min_accounts, а не карта опций. Затем извлеките числовое значение (например, balance.get_currency_units('USD') возвращает сумму Decimal в USD). Как и агрегат запроса, баланс хранит каждую валюту отдельно. Однако для большинства случаев использовать запрос проще.

  2. Проверьте порог: сравните баланс с вашим предопределённым лимитом. Если ниже, запустите оповещение.

  3. Запустите уведомление: это может быть так просто, как вывод предупреждения в консоль, но для настоящих оповещений вы можете отправить электронное письмо или push-уведомление. Вы можете интегрироваться с электронной почтой (через smtplib) или сервисом, например IFTTT или webhook API Slack, чтобы отправить оповещение. Например:

    if balance < 1000:
        send_email("Low balance alert", f"Account XYZ balance is {balance}")

    (Реализуйте send_email с данными вашего почтового сервера.)

Запуская этот скрипт ежедневно (через задание cron или планировщик задач Windows), вы будете получать проактивные предупреждения. Поскольку он использует журнал, он может учитывать все транзакции, включая только что добавленные.

Ближайшие сроки платежей​

Если вы используете Beancount для отслеживания счетов или сроков, вы можете отмечать будущие платежи и поручить скриптам напоминать вам. Два способа представить предстоящие обязательства в Beancount:

  • События: Beancount поддерживает директиву event для произвольных заметок с датой. Например:

    2025-05-10 event "BillDue" "Mortgage payment due"

    Это не влияет на балансы, но фиксирует дату с меткой. Скрипт может сканировать entries на наличие записей Event, где Event.type == "BillDue" (или любого пользовательского типа, который вы выберете), и проверять, попадает ли дата, скажем, в следующие 7 дней от сегодняшнего дня. Если да, запустить оповещение (электронное письмо, уведомление или даже всплывающее окно).

  • Будущие транзакции: некоторые люди вводят транзакции с будущей датой (постдатированные) для таких вещей, как запланированные платежи. Они не появятся в балансах, пока не наступит дата (если только вы не запускаете отчёты на будущие даты). Скрипт может искать транзакции с датой в ближайшем будущем и перечислять их.

Используя это, вы можете создать скрипт-«напоминалку», который при запуске выводит список задач или счетов к оплате в ближайшее время. Интегрируйтесь с API, например Google Calendar или менеджером задач, если хотите автоматически создавать там напоминания.

Обнаружение аномалий​

Помимо известных порогов или дат, вы можете настроить пользовательские оповещения для необычных паттернов. Например, если обычно ежемесячный расход не произошёл (возможно, вы забыли оплатить счёт), или если расходы по категории в этом месяце аномально высоки, ваш скрипт может это отметить. Обычно это включает запрос последних данных и сравнение с историей (что может быть продвинутой темой — возможно, с использованием статистики или ML).

На практике многие пользователи полагаются на сверку для выявления аномалий (неожиданных транзакций). Если вы получаете банковские уведомления (например, письма по каждой транзакции), вы можете разбирать их скриптом и автоматически добавлять в Beancount, или, по крайней мере, проверять, что они записаны. Один энтузиаст даже настроил свой банк на отправку писем-оповещений о транзакциях с планом автоматически разбирать и добавлять их в журнал. Такое событийно-ориентированное оповещение может гарантировать, что ни одна транзакция не останется незаписанной.

Расширение Fava с помощью пользовательских плагинов и представлений​

Fava уже поддерживает скрипты через свою систему расширений. Если вы хотите, чтобы ваша автоматизация или отчёты интегрировались непосредственно в веб-интерфейс, вы можете написать расширение Fava (также называемое плагином) на Python.

Как работают расширения Fava: расширение — это модуль Python, определяющий класс, наследующий fava.ext.FavaExtensionBase. Вы регистрируете его в файле Beancount через пользовательскую опцию. Например, если у вас есть файл myextension.py с классом MyAlerts(FavaExtensionBase), вы можете включить его, добавив в журнал:

1970-01-01 custom "fava-extension" "myextension"

Когда Fava загружается, он импортирует этот модуль и инициализирует ваш класс MyAlerts.

Расширения могут делать несколько вещей:

  • Хуки: они могут подключаться к событиям в жизненном цикле Fava. Например, after_load_file() вызывается после загрузки журнала. Вы можете использовать это для запуска проверок или предварительного вычисления данных. Если вы хотите реализовать проверку низкого баланса внутри Fava, after_load_file может перебрать балансы счетов и, возможно, сохранить предупреждения (хотя для их отображения в интерфейсе потребуется немного больше работы, например вызов FavaAPIError или использование Javascript для показа уведомления).
  • Пользовательские отчёты/страницы: если ваш класс расширения задаёт атрибут report_title, Fava добавит новую страницу в боковой панели для него. Затем вы предоставляете шаблон (HTML/Jinja2) для содержимого этой страницы. Так вы создаёте совершенно новые представления, например дашборд или сводку, которых у Fava нет по умолчанию. Расширение может собрать любые нужные данные (вы можете получить доступ к self.ledger, который содержит все записи, балансы и т. д.), а затем отобразить шаблон.

Например, встроенное расширение portfolio_list в Fava добавляет страницу со списком позиций вашего портфеля. Расширения сообщества идут дальше:

  • Дашборды: плагин fava-dashboards позволяет определять пользовательские графики и панели (используя библиотеки, например Apache ECharts). Он читает YAML-конфигурацию запросов для выполнения, выполняет их через Beancount и генерирует динамическую страницу дашборда в Fava. По сути, он связывает данные Beancount и библиотеку графиков JavaScript для создания интерактивных визуализаций.
  • Анализ портфеля: расширение PortfolioSummary (от пользователей) вычисляет инвестиционные сводки (группировка счетов, расчёт IRR и т. д.) и отображает их в интерфейсе Fava.
  • Проверка транзакций: ещё одно расширение, fava-review, помогает просматривать транзакции с течением времени (например, чтобы убедиться, что вы не пропустили ни одного чека).

Чтобы создать простое расширение самостоятельно, начните с наследования от FavaExtensionBase. Например, минимальное расширение, добавляющее страницу, может выглядеть так:

from fava.ext import FavaExtensionBase
 
class HelloReport(FavaExtensionBase):
    report_title = "Hello World"
 
    def __init__(self, ledger, config):
        super().__init__(ledger, config)
        # any initialization, perhaps parse config if provided
 
    def after_load_file(self):
        # (optional) run after ledger is loaded
        print("Ledger loaded with", len(self.ledger.entries), "entries")

Если вы поместите это в hello.py и добавите custom "fava-extension" "hello" в ваш журнал, Fava покажет новую страницу «Hello World» (вам также понадобится файл шаблона HelloReport.html в подпапке templates, чтобы определить содержимое страницы, если только расширение не использует только хуки). Шаблон может использовать данные, которые вы прикрепите к классу расширения. Fava использует шаблоны Jinja2, поэтому вы можете отобразить свои данные в HTML-таблицу или график в этом шаблоне.

Примечание: система расширений Fava мощна, но считается «нестабильной» (может меняться). Она требует некоторого знакомства с веб-разработкой (HTML/JS), если вы делаете пользовательские страницы. Если ваша цель — просто запускать скрипты или анализ, возможно, проще оставить их как внешние скрипты. Используйте расширения Fava, когда вам нужен индивидуальный опыт внутри приложения для вашего рабочего процесса.

Интеграция сторонних API и данных​

Одно из преимуществ автоматизированных рабочих процессов — возможность подключать внешние данные. Вот распространённые интеграции:

Для хостинговых цен оценки Live Prices предлагает управляемые включения без запланированного скрипта получения цен. Выбирайте поддерживаемые пары активов и валюту котировки в средстве выбора. Локальные файловые рабочие процессы ниже остаются полезными для upstream Beancount, Fava и воспроизводимых отчётов. Управляемое обновление не создаёт Git-коммит в вашем журнале.

  • Курсы обмена и товары: upstream Beancount сам не получает цены, но предоставляет директиву price, чтобы вы указывали курсы. Вы можете автоматизировать получение этих цен. Например, скрипт может запросить API (Yahoo Finance, Alpha Vantage и т. д.) для последнего курса обмена или цены акции и добавить запись о цене в ваш журнал:

    2025-04-30 price BTC 30000 USD
    2025-04-30 price EUR 1.10 USD

    Есть инструменты, такие как bea price, работающие на Beanprice в управляемом движке, которые получают ежедневные котировки и выводят их в формате Beancount. Вы можете включить его один раз с помощью bea engine enable beanprice, затем запланировать запуск bea price main.beancount каждую ночь для обновления включаемого файла prices.beancount. Или используйте Python: например, с библиотекой requests для вызова API. Документация Beancount предлагает для публично торгуемых активов «вызвать некоторый код, который скачает цены и запишет директивы за вас». Другими словами, позвольте скрипту выполнить поиск и вставить строки price, вместо того чтобы делать это вручную.

  • Данные о фондовом портфеле: аналогично курсам обмена, вы можете интегрироваться с API для получения подробных данных об акциях или дивидендах. Например, API Yahoo Finance (или библиотеки сообщества, такие как yfinance) могут получить исторические данные по тикеру. Скрипт может обновлять ваш журнал месячной историей цен для каждой акции, которой вы владеете, что позволяет точно составлять исторические отчёты о рыночной стоимости. Некоторые пользовательские расширения (например, fava_investor) даже получают данные о ценах на лету для отображения, но проще регулярно импортировать цены в журнал.

  • Банковские API (Open Banking/Plaid): вместо скачивания CSV вы можете использовать API для автоматического получения транзакций. Сервисы, такие как Plaid, агрегируют банковские счета и позволяют программно получать доступ к транзакциям. В продвинутой настройке у вас может быть Python-скрипт, который использует API Plaid для ежедневного получения новых транзакций и сохранения их в файл (или прямого импорта в журнал). Один продвинутый пользователь построил систему, в которой Plaid подключается к его конвейеру импорта, делая его книги почти автоматическими. Он отмечает, что «ничто не мешает вам зарегистрироваться в Plaid API и делать то же самое локально» — то есть вы можете написать локальный скрипт для получения банковских данных, а затем использовать логику вашего импортёра Beancount для их разбора в записи журнала. В некоторых регионах есть открытые банковские API, предоставляемые банками; их можно использовать аналогично.

  • Другие API: вы можете интегрировать инструменты бюджетирования (экспорт запланированных бюджетов для сравнения с фактическими в Beancount) или использовать OCR API для чтения чеков и автоматического сопоставления их с транзакциями. Поскольку ваши скрипты имеют полный доступ к экосистеме Python, вы можете интегрировать всё — от почтовых сервисов (для отправки оповещений) до Google Sheets (например, обновление таблицы месячными финансовыми метриками) до мессенджеров (отправка себе сводного отчёта через Telegram-бота).

При использовании сторонних API помните о необходимости защиты ваших учётных данных (используйте переменные окружения или файлы конфигурации для ключей API) и корректной обработки ошибок (проблемы с сетью, недоступность API) в ваших скриптах. Часто разумно кэшировать данные (например, сохранять полученные курсы обмена, чтобы не запрашивать один и тот же исторический курс повторно).

Лучшие практики для модульных и поддерживаемых скриптов​

По мере создания автоматизированных рабочих процессов поддерживайте ваш код организованным и надёжным:

  • Модульность: разделяйте различные задачи на разные скрипты или модули. Например, имейте отдельные скрипты для «импорта/сверки данных», «генерации отчётов» и «оповещений». Вы даже можете создать небольшой Python-пакет для вашего журнала с модулями, например ledger_import.py, ledger_reports.py и т. д. Это делает каждую часть проще для понимания и тестирования.

  • Конфигурация: избегайте жёсткого кодирования значений. Используйте файл конфигурации или переменные в начале скрипта для таких вещей, как имена счетов, пороги, ключи API, диапазоны дат и т. д. Это упрощает настройку без глубокого редактирования кода. Например, определите LOW_BALANCE_THRESHOLDS = {"Assets:Bank:Checking": 500, "Assets:Savings": 1000} в начале, и ваш скрипт оповещений сможет пройти по этому словарю в цикле.

  • Тестирование: относитесь к вашей финансовой автоматизации как к критически важному коду — потому что так оно и есть! Пишите тесты для сложной логики. Beancount предоставляет некоторые вспомогательные средства для тестирования (используемые внутри для тестирования импортёров), которые вы можете использовать для имитации входных данных журнала. Даже без сложных фреймворков вы можете иметь фиктивный CSV и ожидаемые выходные транзакции и проверять, что ваш скрипт импорта создаёт правильные записи. Если вы используете pytest, вы можете легко интегрировать эти тесты (как это сделал Alex Watt через команду just test, оборачивающую pytest).

  • Контроль версий: держите ваш журнал и скрипты под контролем версий (git). Это не только даёт вам резервные копии и историю, но и побуждает вносить изменения контролируемым образом. Вы можете помечать релизы ваших «финансовых скриптов» или просматривать различия при отладке проблемы. Некоторые пользователи даже отслеживают свои финансовые записи в Git, чтобы видеть изменения с течением времени. Только будьте осторожны, чтобы игнорировать конфиденциальные данные (например, необработанные файлы выписок или ключи API) в вашем репозитории.

  • Документация: документируйте ваши пользовательские рабочие процессы для будущего себя. README в вашем репозитории, объясняющий, как настроить окружение, как запускать каждый скрипт и что каждый из них делает, будет бесценен спустя месяцы. Также комментируйте ваш код, особенно любую неочевидную бухгалтерскую логику или взаимодействие с API.

  • Поддержка плагинов Fava: если вы пишете расширение Fava, держите его простым. Fava может меняться, поэтому меньшие расширения с целевой функциональностью легче обновлять. Избегайте дублирования слишком большой логики — используйте движок запросов Beancount или существующие вспомогательные функции, когда это возможно, вместо жёсткого кодирования вычислений, которые могут быть чувствительны к изменениям журнала.

  • Безопасность: поскольку ваши скрипты могут обрабатывать конфиденциальные данные и подключаться к внешним сервисам, относитесь к ним с осторожностью. Не раскрывайте ключи API и рассмотрите возможность запуска вашей автоматизации на защищённой машине. Если вы используете хостинговое решение или облако (например, планирование GitHub Actions или сервер для запуска Fava), убедитесь, что данные вашего журнала зашифрованы в состоянии покоя и что вас устраивают последствия для конфиденциальности.

Следуя этим практикам, вы обеспечите надёжность вашего рабочего процесса даже по мере того, как ваши финансы (и сами инструменты) развиваются. Вам нужны скрипты, которые вы сможете повторно использовать год за годом с минимальными правками.

Заключение​

Beancount и Fava предоставляют мощную, гибкую платформу для технически подкованных пользователей, чтобы полностью настроить отслеживание личных финансов. Написав Python-скрипты, вы можете автоматизировать утомительные задачи, такие как сверка выписок, создавать богатые отчёты, адаптированные под ваши нужды, и быть в курсе своих финансов благодаря своевременным оповещениям. Мы рассмотрели ряд примеров от базовых до продвинутых — начиная с простых запросов и импорта CSV и переходя к полноценным плагинам Fava и интеграциям с внешними API. Реализуя их, начинайте с простого и постепенно наращивайте сложность. Даже несколько небольших скриптов автоматизации могут сэкономить часы работы и значительно повысить точность. И помните: поскольку всё в простом тексте и Python, вы полностью контролируете ситуацию — ваша финансовая система растёт вместе с вами, подстраиваясь под ваши конкретные нужды. Удачного скриптинга!

Источники: описанные выше методы взяты из документации Beancount и опыта сообщества. Для дальнейшего чтения см. официальную документацию Beancount, руководства и блоги сообщества, а также репозиторий Awesome Beancount для ссылок на полезные плагины и инструменты.

Источник: https://beancount.io/ru/docs/Solutions/scriptable-workflows