35. Posiciones, efectivo y resultado realizado y no realizado

Después de registrar operaciones, el sistema debe explicar qué cantidad permanece, cuánto efectivo está disponible y qué parte del resultado proviene de ventas cerradas o de precios que todavía pueden cambiar.

35.1 Tres libros y una valuación

Una cartera no debería calcularse sobrescribiendo un saldo. Conviene conservar operaciones como hechos, derivar posiciones por instrumento, registrar efectivo por moneda y valuar con precios identificados.

OP

Operaciones

Compras, ventas, cargos, ingresos y movimientos.

POS

Posiciones

Cantidad abierta y costo asignado por instrumento.

EF

Efectivo

Saldos disponibles, pendientes y reservados por moneda.

VAL

Valuación

Precio, fuente, instante y resultado no realizado.

Idea central: el resultado realizado nace al cerrar exposición bajo una convención de costo; el no realizado depende de la posición abierta y del precio de valuación.

35.2 Identidades básicas

Para un instrumento simple sin multiplicador, la cantidad neta suma compras y resta ventas. El valor de mercado combina esa cantidad con un precio vigente.

cantidad neta = Σ compras − Σ ventas  ·  valor de mercado = cantidad neta × precio de valuaciónDerivados, bonos y fondos pueden requerir multiplicador, nominal, intereses o factores adicionales.

Estas identidades describen magnitudes económicas; no definen por sí solas reconocimiento contable, fiscal o regulatorio.

35.3 Cantidades con signo

Una convención útil usa cantidades positivas para compras y negativas para ventas. La suma entrega una posición larga positiva, corta negativa o cerrada en cero.

function cantidadFirmada({ side, quantity }) {
  if (!["BUY", "SELL"].includes(side) || !Number.isFinite(quantity) || quantity <= 0) {
    throw new RangeError("Lado o cantidad no válidos");
  }
  return side === "BUY" ? quantity : -quantity;
}

const operaciones = [
  { side: "BUY", quantity: 100 },
  { side: "BUY", quantity: 50 },
  { side: "SELL", quantity: 80 }
];
const posicion = operaciones.reduce((total, operacion) => total + cantidadFirmada(operacion), 0);
console.log("Posición neta:", posicion);

El signo no reemplaza el campo side en los eventos originales. Conservar ambos facilita validación y auditoría.

35.4 Lotes y base de costo

Cada compra puede originar un lote con cantidad, precio, comisión, fecha y moneda. Cuando se vende una parte, debe decidirse qué costo se libera.

La base de costo puede incorporar cargos atribuibles según la finalidad del cálculo. Conviene separar importe bruto, comisiones e impuestos para poder aplicar reglas diferentes.

ComponenteCompraVenta
Importe brutoPrecio × cantidad.Precio × cantidad.
CargosPueden aumentar el costo.Pueden reducir el producido.
EfectivoSalida de fondos.Entrada de fondos.
PosiciónAumenta cantidad.Reduce cantidad larga.

35.5 Métodos para asignar costo

FIFO libera primero los lotes más antiguos; identificación específica selecciona lotes concretos; costo promedio agrupa compras en un costo unitario. Otros métodos pueden existir según producto y jurisdicción.

La elección afecta el momento del resultado realizado y la base restante, aunque no cambia el flujo total de efectivo. Debe definirse antes del cálculo y mantenerse consistente.

No hay un método fiscal universal: el laboratorio usa costo promedio móvil únicamente con finalidad didáctica. Las obligaciones reales dependen de jurisdicción, cuenta e instrumento.

35.6 Costo promedio móvil

Mientras se aumenta una posición larga, el nuevo costo total suma la base anterior y el costo neto de la compra. El promedio surge al dividir por la nueva cantidad.

costo promedio nuevo = (costo abierto anterior + costo de la nueva compra) / cantidad nuevaEn este tema, el costo de compra incluye su comisión ilustrativa.
function agregarCompra(posicion, { quantity, price, fee = 0 }) {
  if ([quantity, price, fee].some(valor => !Number.isFinite(valor)) || quantity <= 0 || price <= 0 || fee < 0) {
    throw new RangeError("Compra no válida");
  }
  const costAdded = quantity * price + fee;
  const newQuantity = posicion.quantity + quantity;
  const openCost = posicion.openCost + costAdded;
  return { quantity: newQuantity, openCost, averageCost: openCost / newQuantity };
}

let posicion = { quantity: 100, openCost: 5010 };
console.log(agregarCompra(posicion, { quantity: 50, price: 60, fee: 10 }));

35.7 Vender y liberar costo

Una venta larga reduce cantidad y libera la base correspondiente. Bajo promedio móvil, el costo unitario existente no cambia por vender; se multiplica por la cantidad cerrada.

resultado realizado = producido neto de venta − costo liberadoproducido neto = precio × cantidad − cargos de venta
function reducirPosicionPromedio(posicion, { quantity, price, fee = 0 }) {
  if (quantity <= 0 || quantity > posicion.quantity || price <= 0 || fee < 0) {
    throw new RangeError("La venta no puede exceder la posición larga");
  }
  const averageCost = posicion.openCost / posicion.quantity;
  const releasedCost = averageCost * quantity;
  const netProceeds = price * quantity - fee;
  return {
    quantity: posicion.quantity - quantity,
    openCost: posicion.openCost - releasedCost,
    realizedPnl: posicion.realizedPnl + netProceeds - releasedCost
  };
}

console.log(reducirPosicionPromedio(
  { quantity: 150, openCost: 8020, realizedPnl: 0 },
  { quantity: 80, price: 70, fee: 10 }
));

35.8 Resultado realizado

El resultado se realiza cuando una operación cierra total o parcialmente una exposición según la convención del sistema. Una venta no implica automáticamente ganancia: debe compararse su producido con el costo liberado.

Dividendos, intereses, cupones, financiamiento e impuestos pueden presentarse en componentes separados. Mezclarlos dentro de la ganancia de compraventa dificulta explicar el resultado.

Si una posición se abre con 150 unidades y se venden 80, solo el costo de esas 80 participa del resultado realizado. Las 70 restantes conservan base abierta.

35.9 Resultado no realizado

El resultado no realizado compara el valor actual de la posición abierta con su base pendiente. Cambia cuando cambia la marca, aun sin nuevas operaciones.

resultado no realizado = valor de mercado − costo abiertoPara una posición larga simple: cantidad restante × marca − costo abierto.
function valuarPosicionLarga(posicion, markPrice) {
  if (!Number.isFinite(markPrice) || markPrice < 0 || posicion.quantity < 0) {
    throw new RangeError("Posición o marca no válida");
  }
  const marketValue = posicion.quantity * markPrice;
  return {
    marketValue,
    unrealizedPnl: marketValue - posicion.openCost,
    averageCost: posicion.quantity ? posicion.openCost / posicion.quantity : null
  };
}

console.log(valuarPosicionLarga({ quantity: 70, openCost: 3742.6666666667 }, 65));

35.10 Precio de valuación y convención de salida

Una posición larga podría valuarse al último precio, punto medio, bid o precio oficial de cierre. Cada elección responde a un propósito. Para estimar una salida inmediata, el bid puede ser más conservador que el medio.

La marca necesita fuente, instante, mercado y estado de calidad. Un resultado calculado con un precio viejo no debe mostrarse como actual.

MarcaVentajaLimitación
Última operaciónRepresenta un precio ejecutado.Puede ser antigua o de tamaño pequeño.
Punto medioNeutral entre puntas visibles.No es necesariamente ejecutable.
Bid para una posición largaAproxima primera salida inmediata.Puede no haber profundidad suficiente.
Cierre oficialFacilita consistencia de reportes.No representa condiciones intradía.

35.11 Reconciliar resultado total

Bajo un modelo simple y coherente, el resultado de compraventa se divide entre la parte cerrada y la variación de la posición abierta.

resultado total = resultado realizado + resultado no realizadoLa igualdad exige aplicar los mismos cargos, monedas, precios y eventos en ambos lados.

También puede controlarse el patrimonio: efectivo más valor de posiciones, comparado con capital inicial y flujos externos. Una diferencia señala operaciones, ingresos, cargos o conversiones omitidos.

35.12 Efectivo por fecha de operación y liquidación

La compra crea una obligación en la fecha de operación, pero el dinero puede transferirse en la fecha de liquidación. Mostrar un único saldo impide distinguir efectivo contable, disponible, reservado y pendiente.

EJECUCIÓNSe acuerda
OBLIGACIÓNSe registra
RESERVASe inmoviliza
LIQUIDACIÓNSe paga
CONCILIACIÓNSe confirma
DISPONIBLESe actualiza
function movimientosDeEfectivo(trade) {
  const gross = trade.quantity * trade.price;
  const amount = trade.side === "BUY" ? -(gross + trade.fee) : gross - trade.fee;
  return {
    tradeDateEntry: { date: trade.tradeDate, currency: trade.currency, amount, status: "PENDING" },
    settlementEntry: { date: trade.settlementDate, currency: trade.currency, amount, status: "SETTLED" }
  };
}

console.log(movimientosDeEfectivo({
  side: "BUY", quantity: 100, price: 50, fee: 10, currency: "ARS",
  tradeDate: "2026-09-13", settlementDate: "2026-09-15"
}));

35.13 Cargos, ingresos y flujos externos

Comisiones y otros cargos reducen patrimonio, pero su asignación entre costo y resultado depende de la finalidad. Dividendos y cupones son ingresos del instrumento; depósitos y retiros son flujos del propietario, no rendimiento.

Separación necesaria: una transferencia de $10.000 aumenta el efectivo, pero no es una ganancia de inversión. Guardar un tipo de movimiento evita inflar el resultado.

35.14 Posiciones cortas

Una venta que abre una posición corta recibe efectivo y crea la obligación de recomprar o entregar el activo. El resultado mejora si el precio baja y empeora si sube.

resultado no realizado corto = producido base abierto − costo actual de recompraDebe incluir préstamo, dividendos compensatorios, margen y otros cargos cuando correspondan.

El algoritmo de una posición larga no debe reutilizarse cambiando solo el signo. Abrir, aumentar, reducir y cruzar de largo a corto requieren reglas explícitas.

35.15 Carteras con varias monedas

Primero se calcula posición, costo y efectivo en la moneda local de cada componente. Luego se convierte a una moneda de informe con tipos de cambio identificados.

La variación total puede separarse entre movimiento del activo y conversión cambiaria, pero la atribución requiere una convención sobre orden y tipo de cambio.

function convertirCartera(saldos, fxPorMoneda, monedaBase) {
  return saldos.reduce((total, saldo) => {
    const fx = saldo.currency === monedaBase ? 1 : fxPorMoneda[saldo.currency];
    if (!Number.isFinite(fx) || fx <= 0) throw new Error(`Falta tipo de cambio para ${saldo.currency}`);
    return total + saldo.amount * fx;
  }, 0);
}

console.log(convertirCartera(
  [{ currency: "ARS", amount: 50000 }, { currency: "USD", amount: 1000 }],
  { USD: 1200 },
  "ARS"
));

35.16 Eventos corporativos y ajustes

Splits, dividendos en especie, amortizaciones, canjes y fusiones modifican cantidades, costos o identificadores sin ser compras o ventas ordinarias. Deben registrarse como eventos con fecha y factor.

En un split 2 por 1, la cantidad se duplica y el costo unitario se reduce a la mitad, mientras el costo total permanece. Redondeos y fracciones pueden generar efectivo adicional.

Los dividendos en efectivo aumentan el saldo cuando nace o se paga el derecho según la convención del sistema; no deben confundirse con una venta parcial.

35.17 Agregar una cartera sin perder detalle

Los resultados pueden agruparse por instrumento, cuenta, moneda, sector o estrategia. La suma solo es significativa después de convertir unidades y aplicar una fecha de valuación común.

function resumirCartera(posiciones) {
  return posiciones.reduce((resumen, posicion) => ({
    marketValue: resumen.marketValue + posicion.marketValue,
    realizedPnl: resumen.realizedPnl + posicion.realizedPnl,
    unrealizedPnl: resumen.unrealizedPnl + posicion.unrealizedPnl
  }), { marketValue: 0, realizedPnl: 0, unrealizedPnl: 0 });
}

console.log(resumirCartera([
  { marketValue: 4550, realizedPnl: 1312.67, unrealizedPnl: 807.33 },
  { marketValue: 2000, realizedPnl: -100, unrealizedPnl: 250 }
]));

El ejemplo supone que todos los importes ya están en la misma moneda y fecha. Esa precondición debería validarse en un sistema real.

35.18 Laboratorio: compras, venta y valuación

El laboratorio procesa dos compras y una venta mediante costo promedio móvil. Las comisiones de compra aumentan el costo; la comisión de venta reduce el producido.

Libro simplificado de una posición larga

Importes ilustrativos en pesos; no representa tratamiento contable ni fiscal.

Cantidad abierta70
Costo promedio$53,47
Costo abierto$3.742,67
Valor de mercado$4.550,00
Resultado realizado$1.312,67
Resultado no realizado$807,33
Efectivo final$97.570,00
Patrimonio final$102.120,00
Realizado
$1.312,67
No realizado
$807,33

Resultado total: $2.120,00. Coincide con patrimonio final menos efectivo inicial.

35.19 Motor integrador con costo promedio

Esta versión compacta procesa compras y ventas largas. Mantiene costo abierto, resultado realizado y efectivo, y rechaza una venta superior a la posición.

function procesarOperaciones(initialCash, trades, markPrice) {
  let state = { quantity: 0, openCost: 0, realizedPnl: 0, cash: initialCash };
  for (const trade of trades) {
    const gross = trade.quantity * trade.price;
    if (trade.side === "BUY") {
      state.quantity += trade.quantity;
      state.openCost += gross + trade.fee;
      state.cash -= gross + trade.fee;
    } else {
      if (trade.quantity > state.quantity) throw new Error("Venta superior a la posición");
      const releasedCost = state.openCost / state.quantity * trade.quantity;
      const netProceeds = gross - trade.fee;
      state.quantity -= trade.quantity;
      state.openCost -= releasedCost;
      state.realizedPnl += netProceeds - releasedCost;
      state.cash += netProceeds;
    }
  }
  const marketValue = state.quantity * markPrice;
  const unrealizedPnl = marketValue - state.openCost;
  return { ...state, marketValue, unrealizedPnl, totalPnl: state.realizedPnl + unrealizedPnl };
}

console.log(procesarOperaciones(100000, [
  { side: "BUY", quantity: 100, price: 50, fee: 10 },
  { side: "BUY", quantity: 50, price: 60, fee: 10 },
  { side: "SELL", quantity: 80, price: 70, fee: 10 }
], 65));

35.20 Conciliación y pruebas

El control más valioso compara fuentes independientes: suma de ejecuciones contra posición, movimientos contra efectivo, custodia contra cantidades y valuación contra precios autorizados.

  • Verificá que posición inicial más movimientos sea igual a posición final.
  • Comprobá que efectivo inicial más flujos internos y externos sea igual al saldo.
  • Probá cierres parciales, cierre exacto y venta excesiva.
  • Reprocesá un identificador duplicado y exigí idempotencia.
  • Probá precios cero, datos ausentes, varias monedas y fechas fuera de orden.
  • Usá tolerancia o aritmética decimal definida para comparaciones monetarias.

Las diferencias no deben corregirse con un asiento opaco. Se registra el ajuste, su motivo, autorización y vínculo con la evidencia.

35.21 Errores frecuentes

  • Tratar depósitos y retiros como ganancias o pérdidas.
  • Restar ventas de compras sin asignar costo a la parte cerrada.
  • Promediar precios sin incluir cantidades y cargos definidos.
  • Confundir efectivo contable con efectivo disponible.
  • Valuar con un precio sin fuente, hora o moneda.
  • Aplicar el algoritmo de posiciones largas a posiciones cortas.
  • Sumar monedas distintas antes de convertirlas.
  • Ignorar splits, cupones, dividendos y amortizaciones.
  • Usar un método ilustrativo como si fuera regla fiscal universal.

35.22 Ideas para recordar

  • Operaciones, posiciones, efectivo y valuaciones son registros distintos.
  • La venta realiza resultado al compararse con un costo asignado.
  • La posición abierta conserva una base y genera resultado no realizado.
  • La marca necesita precio, fuente, instante y convención.
  • Fecha de operación y fecha de liquidación producen estados diferentes.
  • Flujos externos no forman parte del rendimiento.
  • Conciliar identidades permite detectar omisiones y duplicados.

En el próximo tema construiremos el proyecto integrador: monitor y comparador de instrumentos financieros.