Oracle3
Open-source trading engine and MCP server for prediction markets. Fee-aware no-arbitrage trading across related contracts, live on Kalshi, Polymarket and Solana or on paper.
provide up to date information about new companies backed by venture capital and private equity funds
Translation pending
FinanceTypeScript · SvelteGitHub @patrik64OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
Up-to-date information about companies backed by 837 venture capital and private
equity funds. Scrapes each fund's public portfolio page, stores companies in
Supabase (postgres), and highlights newcomers — companies that appeared in a
fund's portfolio since the last fetch.
Built with SvelteKit 2, Svelte 5, remult and Tailwind CSS 4.Read the full README ↗Up-to-date information about companies backed by 1009 venture capital and private equity funds. Scrapes each fund's public portfolio page, stores companies in Supabase (postgres), and highlights newcomers — companies that appeared in a fund's portfolio since the last fetch.
Built with SvelteKit 2, Svelte 5, remult and Tailwind CSS 4.
live at https://portfolio-alert.vercel.app
https://bsky.app/profile/portfolio-alert.bsky.social
https://portfolio-alert.vercel.app/rss.xml
/) — one card per fund with company count, newcomer badge and
last-fetch time; the whole card links to the fund's detail page. A fetch
all button runs every scraper (5 at a time), and each card has its own
refresh button./funds/[slug]) — all companies of a fund with search over
name and category; the fund name links to the fund's own portfolio page./newcomers) — companies found by the latest fetch that were
not in the database before, grouped by fund. The very first fetch of a fund is
a baseline import and is not counted as newcomers. A clean button
acknowledges the current newcomers and empties the list until the next fetch
finds something new./search) — searches all companies across every fund by name or
category (server-side, debounced), listing name, category, fund and first-seen
date./timeline) — every company first seen since the nightly
refresh went live (20 Aug 2026), grouped by the day it appeared and then by
fund, newest day first, in collapsible sections. The start date lives in
src/shared/timeline.ts./statistics) — a month's newcomers in bar charts: the ten
funds that added the most companies, the ten categories most of them are
filed under, the companies found each day, the stages the funds came in at
and what the companies' addresses end in, with the companies more than one
fund added and every month side by side. A picker, or ?month=2026-09,
chooses the month; each chart has its numbers as a table under it./download) — downloads a JSON file with all companies grouped
by fund (name, category, url and first-seen date per company)./about) — what the app does and how the pages fit together./rss.xml) — the nightly newcomer digests as a feed: one item per
night that found some, and per fund let through by hand, naming every
company under its fund.Every company row shows when the company was first encountered ("first seen").
GET /api/v1/companies — the companies the funds' portfolio pages list,
filtered, one entry per company — a company several funds back comes once,
naming them all — newest first. By default it answers the recent window: the
companies that appeared on the pages in the last two weeks (not the ones a
fund's baseline import brought in). With scope=all it answers from the whole
catalogue, baseline portfolios included, for a fund, a name or a category.
Public and read-only; each distinct query is cached for an hour. A misspelt or
malformed parameter gets a 400 that names the problem and lists every
parameter.
| parameter | meaning |
|---|---|
scope | new (default): the companies first seen in the window; all: every company the funds list — needs a fund, name or category to narrow it |
days | the window: companies first seen in the last n days, 1–90 (default 14) |
since | the window from an ISO date or time instead, at most 90 days back |
name | words in the company name, comma-separated, any of them — whole words in any case; a space or hyphen within a word also matches none (open ai finds OpenAI and Open-AI); a trailing * matches word beginnings (neuro*) |
category | words in the category a fund files the company under — its sectors, stage, country, year invested (fintech, series a, 2026) — matched as in name |
exclude | words that rule a company out, in its name or category, matched as in name |
fund | fund slugs, any of them (the slug of each fund is in /api/funds) |
exited | only: companies a fund marks as exited (acquired, listed or otherwise out of); none: the ones still held; any (default) |
limit, offset | paging: up to 500 a page (default 100); the answer links the next page |
format | json (default) or md, a markdown list of links grouped by fund |
Each entry in the JSON answer carries the company's name, url — its site as
the first fund to list it gives it — and funds: every fund backing it, each
with its slug and name, the category its page files the company under, the
same split into tags, the fund's own link and when it first saw the company.
exited says whether any fund marks it so, and firstSeenAt is the earliest
sighting. The answer also echoes the query as understood, the total and the
window.
The health and healthcare companies that turned up in the last month, as a list of links (a whole word matches only itself, so both are named):
curl 'https://portfolio-alert.vercel.app/api/v1/companies?days=30&category=health,healthcare&format=md'
Insight Partners' whole portfolio, the companies it is out of left aside:
curl 'https://portfolio-alert.vercel.app/api/v1/companies?scope=all&fund=insight&exited=none'
pnpm install
Put the Supabase postgres connection string in .env:
DATABASE_URL=postgresql://postgres.<project-ref>:<password>@aws-1-<region>.pooler.supabase.com:6543/postgres
Without DATABASE_URL, remult falls back to JSON files in ./db/ — handy for
local experiments. Tables are created automatically on first use.
pnpm dev # start the dev server
pnpm check # typecheck (svelte-check)
pnpm test-scrapers # smoke-test all 1009 scrapers outside the app
pnpm test-scrapers townhall sequoia # ...or just some of them
pnpm build # production build
The app is deployed on Vercel at https://portfolio-alert.vercel.app
(DATABASE_URL is configured as a sensitive environment variable on the
Vercel project for Preview and Production).
vercel # preview deploy
vercel --prod # production deploy
The deployed app and local dev share the same database. A fetch-all started from the browser is best run locally — the slowest scrapers take minutes and can exceed serverless function duration limits — while the nightly job below drives production one fund at a time, which stays inside them.
.github/workflows/daily-fetch.yml refreshes every fund at 22:23 UTC (around
midnight in Vienna); the workflow can also be run by hand from the Actions
tab.
pnpm fetch-all # refresh every fund against production
pnpm fetch-all --only=a16z,gv # ...or just some of them
pnpm post-newcomers --dry-run # compose the bluesky post, post nothing
pnpm post-newcomers # ...and publish it
pnpm post-newcomers --current # ...covering the whole newcomers page
pnpm post-newcomers --check # prove the app password still works
scripts/fetch-all.mjs calls the same fetchFund endpoint the dashboard's
buttons use, five funds at a time, and leaves what each fund gained in
fetch-results.json. A single stubborn site (a firewall refusing datacenter
addresses, say) does not turn the night red — only a broad failure does. A site
that refuses them outright is marked local: true in src/shared/funds.ts: the
nightly leaves the fund out, the dashboard says so, and it is refreshed from a
laptop against a local dev server now and then:
BASE_URL=http://localhost:5173 pnpm fetch-all --only=sante,speedinvest,stripes
pnpm fetch-all --only=stripes --dry-run # the scrape alone, from production,
# to see whether the site relented
scripts/post-newcomers.mjs then announces the finds on Bluesky as
@portfolio-alert.bsky.social,
naming every company and linking it to its own site. A long list continues into
a thread, and a flood day turns into a count per fund instead. Quiet nights stay
quiet. It needs one repository secret:
gh secret set BLUESKY_APP_PASSWORD # an app password, never the account password
Without that secret the step is a no-op, so the refresh keeps working on its own. Running the workflow by hand offers a skip the refresh, just announce the current newcomers switch, for announcing finds that a run has already recorded.
The same digests are served as an RSS feed at /rss.xml, straight from the
database and covering the same stretch as the timeline: one item per night
that found newcomers, with the companies named under their funds and linked to
their sites. Newcomers that land more than an hour apart (the most a run may
take) are separate items, so a fund let through by hand the next morning gets
an item of its own rather than growing the night's. The newest item is held
back until an hour after its last newcomer, when nothing more can join it, so
readers never cache a half-announced one.
A fund that turns up more than 30 new companies at once is held back: the fetch fails with a note saying so, nothing is stored, and its earlier newcomers stay flagged. That is what a redesigned page suddenly listing its whole back catalog looks like, and it would otherwise be announced as news. A held fund is let through from a local dev server, either as newcomers when the flood is real (an accelerator's new cohort, say) or as baseline when it is a back catalog, which takes the companies in the way a first fetch does, out of the newcomers, the timeline and the feed:
BASE_URL=http://localhost:5173 pnpm fetch-all --only=techstars --accept=newcomers
BASE_URL=http://localhost:5173 pnpm fetch-all --only=collabfund --accept=baseline
Newcomers let through this way are in no night's results, so the nightly post never names them: they are announced by running the workflow by hand with the announce switch above and the fund named, before the next night clears their flags.
The statistics page draws what /statistics.json serves: every month since
the timeline began, its newcomers counted on the server (src/server/statistics.ts)
so that the browser gets a few kilobytes of totals rather than every row. The
newcomers are the feed's — nothing a baseline import brought in, nothing from
before 20 Aug 2026 — and a month is a month of the nightly job's calendar
(Europe/Vienna), so a night's finds stay together. The first month is counted
from the 20th and the latest is still running; the page says so.
A fund's category is a comma-joined list of whatever its page says about a
company: sectors, but also the stage, the cohort or the year it came in, where
it is based and whether it is still held. src/server/tags.ts tells them
apart, so that the top categories are sectors rather than "Fall 2026" and
"Seed": years, cohorts, places, exits and the funds' own vehicles are left
out, the stages are counted on their own, and a category's spellings ("FinTech",
"Fintech"; "AI/ML", "Artificial Intelligence") count together. The patterns
know the labels common across the funds; one fund's own word for a programme
can still pass for a category.
The charts are Chart.js bar charts behind a small
wrapper (src/lib/charts): one colour, the count written on each bar, and the
same numbers as a table under every chart.
The registry in src/server/scrapers/index.ts maps fund slugs (see src/shared/funds.ts) to
scraper implementations. Techniques vary per fund — server-rendered HTML,
paginated listings, WordPress/GraphQL/Typesense APIs — and some scrapers visit
each company's detail page to pick up sector and website (batched; e.g.
Sequoia, Insight, Fil Rouge).
Company identity is (fund, normalized name) — urls and categories are too
inconsistent across funds to key on. Scrapers break when sites redesign; a
failing fund shows its error on the dashboard card while the other fetches
continue.