Saltar al contenido principal

Gestión de Inventario

Aprenda a gestionar eficazmente el inventario en Beancount, centrándose en el seguimiento de activos como acciones y divisas, comprendiendo la base de costo y calculando las ganancias de capital para un mejor rendimiento de la cartera.

El sistema de inventario de Beancount es una función poderosa para rastrear activos que se compran y venden con el tiempo, como acciones, fondos mutuos o monedas extranjeras. Permite un seguimiento preciso del costo base, que es esencial para calcular las ganancias de capital y entender el rendimiento de la cartera. Este tutorial cubre los conceptos fundamentales de la gestión de inventarios en su libro mayor.

Conceptos Básicos

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

Tipos de Posición

  1. Posición Simple (Sin Costo): Esta es una contabilidad estándar de saldo. Representa una cantidad de una mercancía sin ningún costo de adquisición asociado. Es adecuada para efectivo o afirmaciones de saldo simples.

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

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

    En este ejemplo, poseemos 10 unidades de VTSAX. Cada unidad fue adquirida 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 puede realizar sobre un inventario:

  1. Aumentos (Agregar al inventario): Cuando compra una mercancía, aumenta su inventario. Crea un nuevo lote con un número específico de unidades y un costo base.

    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 (Eliminar del inventario): Cuando vende una mercancía, reduce su inventario. Debe especificar de qué lote está vendiendo. Esto se hace proporcionando información coincidente dentro de 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, estamos vendiendo 25 unidades de STOCK del lote que fue comprado a $25.00 USD por unidad.

Métodos de Registro

Cuando reduce un inventario, Beancount necesita una regla para decidir de qué lote específico retirar si varios lotes coinciden con la reducción. Esta regla se llama "método de registro." Puede establecer un valor predeterminado para todo el archivo con una opción o asignar un método propio a una cuenta mediante la directiva open.

Beancount 3.2.3 acepta siete nombres de método: STRICT (el predeterminado), STRICT_WITH_SIZE, NONE, FIFO, LIFO, HIFO y AVERAGE. Seis de ellos están implementados; AVERAGE analiza pero genera un error en el momento en que tiene que registrar 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 registro más seguro. Obliga a un emparejamiento explícito e inequívoco.

2024-01-01 open Assets:Invest:STOCK "STRICT"
  • Requiere coincidencia exacta de lote: El especificador de costo de la contabilización de reducción ({...}) debe identificar un solo lote, ya sea por costo, fecha de adquisición, etiqueta o 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 que el especificador coincide, se permite un especificador vacío ({}) y la reducción se divide entre esos lotes.

Este libro mayor contiene dos lotes y vende uno de ellos nombrando su costo, lo cual es inequívoco:

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 esa última contabilización 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 lista a los candidatos. Sin embargo, vender la posición completa está bien porque no queda nada para 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 del propio STRICT, no algo para lo que debas 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 que coinciden más antiguos.
  • Coincidencia cronológica: Se asume que vendes los activos que has tenido por más tiempo. Varias autoridades fiscales consideran esto como el predeterminado cuando no identificas un lote.

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

El método LIFO es el opuesto de 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 las partidas coincidentes adquiridas más recientemente.
  • El más nuevo, no el más caro: LIFO elige por fecha de adquisición solamente. Suele vender las acciones de mayor coste cuando los precios han estado subiendo, pero si la partida más nueva es la más barata — que es lo que muestra el ejemplo a continuación — LIFO realizará la mayor ganancia, no la menor. 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 anota reducciones contra las partidas más caras disponibles primero, sea cual sea su fecha.

2024-01-01 open Assets:Invest:STOCK "HIFO"
  • Coincidencia clasificada por coste: Selecciona las partidas coincidentes con el coste base más alto.
  • Ganancia realizada más pequeña: Para un precio de venta dado, vender las acciones de mayor coste realiza la ganancia más baja (o la pérdida mayor). Su uso depende de la jurisdicción: en Estados Unidos, por ejemplo, elegir una partida requiere identificación específica en el momento de la venta — así que trate este método como un mecanismo contable y confirme la elección fiscal por separado.

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

Los tres métodos solo difieren cuando la partida más antigua, la más nueva y la más cara son tres partidas diferentes. Este libro arregla exactamente eso — la partida A es la más antigua, la partida C es la más nueva, y la partida intermedia B es la más cara — y luego vende 10 acciones de tres cuentas que solo difieren en su método de contabilizació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 sin errores y registra una ganancia total de $1,400.00, dividida así:

CuentaMétodoPartida anotadaCoste baseGanancia realizadaPartidas restantes
Assets:Broker:FifoFIFOpartida A, 2024-01-10$100.00$500.0010 @ $120.00, 10 @ $90.00
Assets:Broker:LifoLIFOpartida C, 2024-03-10$90.00$600.0010 @ $100.00, 10 @ $120.00
Assets:Broker:HifoHIFOpartida B, 2024-02-10$120.00$300.0010 @ $100.00, 10 @ $90.00

La fila LIFO es la que merece atención: realizó la mayor ganancia de las tres, porque la partida más nueva también era la más barata.

6. STRICT_WITH_SIZE

STRICT_WITH_SIZE es STRICT más un criterio adicional para desempatar: cuando varias partidas coinciden pero exactamente una tiene la cantidad precisa de unidades que está retirando, esa partida es la elegida.

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 — option "booking_method" "AVERAGE" y open … "AVERAGE" ambos se analizan — pero Beancount 3.2.3 no tiene 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 debe registrarse, el cargador se detiene con:

AVERAGE method is not supported

No planifiques un libro mayor alrededor de esto. Si quieres 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 completamente 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 tenga saldos positivos y negativos de la misma mercancía simultáneamente. Este comportamiento es similar a cómo la herramienta Ledger CLI maneja las mercancías.

Especificación de Lotes

Un "lote" es un bloque específico de una mercancía adquirido en un momento y precio particular. 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 solo 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 si la omites, por eso los mensajes de error arriba 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 práctica estándar. Las llaves deben permanecer en una línea, y los comentarios dentro de un ledger comienzan con ;, nunca #.

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 registro: Un conjunto vacío de llaves {} no nombra ningún lote, así que el método de registro de la cuenta elige. Bajo FIFO, LIFO o HIFO eso es el lote más antiguo, más nuevo o más caro que coincida; 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 cost basis ({}) y price (@). Sirven para propósitos diferentes y no son intercambiables.

Precio vs Costo

  • {cost}: Define el costo de adquisición de un activo. Es parte del lote de inventario en sí y se usa para registrar 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 moneda o para anotar el valor de mercado en una fecha concreta.

Aquí están los tres escenarios:

  1. Anotación de Precio (Conversión): Use @ para convertir de una moneda a otra.

    Assets:Forex     1000 USD @ 0.85 EUR
  2. Cost Basis (Adquisición): Use {} 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, use {} para identificar el lote que se vende y @ para registrar el precio de venta. Esto permite calcular automáticamente las ganancias de capital.

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

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

Reglas de Uso del Precio

  1. Las anotaciones de precio (@) no afectan cuál lote se registra. El emparejamiento de lotes se maneja exclusivamente mediante el cost basis ({}) y el método de registro de la cuenta.
  2. El símbolo @ se usa solo para:
  • Conversiones de moneda.
  • Registrar el valor de mercado de un activo en el momento de una transacción.
  • Proporcionar el precio de venta para cálculos de ganancias de capital.

Configuración

Puede configurar métodos de registro globalmente o por cuenta.

Método de Registro Global

Puede establecer un método de registro predeterminado para todo su archivo Beancount usando la directiva option.

option "booking_method" "STRICT"

Los valores aceptados son "STRICT" (el predeterminado si no establece nada), "STRICT_WITH_SIZE", "NONE", "FIFO", "LIFO", "HIFO" y "AVERAGE". Cualquier otra cadena es rechazada en tiempo de carga con Error for option 'booking_method'. "AVERAGE" se acepta aquí y en open, pero registrar una reducción bajo él falla, como la sección AVERAGE anterior demuestra.

Anulación Por Cuenta

A menudo es útil tener métodos diferentes para distintas cuentas. Por ejemplo, puede querer FIFO para una cuenta de retiro pero STRICT para una cuenta de corretaje sujeta a impuestos para asegurarse de que está vendiendo lotes fiscales específicos. Puede establecer el método de registro cuando abre la cuenta.

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

Mejores Prácticas

  1. Organización de Inventario: Para mantener su libro limpio y simple, se recomienda encarecidamente usar cuentas separadas para cada tipo de commodity que posea, y restringir cada una a esa 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

    Evite 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 una contabilización errante en lugar de mezclar silenciosamente dos inventarios.

  2. Gestión de Lotes:

  • Use etiquetas significativas para los lotes, especialmente para transacciones específicas como la cosecha de pérdidas fiscales o las concesiones de acciones a empleados.

    Assets:Invest:STOCK  10 STOCK {100.00 USD, "tax-loss-harvest-2024"}
  • Documente sus operaciones con comentarios. Esto facilita la lectura y comprensión del libro mayor más adelante.

    Assets:Invest:STOCK  -10 STOCK {100.00 USD} @ 110.00 USD ; Gain: 10%
  1. Depuración: Si encuentra errores o comportamientos inesperados, Beancount proporciona herramientas para inspeccionar el estado de su inventario.
  • Examinar Estado del Inventario: Use bea doctor context main.beancount 42 para inspeccionar la transacción en la línea 42, incluyendo sus asientos y los saldos de cuenta afectados. Reemplace el nombre del archivo y el número de línea con la transacción que desea inspeccionar.

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

  • Verificar Asociación de Lotes: La herramienta bea check valida todo su archivo. Detectará cualquier error de contabilización, como coincidencias ambiguas de lotes en modo STRICT.

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