Referencia técnica completa del pipeline de valuación de activos financieros
El sistema procesa archivos Excel de la sociedad de bolsa Balanz junto con archivos de tipo de cambio (TC) para clasificar movimientos bursátiles, asignar tipos de cambio correspondientes y ejecutar la valuación FIFO (First In, First Out) — conocida localmente como PEPS (Primero Entrado, Primero Salido) — para la determinación de resultados gravados.
Los archivos se suben via FormData desde el cliente. El flujo es:
Cliente (FormData)
→ API Routes (app/api/*) — Validación, manejo de errores HTTP
→ Server Actions (app/actions/*) — Lógica de negocio ("use server")
→ Engine Functions (lib/activos/) — Funciones puras de procesamientoEl frontend nunca llama directamente a las server actions; siempre pasa por las API routes.
Cuando el usuario sube los archivos y presiona "CLASIFICAR", se ejecutan los siguientes pasos en orden:
Lee el archivo xlsx de Balanz. Busca la fila de encabezado escaneando hasta encontrar la celda que contiene IMPORTE. A partir de ahí extrae las siguientes columnas:
| Campo | Origen |
|---|---|
| Descripcion | Columna A |
| Ticker | Columna B |
| TipoInstrumento | Columna C |
| Fecha | Columna D (Concertación) |
| Cantidad | Columna E |
| Precio | Columna F |
| Liquidacion | Columna G |
| Moneda | Columna H |
| Importe | Columna I |
Adicionalmente extrae el número de boleto del texto de la descripción y determina la fuente (ARGENTINA o EXTRANJERA) según el tipo de instrumento.
Aplica 21+ reglas secuenciales basadas en palabras clave del campo "Detalle" (descripción). La última regla que matchea gana (no la primera). Las reglas son:
| Regla | Condición | Clasificación |
|---|---|---|
| R0 | Contiene "ANULADO" | Se omite (skip) |
| R1 | "COMPRA" + precio > 0 | COMPRA |
| R1 | "COMPRA" + precio < 0 | GASTOS |
| R1 | "Acreditación" + "ratio" | CAMBIO RATIO |
| R1 | "Distrib" + "acciones" | COMPRA |
| R2 | "VENTA" + precio > 0 | VENTA |
| R2 | "VENTA" + precio < 0 | GASTOS |
| R3 | "Recibo de Cobro" | TRANSFERENCIA RECIBIDA |
| R4 | "Comprobante de Pago" | TRANSFERENCIA REALIZADA |
| R5 | "Renta" + monto > 0 | RENTA |
| R6 | "Movimiento Manual" | MOVIMIENTO MANUAL |
| R8 | "Cargo por Descubierto" | GASTOS |
| R9 | "Dividendo en efectivo" + monto > 0 (Cedears) | DIVIDENDOS GRAVADOS |
| R9 | "Dividendo en efectivo" + monto > 0 (argentinas) | DIVIDENDOS GRAVADOS AL 7% |
| R9 | "Dividendo en efectivo" + monto < 0 | GASTOS |
| R10 | "APCOLFUT" + monto > 0 | VENCIMIENTO CAUCIÓN |
| R11 | "APCOLCON" + monto < 0 | COLOCACIÓN CAUCIÓN |
| R12 | "Garant" | GARANTÍA |
| R13 | "Div" + "especie" | DIVIDENDOS EN ESPECIE |
| R14 | "Interes" / "Amortizaci" + monto > 0 | INTERESES |
| R14 | "Interes" / "Amortizaci" / "Renta" + monto < 0 | GASTOS |
| R15 | "Transfer" + "Externa" | TRANSF. EXTERNA |
| R16 | "Devoluci" | DEVOLUCIÓN |
| R17 | "Liquidación" (no Futuro) según cantidad | COMPRA / VENTA |
| R7 | "Liquidación de Futuro" (overrides R17) | LIQUIDACIÓN FUTUROS |
| R18 | "Opera" + "Diferida" según cantidad | COMPRA / VENTA |
| R18b | "Suscrip" | COMPRA |
| R18b | "Rescate" | VENTA |
| R19 | "Interes" / "Amortizaci" + Bonos + fuente ARG | INTERESES BONOS ARG |
| R19 | "Interes" / "Amortizaci" + Bonos + fuente EXT | INTERESES BONOS EXT |
| R20 | "Ret" (no Renta ni Rescate) | RETENCIÓN GANANCIAS |
| R21 | "Convers" + combinaciones de moneda | CONVERSIÓN A DOLAR MEP / CV |
| — | Sin coincidencia | A CLASIFICAR |
Post-procesamiento: los movimientos cuyo ticker sea "Dólares ROFEX" se reclasifican como tipo Futuros. Los movimientos de Futuros con clasificación FICOMPRA o FIVENTA se reclasifican como GASTOS.
Lee el archivo xlsx de tipo de cambio. Extrae las columnas:
Codigo Moneda — Identificador de la monedaFecha — Fecha de cotizaciónCompra — Tipo de cambio compradorVenta — Tipo de cambio vendedorFiltra filas de encabezado duplicadas o vacías.
Para cada movimiento, busca el tipo de cambio por fecha y moneda. Si no encuentra la fecha exacta, retrocede al día hábil anterior más cercano. Los movimientos en pesos siempre tienen TC = 1. Asigna tanto tcc (tipo cambio compra) como tcv (tipo cambio venta).
montoPesos = monto × tcc
Convierte el importe original a pesos argentinos usando el tipo de cambio comprador.
Separa los movimientos en 4 arrays según la moneda:
| Array | Moneda | Código |
|---|---|---|
| Pesos | Pesos argentinos | ARS |
| USD | Dólares estadounidenses | USD |
| Cable | Dólares C.V. 7000 | D.CV 7000 |
| Futuros | Dólares ROFEX | ROFEX |
Para cada array calcula el saldo corrido (running balance) acumulando los importes en orden cronológico.
Incluye todos los pasos del Clasificador (1–6) y agrega los siguientes:
Antes de procesar, el usuario configura los parámetros globales del período: Banco, Período, Moneda, CUIT, Razón Social y Nro. Cuenta. Después de procesar, este tab muestra un resumen global de transacciones por clasificación.
Tabla editable para registrar transferencias de títulos entre cuentas propias o de terceros. Campos: Nombre, Código, Tipo Activo, Fecha, Cantidad, Monto, TC, Es Repago.
El motor cruza las transferencias con DETALLE_MOV por código + fecha + cantidad para asignar montos y TC cuando la operación original solo muestra cantidades sin precio. Actualmente solo se muestra la tabla editable — el procesamiento automático de transferencias se implementará en una versión futura.
Consolida los movimientos relevantes para la valuación. Solo incluye las clasificaciones que afectan el stock de activos:
Ordena por:
código ASC (alfabético)fecha ASC (cronológico)Asigna un Nro.Ref secuencial por cada código de activo único.
Ejecuta validaciones sobre los movimientos consolidados:
Genera un array de errores que se muestra al usuario antes de continuar.
Esta es la función central del sistema. Para cada activo (agrupado por Nro.Ref) ejecuta el motor FIFO bidireccional.
| Tipo | Cantidad | Monto | Lógica |
|---|---|---|---|
| COMPRA / SALDO INICIAL | +abs | -abs | "Pagué plata" |
| VENTA | -abs | +abs | "Recibí plata" |
| DIVIDENDOS EN ESPECIE | +abs | 0 | "Recibí acciones gratis" |
| TRANSF. EXTERNA | signo original | signo original | Según dirección de la transferencia |
| CAMBIO RATIO | Resetea todo el stock a un solo lote con nuevo costo promedio | ||
El motor mantiene dos pilas de lotes: buyLayers (compras pendientes de consumir) y sellLayers (ventas en corto / stock negativo).
ENTRADA (compra):
sellLayers pendientes (ventas en corto previas), la compra las cierra primero (FIFO inverso).buyLayers como un lote nuevo.cu = |monto| / |cantidad|SALIDA (venta):
buyLayers del más viejo al más nuevo (FIFO puro).compraPesos = take × |cuCompra| × tcCompra ventaPesos = take × cuVenta × tcVenta resultado = ventaPesos - compraPesos
sellLayer (posición corta / stock negativo).rdo de todas las operaciones del mismo RefPara cada código de activo, suma desde DETALLE_MOVIMIENTOS los conceptos complementarios:
| Concepto | Clasificaciones incluidas |
|---|---|
| Gastos | Movimientos clasificados como GASTOS con código coincidente |
| Renta | Movimientos clasificados como RENTA |
| Dividendos | Movimientos con "DIVIDENDO" (no EN ESPECIE) |
| Intereses | Movimientos con "INTERES" |
La fórmula aplicada para cada concepto es: montoPesos = monto × tcc, acumulado por código.
Tabla completa de todas las clasificaciones posibles y su impacto en el sistema:
| Clasificación | Descripción | PEPS | Cash |
|---|---|---|---|
| COMPRA | Adquisición de activos financieros | Sí | Sí |
| VENTA | Enajenación de activos financieros | Sí | Sí |
| AMORTIZACION | Repago parcial de capital de bonos/ONs | Sí | Sí |
| REPAGO CAPITAL | Devolución de capital invertido (similar a amortización) | Sí | Sí |
| SALDO INICIAL | Posición de activos al inicio del período (cargada manualmente) | Sí | No |
| DIVIDENDOS | Dividendos en efectivo (genérico) | No | Sí |
| DIVIDENDOS GRAVADOS | Dividendos gravados de Cedears (fuente extranjera) | No | Sí |
| DIVIDENDOS GRAVADOS AL 7% | Dividendos de acciones argentinas (alícuota 7%) | No | Sí |
| DIVIDENDOS EN ESPECIE | Dividendos pagados en acciones (no efectivo) | Sí | No |
| CAMBIO RATIO | Cambio de ratio de conversión (ej. split de Cedear) | Sí | No |
| INTERESES | Cobro de intereses (genérico) | No | Sí |
| INTERESES BONOS ARG | Intereses/amortización de bonos fuente argentina | No | Sí |
| INTERESES BONOS EXT | Intereses/amortización de bonos fuente extranjera | No | Sí |
| GASTOS | Comisiones, derechos de mercado, impuestos operativos | No | Sí |
| RETENCION TAX | Retención impositiva genérica | No | Sí |
| RETENCIÓN GANANCIAS | Retención del impuesto a las ganancias | No | Sí |
| TRANSF. EXTERNA | Transferencia de títulos desde/hacia otro broker | Sí | No |
| TRANSFERENCIA RECIBIDA | Ingreso de fondos (recibo de cobro) | No | Sí |
| TRANSFERENCIA REALIZADA | Egreso de fondos (comprobante de pago) | No | Sí |
| CONSUMO | Consumos con tarjeta asociada a la cuenta | No | Sí |
| COLOCACIÓN CAUCIÓN | Colocación de caución bursátil (préstamo) | No | Sí |
| VENCIMIENTO CAUCIÓN | Vencimiento/cobro de caución bursátil | No | Sí |
| RENTA | Cobro de renta de títulos (cupón) | No | Sí |
| LIQUIDACIÓN FUTUROS | Liquidación de contratos de futuros | No | Sí |
| CONVERSIÓN A DOLAR MEP | Conversión de moneda vía operatoria MEP | No | Sí |
| CONVERSIÓN A DOLAR CV | Conversión de moneda vía cable (contado con liquidación) | No | Sí |
| GARANTÍA | Constitución/liberación de garantías | No | Sí |
| DEVOLUCIÓN | Devolución de montos cobrados/debitados por error | No | Sí |
| MOVIMIENTO MANUAL | Ajuste manual ingresado por el broker | No | Sí |
| ANULACIÓN | Movimiento anulado (se descarta del procesamiento) | No | No |
| A CLASIFICAR | Ninguna regla coincidió — requiere clasificación manual | No | Sí |
Extracto de cuenta exportado desde la plataforma de Balanz. El sistema busca automáticamente la fila de encabezado.
| Columna | Campo | Tipo |
|---|---|---|
| A | Descripcion | string |
| B | Ticker | string |
| C | TipoInstrumento | string |
| D | Concertación (Fecha) | date |
| E | Cantidad | number |
| F | Precio | number |
| G | Liquidación | date |
| H | Moneda | string |
| I | Importe | number |
Archivo con cotizaciones históricas de monedas extranjeras.
| Columna | Campo | Tipo |
|---|---|---|
| B | Codigo Moneda | string |
| C | Fecha | date |
| D | Compra | number |
| E | Venta | number |
Posición de activos al inicio del período fiscal, ingresada manualmente por el usuario. Se incorporan al pipeline como SALDO INICIAL.
| Campo | Descripción | Tipo |
|---|---|---|
| TipoInstrumento | Tipo del instrumento (Acción, Cedear, Bono, etc.) | string |
| Código | Ticker/código del activo | string |
| Fecha | Fecha de valuación del saldo inicial | date |
| Cantidad | Cantidad de unidades en cartera | number |
| Monto | Costo total de la posición en moneda original | number |
| TC | Tipo de cambio al momento de la valuación | number |
El sistema genera múltiples hojas/tabs de resultados. Cada una representa una vista distinta de los datos procesados:
DETALLE_MOVIMIENTOS (G-1)
Todos los movimientos clasificados del extracto Balanz con tipo de cambio asignado, monto en pesos y clasificación. Es la base de datos completa del período.
DETALLE PESOS / USD / CABLE / FUTUROS
Movimientos separados por moneda con saldo corrido. Cada hoja muestra únicamente los movimientos de esa moneda con columnas de saldo y saldoPesos acumulados.
ACTIVOS_TOTAL
Movimientos consolidados que afectan la valuación PEPS (COMPRA, VENTA, AMORTIZACION, TRANSF. EXTERNA, CAMBIO RATIO, DIVIDENDOS EN ESPECIE, SALDO INICIAL). Ordenados por código y fecha con Nro.Ref asignado.
VALUACION_ACTIVOS (G-2)
Resultado del matching FIFO. 21 columnas: Ref, Tipo Op, NroOp, Nombre, Código, Tipo Activo, Fecha, Cant, Monto USD, TC Compra, CU Compra, TCV Venta, CU Venta, TCC Venta, Compra $, Venta $, Rdo (V/C), Dif Cambio, Rdo USD, Val Op USD, Control.
VALUACION_REPAGO_CAPITAL (G-2-1)
FIFO separado para operaciones de Repago de Capital (amortización programada de bonos). Misma estructura que G-2. Placeholder — se implementará cuando se agregue la lógica de separación por tipo.
VALUACION_DERIVADOS (G-2-2)
Valuación FIFO de derivados (opciones put/call). Soporta stock negativo (short selling). Solo Morgan Stanley por ahora. Placeholder.
VALUACION_EJERCIDAS (G-2-3)
Derivados que fueron ejercidos (opciones ejecutadas). Solapas segregadas. Placeholder.
RESULTADO_ACTIVOS (G-3)
Una fila por activo con 18 columnas: Ref, Nombre, Código, Cant, Costo USD, Valor IAG ARS, Mkt Value 31/12, Val. IBP $, Tipo Activo, Fuente, Intereses, Dividendos, Rdo Vta ARS, Dif. Cambio, Int+Div, RENDIMIENTO, RESULTADO C/V, BS.PERS. Columnas de tratamiento impositivo (GRAVADO 7%, CEDULAR 15%, ESCALA, EXENTO, CDI, BIENES PERSONALES) se agregarán en versión futura.
RESULTADO_DERIVADOS (G-3)
Resultado por derivado con tratamiento impositivo. Misma estructura que RESULTADO_ACTIVOS pero para categoría DERIV. Placeholder.
STOCK_FINAL_ACTIVOS (G-4)
Lotes remanentes al cierre del período. 12 columnas incluyendo flag Es Repago Capital. Muestra buyLayers con cantidad positiva y sellLayers con cantidad negativa.
STOCK_FINAL_DERIVADOS (G-4)
Stock de derivados al cierre. Misma estructura que STOCK_FINAL_ACTIVOS pero para categoría DERIV. Placeholder.
IPC
Tabla IPC Nacional mensual para ajuste de costo de ADRs argentinas. Se usará para calcular coeficiente = IPC_venta / IPC_compra y costo actualizado. Placeholder.
Todas las fórmulas utilizadas en el pipeline de valuación:
Conversión a pesos:
montoPesos = monto × tcc
Costo unitario de un lote:
costoUnitario = |monto| / |cantidad|
Costo de compra en pesos (al consumir un lote FIFO):
compraPesos = cantidadTomada × |costoUnitCompra| × tcCompra
Ingreso de venta en pesos:
ventaPesos = cantidadTomada × costoUnitVenta × tcVenta
Resultado de la operación:
resultado = ventaPesos - compraPesos
Saldo corrido en moneda original:
saldo = saldoAnterior + monto
Saldo corrido en pesos:
saldoPesos = saldoPesosAnterior + montoPesos
Errores frecuentes durante el procesamiento y cómo resolverlos:
| Error | Causa | Resolución |
|---|---|---|
| Falta Código Activo | Un movimiento de COMPRA o VENTA no tiene ticker/código de activo asignado en el extracto de Balanz. | Verificar el extracto original. Si el movimiento es válido, asignar manualmente el código del activo antes de reprocesar. |
| Stock Negativo | Se vendieron más unidades de las que había en stock. El motor crea un sellLayer (posición corta) para la cantidad excedente. | Verificar si falta un SALDO INICIAL o una COMPRA previa. Si es correcto (venta en corto real), el sellLayer se resolverá cuando ingrese una compra posterior. |
| A CLASIFICAR | Ninguna regla de clasificación coincidió con la descripción del movimiento. | Revisar la descripción del movimiento y clasificarlo manualmente. Si es un patrón nuevo recurrente, agregar una regla al clasificador. |
| TC = 0 | No se encontró tipo de cambio para la fecha y moneda del movimiento en el archivo TC, ni para fechas anteriores cercanas. | Verificar que el archivo de tipo de cambio cubra el rango de fechas del extracto. Agregar las cotizaciones faltantes y reprocesar. |
| Cantidad = 0 | Un movimiento de COMPRA o VENTA tiene cantidad cero, lo cual invalida el cálculo del costo unitario (división por cero). | Revisar el extracto original. Si el movimiento es un gasto o comisión, reclasificarlo como GASTOS. Si es un error de datos, corregir la cantidad. |
Ejemplo concreto de valuación FIFO para el activo AAPL (Cedear de Apple) con tres operaciones:
Compra 1: 100 AAPL a USD 150 (01/03, TC = 900) Compra 2: 50 AAPL a USD 160 (15/05, TC = 950) Venta: 120 AAPL a USD 180 (01/09, TC = 1.000)
| Lote | Cantidad | CU (USD) | TC |
|---|---|---|---|
| Compra 1 | 100 | 150,00 | 900 |
| Compra 2 | 50 | 160,00 | 950 |
Paso 1 — Consumir Compra 1 (100 unidades)
take = 100 (se agota el lote completo) compraPesos = 100 × 150 × 900 = 13.500.000 ventaPesos = 100 × 180 × 1.000 = 18.000.000 resultado = 18.000.000 - 13.500.000 = +4.500.000
Quedan 120 - 100 = 20 unidades por consumir. Compra 1 se agota.
Paso 2 — Consumir Compra 2 (20 de 50 unidades)
take = 20 (consumo parcial del lote) compraPesos = 20 × 160 × 950 = 3.040.000 ventaPesos = 20 × 180 × 1.000 = 3.600.000 resultado = 3.600.000 - 3.040.000 = +560.000
Se consumieron las 120 unidades. Compra 2 queda con 30 unidades remanentes.
| Lote | Cantidad | CU (USD) | TC |
|---|---|---|---|
| Compra 2 (remanente) | 30 | 160,00 | 950 |
Resultado Compra 1 → Venta: +4.500.000
Resultado Compra 2 → Venta: +560.000
──────────
Resultado Total AAPL: +5.060.000El resultado total de +5.060.000 ARS representa la ganancia gravada por la enajenación de 120 Cedears de AAPL, calculada respetando el orden FIFO de adquisición y convirtiendo tanto el costo de compra como el ingreso de venta al tipo de cambio vigente en cada momento.
Las siguientes funcionalidades están planificadas pero aún no implementadas. Los tabs correspondientes aparecen como placeholders en el Motor PEPS:
IPC_venta / IPC_compra para actualizar el costo histórico de activos de fuente ARGENTINA.sellLayers para stock negativo. Hojas segregadas: VALUACION_DERIVADOS (G-2-2), VALUACION_EJERCIDAS (G-2-3), RESULTADO_DERIVADOS (G-3), STOCK_FINAL_DERIVADOS (G-4).