import {setupPopover} from './popover'; const roiCache: Record> = {}; async function getROI(cx: string) { const response = await fetch(`/roi_${cx.toLowerCase()}.json`); const lastModified = new Date(response.headers.get('last-modified')!); const profits = await response.json(); return {lastModified, profits}; } const lowVolume = document.querySelector('input#low-volume') as HTMLInputElement; const expertise = { AGRICULTURE: 'agri', CHEMISTRY: 'chem', CONSTRUCTION: 'const', ELECTRONICS: 'elec', FOOD_INDUSTRIES: 'food ind', FUEL_REFINING: 'fuel', MANUFACTURING: 'mfg', METALLURGY: 'metal', RESOURCE_EXTRACTION: 'res ext', } as const; const expertiseSelect = document.querySelector('select#expertise') as HTMLSelectElement; for (const key of Object.keys(expertise)) { const option = document.createElement('option'); option.value = key; option.textContent = key.replace('_', ' ').toLowerCase(); expertiseSelect.appendChild(option); } const cxSelect = document.querySelector('select#cx') as HTMLSelectElement; const formatDecimal = new Intl.NumberFormat(undefined, {maximumFractionDigits: 2, maximumSignificantDigits: 6, roundingPriority: 'lessPrecision'}).format; const formatWhole = new Intl.NumberFormat(undefined, {maximumFractionDigits: 0}).format; async function render() { const tbody = document.querySelector('tbody')!; tbody.innerHTML = ''; const cx = cxSelect.value; if (!roiCache[cx]) roiCache[cx] = getROI(cx); const {lastModified, profits} = await roiCache[cx]; for (const p of profits) { const volumeRatio = p.output_per_day / p.average_traded_7d; if (!lowVolume.checked && volumeRatio > 0.05) continue; if (expertiseSelect.value !== '' && p.expertise !== expertiseSelect.value) continue; const tr = document.createElement('tr'); const profitPerArea = p.profit_per_day / p.area; const breakEven = p.profit_per_day > 0 ? p.capex / p.profit_per_day : Infinity; tr.innerHTML = ` ${p.outputs.map(o => o.ticker).join(', ')} ${expertise[p.expertise]} ${formatDecimal(profitPerArea)} ${formatDecimal(breakEven)}d ${formatWhole(p.capex)} ${formatWhole(p.cost_per_day)} ${formatDecimal(p.logistics_per_area)} ${formatDecimal(p.output_per_day)}
${formatWhole(p.average_traded_7d)} `; const output = tr.querySelector('td')!; output.dataset.tooltip = p.recipe; const profitCell = tr.querySelectorAll('td')[2]; const revenue = p.outputs.reduce((sum, o) => sum + o.amount * o.vwap_7d, 0); const inputCost = p.input_costs.reduce((sum, o) => sum + o.amount * o.vwap_7d, 0); profitCell.dataset.tooltip = formatMatPrices(p.outputs) + '\n\n' + formatMatPrices(p.input_costs) + '\n' + 'worker consumables: ' + formatWhole(p.worker_consumable_cost_per_day) + '\n\n' + `(${formatWhole(revenue)} - ${formatWhole(inputCost)}) × ${formatDecimal(p.runs_per_day)} runs ` + `- ${formatWhole(p.worker_consumable_cost_per_day)} = ${formatDecimal(p.profit_per_day)}\n` + `${formatDecimal(p.profit_per_day)} / ${formatWhole(p.area)} area = ${formatDecimal(profitPerArea)}`; tbody.appendChild(tr); } document.getElementById('last-updated')!.textContent = `last updated: ${lastModified.toLocaleString(undefined, {dateStyle: 'full', timeStyle: 'long', hour12: false})}`; } function color(n: number, low: number, high: number): string { // scale n from low..high to 0..1 clamped const scale = Math.min(Math.max((n - low) / (high - low), 0), 1); return `color-mix(in xyz, #0aa ${scale * 100}%, #f80)`; } function formatMatPrices(matPrices: MatPrice[]): string { return matPrices.map(({ticker, amount, vwap_7d}) => `${ticker}: ${amount} × ${formatDecimal(vwap_7d)} = ${formatWhole(amount * vwap_7d)}`).join('\n'); } setupPopover(); lowVolume.addEventListener('change', render); expertiseSelect.addEventListener('change', render); cxSelect.addEventListener('change', render); render(); interface Profit { outputs: MatPrice[] recipe: string expertise: keyof typeof expertise profit_per_day: number area: number capex: number cost_per_day: number input_costs: MatPrice[] worker_consumable_cost_per_day: number runs_per_day: number logistics_per_area: number output_per_day: number average_traded_7d: number } interface MatPrice { ticker: string amount: number vwap_7d: number }