#!/usr/bin/env python3
"""Renders site/template.html and THESIS.md into docs/, so the page carries real numbers even with JavaScript off."""
import datetime, gzip, hashlib, html, json, pathlib, re, shutil, sys
ROOT = pathlib.Path(__file__).resolve().parent.parent
DOCS = ROOT / "docs"
def compact(v, digits=1):
a = abs(v)
for size, suffix in ((1e12, "T"), (1e9, "B"), (1e6, "M"), (1e3, "K")):
if a >= size:
text = f"{v / size:.{digits}f}"
return (text.rstrip("0").rstrip(".") if "." in text else text) + suffix
return f"{v:.0f}"
FORMATS = {
"text": str,
"n": lambda v: f"{int(v):,}",
"usd": lambda v: "$" + compact(v),
"usd2": lambda v: f"${v:.2f}",
"usd3": lambda v: f"${v:.3f}",
"usdm": lambda v: "$" + compact(v, 0),
"nearm": lambda v: compact(v, 2 if abs(v) < 1e8 else 0) + " NEAR",
"near0": lambda v: f"{v:,.0f} NEAR",
"nearb2": lambda v: f"{v / 1e9:.2f}B NEAR",
"pct0": lambda v: f"{v:.0f}%",
"pct1": lambda v: f"{v:.1f}%",
"pct2": lambda v: f"{v:.2f}%",
"pct_from_x": lambda v: f"{v * 100:.0f}%",
"x0": lambda v: f"{v:.0f}x",
"x1": lambda v: f"{v:.1f}x",
"usd0": lambda v: f"${v:,.0f}",
"num": lambda v: compact(v),
"signed0": lambda v: f"{v:+.0f}%",
"abs0": lambda v: f"{abs(v):.0f}%",
"abs1": lambda v: f"{abs(v):.1f}%",
"bps1": lambda v: f"{v:.1f} bps",
"num2": lambda v: f"{v:.2f}",
"bps0": lambda v: f"{v:.0f} bps",
"bps2": lambda v: f"{v:.2f} bps",
}
def derived(metrics):
"""Display-only values built from registered metrics."""
v = lambda k: metrics[k]["value"]
out = dict(metrics)
add = lambda k, val: out.__setitem__(k, {"value": val})
add("tx_drop_apr26_abs", abs(v("tx_drop_apr26_pct")))
add("signers_engaged_change_abs", abs(v("signers_engaged_change_pct")))
add("as_of_month", datetime.date.fromisoformat(v("as_of")).strftime("%B %Y"))
add("rail_premium_rune", v("mcap_per_volume_near") / v("mcap_per_volume_rune"))
add("dex_annual", v("spot_dex_30d") * 12)
add("window_month_name", datetime.date.fromisoformat(v("window_month") + "-01").strftime("%B %Y"))
add("tg_engaged_change_abs", abs(v("tg_engaged_change_pct")))
add("self_engaged_change_abs", abs(v("self_engaged_change_pct")))
add("peer_chg_1y_median_abs", abs(v("peer_chg_1y_median_pct")))
add("capture_fraction_pct", v("capture_30d_pct"))
add("per100_captured_bps", v("per100_captured") * 100)
add("capture_month_pct", v("net_bps_30d") / v("gross_bps_30d") * 100)
return out
def render(text, metrics, model, grades, escape=True):
def sub_metric(m):
key, fmt = m.group(1), m.group(2)
val = (metrics.get(key) or {}).get("value")
if val is None:
return "n/a"
out = FORMATS[fmt](val)
return html.escape(out) if escape else out
def sub_model(m):
key = m.group(1)
implied = model["implied_base"] or {}
vals = {"weighted": f"${model['weighted']:.2f}",
"gap": f"{abs(model['weighted'] / model['price'] - 1) * 100:.0f}%",
"bear": f"${model['results']['bear']['price']:.2f}", "base": f"${model['results']['base']['price']:.2f}", "bull": f"${model['results']['bull']['price']:.2f}",
"implied_multiple_low": f"{model['implied_rich']['chain_3_9b']['y5_volume'] / metrics['vol_annualised']['value']:.0f}x",
"implied_multiple_40x": f"{model['implied_rich']['exit_40x']['y5_volume'] / metrics['vol_annualised']['value']:.0f}x",
"weighted_bull_30": f"${model['reweighted']['30']:.2f}", "weighted_bull_10": f"${model['reweighted']['10']:.2f}",
"base_chain_share": f"{model['results']['base']['l1_value'] / model['results']['base']['equity'] * 100:.0f}%",
"base_chain_value": "$" + compact(model['results']['base']['l1_value'], 2),
"premium_over_base": f"{(model['price'] / model['results']['base']['price'] - 1) * 100:.0f}%",
"base_equity": "$" + compact(model['results']['base']['equity'], 2),
"supply": compact(model['scenarios']['base']['inputs']['supply'], 2) + " NEAR",
"base_chain_per_near": f"${model['results']['base']['l1_value'] / model['scenarios']['base']['inputs']['supply']:.2f}",
"implied_cagr_raw": f"{implied.get('cagr', 0):.3f}" if implied else "n/a",
"implied_y5_share": f"{implied.get('y5_volume', 0) / metrics['dex_annual']['value'] * 100:.0f}%" if implied else "n/a",
**{f"if_{k}": f"${v:.2f}" for k, v in model.get("what_if", {}).items()},
"implied_cagr": f"{implied.get('cagr', 0) * 100:.0f}%" if implied else "n/a",
"implied_y5": "$" + compact(implied.get("y5_volume", 0), 0) if implied else "n/a",
"implied_multiple": f"{implied.get('y5_volume', 0) / metrics['vol_annualised']['value']:.0f}x" if implied else "n/a",
"implied_bull": f"{model['implied_bull_probability'] * 100:.0f}%",
"bull_weight": f"{model['scenarios']['bull']['probability'] * 100:.0f}%"}
return html.escape(vals[key]) if escape else vals[key]
def sub_static(m):
kind = m.group(1)
out = KILL_STATIC(grades) if kind == "kill" else ELAST_TEXT(metrics) if kind == "elast" else html.escape(grades["note"])
return out if escape else re.sub(r"<[^>]+>", "", out)
def sub_grades(m):
live = sum(1 for r in grades["rows"] if r["status"] == "live")
tripped = len(grades["tripped"])
return f"{tripped} of {len(grades['rows'])} kill criteria tripped."
# Asset versions come from their content, so a visitor never mixes a new page with an old cached script.
if "{{build}}" in text:
digest = hashlib.sha256(b"".join((DOCS / n).read_bytes() for n in ("app.js", "charts.js", "model.js", "styles.css"))).hexdigest()[:10]
text = text.replace("{{build}}", digest)
text = re.sub(r"\{\{m:([a-z0-9_]+):([a-z0-9_]+)\}\}", sub_metric, text)
text = re.sub(r"\{\{model:([a-z0-9_]+)\}\}", sub_model, text)
def sub_case(m):
case, field, fmt = m.group(1), m.group(2), m.group(3)
sc = model["scenarios"][case]
val = sc["probability"] if field == "probability" else sc["inputs"][field]
return FORMATS[fmt](val * (100 if fmt.startswith("pct") else 1))
text = re.sub(r"\{\{s:([a-z]+):([a-z0-9_]+):([a-z0-9_]+)\}\}", sub_case, text)
text = re.sub(r"\{\{static:(killnote|kill|elast)\}\}", sub_static, text)
return re.sub(r"\{\{grades:summary\}\}", sub_grades, text)
def _kill_value(r, key):
v = r[key]
if v is None:
return "n/a"
if r["unit"] == "usd":
return "$" + compact(v, 0 if key == "threshold" else 1)
if r["unit"] == "NEAR":
return compact(v, 0 if key == "threshold" else 1)
if key == "threshold":
num = f"{v:g}"
else:
num = f"{v:.2f}" if abs(v) < 10 else f"{round(v)}"
return num + ("%" if r["unit"] == "%" else "x" if r["unit"] == "x" else " " + r["unit"])
def KILL_STATIC(grades):
"""The kill list as plain HTML, so a reader with JavaScript off (or a crawler) sees every row. app.js redraws it."""
e = html.escape
out = [' Thresholds and dates are fixed. Values are from the build '
f'graded on {e(grades["graded_on"])} (data through {e(grades["as_of"])}).Criteria, graded by code at each build
\1", text)
text = re.sub(r"\*\*([^*]+)\*\*", r"\1", text)
return re.sub(r"\[([^\]]+)\]\((https?://[^)\s\"'<>&]+|(?!//)[a-zA-Z0-9_./#-]+)\)", r'\1', text)
def markdown(src):
"""A small Markdown subset: headings, paragraphs, bulleted and numbered lists, tables. The memo uses nothing else."""
out, para, lst, tbl = [], [], False, []
def flush():
nonlocal para, lst, tbl
if para:
out.append("" + inline(" ".join(para)) + "
") para = [] if lst: out.append("%s>" % lst) lst = False if tbl: rows = [[c.strip() for c in r.strip().strip("|").split("|")] for r in tbl if not re.match(r"^\|?[\s:|-]+\|?$", r)] out.append('| {inline(c)} | " for c in rows[0]) + "
|---|
| {inline(c)} | ' for c in r) + "