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.
Operaciones
Compras, ventas, cargos, ingresos y movimientos.
Posiciones
Cantidad abierta y costo asignado por instrumento.
Efectivo
Saldos disponibles, pendientes y reservados por moneda.
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.
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.
| Componente | Compra | Venta |
|---|---|---|
| Importe bruto | Precio × cantidad. | Precio × cantidad. |
| Cargos | Pueden aumentar el costo. | Pueden reducir el producido. |
| Efectivo | Salida de fondos. | Entrada de fondos. |
| Posición | Aumenta 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.
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.
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.
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.
| Marca | Ventaja | Limitación |
|---|---|---|
| Última operación | Representa un precio ejecutado. | Puede ser antigua o de tamaño pequeño. |
| Punto medio | Neutral entre puntas visibles. | No es necesariamente ejecutable. |
| Bid para una posición larga | Aproxima primera salida inmediata. | Puede no haber profundidad suficiente. |
| Cierre oficial | Facilita 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.
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.
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.
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.
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.