Saltar al contenido principal

Lotes de Beancount, base de coste y métodos de asignación

Cómo contabiliza Beancount los lotes cuando vendes acciones o divisas: base de coste, especificaciones de lote, contabilización STRICT, FIFO, LIFO y HIFO, precio frente a coste, anulaciones por cuenta.

El sistema de inventario de Beancount es una función potente para rastrear activos que se compran y venden a lo largo del tiempo, como acciones, fondos mutuos o divisas extranjeras. Permite un seguimiento preciso de la base de costo, lo cual es esencial para calcular ganancias de capital y comprender el rendimiento de la cartera. Este tutorial cubre la mecánica central de la gestión de inventarios en tu libro contable.

Conceptos Básicos​

En esencia, la gestión de inventario gira en torno al seguimiento de posiciones. Una "posición" es simplemente una cantidad de un commodity mantenida en una cuenta. Beancount distingue entre dos tipos fundamentales de posiciones.

Tipos de Posición​

  1. Posición simple (sin costo): Este es un registro de saldo estándar. Representa una cantidad de un commodity sin ningún costo de adquisición asociado. Es adecuado para efectivo o aserciones de saldo simples.

    Assets:Bank:Checking      100.00 USD
  2. Posición con base de costo: Este tipo de posición incluye no solo el número de unidades y el commodity, sino también el costo al que se adquirió. Esta es la base del seguimiento de inventario. El costo se especifica dentro de llaves {}.

    Assets:Invest:VTSAX      10 VTSAX {100.00 USD, "lot-1"}

    En este ejemplo, tenemos 10 unidades de VTSAX. Cada unidad se adquirió a un costo de $100.00 USD. Este lote específico de acciones se identifica como un "lote".

Operaciones de Inventario​

Hay dos operaciones principales que puedes realizar sobre un inventario:

  1. Aumentos (agregar al inventario): Cuando compras un commodity, aumentas tu inventario. Creas un nuevo lote con un número específico de unidades y una base de costo.

    2024-01-15 * "Buy shares"
      Assets:Invest:STOCK     50 STOCK {25.00 USD, "lot-1"}
      Assets:Bank:Checking   -1250.00 USD

    Aquí, compramos 50 unidades de STOCK a un costo por unidad de $25.00 USD. Esto crea un lote en la cuenta Assets:Invest:STOCK.

  2. Reducciones (quitar del inventario): Cuando vendes un commodity, reduces tu inventario. Debes especificar de qué lote estás vendiendo. Esto se hace proporcionando información coincidente en las llaves.

    2024-01-20 * "Sell shares"
      Assets:Invest:STOCK    -25 STOCK {25.00 USD}
      Assets:Bank:Checking    625.00 USD

    En esta transacción, vendemos 25 unidades de STOCK del lote que se compró a $25.00 USD por unidad.

Métodos de Registro​

Cuando reduces un inventario, Beancount necesita una regla para decidir de qué lote específico tomar si varios lotes coinciden con la reducción. Esta regla se llama "método de asignación". Puedes establecer un valor predeterminado para todo el archivo con una opción, o dar a una cuenta su propio método en la directiva open.

Beancount 3.2.3 acepta siete nombres de métodos: STRICT (el predeterminado), STRICT_WITH_SIZE, NONE, FIFO, LIFO, HIFO y AVERAGE. Seis de ellos están implementados; AVERAGE se analiza sintácticamente pero genera un error en el momento en que tiene que asignar una reducción, como muestra la sección AVERAGE a continuación.

1. STRICT (Predeterminado)​

El método STRICT es el predeterminado y el método de asignación más seguro. Impone una coincidencia explícita e inequívoca.

2024-01-01 open Assets:Invest:STOCK "STRICT"
  • Requiere una coincidencia exacta de lote: El especificador de costo del registro de reducción ({...}) debe identificar un único lote — por costo, por fecha de adquisición, por etiqueta o por cualquier combinación de ellos.
  • Errores en coincidencias ambiguas: Si el especificador coincide con más de un lote, Beancount genera un AmbiguousMatchError en lugar de adivinar.
  • Excepción: Si una reducción elimina exactamente el número total de unidades con el que coincide el especificador, se permite un especificador vacío ({}), y la reducción se divide entre esos lotes.

Este libro contable tiene dos lotes y vende uno de ellos nombrando su costo, lo cual no es ambiguo:

1970-01-01 commodity STK
1970-01-01 open Assets:Broker:Strict  STK  "STRICT"
1970-01-01 open Assets:Broker:Cash    USD
1970-01-01 open Income:Gains          USD
 
2024-01-10 * "Buy the first lot"
  Assets:Broker:Strict    10 STK {100.00 USD}
  Assets:Broker:Cash   -1000.00 USD
 
2024-02-10 * "Buy the second lot"
  Assets:Broker:Strict    10 STK {120.00 USD}
  Assets:Broker:Cash   -1200.00 USD
 
; The cost identifies exactly one lot, so STRICT is satisfied.
2024-06-01 * "Sell the $120.00 lot"
  Assets:Broker:Strict   -10 STK {120.00 USD} @ 150.00 USD
  Assets:Broker:Cash    1500.00 USD
  Income:Gains

Se carga sin errores, registra $300.00 de ganancia en Income:Gains y deja 10 STK {100.00 USD} en la cuenta.

Reemplaza ese último registro con un especificador vacío y el mismo archivo falla:

; Rejected under STRICT: "-10 STK {}" matches both lots.
2024-06-01 * "Sell 10 shares"
  Assets:Broker:Strict  -10 STK {} @ 150.00 USD
  Assets:Broker:Cash   1500.00 USD
  Income:Gains

Beancount informa Ambiguous matches for "-10 STK {}" y enumera los candidatos. Vender la posición completa sí está bien, sin embargo, porque no queda nada entre lo que elegir:

; Allowed under STRICT: -20 STK is the entire holding, so the empty
; specifier is split across both lots.
2024-06-01 * "Close the position"
  Assets:Broker:Strict  -20 STK {} @ 150.00 USD
  Assets:Broker:Cash   3000.00 USD
  Income:Gains

Eso registra $800.00 de ganancia — $3,000.00 de ingresos contra $1,000.00 + $1,200.00 de base — y deja la cuenta vacía. Esta es una propiedad de STRICT en sí mismo, no algo para lo que tengas que cambiar a STRICT_WITH_SIZE.

2. FIFO (Primero en entrar, primero en salir)​

El método FIFO registra automáticamente las reducciones contra los lotes disponibles más antiguos primero.

2024-01-01 open Assets:Invest:STOCK "FIFO"
  • Resolución automática: Resuelve la ambigüedad seleccionando los lotes coincidentes más antiguos.
  • Coincidencia cronológica: Asumes que estás vendiendo los activos que has mantenido durante más tiempo. Varias autoridades fiscales lo tratan como el predeterminado cuando no has identificado un lote.

3. LIFO (Último en entrar, primero en salir)​

El método LIFO es lo opuesto a FIFO. Registra las reducciones contra los lotes disponibles más recientes primero.

2024-01-01 open Assets:Invest:STOCK "LIFO"
  • Orden cronológico inverso: Selecciona los lotes coincidentes adquiridos más recientemente.
  • El más reciente, no el más caro: LIFO elige solo por fecha de adquisición. Da la casualidad de que vende las acciones de mayor costo cuando los precios han estado subiendo, pero si tu lote más reciente es el más barato — que es lo que el ejemplo siguiente está diseñado para mostrar — LIFO realizará la ganancia más grande, no la más pequeña. El método que siempre vende las acciones más caras es HIFO, descrito a continuación.

4. HIFO (Highest-In, First-Out)​

El método HIFO registra las reducciones contra los lotes disponibles más caros primero, sea cual sea su fecha.

2024-01-01 open Assets:Invest:STOCK "HIFO"
  • Coincidencia clasificada por costo: Selecciona los lotes coincidentes con la base de costo más alta.
  • Ganancia realizada más pequeña: Para un precio de venta dado, vender las acciones de mayor costo realiza la ganancia más pequeña (o la pérdida más grande). Si puedes usarlo es una cuestión de jurisdicción — en Estados Unidos, por ejemplo, elegir un lote en absoluto requiere identificación específica en el momento de la venta — así que trata el método como un mecanismo de contabilidad y confirma la elección fiscal por separado.

5. Comparando FIFO, LIFO y HIFO sobre las mismas partidas​

Los tres métodos solo difieren cuando el lote más antiguo, el más reciente y el más caro son tres lotes diferentes. Este libro contable organiza exactamente eso — el lote A es el más antiguo, el lote C es el más reciente, y el lote intermedio B es el más caro — y luego vende 10 acciones de tres cuentas que difieren solo en su método de asignación:

1970-01-01 commodity STK
1970-01-01 open Assets:Broker:Fifo  STK  "FIFO"
1970-01-01 open Assets:Broker:Lifo  STK  "LIFO"
1970-01-01 open Assets:Broker:Hifo  STK  "HIFO"
1970-01-01 open Assets:Broker:Cash  USD
1970-01-01 open Income:Gains        USD
 
; Lot A - the oldest, at $100.00 per share
2024-01-10 * "Buy lot A"
  Assets:Broker:Fifo     10 STK {100.00 USD}
  Assets:Broker:Lifo     10 STK {100.00 USD}
  Assets:Broker:Hifo     10 STK {100.00 USD}
  Assets:Broker:Cash  -3000.00 USD
 
; Lot B - the most expensive, at $120.00 per share
2024-02-10 * "Buy lot B"
  Assets:Broker:Fifo     10 STK {120.00 USD}
  Assets:Broker:Lifo     10 STK {120.00 USD}
  Assets:Broker:Hifo     10 STK {120.00 USD}
  Assets:Broker:Cash  -3600.00 USD
 
; Lot C - the newest, at $90.00 per share
2024-03-10 * "Buy lot C"
  Assets:Broker:Fifo     10 STK {90.00 USD}
  Assets:Broker:Lifo     10 STK {90.00 USD}
  Assets:Broker:Hifo     10 STK {90.00 USD}
  Assets:Broker:Cash  -2700.00 USD
 
; Sell 10 shares out of each account at $150.00 and let each
; account's booking method choose which lot leaves.
2024-06-01 * "Sell 10 shares from each account"
  Assets:Broker:Fifo    -10 STK {} @ 150.00 USD
  Assets:Broker:Lifo    -10 STK {} @ 150.00 USD
  Assets:Broker:Hifo    -10 STK {} @ 150.00 USD
  Assets:Broker:Cash   4500.00 USD
  Income:Gains

Se carga con cero errores y registra $1,400.00 de ganancia en total, dividida así:

CuentaMétodoLote asignadoBase de costoGanancia realizadaLotes restantes
Assets:Broker:FifoFIFOlote A, 2024-01-10$100.00$500.0010 @ $120.00, 10 @ $90.00
Assets:Broker:LifoLIFOlote C, 2024-03-10$90.00$600.0010 @ $100.00, 10 @ $120.00
Assets:Broker:HifoHIFOlote B, 2024-02-10$120.00$300.0010 @ $100.00, 10 @ $90.00

La fila de LIFO es la que vale la pena mirar fijamente: realizó la ganancia más grande de las tres, porque el lote más reciente también era el más barato.

6. STRICT_WITH_SIZE​

STRICT_WITH_SIZE es STRICT más un desempate adicional: cuando varios lotes coinciden pero exactamente uno de ellos tiene precisamente el número de unidades que estás eliminando, se elige ese lote.

1970-01-01 commodity STK
1970-01-01 open Assets:Broker:Sized  STK  "STRICT_WITH_SIZE"
1970-01-01 open Assets:Broker:Cash   USD
1970-01-01 open Income:Gains         USD
 
2024-01-10 * "Buy 10 shares"
  Assets:Broker:Sized    10 STK {100.00 USD}
  Assets:Broker:Cash  -1000.00 USD
 
2024-02-10 * "Buy 7 shares"
  Assets:Broker:Sized     7 STK {120.00 USD}
  Assets:Broker:Cash   -840.00 USD
 
; Only one lot holds exactly 7 units, so the empty specifier resolves.
2024-06-01 * "Sell 7 shares"
  Assets:Broker:Sized    -7 STK {} @ 150.00 USD
  Assets:Broker:Cash   1050.00 USD
  Income:Gains

Eso registra $210.00 de ganancia contra el lote de $120.00. El archivo idéntico con "STRICT" en la línea open falla con Ambiguous matches for "-7 STK {}".

7. PROMEDIO (aceptado, pero no implementado)​

AVERAGE es un nombre válido — tanto option "booking_method" "AVERAGE" como open … "AVERAGE" se analizan sintácticamente — pero Beancount 3.2.3 no tiene ninguna implementación detrás. Todo aquí se carga hasta la venta:

1970-01-01 commodity STK
1970-01-01 open Assets:Broker:Avg   STK  "AVERAGE"
1970-01-01 open Assets:Broker:Cash  USD
1970-01-01 open Income:Gains        USD
 
2024-01-10 * "Buy 10 shares at $10.00"
  Assets:Broker:Avg      10 STK {10.00 USD}
  Assets:Broker:Cash  -100.00 USD
 
2024-02-10 * "Buy 10 more at $8.00"
  Assets:Broker:Avg      10 STK {8.00 USD}
  Assets:Broker:Cash   -80.00 USD
 
; An average-cost engine would book this at $9.00 per share. This one refuses.
2024-06-01 * "Sell 5 shares"
  Assets:Broker:Avg      -5 STK {}
  Assets:Broker:Cash    45.00 USD
  Income:Gains

En el momento en que esa reducción tiene que asignarse, el cargador se detiene con:

AVERAGE method is not supported

No planifiques un libro contable en torno a él. Si quieres un comportamiento de costo promedio hoy, mantén la posición en una cuenta NONE y calcula el promedio tú mismo, o rastrea cada lote y acepta ganancias a nivel de lote.

8. NINGUNO​

El método NONE desactiva por completo la coincidencia de lotes.

2024-01-01 open Assets:Invest:STOCK "NONE"
  • Sin coincidencia de lotes: Beancount no intenta hacer coincidir reducciones con aumentos.
  • Permite signos mixtos: Esto permite que una cuenta mantenga saldos tanto positivos como negativos del mismo commodity simultáneamente. Este comportamiento es similar a cómo la herramienta de CLI Ledger maneja los commodities.

Especificación de Lotes​

Un "lote" es un bloque específico de un commodity adquirido en un momento y precio particulares. Cuando creas o reduces una posición, puedes especificar sus atributos de lote en detalle.

Especificación Completa​

Al aumentar un inventario (comprar), puedes especificar hasta tres atributos para el lote, separados por comas dentro de un único par de llaves:

Assets:Invest:STOCK  10 STOCK {100.00 USD, 2024-01-15, "lot-identifier"}
  • 100.00 USD — la base de costo, expresada por unidad.
  • 2024-01-15 — la fecha de adquisición. Beancount la completa a partir de la fecha de la transacción cuando la omites, que es por lo que los mensajes de error anteriores muestran una fecha en cada lote.
  • "lot-identifier" — una etiqueta de cadena opcional.

Aunque los tres son opcionales, proporcionar al menos la base de costo es una práctica estándar. Las llaves deben permanecer en una sola línea, y los comentarios dentro de un libro contable comienzan con ;, nunca con #.

Métodos de Coincidencia​

Al reducir un inventario (vender), usas la misma sintaxis para especificar de qué lote(s) vender.

  • Coincidir por costo: Este es el método más común.

    Assets:Invest:STOCK  -5 STOCK {100.00 USD}
  • Coincidir por fecha: Si los costos son idénticos, puedes desambiguar usando la fecha de adquisición.

    Assets:Invest:STOCK  -5 STOCK {2024-01-15}
  • Coincidir por etiqueta: Las etiquetas proporcionan una forma infalible de identificar un lote.

    Assets:Invest:STOCK  -5 STOCK {"lot-identifier"}
  • Dejar el lote al método de asignación: Un conjunto vacío de llaves {} no nombra ningún lote, así que el método de asignación de la cuenta elige. Bajo FIFO, LIFO o HIFO ese es el lote coincidente más antiguo, más reciente o más caro; bajo el STRICT predeterminado es un AmbiguousMatchError a menos que la reducción vacíe exactamente los lotes coincidentes.

    Assets:Invest:STOCK  -5 STOCK {}

Manejo de Precios​

Es crucial entender la diferencia entre base de costo ({}) y precio (@). Sirven a propósitos diferentes y no son intercambiables.

Precio vs Costo​

  • {cost}: Define el costo de adquisición de un activo. Es parte del propio lote de inventario y se usa para asignar reducciones y calcular ganancias de capital.
  • @ price: Una anotación que registra un precio de mercado en el momento de una transacción. Se usa para conversiones de divisas o para anotar el valor de mercado en una fecha particular.

Aquí están los tres escenarios:

  1. Anotación de precio (conversión): Usa @ para convertir de una divisa a otra.

    Assets:Forex     1000 USD @ 0.85 EUR
  2. Base de costo (adquisición): Usa {} al comprar un activo para establecer su costo.

    Assets:Invest    10 STOCK {100.00 USD}
  3. Ambos (venta con registro de precio): Al vender un activo, usa {} para identificar el lote que se vende y @ para registrar el precio de venta. Esto permite el cálculo automatizado de ganancias de capital.

    Assets:Invest    -10 STOCK {100.00 USD} @ 105.00 USD

    Este asiento vende 10 STOCK del lote que costó $100.00 cada uno, a un precio de venta de $105.00 cada uno.

Una directiva price independiente proporciona datos de referencia para la valoración de mercado. Precios en vivo puede mantener estas directivas para activos compatibles en libros contables alojados. Una actualización deja tus lotes, método de asignación, costos de adquisición e ingresos de venta registrados sin cambios.

Reglas de Uso del Precio​

  1. Las anotaciones de precio (@) no afectan qué lote se asigna. La coincidencia de lotes se maneja exclusivamente por la base de costo ({}) y el método de asignación de la cuenta.
  2. El símbolo @ se usa solo para:
  • Conversiones de divisas.
  • Registrar el valor de mercado de un activo en el momento de una transacción.
  • Proporcionar el precio de venta para los cálculos de ganancias de capital.

Configuración​

Puedes configurar los métodos de asignación globalmente o por cuenta.

Método de Registro Global​

Puedes establecer un método de asignación predeterminado para todo tu archivo de Beancount usando la directiva option.

option "booking_method" "STRICT"

Los valores aceptados son "STRICT" (el predeterminado cuando no estableces nada), "STRICT_WITH_SIZE", "NONE", "FIFO", "LIFO", "HIFO" y "AVERAGE". Cualquier otra cadena se rechaza en el momento de la carga con Error for option 'booking_method'. "AVERAGE" se acepta aquí y en open, pero asignar una reducción bajo él falla, como muestra la sección AVERAGE anterior.

Anulación Por Cuenta​

A menudo es útil tener métodos diferentes para cuentas diferentes. Por ejemplo, podrías querer FIFO para una cuenta de jubilación pero STRICT para una cuenta de corretaje gravable para asegurarte de vender lotes fiscales específicos. Puedes establecer el método de asignación cuando abres la cuenta.

2024-01-01 open Assets:Retirement:401K "FIFO"
2024-01-01 open Assets:Taxable:Stock  "STRICT"

Mejores Prácticas​

  1. Organización del inventario: Para mantener tu libro contable limpio y simple, se recomienda encarecidamente usar cuentas separadas para cada commodity único que poseas, y restringir cada una a ese commodity en su directiva open.

    ; GOOD: separate accounts by commodity, each constrained to one
    2024-01-01 open Assets:Invest:VTSAX  VTSAX
    2024-01-01 open Assets:Invest:VFIAX  VFIAX

    Evita mezclar diferentes acciones o fondos en la misma cuenta, ya que complica la gestión del inventario. La lista de commodities en open hace que Beancount rechace un asiento errante en lugar de mezclar silenciosamente dos inventarios.

  2. Gestión de lotes:

  • Usa etiquetas significativas para los lotes, especialmente para transacciones específicas como recolección de pérdidas fiscales o concesiones de acciones de empleados.

    Assets:Invest:STOCK  10 STOCK {100.00 USD, "tax-loss-harvest-2024"}
  • Documenta tus operaciones con comentarios. Esto hace que tu libro contable sea más fácil de leer y entender después.

    Assets:Invest:STOCK  -10 STOCK {100.00 USD} @ 110.00 USD ; Gain: 10%
  1. Depuración: Si encuentras errores o comportamiento inesperado, Beancount proporciona herramientas para inspeccionar el estado de tu inventario.
  • Examinar el estado del inventario: Usa bea doctor context main.beancount 42 para inspeccionar la transacción en la línea 42, incluidos sus asientos y los saldos de cuenta afectados. Reemplaza el nombre del archivo y el número de línea con la transacción que quieres inspeccionar.

    Reemplaza <LINENO> con el número de línea justo después de una transacción para ver su efecto.

  • Verificar la coincidencia de lotes: La herramienta bea check valida todo tu archivo. Detendrá cualquier error de asignación, como coincidencias de lote ambiguas en modo STRICT.

Fuente: https://beancount.io/es/docs/Basics/inventories