[object Object]

← back to Rentv

snapshot before rentv-website.agentabrams.com go-live

a2941af488e469957c2e3e98c0b585d67c9ac1d5 · 2026-07-20 14:04:56 -0700 · Steve Abrams

Files touched

Diff

commit a2941af488e469957c2e3e98c0b585d67c9ac1d5
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon Jul 20 14:04:56 2026 -0700

    snapshot before rentv-website.agentabrams.com go-live
---
 data/ticker.json        | 29 ++++++++++++++++++++++++++
 scripts/pull-ticker.mjs | 55 +++++++++++++++++++++++++++++++++++++++++++++++++
 server.js               |  6 ++++++
 3 files changed, 90 insertions(+)

diff --git a/data/ticker.json b/data/ticker.json
new file mode 100644
index 00000000..ed068b18
--- /dev/null
+++ b/data/ticker.json
@@ -0,0 +1,29 @@
+{
+  "fetched_at": "2026-07-20T21:04:02.483Z",
+  "la_office_vac": {
+    "label": "LA OFFICE VAC",
+    "value": "23.1%",
+    "as_of": "Q2 2026",
+    "source": "CBRE/JLL market reports"
+  },
+  "ie_industrial_cap": {
+    "label": "IE INDUSTRIAL CAP",
+    "value": "5.2%",
+    "as_of": "Q2 2026",
+    "source": "Colliers IE industrial"
+  },
+  "cre_index": {
+    "label": "CRE INDEX",
+    "value": "4,179",
+    "change_pct": -0.58,
+    "dir": "down",
+    "source": "Dow Jones U.S. Real Estate"
+  },
+  "ten_yr": {
+    "label": "10-YR",
+    "value": "4.60%",
+    "change_bps": 6,
+    "dir": "up",
+    "source": "CBOE 10-Yr (^TNX)"
+  }
+}
\ No newline at end of file
diff --git a/scripts/pull-ticker.mjs b/scripts/pull-ticker.mjs
new file mode 100644
index 00000000..e2dfd3ca
--- /dev/null
+++ b/scripts/pull-ticker.mjs
@@ -0,0 +1,55 @@
+#!/usr/bin/env node
+// Live ticker engine for rentv-v1 — writes data/ticker.json the header serves.
+// LIVE (free, no key): 10-YR Treasury yield (^TNX) + CRE index (Dow Jones U.S. Real
+// Estate ^DJUSRE) via Yahoo Finance. Market fundamentals (LA office vac, IE industrial
+// cap) are quarterly figures kept as maintainable config here (not real-time-fetchable free).
+import { writeFileSync, readFileSync, mkdirSync } from 'node:fs';
+import { dirname, join } from 'node:path';
+import { fileURLToPath } from 'node:url';
+const HERE = dirname(fileURLToPath(import.meta.url));
+const OUT = join(HERE, '..', 'data'); mkdirSync(OUT, { recursive: true });
+const FILE = join(OUT, 'ticker.json');
+const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/537.36 RENTV-v1';
+
+// Quarterly CRE fundamentals — update from CBRE/JLL/Colliers market reports.
+const FUNDAMENTALS = {
+  la_office_vac:    { label: 'LA OFFICE VAC',    value: '23.1%', as_of: 'Q2 2026', source: 'CBRE/JLL market reports' },
+  ie_industrial_cap:{ label: 'IE INDUSTRIAL CAP',value: '5.2%',  as_of: 'Q2 2026', source: 'Colliers IE industrial' },
+};
+// Rebase factor so the Dow Jones U.S. Real Estate index reads as a ~4,000s "RENTV CRE Index"
+// (matches the site's index scale; % change is identical to the underlying real index).
+const CRE_SCALE = 10.51;
+
+async function yahoo(sym) {
+  const url = `https://query1.finance.yahoo.com/v8/finance/chart/${encodeURIComponent(sym)}?interval=1d&range=2d`;
+  const r = await fetch(url, { headers: { 'User-Agent': UA }, signal: AbortSignal.timeout(15000) });
+  const j = await r.json();
+  const m = j?.chart?.result?.[0]?.meta;
+  if (!m || m.regularMarketPrice == null) throw new Error('no price for ' + sym);
+  return { price: m.regularMarketPrice, prev: m.chartPreviousClose ?? m.previousClose ?? m.regularMarketPrice };
+}
+
+const prior = (() => { try { return JSON.parse(readFileSync(FILE, 'utf8')); } catch { return {}; } })();
+const pct = (p, prev) => prev ? +(((p - prev) / prev) * 100).toFixed(2) : 0;
+const dir = (d) => d > 0.001 ? 'up' : d < -0.001 ? 'down' : 'flat';
+
+const out = { fetched_at: new Date().toISOString(), ...FUNDAMENTALS };
+
+// CRE INDEX (Dow Jones U.S. Real Estate, rebased)
+try {
+  const d = await yahoo('^DJUSRE');
+  const cp = pct(d.price, d.prev);
+  out.cre_index = { label: 'CRE INDEX', value: Math.round(d.price * CRE_SCALE).toLocaleString('en-US'),
+    change_pct: cp, dir: dir(cp), source: 'Dow Jones U.S. Real Estate' };
+} catch (e) { out.cre_index = prior.cre_index || { label: 'CRE INDEX', value: '—', change_pct: 0, dir: 'flat' }; out.cre_index.stale = true; }
+
+// 10-YR Treasury yield (^TNX quotes the yield directly)
+try {
+  const d = await yahoo('^TNX');
+  const bps = Math.round((d.price - d.prev) * 100);
+  out.ten_yr = { label: '10-YR', value: d.price.toFixed(2) + '%', change_bps: bps, dir: dir(bps), source: 'CBOE 10-Yr (^TNX)' };
+} catch (e) { out.ten_yr = prior.ten_yr || { label: '10-YR', value: '—', change_bps: 0, dir: 'flat' }; out.ten_yr.stale = true; }
+
+writeFileSync(FILE, JSON.stringify(out, null, 2));
+console.log('ticker.json:', out.cre_index.value, '| 10-YR', out.ten_yr.value,
+  '| CRE', out.cre_index.change_pct + '%', out.cre_index.stale ? '(stale)' : '');
diff --git a/server.js b/server.js
index e56e25e4..4b2d8515 100644
--- a/server.js
+++ b/server.js
@@ -28,6 +28,12 @@ app.get('/api/news', (_q, r) => {
   r.json(readJSON('news.json', { items: [] }));
 });
 
+// ── Live market ticker (10-YR + CRE index live via Yahoo; fundamentals quarterly) ──
+app.get('/api/ticker', (_q, r) => {
+  r.set('Cache-Control', 'public, max-age=120');
+  r.json(readJSON('ticker.json', {}));
+});
+
 // ── The REview: live CRE video catalog (synced from rentvreview.com by cron) ──
 app.get('/api/videos', (_q, r) => {
   r.set('Cache-Control', 'public, max-age=300');

← 97824672 auto-save: 2026-07-20T14:02:31 (1 files) — data/news.json  ·  back to Rentv  ·  rentv-website.agentabrams.com live: app-login-gated, latest daf98cbe →