/** * Margin arithmetic — the reason PIG exists. * * A two-sided compute business buys capacity in blocks (a commitment) and sells * it in slices (allocations against deals). The spread between what a * GPU-hour cost and what it sold for, net of the hours nobody bought, is the * business. These functions are deliberately pure and unit-tested: every * dashboard number and every agent answer resolves through them, so an error * here is an error everywhere. * * All money is handled in minor units (cents) as integers. Floating-point * currency in a system that reports margin is a defect waiting to be found by * an accountant. */ /** A block of capacity purchased from a supplier. */ export interface CommitmentInput { /** Total GPU-hours contracted over the term. */ gpuHours: number; /** What we pay per GPU-hour, in cents. */ costPerGpuHourCents: number; } /** A slice of that block sold to a customer. */ export interface AllocationInput { /** GPU-hours allocated to a demand deal. */ gpuHours: number; /** What the customer pays per GPU-hour, in cents. */ pricePerGpuHourCents: number; } export interface MarginResult { /** Hours bought. */ committedGpuHours: number; /** Hours sold. */ allocatedGpuHours: number; /** Hours bought and not sold. This is the number that hurts. */ idleGpuHours: number; /** Share of committed capacity that is sold, 0–1. */ utilisation: number; /** Total paid to the supplier, cents. */ costCents: number; /** Total billed to customers, cents. */ revenueCents: number; /** * Revenue minus the FULL cost of the commitment — not merely the cost of the * hours that sold. Unsold hours on a commitment are already paid for, so * charging only allocated cost would flatter the number and hide the very * problem this system exists to surface. */ grossMarginCents: number; /** Gross margin as a share of revenue, 0–1. Null when there is no revenue. */ grossMarginPct: number | null; /** Effective blended margin per GPU-hour sold, in cents. Null if nothing sold. */ marginPerAllocatedGpuHourCents: number | null; } export function computeMargin( commitment: CommitmentInput, allocations: readonly AllocationInput[], ): MarginResult { const committedGpuHours = commitment.gpuHours; const allocatedGpuHours = allocations.reduce((sum, a) => sum + a.gpuHours, 0); // Over-allocation is possible and legitimate: capacity can be oversubscribed // deliberately, on the assumption not every buyer uses their full reservation. // Clamping idle at zero keeps the figure meaningful when that happens. const idleGpuHours = Math.max(0, committedGpuHours - allocatedGpuHours); const costCents = Math.round(committedGpuHours * commitment.costPerGpuHourCents); const revenueCents = allocations.reduce( (sum, a) => sum + Math.round(a.gpuHours * a.pricePerGpuHourCents), 0, ); const grossMarginCents = revenueCents - costCents; return { committedGpuHours, allocatedGpuHours, idleGpuHours, utilisation: committedGpuHours > 0 ? allocatedGpuHours / committedGpuHours : 0, costCents, revenueCents, grossMarginCents, grossMarginPct: revenueCents > 0 ? grossMarginCents / revenueCents : null, marginPerAllocatedGpuHourCents: allocatedGpuHours > 0 ? grossMarginCents / allocatedGpuHours : null, }; } /** * The break-even sell price for the remaining unsold hours on a commitment. * * This is the number a seller actually wants mid-quarter: "the block is half * sold and already paid for — what must I get for the rest to come out even?" * It falls as more of the block sells, which is why it is computed against * remaining hours rather than the whole commitment. * * Returns null when the block is fully allocated: there is nothing left to * price, and dividing by zero hours would produce a confident-looking * Infinity. */ export function breakEvenPricePerGpuHourCents( commitment: CommitmentInput, allocations: readonly AllocationInput[], ): number | null { const m = computeMargin(commitment, allocations); if (m.idleGpuHours <= 0) return null; const uncoveredCents = m.costCents - m.revenueCents; // Already in profit: any further sale is upside, so the floor is zero rather // than a negative price, which would be nonsense to display. if (uncoveredCents <= 0) return 0; return uncoveredCents / m.idleGpuHours; } /** * Aggregate margin across many commitments — a book-level view. * * Deliberately sums the underlying cents rather than averaging the per-block * percentages: an average of ratios weights a tiny block equally with a huge * one and produces a number that is wrong in the direction of whichever blocks * happen to be small. */ export function aggregateMargin( books: readonly { commitment: CommitmentInput; allocations: readonly AllocationInput[] }[], ): MarginResult { const results = books.map((b) => computeMargin(b.commitment, b.allocations)); const committedGpuHours = results.reduce((s, r) => s + r.committedGpuHours, 0); const allocatedGpuHours = results.reduce((s, r) => s + r.allocatedGpuHours, 0); const idleGpuHours = results.reduce((s, r) => s + r.idleGpuHours, 0); const costCents = results.reduce((s, r) => s + r.costCents, 0); const revenueCents = results.reduce((s, r) => s + r.revenueCents, 0); const grossMarginCents = revenueCents - costCents; return { committedGpuHours, allocatedGpuHours, idleGpuHours, utilisation: committedGpuHours > 0 ? allocatedGpuHours / committedGpuHours : 0, costCents, revenueCents, grossMarginCents, grossMarginPct: revenueCents > 0 ? grossMarginCents / revenueCents : null, marginPerAllocatedGpuHourCents: allocatedGpuHours > 0 ? grossMarginCents / allocatedGpuHours : null, }; } /** Format cents as a currency string for display. */ export function formatCents(cents: number, currency = 'USD'): string { return new Intl.NumberFormat('en-US', { style: 'currency', currency, maximumFractionDigits: 2, }).format(cents / 100); }