メインコンテンツへスキップ

PythonスクリプトがBeancountとFavaを自動化する方法

BeancountとFavaはスクリプト可能なままです:Pythonを使って、台帳に対するレポート、残高、カスタムワークフローを自動化できます。

Beancount(プレーンテキストの複式簿記ツール)とFava(そのWebインターフェース)は、高度に拡張可能でスクリプト化できます。その設計により、Pythonスクリプトを書くことで、財務タスクの自動化、カスタムレポートの生成、アラートの設定が可能です。あるユーザーの言葉を借りれば、「データをこんなに便利な形式で持てるのが本当に気に入っているし、思う存分自動化できるのもいい。ディスク上のファイルほど優れたAPIはない。統合が簡単だ。」 このガイドでは、初心者向けの自動化から高度なFavaプラグインまで、スクリプト化されたワークフローの作成方法を順を追って説明します。

実際の元帳の例をご覧ください:

新しいタブで Example Ledger を開く

bea コマンドラインから始める​

Pythonを書く前に、beaがすでにその仕事をしてくれないか確認しましょう。元帳の検証、BQLクエリの実行、4つの財務レポートの生成、銀行のエクスポートのインポートを行い、グローバルな--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 Query Language (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はv2のingestモジュールとそのextractコマンドを削除しました。beangulp.Importerをサブクラス化した小さなPythonクラスを書いて、指定された形式(CSV、OFX、PDFなど)を解析して取引を生成します。短いingestスクリプトに登録し、管理されたエンジンでbea ingestを通して実行します:

  • 銀行のCSV形式用のインポーター(identify()、account()、extract()メソッドを持つPythonクラス)を書きます。
  • インポーターを登録するingestスクリプトを追加します。bea ingestはスクリプトのidentify、extract、archiveコマンドを実行します。たとえば、あるワークフローは~/Downloads内のすべてのファイルにextractを実行し、取引を一時ファイルに出力します。
  • 手動でレビューし、一時ファイルから取引をメイン元帳にコピーしてから、bea checkを実行して残高が照合されることを確認します。

最小限の例: date,description,amount列を持つstatement.csvを、このインポーター(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ライブラリをインストールします。1回限りのenableコマンドで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 to Beancountコンバーターに貼り付けるか、.ofx、.qfx、.qifのダウンロードにはOFX & QIF to Beancountを使います。どちらも完全にブラウザ内で実行されるので、明細書がマシンから出ることはありません。

このプロセスにはまだレビューのステップが含まれますが、エントリの解析とフォーマットという面倒な作業の多くは自動化されます。インポータースクリプトは、カテゴリを自動割り当てし、残高のアサーション(期待される残高の記述)を設定して不一致を検出することもできます。たとえば、インポート後に2025-04-30 balance Assets:Bank:Checking 1234.56 USDのような行があり、これが期末残高を表明します。bea checkを実行すると、Beancountは_これらの残高アサーションがすべて正しいことを検証_し、取引が欠落または重複している場合はエラーを通知します。これはベストプラクティスです: 明細書の期間ごとに残高アサーションを自動生成して、コンピュータに未照合の差異を見つけてもらいましょう。

カスタム調整スクリプト(中級)​

より細かい制御が必要な場合は、銀行の取引リスト(CSVまたはAPI経由)と元帳のエントリを比較するカスタムPythonスクリプトを書くことができます:

  1. 外部データを読み込む: Pythonのcsvモジュール(またはPandas)を使って銀行のCSVファイルを解析します。データを取引のリストに正規化します(例: それぞれに日付、金額、説明)。
  2. 元帳の取引をロードする: 前述のとおりloader.load_fileを使ってすべての元帳エントリを取得します。このリストを対象の勘定科目(例: 当座預金)とおそらく明細書の日付範囲にフィルタリングします。
  3. 比較して不一致を見つける:
  • 各外部取引について、元帳に同一のエントリが存在するか確認します(日付と金額、おそらく説明で照合)。見つからない場合は「新規」としてマークし、レビュー用にBeancount形式の取引として出力します。
  • 逆に、その勘定科目の元帳エントリで外部ソースに現れないものを特定します – これらはデータ入力のエラーか、銀行でまだクリアされていない取引である可能性があります。
  1. 結果を出力する: レポートを印刷するか、欠落している取引を含む新しい.beancountスニペットを作成します。

例として、reconcile.pyというコミュニティスクリプトはまさにこれを行います: Beancountファイルと入力CSVを受け取り、インポートすべき新しい取引のリストと、入力に含まれない既存の元帳記帳(誤分類の兆候である可能性があります)を出力します。このようなスクリプトを使えば、月次の照合はそれを実行し、提案された取引を元帳に追記するだけで済みます。あるBeancountユーザーは、「毎月すべての勘定科目で照合プロセスを行っている」と述べ、成長し続けるPythonコードのコレクションを使って、データのインポートと照合における手作業の多くを排除しています。

ヒント: 照合中は、正確性のためにBeancountのツールを活用しましょう:

  • 前述の残高アサーションを使って、勘定残高を自動チェックします。
  • 必要に応じてpadディレクティブを使います。これにより、わずかな丸め誤差のバランス調整エントリを自動挿入できます(注意して使用してください)。
  • インポーターや照合ロジックの単体テストを書きます(Beancountはテストヘルパーを提供しています)。たとえば、あるワークフローでは、サンプルCSVを取り、期待される取引を含む失敗するテストを書き、すべてのテストが通るまでインポーターを実装しました。これにより、インポートスクリプトがさまざまなケースで正しく動作することが保証されます。

カスタムレポートとサマリーの生成​

Favaは多くの標準レポート(損益計算書、貸借対照表など)を提供しますが、スクリプトを使ってカスタムレポートを作成できます。これは単純なコンソール出力から、リッチにフォーマットされたファイルやチャートまで多岐にわたります。

レポート用データのクエリ(初心者向け)​

基本的なレベルでは、Beancount Query Language (BQL)を使ってサマリーデータを取得し、印刷または保存できます。例:

  • キャッシュフローサマリー: クエリを使って純キャッシュフローを計算します。「キャッシュフロー」は、ある期間における特定の勘定科目の残高の変化として定義できます。BQLを使うと、次のようにできます:

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

    これは月ごとにすべての収益と費用の記帳を純額化します。~と正規表現でフィルタリングします: LIKEはbeanquery 0.2.0では構文エラーです。記帳はamountではなくpositionを持ちます。各行は1つのInventoryを保持するので、すべての通貨が変換されずに個別にリストされます。収益は負で、費用は正で到着します。これをbea query経由か、前述のbeanquery Python API経由で実行し、結果をフォーマットできます。

  • カテゴリ支出レポート: カテゴリごとの総支出をクエリします:

    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またはスクリプトで財務データを分析することが_「簡単」_だと感じ、1つのPythonスクリプトでQuery Language経由で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 Notebookと統合することもできますが、これは自動化というより探索向けです。

台帳からのアラート発動​

スクリプト化されたワークフローのもう一つの強力な使い方は、財務データの条件に基づいてアラートを設定することです。元帳は定期的に更新され(今後の請求書や予算のような将来の日付の項目も含めることができます)、スクリプトでスキャンして重要なイベントの通知を受けることができます。

低残高警告​

当座貸越を避けたり、最低残高を維持したりするために、いずれかの勘定科目(例: 当座預金や普通預金)がしきい値を下回った場合にアラートを出したいことがあります。実装方法は次のとおりです:

  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

    エントリのみを渡します: 第2パラメータはmin_accountsであり、オプションのマップではありません。次に数値を抽出します(例: balance.get_currency_units('USD')はUSDのDecimal金額を返します)。クエリの集計と同様に、残高はすべての通貨を個別に保持します。ただし、ほとんどのケースではクエリを使う方が簡単です。

  2. しきい値をチェックする: 残高を事前定義した制限と比較します。下回っている場合は、アラートをトリガーします。

  3. 通知をトリガーする: これはコンソールに警告を印刷するだけで済むかもしれませんが、実際のアラートにはメールやプッシュ通知を送信したいでしょう。メール(smtplib経由)やIFTTTやSlackのwebhook APIのようなサービスと統合してアラートをプッシュできます。例:

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

    (send_emailはあなたのメールサーバーの詳細で実装します。)

このスクリプトを毎日(cronジョブまたはWindowsタスクスケジューラ経由で)実行することで、プロアクティブな警告を受け取れます。元帳を使っているので、たった今追加したものを含め、_すべての_取引を考慮できます。

支払い期限の到来​

Beancountを使って請求書や期限を追跡している場合、将来の支払いをマークし、スクリプトにリマインドさせることができます。Beancountで今後の義務を表現する2つの方法:

  • イベント: Beancountは任意の日付のメモ用のeventディレクティブをサポートしています。例:

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

    これは残高には影響しませんが、ラベル付きの日付を記録します。スクリプトはentriesをスキャンして、Event.type == "BillDue"(または選択した任意のカスタムタイプ)のEventエントリを探し、日付がたとえば今日から次の7日以内かどうかを確認できます。該当する場合は、アラート(メール、通知、またはポップアップ)をトリガーします。

  • 将来の取引: 一部の人は、スケジュールされた支払いなどのために将来の日付の取引(事後日付)を入力します。これは日付が過ぎるまで残高に現れません(将来の日付時点でのレポートを実行しない限り)。スクリプトは近い将来に日付が設定された取引を探してリストできます。

これらを使うと、実行時に近日中のタスクや請求書のリストを出力する「ティックラー」スクリプトを作成できます。そこに自動的にリマインダーを作成したい場合は、Google CalendarやタスクマネージャーのようなAPIと統合します。

異常検知​

既知のしきい値や日付を超えて、異常なパターンのカスタムアラートをスクリプト化できます。たとえば、通常は毎月発生する費用が発生していない場合(請求書の支払いを忘れたかもしれません)、または今月あるカテゴリの支出が異常に高い場合、スクリプトがそれを指摘できます。これは通常、最近のデータをクエリして履歴と比較することを伴います(これは高度なトピックかもしれません – 統計やMLを使う可能性があります)。

実際には、多くのユーザーは照合に頼って異常(予期しない取引)を捕捉しています。銀行の通知(各取引のメールなど)を受け取る場合、スクリプトでそれらを解析し、自動的にBeancountに追加するか、少なくとも記録されていることを確認できます。ある愛好家は、取引アラートメールを送信するように銀行を設定し、それを解析して元帳に自動的に追記する計画を立てました。この種のイベント駆動型アラートは、どの取引も記録されずに残らないことを保証できます。

カスタムプラグインとビューでFavaを拡張する​

Favaはすでに拡張システムを通じてスクリプト化可能です。自動化やレポートをWebインターフェースに直接統合したい場合は、PythonでFava拡張(プラグインとも呼ばれます)を書くことができます。

Fava拡張の仕組み: 拡張はfava.ext.FavaExtensionBaseを継承したクラスを定義するPythonモジュールです。カスタムオプション経由でBeancountファイルに登録します。たとえば、MyAlerts(FavaExtensionBase)クラスを持つmyextension.pyファイルがある場合、元帳に以下を追加して有効にできます:

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

Favaがロードされると、そのモジュールをインポートし、MyAlertsクラスを初期化します。

拡張はいくつかのことができます:

  • フック: Favaのライフサイクルのイベントにフックできます。たとえば、after_load_file()は元帳がロードされた後に呼び出されます。これを使ってチェックを実行したり、データを事前計算したりできます。低残高チェックを_Favaの内部で_実装したい場合、after_load_fileは勘定残高を反復処理し、おそらく警告を保存できます(ただし、UIにそれを表示するにはもう少し作業が必要かもしれません。たとえば、FavaAPIErrorを発生させるか、JavaScriptを使って通知を表示します)。
  • カスタムレポート/ページ: 拡張クラスがreport_title属性を設定すると、Favaはサイドバーに新しいページを追加します。次に、そのページのコンテンツ用のテンプレート(HTML/Jinja2)を提供します。これが、Favaがデフォルトで持っていないまったく新しいビュー(ダッシュボードやサマリーなど)を作成する方法です。拡張は必要なデータを収集し(self.ledgerにアクセスでき、すべてのエントリ、残高などがあります)、テンプレートをレンダリングします。

たとえば、Favaに組み込まれているportfolio_list拡張はポートフォリオのポジションをリストするページを追加します。コミュニティ拡張はさらに進んでいます:

  • ダッシュボード: fava-dashboardsプラグインは、カスタムチャートとパネル(Apache EChartsなどのライブラリを使用)を定義できます。実行するクエリのYAML設定を読み、Beancount経由で実行し、Favaに動的なダッシュボードページを生成します。本質的に、BeancountデータとJavaScriptチャートライブラリを結びつけてインタラクティブな可視化を生成します。
  • ポートフォリオ分析: PortfolioSummary拡張(ユーザー寄稿)は投資サマリー(勘定科目のグループ化、IRRの計算など)を計算し、FavaのUIに表示します。
  • 取引レビュー: 別の拡張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の拡張システムは強力ですが、「不安定」(変更される可能性がある)と見なされています。カスタムページを作る場合は、Web開発(HTML/JS)にある程度精通している必要があります。単にスクリプトや分析を実行することが目的なら、それらを外部スクリプトとして保つ方が簡単かもしれません。ワークフロー用にカスタマイズされたアプリ内体験が欲しいときにFava拡張を使いましょう。

サードパーティのAPIとデータの統合​

スクリプト化されたワークフローの利点の1つは、外部データを取り込めることです。以下は一般的な統合です:

ホストされた評価価格については、Live Pricesがスケジュールされた価格取得スクリプトなしで管理されたincludeを提供します。ピッカーでサポートされている資産ペアとクォート通貨を選択します。以下のローカルファイルベースのワークフローは、アップストリームのBeancount、Fava、再現可能なレポートに引き続き役立ちます。管理されたリフレッシュは元帳にGitコミットを作成しません。

  • 為替レートとコモディティ: アップストリームのBeancountは価格を自分で取得しませんが、レートを提供するためのpriceディレクティブを提供します。これらの価格の取得を自動化できます。たとえば、スクリプトがAPI(Yahoo Finance、Alpha Vantageなど)に最新の為替レートや株価をクエリし、priceエントリを元帳に追記できます:

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

    管理されたエンジンのBeanpriceに支えられた**bea price**のようなツールがあり、毎日のクォートを取得してBeancount形式で出力します。bea engine enable beanpriceで一度有効にしてから、bea price main.beancountを毎晩実行するようにスケジュールしてprices.beancountインクルードファイルを更新できます。あるいはPythonを使います: たとえば、requestsライブラリでAPIを呼び出します。Beancountのドキュメントは、公開取引されている資産については、「価格をダウンロードしてディレクティブを書き出すコードを呼び出す」ことができると示唆しています。 言い換えれば、手動で行うのではなく、スクリプトにルックアップさせてprice行を挿入させるのです。

  • 株式ポートフォリオデータ: 為替レートと同様に、APIと統合して詳細な株式データや配当を取得できます。たとえば、Yahoo Finance API(またはyfinanceのようなコミュニティライブラリ)は、ティッカーの履歴データを取得できます。スクリプトは、所有する各株式の月次価格履歴で元帳を更新し、市場価値の正確な履歴レポートを可能にします。一部のカスタム拡張(_fava_investor_など)は、表示用にオンザフライで価格データを取得しますが、最も簡単なのは価格を定期的に元帳にインポートすることです。

  • 銀行API(Open Banking/Plaid): CSVをダウンロードする代わりに、APIを使って取引を自動的に取得できます。Plaidのようなサービスは銀行口座を集約し、取引へのプログラムによるアクセスを可能にします。高度なセットアップでは、PlaidのAPIを使って毎日新しい取引を取得し、ファイルに保存する(または元帳に直接インポートする)Pythonスクリプトを持つことができます。あるパワーユーザーは、Plaidがインポートパイプラインに流れ込むシステムを構築し、帳簿をほぼ自動化しました。彼らは「Plaid APIにサインアップしてローカルで同じことをするのを妨げるものは何もない」と述べています – つまり、銀行データを取得するローカルスクリプトを書き、Beancountインポーターのロジックを使って元帳エントリに解析できます。一部の地域には銀行が提供するオープンバンキングAPIがあり、同様に使えます。

  • その他のAPI: 予算管理ツールを統合したり(計画した予算をエクスポートしてBeancountの実績と比較)、OCR APIを使って領収書を読み取り、取引と自動マッチングしたりできます。スクリプトはPythonのエコシステムに完全にアクセスできるので、メールサービス(アラート送信用)からGoogle Sheets(月次の財務指標でシートを更新するなど)、メッセージングアプリ(Telegramボット経由でサマリーレポートを自分に送信する)まで、あらゆるものを統合できます。

サードパーティAPIを使うときは、認証情報を安全に保つこと(APIキーには環境変数や設定ファイルを使う)と、スクリプトでエラー(ネットワークの問題、APIのダウンタイム)を優雅に処理することを忘れないでください。データをキャッシュすることも多くの場合賢明です(たとえば、取得した為替レートを保存して、同じ履歴レートを繰り返し要求しないようにします)。

モジュール化され、維持可能なスクリプトのベストプラクティス​

スクリプト化されたワークフローを構築する際は、コードを整理して堅牢に保ちましょう:

  • モジュール性: 異なる関心事を異なるスクリプトやモジュールに分割します。たとえば、「データのインポート/照合」と「レポート生成」と「アラート」の別々のスクリプトを持ちます。ledger_import.py、ledger_reports.pyなどのモジュールを持つ小さなPythonパッケージを元帳用に作成することもできます。これにより、各部分が理解しやすくテストしやすくなります。

  • 設定: 値をハードコードするのを避けます。勘定科目名、しきい値、APIキー、日付範囲などのために設定ファイルやスクリプトの先頭の変数を使います。これにより、コードを深く編集せずに簡単に調整できます。たとえば、LOW_BALANCE_THRESHOLDS = {"Assets:Bank:Checking": 500, "Assets:Savings": 1000}を先頭で定義し、アラートスクリプトがこのdictをループできます。

  • テスト: 財務自動化をミッションクリティカルなコードとして扱いましょう – 実際そうなのですから! 複雑なロジックにはテストを書きます。Beancountは、元帳入力をシミュレートするために活用できるいくつかのテストヘルパー(インポーターテストの内部で使用)を提供します。凝ったフレームワークがなくても、ダミーのCSVと期待される出力取引を用意し、インポートスクリプトが正しいエントリを生成することをアサートできます。pytestを使う場合は、これらのテストを簡単に統合できます(Alex Wattがpytestをラップしたjust testコマンドで行ったように)。

  • バージョン管理: 元帳とスクリプトをバージョン管理下(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/ja/docs/Solutions/scriptable-workflows