Issues Transferring Lots between accounts

191 views
Skip to first unread message

Alex Kaufman

unread,
Jun 20, 2025, 6:47:41 AMJun 20
to Beancount
Hello, I have been reading through docs and trying to make heads or tails of how to do this operation. Recently I consolidated retirement accounts. As part of this process my holdings were transferred over directly from one account to the other. I am sure I am misunderstanding something but I have been testing various methods for a while and cannot figure out the right way to approach this problem. Any recommendations for ways to reframe the problem or think about it differently instead of just accomplishing the immediate goal are welcome and encouraged.

It may be as simple as my misunderstanding price/cost or the cost and market value views in fava. I could also be drastically over complicating things. Thank you all in advance for your help! Below is an initial state beancount file to provide a minimal example. I have also included two examples of some potential transfer transactions to append to the setup file and the reasons why they dont really work. 

Broker1 is set up as "NONE" bookkeeping due to the way the broker provided statements. This was a major motivator for migrating to Broker2. 

option "operating_currency" "USD"

2024-01-01 open Assets:BankAccount
2024-01-01 open Equity:Opening-Balances

2024-01-02 pad Assets:BankAccount Equity:Opening-Balances
2024-01-03 balance Assets:BankAccount 100 USD

2024-12-31 open Assets:Broker1:STOCK "NONE"
2024-12-31 open Assets:Broker2:STOCK

2025-01-01 * "invest 1"
  Assets:Broker1:STOCK 10.00 STOCK {1.00 USD}
  Assets:BankAccount -10.00 USD

2025-01-02 * "invest 2"
  Assets:Broker1:STOCK 10.00 STOCK {2.00 USD}
  Assets:BankAccount  -20.00 USD

Example 1:

2025-01-03 * "transfer"
  Assets:Broker1:STOCK -10.00 STOCK {}
  Assets:Broker2:STOCK 10 STOCK

Here is one attempt I have tried. It sort of works in that there are no errors shown in fava but in the cost and market value views it doesnt convert to USD. 

Screenshot 2025-06-19 at 9.50.10 PM.png

Example 2: 

2025-01-03 * "transfer"
  Assets:Broker1:STOCK -20.00 STOCK {USD}
  Assets:Broker2:STOCK 20 STOCK {{40.00 USD}}


Another attempt I have made is to change the transfer according to the syntax defined in github issue 476. If I do not specify the cost of the stock in broker 2 I get an error stating the transaction is not balanced. However if I do specify the current price/cost of the stock it seems to show that there has been a $10 negative balance to Broker 1. In the MarketValue view this shows correctly.. 


Screenshot 2025-06-19 at 9.59.39 PM.png

Example 3: 

2025-01-03 * "transfer"
  Assets:Broker1:STOCK -20.00 STOCK {} @ 2.00 USD
  Assets:Broker2:STOCK 20.00 STOCK {{40.00 USD}}
  Income:Investment -10.00 USD

This option resolves the confusion in both the cost and market value views. However now we are including a sort of fake income that we havent actually realized. It also requires that we know the exact amount of that income because leaving the specific value blank to try to soak up any income results in the error Too many missing numbers for currency group 'USD',  In my case I dont know the exact income value for each of the stocks coming over from broker1 to broker 2 because it is a retirement account and is basically using average bookkeeping. 


  

Martin Blais

unread,
Jun 20, 2025, 10:57:39 AMJun 20
to bean...@googlegroups.com
On Fri, Jun 20, 2025 at 6:47 AM Alex Kaufman <akau...@gmail.com> wrote:
Hello, I have been reading through docs and trying to make heads or tails of how to do this operation. Recently I consolidated retirement accounts. As part of this process my holdings were transferred over directly from one account to the other. I am sure I am misunderstanding something but I have been testing various methods for a while and cannot figure out the right way to approach this problem. Any recommendations for ways to reframe the problem or think about it differently instead of just accomplishing the immediate goal are welcome and encouraged.

It may be as simple as my misunderstanding price/cost or the cost and market value views in fava. I could also be drastically over complicating things. Thank you all in advance for your help! Below is an initial state beancount file to provide a minimal example. I have also included two examples of some potential transfer transactions to append to the setup file and the reasons why they dont really work. 

Broker1 is set up as "NONE" bookkeeping due to the way the broker provided statements. This was a major motivator for migrating to Broker2. 

option "operating_currency" "USD"

2024-01-01 open Assets:BankAccount
2024-01-01 open Equity:Opening-Balances

2024-01-02 pad Assets:BankAccount Equity:Opening-Balances
2024-01-03 balance Assets:BankAccount 100 USD

2024-12-31 open Assets:Broker1:STOCK "NONE"
2024-12-31 open Assets:Broker2:STOCK

2025-01-01 * "invest 1"
  Assets:Broker1:STOCK 10.00 STOCK {1.00 USD}
  Assets:BankAccount -10.00 USD

2025-01-02 * "invest 2"
  Assets:Broker1:STOCK 10.00 STOCK {2.00 USD}
  Assets:BankAccount  -20.00 USD

Example 1:

2025-01-03 * "transfer"
  Assets:Broker1:STOCK -10.00 STOCK {}
  Assets:Broker2:STOCK 10 STOCK

Here is one attempt I have tried. It sort of works in that there are no errors shown in fava but in the cost and market value views it doesnt convert to USD. 

Here it doesn't know what to do; Do you want to exchange dollars?  You're only saying STOCK units on the second posting.
This gets resolved as

2025-01-03 * "transfer"
  Assets:Broker1:STOCK -20.00 STOCK {USD}
  Assets:Broker2:STOCK 20 STOCK {{40.00 USD}}

(Use the "bean-doctor context" command)
Regardless, this input is incorrect.

 

Screenshot 2025-06-19 at 9.50.10 PM.png

Example 2: 

2025-01-03 * "transfer"
  Assets:Broker1:STOCK -20.00 STOCK {USD}
  Assets:Broker2:STOCK 20 STOCK {{40.00 USD}}


Another attempt I have made is to change the transfer according to the syntax defined in github issue 476. If I do not specify the cost of the stock in broker 2 I get an error stating the transaction is not balanced. However if I do specify the current price/cost of the stock it seems to show that there has been a $10 negative balance to Broker 1. In the MarketValue view this shows correctly.. 

But you don't have $40 of cost basis. You have $30 of cost basis. Try with that (which is correctd).
It will resolve as

2025-01-03 * "transfer"
  Assets:Broker1:STOCK  -10.00 STOCK {1.00 USD, 2025-01-01}  ; -10.0000 USD
  Assets:Broker1:STOCK  -10.00 STOCK {2.00 USD, 2025-01-02}  ; -20.0000 USD
  Assets:Broker2:STOCK   20.00 STOCK {1.50 USD, 2025-01-03}  ;    30.00 USD




 


Screenshot 2025-06-19 at 9.59.39 PM.png

Example 3: 

2025-01-03 * "transfer"
  Assets:Broker1:STOCK -20.00 STOCK {} @ 2.00 USD
  Assets:Broker2:STOCK 20.00 STOCK {{40.00 USD}}
  Income:Investment -10.00 USD

This option resolves the confusion in both the cost and market value views. However now we are including a sort of fake income that we havent actually realized.

No. You're realizing the profit here and erroneously stepping up your cost basis to $40.
This is what it resolves to:

2025-01-03 * "transfer"
  Assets:Broker1:STOCK  -10.00 STOCK {1.00 USD, 2025-01-01} @ 2.00 USD  ; -10.0000 USD
  Assets:Broker1:STOCK  -10.00 STOCK {2.00 USD, 2025-01-02} @ 2.00 USD  ; -20.0000 USD
  Assets:Broker2:STOCK   20.00 STOCK {2.00 USD, 2025-01-03}             ;    40.00 USD
  Income:Investment     -10.00 USD                                      ;   -10.00 USD

 
It also requires that we know the exact amount of that income because leaving the specific value blank to try to soak up any income results in the error Too many missing numbers for currency group 'USD',  In my case I dont know the exact income value for each of the stocks coming over from broker1 to broker 2 because it is a retirement account and is basically using average bookkeeping. 


If you're not selling you want to avoid realizing _any_ income.
You want to close all the postings in the account prior to the transaction.
You can either list all the postings using "bean-doctor context" and copy them over, or, use the total number of units, which should unambiguously resolve to all the postings.

2025-01-03 * "transfer"
  Assets:Broker1:STOCK -20.00 STOCK {}
  Assets:Broker2:STOCK 20.00 STOCK {USD}

resolves to

2025-01-03 * "transfer"
  Assets:Broker1:STOCK  -10.00 STOCK {1.00 USD, 2025-01-01}  ; -10.0000 USD
  Assets:Broker1:STOCK  -10.00 STOCK {2.00 USD, 2025-01-02}  ; -20.0000 USD
  Assets:Broker2:STOCK   20.00 STOCK {1.50 USD, 2025-01-03}  ;  30.0000 USD


On the opening sice, if you want to replicate all the same postings, you could do this instead:

2025-01-03 * "transfer"
  Assets:Broker1:STOCK                -10.00 STOCK {1.00 USD, 2025-01-01}
  Assets:Broker1:STOCK                -10.00 STOCK {2.00 USD, 2025-01-02}
  Assets:Broker2:STOCK                 10.00 STOCK {1.00 USD, 2025-01-01}
  Assets:Broker2:STOCK                 10.00 STOCK {2.00 USD, 2025-01-02}



 



 
  

--
You received this message because you are subscribed to the Google Groups "Beancount" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beancount+...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/beancount/d798059c-af75-4622-b5c8-adb0a7666154n%40googlegroups.com.

Alex Kaufman

unread,
Jun 20, 2025, 7:06:38 PMJun 20
to Beancount
Thank you for the help here. It sounds like the most straightforward option for transferring accounts with average cost booking without realizing nonexistent profit or changing the cost basis then is the final option you mentioned.

I attempted the following transaction and receive an error "Too many missing numbers for currency group 'USD'" .  

2025-01-03 * "transfer"
  Assets:Broker1:STOCK -20.00 STOCK {}
  Assets:Broker2:STOCK 20.00 STOCK {USD}

Message has been deleted

Red S

unread,
Jun 21, 2025, 2:13:06 AMJun 21
to Beancount

Here’s a plugin that does what you want. But more interestingly, the README on that page explains and shows you how to do it.

When transferring assets in-kind (for example between two brokerages) you want to preserve the cost basis and acquisition date of each lot. You can do this manually in beancount like so:

2021-09-10 * "Transfer APPL from Schwab to Fidelity" Assets:Schwab -100 AAPL {10.00 USD, 2018-05-18} Assets:Fidelity 100 APPL {10.00 USD, 2018-05-18}

bean-doctor will show you the list of lots. From there, simply use your editor to get the transaction you want.

If you’re wanting to simulate average cost booking when doing the transfer, something like this should work (untested), where 156.67 is the average cost basis you manually calculate:

2024-06-01 * "Transfer and consolidate AAPL" Assets:Brokerage:Old -30 AAPL @ 156.67 USD Assets:Brokerage:New 30 AAPL {156.67 USD}

Martin Blais

unread,
Jun 21, 2025, 11:57:22 AMJun 21
to bean...@googlegroups.com
What's in your inventory prior to the transaction? 
I bet it has postings that are either not at cost (an input error).
Use bean-doctor context to resolve those issues.


Alex Kaufman

unread,
Jun 21, 2025, 1:45:48 PMJun 21
to Beancount
I am trying to get it to work with that minimal beancount example before I try it in my personal ledger. This is the whole test beancount ledger. The error occurs in the transfer transaction with the message "Too many missing numbers for currency group 'USD'" 

option "operating_currency" "USD"

2024-01-01 open Assets:BankAccount
2024-01-01 open Equity:Opening-Balances

2024-01-02 pad Assets:BankAccount Equity:Opening-Balances
2024-01-03 balance Assets:BankAccount 100 USD

2024-12-31 open Assets:Broker1:STOCK "NONE"
2024-12-31 open Assets:Broker2:STOCK

2025-01-01 * "invest 1"
  Assets:Broker1:STOCK 10.00 STOCK {1.00 USD}
  Assets:BankAccount -10.00 USD

2025-01-02 * "invest 2"
  Assets:Broker1:STOCK 10.00 STOCK {2.00 USD}
  Assets:BankAccount  -20.00 USD

2025-01-03 * "transfer"
  Assets:Broker1:STOCK -20.00 STOCK {}
  Assets:Broker2:STOCK 20.00 STOCK {USD}

This is the bean-doctor output. It looks like in the Unbooked transactions section it is not resolving the transaction with the total number of units. 

Martin Blais

unread,
Jun 21, 2025, 2:30:51 PMJun 21
to bean...@googlegroups.com
If you remove the "NONE" booking method that works.
I'm guessing - would have to look in code, can't do that now, am traveling - that when booking is disabled that the total units rule does not apply.

Alex Kaufman

unread,
Jun 21, 2025, 2:47:05 PMJun 21
to Beancount
Thank you, I'll get that a try. Appreciate your time and help with this. Hope you enjoy your travels.

-Alex

Alex Kaufman

unread,
Jun 21, 2025, 5:20:31 PMJun 21
to Beancount
Thank you Red! The explanation here helped a lot. I think my main issue is actually a deeper conceptual issue. I will start a different thread for that question though. 

On Friday, June 20, 2025 at 11:13:06 PM UTC-7 Red S wrote:

Here’s a plugin that does what you want. But more interestingly, the README on that page explains and shows you how to do it.

When transferring assets in-kind (for example between two brokerages) you want to preserve the cost basis and acquisition date of each lot. You can do this manually in beancount like so:

2021-09-10 * "Transfer APPL from Schwab to Fidelity" Assets:Schwab -100 AAPL {10.00 USD, 2018-05-18} Assets:Fidelity 100 APPL {10.00 USD, 2018-05-18}

bean-doctor will show you the list of lots. From there, simply use your editor to get the transaction you want.

If you’re wanting to simulate average cost booking when doing the transfer, something like this should work (untested), where 156.67 is the average cost basis you manually calculate:

2024-06-01 * "Transfer and consolidate AAPL" Assets:Brokerage:Old -30 AAPL @ 156.67 USD Assets:Brokerage:New 30 AAPL {156.67 USD}

Simulating average cost booking seems like a good way to do this as well. If I do not include the `{}` on the Old account though I end up with -30 AAPL similar to the result in example 1. Adding the lost selector solves this though. 

Red S

unread,
Jun 21, 2025, 6:39:51 PMJun 21
to Beancount

Simulating average cost booking seems like a good way to do this as well. If I do not include the {} on the Old account though I end up with -30 AAPL similar to the result in example 1. Adding the lost selector solves this though.

Glad it helped! As you already mentioned, the underlying concepts of how lot matching is done or not are a good idea to understand. In addition, how those concepts are exercised based on your Beancount transaction syntax would be very helpful. The good thing is, documentation for all those things is excellent with Beancount.

With STRICT booking, if you augment (buy) 30 AAPL, then reduce (sell) -20 AAPL, the reduction will have to to be matched to the existing 30, and will specifically reduce 20 out of the 30.

With NONE booking, if you do the same augment and reduction, then see this doc:


No Booking

However, there is another way to deal with non-taxable accounts in the meantime: you can simply disable the booking. There is a booking method called “NONE” which implements a very liberal strategy which accepts any new lot.. New lots are always appended unconditionally to the inventory. Using this strategy on the transactions from the previous section would result in this inventory:

units ccy cost cost-ccy lot-date label 45.0045 VBMPX {11.11 USD, 2016-07-28, None} 54.5951 VBMPX {10.99 USD, 2016-10-12, None} -1.4154 VBMPX {10.59 USD, 2016-12-30, None}

Observe how the resulting inventory has a mix of signs; normally this is not allowed, but it is tolerated under this degenerate booking method. Note that under this method, the only meaningful values are the total number of units and the total or average cost amounts. The individual lots aren’t really lots, they only represent the list of all postings made to that account.

Justus Pendleton

unread,
Jun 27, 2025, 7:22:46 AMJun 27
to Beancount
On Saturday, June 21, 2025 at 3:43:06 PM UTC+9:30 Red S wrote:

Here’s a plugin that does what you want. But more interestingly, the README on that page explains and shows you how to do it.


Note that that plugin doesn't actually work due to beancount's loader running interpolation on incomplete booking entries before it runs transformation plugins. (See bug #1 in the project for more detail.)

Red S

unread,
Jun 27, 2025, 12:33:38 PMJun 27
to Beancount
Ah yes, [this post](https://groups.google.com/g/beancount/c/vDX1oA2mJXA/m/08dg_fnIBAAJ). Perhaps worth noting this at the top of your README in that repo?

Also, I wonder if any of the [ideas here](https://groups.google.com/g/beancount/c/vDX1oA2mJXA/m/iXhBXB7ZBAAJ) were implemented in v3?
Message has been deleted

CD

unread,
Jul 6, 2025, 10:40:30 PMJul 6
to Beancount
I tried to create a plugin myself but ended up just creating a script that created the transfers.  It works, but you have to make sure you add them in order or you'll have issues.  It's helpful if you have a lot of lots and transfer cryptocurrencies between accounts or exchanges...

```
#!/usr/bin/env python3
"""
Beancount Crypto Transfer Transaction Generator v2

This script uses the native Beancount Python API instead of subprocess calls
to query your Beancount file and generate transfer transactions automatically
using FIFO lot selection.

Usage:
    python transfer_generator_v2.py ledger.beancount "Assets:CryptoExchange" "Assets:PrivateWallet" 20 BSV "Transfer to cold storage"
"""

import sys
import os
from decimal import Decimal
from datetime import datetime
import argparse

from beancount import loader
from beanquery.query import run_query


def get_account_lots(beancount_file, account, currency):
    """Get all lots for a specific account and currency using native Beancount API"""
   
    try:
        # Load the beancount file
        entries, errors, options_map = loader.load_file(beancount_file)
       
        if errors:
            print(f"Warning: Found {len(errors)} errors in Beancount file:")
            for error in errors[:5]:  # Show first 5 errors
                print(f"  {error}")
            if len(errors) > 5:
                print(f"  ... and {len(errors) - 5} more errors")
   
        # Run the query directly using beanquery
        query = f"""
        SELECT account,
               units(sum(position)) as units,
               cost_number as cost,
               cost_currency as cost_currency,
               cost_date as acquisition_date,
               cost_label as lot_label
        WHERE account = '{account}' AND currency = '{currency}'
        GROUP BY account, cost_number, cost_currency, cost_date, cost_label
        ORDER BY cost_date
        """
       
        # This returns structured data, not CSV text!
        result_types, result_rows = run_query(entries, options_map, query)
       
        lots = []
        for row in result_rows:
            # Direct access to typed data instead of parsing CSV strings
            units_inventory = row[1]  # This is an Inventory object, not Amount
           
            # Skip empty inventories
            if units_inventory is None:
                continue
           
            # Handle Beancount Inventory objects
            try:
                if hasattr(units_inventory, 'is_empty') and units_inventory.is_empty():
                    continue
               
                # Extract the Amount from the Inventory
                # An Inventory can contain multiple positions, we want the one for our currency
                amount = None
                if hasattr(units_inventory, '__iter__'):
                    for position in units_inventory:
                        if hasattr(position, 'units') and position.units.currency == currency:
                            amount = position.units
                            break
               
                if amount is None or amount.number <= 0:
                    continue
                   
                lot = {
                    'account': str(row[0]) if row[0] else '',
                    'units': amount.number,
                    'cost': row[2] if row[2] is not None else None,
                    'cost_currency': str(row[3]) if row[3] else None,
                    'acquisition_date': str(row[4]) if row[4] else None,
                    'lot_label': str(row[5]) if row[5] else None
                }
                lots.append(lot)
               
            except Exception as e:
                print(f"Debug: Exception processing row: {e}")
                continue
       
        return lots
       
    except Exception as e:
        print(f"Error querying lots: {e}")
        return []


def select_lots_fifo(lots, transfer_amount):
    """Select lots using FIFO method for the transfer amount"""
    selected_lots = []
    remaining = Decimal(str(transfer_amount))
   
    # Sort by acquisition date (FIFO) - handle None dates
    sorted_lots = sorted(lots, key=lambda x: x['acquisition_date'] or '1900-01-01')
   
    for lot in sorted_lots:
        if remaining <= 0:
            break
           
        # Take either the full lot or remaining amount
        take_amount = min(remaining, lot['units'])
       
        selected_lot = {
            'account': lot['account'],
            'units': take_amount,
            'cost': lot['cost'],
            'cost_currency': lot['cost_currency'],
            'acquisition_date': lot['acquisition_date'],
            'lot_label': lot['lot_label']
        }
       
        selected_lots.append(selected_lot)
        remaining -= take_amount
   
    if remaining > 0:
        print(f"Warning: Not enough units available. Short by {remaining}")
   
    return selected_lots


def format_cost_spec(lot):
    """Format the cost specification for a lot"""
    parts = []
   
    if lot['cost'] and lot['cost_currency']:
        parts.append(f"{lot['cost']} {lot['cost_currency']}")
   
    if lot['acquisition_date']:
        parts.append(lot['acquisition_date'])
       
    if lot['lot_label']:
        parts.append(f'"{lot["lot_label"]}"')
   
    if parts:
        return "{" + ", ".join(parts) + "}"
    else:
        return "{}"


def generate_transfer_transaction(date, from_account, to_account, currency,
                                description, selected_lots):
    """Generate a Beancount transfer transaction"""
   
    lines = [f'{date} ! "{description}"']
   
    for i, lot in enumerate(selected_lots):
        cost_spec = format_cost_spec(lot)
       
        # Format with proper spacing
        from_line = f"  {from_account:<45} -{lot['units']} {currency} {cost_spec}"
        to_line = f"  {to_account:<45} {lot['units']} {currency} {cost_spec}"
       
        lines.append(from_line)
        lines.append(to_line)
   
    return '\n'.join(lines)


def main():
    parser = argparse.ArgumentParser(
        description='Generate Beancount crypto transfer transactions with automatic lot selection (v2 - Native API)'
    )
    parser.add_argument('beancount_file', help='Path to your Beancount file')
    parser.add_argument('from_account', help='Source account (e.g., Assets:CryptoExchange)')
    parser.add_argument('to_account', help='Destination account (e.g., Assets:PrivateWallet)')
    parser.add_argument('amount', type=str, help='Amount to transfer')
    parser.add_argument('currency', help='Currency symbol (e.g., BSV, BTC)')
    parser.add_argument('description', help='Transaction description')
    parser.add_argument('--date', help='Transaction date (YYYY-MM-DD)',
                       default=datetime.now().strftime('%Y-%m-%d'))
    parser.add_argument('--method', choices=['FIFO', 'LIFO'], default='FIFO',
                       help='Lot selection method (default: FIFO)')
   
    args = parser.parse_args()
   
    # Convert relative path to absolute path
    args.beancount_file = os.path.abspath(args.beancount_file)
   
    # Check if file exists
    if not os.path.exists(args.beancount_file):
        print(f"Error: Beancount file '{args.beancount_file}' does not exist")
        print(f"Current directory: {os.getcwd()}")
        print("Available files:")
        for f in os.listdir('.'):
            if f.endswith('.beancount') or f.endswith('.bean'):
                print(f"  {f}")
        return
   
    # Get lots from source account
    print(f"Querying lots in {args.from_account} for {args.currency}...")
   
    lots = get_account_lots(args.beancount_file, args.from_account, args.currency)
   
    if not lots:
        print(f"No lots found in {args.from_account} for {args.currency}")
        return
   
    print(f"Found {len(lots)} lots:")
    total_available = sum(lot['units'] for lot in lots)
    print(f"Total available: {total_available} {args.currency}")
   
    for i, lot in enumerate(lots):
        label_display = f' ("{lot["lot_label"]}")' if lot['lot_label'] else ''
        print(f"  Lot {i+1}: {lot['units']} {args.currency} @ {lot['cost']} {lot['cost_currency']} ({lot['acquisition_date']}){label_display}")
   
    # Check if we have enough
    transfer_amount = Decimal(args.amount)
    if transfer_amount > total_available:
        print(f"Error: Requested {transfer_amount} {args.currency} but only {total_available} available")
        return
   
    # Select lots using specified method
    if args.method == 'LIFO':
        lots.reverse()  # Reverse for LIFO
   
    selected_lots = select_lots_fifo(lots, transfer_amount)
   
    print(f"\nSelected lots for transfer ({args.method}):")
    for i, lot in enumerate(selected_lots):
        label_display = f' ("{lot["lot_label"]}")' if lot['lot_label'] else ''
        print(f"  {lot['units']} {args.currency} @ {lot['cost']} {lot['cost_currency']} ({lot['acquisition_date']}){label_display}")
   
    # Generate transaction
    transaction = generate_transfer_transaction(
        args.date, args.from_account, args.to_account,
        args.currency, args.description, selected_lots
    )
   
    print(f"\nGenerated transaction:")
    print("=" * 80)
    print(transaction)
    print("=" * 80)
   
    # Save to file option
    save = input("\nSave to file? (y/N): ").lower().strip()
    if save == 'y':
        filename = f"transfer_{args.currency}_{args.date.replace('-', '')}.beancount"
        with open(filename, 'w') as f:
            f.write(transaction + '\n')
        print(f"Saved to {filename}")


if __name__ == '__main__':
    if len(sys.argv) == 1:
        # Example usage
        print("Example usage:")
        print('python transfer_generator_v2.py ledger.beancount "Assets:CryptoExchange" "Assets:PrivateWallet" 20 BSV "Transfer to cold storage"')
        print("")
        print("Optional arguments:")
        print("  --date YYYY-MM-DD     (default: today)")
        print("  --method FIFO|LIFO    (default: FIFO)")
        sys.exit(1)
   
    main()
```

It works, but there's still a bunch of manual work when I use it.  If there is a more elegant way to do this I would love to hear it.

-Chris


Red S

unread,
Jul 7, 2025, 12:06:43 PMJul 7
to Beancount
On Sunday, July 6, 2025 at 7:40:30 PM UTC-7 CD wrote:
I tried to create a plugin myself but ended up just creating a script that created the transfers. 

It's not possible to do this via a plugin. The thread I linked above has details. Script is best.

CD

unread,
Jul 8, 2025, 7:21:48 AMJul 8
to Beancount
How do you use bean-doctor context to figure out inventory as you mention here?  I can't get it to work.

Alex Kaufman

unread,
Jul 11, 2025, 11:37:23 PMJul 11
to bean...@googlegroups.com
Hi! Here are the steps I took. I am not sure this is a good way to do it but its how I did it. 

1. Run bean-doctor and point it at the line for the transaction that is supposed to close the account
2. Look at the value it says is available in the account you are trying to close
3. Use that value to zero out the account you are closing. Transfer however much is supposed to go in the new account and use a rounding account or something to absorb whatever rounding error issues have accumulated. 

I use a main.bean file to include a bunch of files in a folder so this is what step 1 looks like for me: 
`bean-doctor context main.bean ledger/2025-transactions.bean:15`

You received this message because you are subscribed to a topic in the Google Groups "Beancount" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/beancount/C8dpE9M61Qo/unsubscribe.
To unsubscribe from this group and all its topics, send an email to beancount+...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/beancount/2f8baef4-46cf-4663-ad0c-963b64e95742n%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages