near-toll-audit / docs/model.js · raw
// Valuation engine used by the live page. pipeline/model.py is the same maths; tests/test_model.py proves they agree.(function (root) { "use strict"; // Linear path from a start value in year 1 to an end value in year 5. function path(a, b, t) { return a + (b - a) * (Math.min(t, 5) - 1) / 4; } function issuanceRate(p, t) { var ramp = Math.max(1, p.ramp_years); return p.issuance_start + (p.issuance_end - p.issuance_start) * Math.min(t, ramp) / ramp; } function value(p) { var ownership = 1, pv = 0, rows = [], earnings = 0, vol = p.vol0; for (var t = 1; t <= 5; t++) { vol = vol * (1 + p.cagr); var gross = vol * path(p.gross_bps_start, p.gross_bps_end, t) / 1e4; var captured = gross * path(p.capture_start, p.capture_end, t); var l1 = p.l1_burn_usd * Math.pow(1 + p.l1_growth, t); earnings = captured * p.claim + l1; // Staking rewards are a wash for a holder who stakes. Only the treasury share and commission leave the holder base. var leak = issuanceRate(p, t) * (p.treasury_share + (1 - p.treasury_share) * p.commission); ownership *= 1 - leak; pv += earnings * ownership / Math.pow(1 + p.discount, t); rows.push({ year: t, volume: vol, gross: gross, captured: captured, earnings: earnings, ownership: ownership }); } var terminal = earnings * p.exit_multiple * ownership / Math.pow(1 + p.discount, 5); var total = pv + terminal + p.l1_value; return { price: total / p.supply, equity: total, pv_cash: pv, pv_terminal: terminal, l1_value: p.l1_value, y5_volume: vol, y5_captured: rows[4].captured, y5_earnings: earnings, rows: rows }; } // Year-5 volume at which the model equals a target price, all else held. function impliedVolume(p, target) { var lo = -0.95, hi = 20, q = Object.assign({}, p); function at(g) { q.cagr = g; return value(q).price; } if (at(hi) < target) return null; if (at(lo) > target) return { cagr: lo, y5_volume: p.vol0 * Math.pow(1 + lo, 5) }; for (var i = 0; i < 80; i++) { var mid = (lo + hi) / 2; if (at(mid) < target) lo = mid; else hi = mid; } var g = (lo + hi) / 2; return { cagr: g, y5_volume: p.vol0 * Math.pow(1 + g, 5) }; } function weighted(scenarios) { var out = 0; Object.keys(scenarios).forEach(function (k) { out += scenarios[k].probability * value(scenarios[k].inputs).price; }); return out; } // Probability on the bull case that makes the weighted value equal the market price, // holding the bear to base ratio fixed. function impliedBull(scenarios, target) { var bear = value(scenarios.bear.inputs).price, base = value(scenarios.base.inputs).price, bull = value(scenarios.bull.inputs).price; var a = scenarios.bear.probability, b = scenarios.base.probability; var rest = (a * bear + b * base) / (a + b); if (bull === rest) return null; return (target - rest) / (bull - rest); } var api = { value: value, impliedVolume: impliedVolume, weighted: weighted, impliedBull: impliedBull, issuanceRate: issuanceRate }; if (typeof module !== "undefined" && module.exports) module.exports = api; else root.NearModel = api;})(typeof window !== "undefined" ? window : this);