Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/screenshots/signals-gaps.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/signals-top.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
114 changes: 114 additions & 0 deletions src/app/signals/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import type { Metadata } from 'next'
import Breadcrumb from '@/components/Breadcrumb'
import {
predictionMarketMeta,
predictionMarketMappings,
fetchPolymarketOdds,
type MarketMatch,
} from '@/lib/predictionMarkets'

export const metadata: Metadata = {
title: 'Signals β€” Inflection points, priced by the crowd',
description: predictionMarketMeta.subtitle,
}

// Revalidate hourly (odds are cached per-slug in the fetch helper too).
export const revalidate = 3600

const matchLabel: Record<MarketMatch, string> = {
direct: 'Direct market',
proxy: 'Proxy market',
gap: 'No market yet',
}

const matchStyle: Record<MarketMatch, string> = {
direct: 'bg-emerald-50 text-emerald-700 border-emerald-200',
proxy: 'bg-amber-50 text-amber-700 border-amber-200',
gap: 'bg-gray-100 text-gray-500 border-gray-200',
}

export default async function SignalsPage() {
// Fetch live odds per mapping (passing the mapping's question as a hint so
// multi-threshold events resolve to the right line).
const odds = await Promise.all(
predictionMarketMappings.map((m) =>
m.market.slug ? fetchPolymarketOdds(m.market.slug, m.market.question) : Promise.resolve(null),
),
)

return (
<div className="max-w-6xl mx-auto px-6 pt-8 pb-16">
<Breadcrumb items={[{ label: 'Signals' }]} />

<h1 className="mt-6 text-lg md:text-[32px] leading-tight font-semibold">
{predictionMarketMeta.title}
</h1>
<p className="mt-4 text-gray-600 leading-relaxed max-w-3xl">
{predictionMarketMeta.subtitle}
</p>

<div className="mt-8 grid gap-4 md:grid-cols-2">
{predictionMarketMappings.map((m, i) => {
const o = odds[i]
const pct = o && o.yesPrice != null ? Math.round(o.yesPrice * 100) : null
return (
<div
key={i}
className="rounded-xl border border-gray-200 bg-white p-5 flex flex-col gap-3"
>
<div className="flex items-center justify-between gap-3">
<span className="text-xs uppercase tracking-wide text-gray-400">
{m.area}
</span>
<span
className={`text-[11px] px-2 py-0.5 rounded-full border ${matchStyle[m.match]}`}
>
{matchLabel[m.match]}
</span>
</div>

<div className="font-semibold text-gray-900">{m.opportunitySpace}</div>
<p className="text-sm text-gray-600 leading-relaxed">{m.inflectionPoint}</p>

<div className="mt-auto pt-3 border-t border-gray-100">
{m.match === 'gap' ? (
<p className="text-sm text-gray-500 italic">{m.market.question}</p>
) : (
<div className="flex items-center justify-between gap-4">
<div className="min-w-0">
<div className="text-xs text-gray-400">{m.market.platform}</div>
<a
href={m.market.url}
target="_blank"
rel="noopener noreferrer"
className="text-sm text-gray-800 hover:underline block truncate"
title={m.market.question}
>
{m.market.question}
</a>
</div>
<div className="text-right shrink-0">
<div className="text-2xl font-semibold tabular-nums text-gray-900">
{pct != null ? `${pct}%` : 'β€”'}
</div>
<div className="text-[11px] text-gray-400">crowd&nbsp;yes</div>
</div>
</div>
)}
{m.market.note && (
<p className="mt-2 text-[12px] text-gray-400 leading-snug">
{m.market.note}
</p>
)}
</div>
</div>
)
})}
</div>

<p className="mt-8 text-xs text-gray-400 max-w-3xl leading-relaxed">
{predictionMarketMeta.disclaimer}
</p>
</div>
)
}
121 changes: 121 additions & 0 deletions src/data/prediction-markets.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
{
"meta": {
"title": "Inflection points, priced by the crowd",
"subtitle": "An experiment: each PL R&D inflection point mapped to the closest live prediction market on Polymarket / Kalshi. Where a market exists, the crowd's probability is a running scoreboard for our bets. Where none exists, the gap is itself a signal of how early or contrarian the bet is.",
"disclaimer": "Prototype for discussion. Odds are fetched live from public prediction-market APIs and are proxies, not exact restatements of our inflection points. Not investment advice; nothing here endorses gambling."
},
"mappings": [
{
"area": "AI & Robotics",
"areaSlug": "ai-robotics",
"opportunitySpace": "Open Compute Networks",
"inflectionPoint": "A decentralized / open compute network trains a model competitive with the hyperscalers' frontier.",
"match": "proxy",
"market": {
"platform": "Polymarket",
"question": "Will an open-weights (Chinese) model be the best AI model by Dec 31?",
"slug": "will-a-chinese-company-have-the-best-ai-model-by-december-31",
"url": "https://polymarket.com/event/will-a-chinese-company-have-the-best-ai-model-by-december-31",
"note": "Open-weights momentum is the best available proxy for open/distributed compute reaching the frontier."
}
},
{
"area": "AI & Robotics",
"areaSlug": "ai-robotics",
"opportunitySpace": "Embodied AI & Robotics Data Networks",
"inflectionPoint": "Robotics datasets and embodied models reach the behavioral repertoire needed for real-world deployment.",
"match": "proxy",
"market": {
"platform": "Polymarket",
"question": "Will Tesla release a consumer Optimus humanoid by Dec 31, 2026?",
"slug": "will-tesla-release-optimus-by-june-30-2026",
"url": "https://polymarket.com/event/will-tesla-release-optimus-by-june-30-2026",
"note": "Commercial humanoid release is a visible downstream marker of embodied-AI data maturity. Figure F.03 reliability markets are a second proxy."
}
},
{
"area": "AI & Robotics",
"areaSlug": "ai-robotics",
"opportunitySpace": "Agent Coordination & Agent-Native Economic Infrastructure",
"inflectionPoint": "Autonomous agents coordinate and transact across organizations without human intermediation.",
"match": "gap",
"market": {
"platform": "β€”",
"question": "No direct market yet β€” agents already trade ON Polymarket, but none prices agent-to-agent economies.",
"slug": "",
"url": "",
"note": "A genuine white space. Could be worth proposing/seeding such a market."
}
},
{
"area": "Digital Human Rights",
"areaSlug": "digital-human-rights",
"opportunitySpace": "Portable Identity, Credentials & Trust",
"inflectionPoint": "A 100M+ service identifies users without state/platform KYC via self-sovereign identity.",
"match": "direct",
"market": {
"platform": "Polymarket",
"question": "Over 30M humans verified on World Network by Dec 31?",
"slug": "over-30m-humans-verified-on-world-network-by-december-31",
"url": "https://polymarket.com/event/over-30m-humans-verified-on-world-network-by-december-31",
"note": "Proof-of-human adoption at scale is the clearest live analogue to the portable-identity inflection."
}
},
{
"area": "Digital Human Rights",
"areaSlug": "digital-human-rights",
"opportunitySpace": "Verifiable Public Knowledge & Provenance",
"inflectionPoint": "Frontier AI + major institutions adopt cryptographic content provenance as a baseline.",
"match": "gap",
"market": {
"platform": "β€”",
"question": "No clean market yet (EU AI Act Art. 50 enforcement, Aug 2026, is a candidate to track).",
"slug": "",
"url": "",
"note": "C2PA / SynthID adoption is measurable but not yet a liquid market β€” another white space."
}
},
{
"area": "Neuro",
"areaSlug": "neurotech",
"opportunitySpace": "Neural Augmentation (BCI)",
"inflectionPoint": "An FDA-cleared BCI opens a third-party app layer, normalizing elective implantation.",
"match": "proxy",
"market": {
"platform": "Polymarket",
"question": "Will Neuralink's valuation hit $75B by Dec 31?",
"slug": "will-neuralinks-valuation-hit-by-december-31",
"url": "https://polymarket.com/event/will-neuralinks-valuation-hit-by-december-31",
"note": "Valuation / FDA-milestone markets track BCI momentum; a dedicated FDA-clearance market would be the direct measure."
}
},
{
"area": "Economies & Governance",
"areaSlug": "economies-governance",
"opportunitySpace": "Sovereign Digital Public Infrastructure",
"inflectionPoint": "A nation-state runs core systems (identity, payments, registries) on crypto rails.",
"match": "proxy",
"market": {
"platform": "Polymarket",
"question": "Over 30M humans verified on World Network by Dec 31?",
"slug": "over-30m-humans-verified-on-world-network-by-december-31",
"url": "https://polymarket.com/event/over-30m-humans-verified-on-world-network-by-december-31",
"note": "World becoming a mandated digital-identity layer is one of our own examples; shares the World market as a proxy."
}
},
{
"area": "Economies & Governance",
"areaSlug": "economies-governance",
"opportunitySpace": "Crypto-Native Governance & Decentralized Climate Finance",
"inflectionPoint": "A city/jurisdiction adopts crypto-native governance; a billion-dollar DePIN climate business emerges.",
"match": "gap",
"market": {
"platform": "β€”",
"question": "No liquid market yet for network-state governance adoption or DePIN climate scale.",
"slug": "",
"url": "",
"note": "Long-horizon, illiquid themes β€” the absence of a market underlines how contrarian these bets are."
}
}
]
}
98 changes: 98 additions & 0 deletions src/lib/predictionMarkets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import mappingData from '@/data/prediction-markets.json'

export type MarketMatch = 'direct' | 'proxy' | 'gap'

export type MarketMapping = {
area: string
areaSlug: string
opportunitySpace: string
inflectionPoint: string
match: MarketMatch
market: {
platform: string
question: string
slug: string
url: string
note: string
}
}

export type LiveOdds = {
yesPrice: number | null // 0..1
question: string | null
fetchedAt: string
}

export const predictionMarketMeta = (mappingData as { meta: { title: string; subtitle: string; disclaimer: string } }).meta
export const predictionMarketMappings = (mappingData as { mappings: MarketMapping[] }).mappings

const GAMMA = 'https://gamma-api.polymarket.com'

/**
* Fetch the current "Yes" probability for a Polymarket event by slug.
* Uses the public Gamma API (no auth). Returns the highest-priced Yes outcome
* across the event's markets as a simple headline number for the prototype.
*/
export async function fetchPolymarketOdds(slug: string, hint?: string): Promise<LiveOdds> {
const empty: LiveOdds = { yesPrice: null, question: null, fetchedAt: new Date().toISOString() }
if (!slug) return empty
try {
const res = await fetch(`${GAMMA}/events?slug=${encodeURIComponent(slug)}`, {
// Revalidate hourly so the page stays fresh without hammering the API.
next: { revalidate: 3600 },
})
if (!res.ok) return empty
const events = (await res.json()) as Array<{
title?: string
markets?: Array<{ question?: string; outcomes?: string; outcomePrices?: string }>
}>
const event = events?.[0]
if (!event?.markets?.length) return empty

// Parse every sub-market's Yes price once.
const parsed = event.markets
.map((m) => {
const outcomes = safeParse(m.outcomes)
const prices = safeParse(m.outcomePrices)
if (!outcomes || !prices) return null
const yesIdx = outcomes.findIndex((o) => o.toLowerCase() === 'yes')
if (yesIdx === -1) return null
const yes = Number(prices[yesIdx])
if (Number.isNaN(yes)) return null
return { yes, question: m.question || event.title || '' }
})
.filter((x): x is { yes: number; question: string } => x != null)
if (!parsed.length) return empty

// If the mapping gives a hint (e.g. a "$75B" threshold), prefer the
// sub-market whose question shares a distinctive token with it. This keeps
// multi-threshold events (Neuralink valuation, etc.) on the right line
// instead of defaulting to the near-certain lowest threshold.
const token = hint ? distinctiveToken(hint) : null
const pick =
(token && parsed.find((m) => m.question.toLowerCase().includes(token))) ||
parsed[0]
return { yesPrice: pick.yes, question: pick.question, fetchedAt: new Date().toISOString() }
} catch {
return empty
}
}

// Pull a distinctive token (a $-amount or number) from the mapping question
// to disambiguate multi-threshold events.
function distinctiveToken(s: string): string | null {
const money = s.match(/\$[0-9]+(?:\.[0-9]+)?[bmk]?/i)
if (money) return money[0].toLowerCase()
const num = s.match(/[0-9]+m\b|[0-9]{2,}/i)
return num ? num[0].toLowerCase() : null
}

function safeParse(s?: string): string[] | null {
if (!s) return null
try {
const v = JSON.parse(s)
return Array.isArray(v) ? v.map(String) : null
} catch {
return null
}
}