Implements a script to import Beancount ledger transactions into the Castle accounting extension. The script fetches BTC/EUR rates, retrieves accounts from the Castle API, maps users, parses Beancount transactions, converts EUR to sats, and uploads the data to Castle. Adds error handling, dry-run mode, and detailed logging for improved usability. Displays equity account status and validates the existence of user equity accounts.
4.1 KiB
Castle Beancount Import Helper
Import Beancount ledger transactions into Castle accounting extension.
📁 Files
import_beancount.py- Main import scriptbtc_eur_rates.csv- Daily BTC/EUR rates (create your own)README.md- This file
🚀 Setup
1. Create BTC/EUR Rates CSV
Create btc_eur_rates.csv in this directory with your actual rates:
date,btc_eur_rate
2025-07-01,86500
2025-07-02,87200
2025-07-03,87450
2. Update User Mappings
Edit import_beancount.py and update the USER_MAPPINGS dictionary:
USER_MAPPINGS = {
"Pat": "actual_wallet_id_for_pat",
"Alice": "actual_wallet_id_for_alice",
"Bob": "actual_wallet_id_for_bob",
}
How to get wallet IDs:
- Check your LNbits admin panel
- Or query:
curl -X GET http://localhost:5000/api/v1/wallet -H "X-Api-Key: user_invoice_key"
3. Set API Key
export CASTLE_ADMIN_KEY="your_lnbits_admin_invoice_key"
export LNBITS_URL="http://localhost:5000" # Optional
📖 Usage
cd /path/to/castle/helper
# Test with dry run
python import_beancount.py ledger.beancount --dry-run
# Actually import
python import_beancount.py ledger.beancount
📄 Beancount File Format
Your Beancount transactions must have an Equity:<name> account:
2025-07-06 * "Foix market"
Expenses:Groceries 69.40 EUR
Equity:Pat
2025-07-07 * "Gas station"
Expenses:Transport 45.00 EUR
Equity:Alice
Requirements:
- Every transaction must have an
Equity:<name>account - Account names must match exactly what's in Castle
- The name after
Equity:must be inUSER_MAPPINGS
🔄 How It Works
- Loads rates from
btc_eur_rates.csv - Loads accounts from Castle API automatically
- Maps users - Extracts user name from
Equity:Nameaccounts - Parses Beancount transactions
- Converts EUR → sats using daily rate
- Uploads to Castle with metadata
📊 Example Output
$ python import_beancount.py ledger.beancount
======================================================================
🏰 Beancount to Castle Import Script
======================================================================
📊 Loaded 15 daily rates from btc_eur_rates.csv
Date range: 2025-07-01 to 2025-07-15
🏦 Loaded 28 accounts from Castle
👥 User ID mappings:
- Pat → wallet_abc123
- Alice → wallet_def456
- Bob → wallet_ghi789
📄 Found 25 potential transactions in ledger.beancount
✅ Transaction 1: 2025-07-06 - Foix market (User: Pat) (Rate: 87,891 EUR/BTC)
✅ Transaction 2: 2025-07-07 - Gas station (User: Alice) (Rate: 88,100 EUR/BTC)
✅ Transaction 3: 2025-07-08 - Restaurant (User: Bob) (Rate: 88,350 EUR/BTC)
======================================================================
📊 Summary: 25 succeeded, 0 failed, 0 skipped
======================================================================
✅ Successfully imported 25 transactions to Castle!
❓ Troubleshooting
"No account found in Castle"
Error: No account found in Castle with name 'Expenses:XYZ'
Solution: Create the account in Castle first with that exact name.
"No user ID mapping found"
Error: No user ID mapping found for 'Pat'
Solution: Add Pat to the USER_MAPPINGS dictionary in the script.
"No BTC/EUR rate found"
Error: No BTC/EUR rate found for 2025-07-15
Solution: Add that date to btc_eur_rates.csv.
"Could not determine user ID"
Error: Could not determine user ID for transaction
Solution: Every transaction needs an Equity:<name> account (e.g., Equity:Pat).
📝 Transaction Metadata
Each imported transaction includes:
{
"meta": {
"source": "beancount_import",
"imported_at": "2025-11-08T12:00:00",
"btc_eur_rate": 87891.0,
"user_id": "wallet_abc123"
}
}
And each line includes:
{
"metadata": {
"fiat_currency": "EUR",
"fiat_amount": "69.400",
"fiat_rate": 1137.88,
"btc_rate": 87891.0
}
}
This preserves the original EUR amount and exchange rate for auditing.