Skip to content
daggerokPublic

About

SP Funds ETF holdings-to-watchlist: static ./api/spfunds feed generated by a Bun updater from sp-funds.com homepage catalog, fund pages and daily holdings CSV, SEC EDGAR N-PORT-P holdings fallback, Yahoo Finance history/dividends

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

55 Commits

Folders and files

Repository files navigation

SP Funds

One of the app's features lets you select SP Funds ETFs in the Watchlist and aggregate their holdings to see how often each ticker appears across the selected funds. Repeated holdings make overlapping exposure visible: the more selected funds include a ticker, the greater its potential influence on the portfolio; gains in that holding may help, while declines may hurt, and actual impact also depends on each fund's position size. Another feature makes it faster and easier to find funds with stronger growth over different periods, higher dividend yields or distributions, greater Total Return (price performance plus dividends), and other key performance metrics. A single-file client-side tool that reads the generated ./api/spfunds static feed (official sp-funds.com homepage catalog and per-fund pages - legal name, identifiers, pricing, month-end/quarter-end returns, distribution history and document links - plus each fund's published daily holdings CSV, with SEC EDGAR N-PORT-P as a holdings fallback and Yahoo Finance daily prices/history/dividend fallbacks) into a searchable ETF catalog with per-fund tabs, watchlist aggregation, ticker copy and CSV/TXT export - the same look, feel, columns and business logic as the sibling applications.

Using Bun

bunx degit daggerok/SP-Funds#main ./12345 && cd $_
bun install
bun run serve

bun run serve starts the Parcel dev server (it copies api/ to dist/api first) and prints the local address. bun run build writes the production site into dist, and bun run build-github-pages does the same with the /SP-Funds/ public URL.

The application is live at https://daggerok.github.io/SP-Funds/ (GitHub Pages is deployed from main by .github/workflows/github-pages.yml, which builds dist and publishes it).

Column types and filters

Every column of the ETF catalog and of the Watchlist, Holdings, History and Distributions tabs has a type: text (ABC), number (123), percentage (%), money ($), date (D), date and time (DT) or time of day (T). The type is detected from the texts the column shows (80% of the filled cells must agree, otherwise text) and is written in the badge next to the column title: click it to cycle the type, Shift+click to return to auto-detection. Dates are read as 2024-06-15, 6/15/2024, 15.06.2024, Jun 15, 2024 or 15-Jun-2024, date and time as 2024-06-15T09:30:00Z or 2024-06-15 09:30, time as 09:30, 16:00:00 or 9:30 PM

A row of filter inputs sits under the column headers (the Filters button hides it, Clear filters empties it). Filters of different columns are combined with AND, the search box applies on top, and Copy Tickers and the exports use the filtered rows. Filters and type overrides are remembered in the browser. Sticky # (next to Filters, off by default, remembered in the browser) numbers the rows by their rank in the table sorted by the current column before the column filters, so a filtered fund keeps its rank and the numbers keep gaps; the sort, the search and the category and blacklist choices rank again. The catalog starts sorted by Net Assets, largest first, unavailable values sort last in both directions, and every export starts with the # column. The red Clear button opens a dialog that lists what can be reset (the selection, searches, sort order, open tab, shown columns, column filters, remembered table views and the blacklist), all ticked the first time and afterwards as they were left at the last OK; Enter confirms, Esc or a click outside cancels, the theme is always kept, so the page looks like a first visit (also after a reload)

Inside one filter: a space means AND, a comma means OR, a leading ! means NOT, ? matches an empty or unavailable value and !? a value that is there; a value that is unavailable matches only ? and negated conditions. An unquoted space ends the value, so quote values that contain one (>="2024-06-15 09:30")

Type Examples
Text bank contains, "two words", !bank, =exact, ^starts, ends$, /regex/, tech, health
Number, percentage, money >10, >=10 <50, =22 (matches what rounds to 22), !=22, 10..50, ..50, 10.., >1B and K M B T suffixes, an optional $ or %
Date, date and time >2024-06-01, 2024 (the whole year), 2024-06 (the whole month), 2024-01..2024-06, today, yesterday, -7d.. (the last 7 days), +2w, -3m, -1y
Time >09:30, 09:30..16:00, =12:00 (the whole minute)

The Columns menu next to Filters lists every column of the ETF table from the first to the last, all of them shown by default, with a search box and the All, Clear, Toggle and Reset buttons. Use and Ticker are listed but locked. Hiding a column only removes it from the table: the filters, the sorting, the exports and Copy Tickers still use it. The choice is remembered in the browser (localStorage, never the data) and the menu is shown on the ETF catalog only

The asset classes are one Asset classes multi-select next to the All ETFs pill instead of one tab per class: every class is selected by default (= all ETFs), Only or unchecking narrows the table, and the All ETFs pill is lit only while nothing narrows it (all or none of the classes checked); clicking the pill clears the selection. The choice is remembered in the browser (localStorage, never the data)

Updating the static SP Funds data

Run the updater with Bun:

bun test
./scripts/update-data.ts

Run ./scripts/update-data.ts -h (or --help) to print every configuration variable with its default and usage examples.

Defaults live in scripts/update-data.config.json (every control as a string). Precedence: file defaults < advanced JSON < nonblank individual inputs < protected Actions variable/environment. An explicitly set environment variable always wins, even when empty, and invalid values fail with a clear message instead of falling back silently. The Update SP Funds ETF data GitHub Actions workflow runs on Sundays at 00:00 UTC and on demand, and resolves controls with the same resolveControls function as the CLI: individual workflow_dispatch inputs are blank by default and inherit the file, and the advanced input accepts a JSON object with any control (for example {"EDGAR_FALLBACK":"false","VERBOSE":"true"}; GitHub allows at most 25 inputs). The updater only writes below api/spfunds. All supplied filters use AND logic, and pacing stays polite at 1 second per lane with 2 lanes because sp-funds.com is not WAF-protected.

Data sources

Block Source
Catalog (all five SP Funds ETFs) Official sp-funds.com "Our ETFs" homepage menu - SPUS, SPRE, SPSK, SPTE, SPWO; a built-in five-fund lineup is retained if the homepage markup changes.
Holdings per fund Official daily CSV https://www.sp-funds.com/wp-content/uploads/data/TidalFG_Holdings_{TICKER}.csv (Date, Account, StockTicker, CUSIP, SecurityName, Shares, Price, MarketValue, Weightings, NetAssets, SharesOutstanding, CreationUnits); e.g. SPUS.
Fund page facts https://www.sp-funds.com/<ticker-lowercase>/: Fund Details (inception, primary exchange, CUSIP, expense ratio, 30-day SEC yield), Fund Details & Pricing (NAV, net assets, shares outstanding, premium/discount, closing price), Performance Statistics (Month-End and Quarter-End tables), Distribution Details and the documents block. The homepage menu link wins over a homepage card that points at a section page.
Daily history, distributions Yahoo Finance chart API (daily close/adjusted close/volume and dividends, adjusted closes rounded to two decimals); the fund page's Distribution Details table supplies the published distribution calendar and frequency when present.
Fallback SEC EDGAR N-PORT-P holdings only (Tidal Trust I, CIK 0001742912, for SPUS/SPRE/SPSK; SP Funds Trust, CIK 0001989916, for SPTE/SPWO - exact series/registrant matching) + previously published data as the last resort.

SEC endpoints have answered HTTP 403 to GitHub-hosted runners, so the N-PORT-P fallback is exercised offline against a small inline sample and is used only when the issuer's daily CSV is unavailable. Yahoo's adjusted close is recomputed server-side and can move by one cent between otherwise identical requests, so a repeat run may rewrite a history page without any other change.

Metrics and caveats

  • Holdings come from the issuer's own daily CSV, which publishes no asset-class column; the Watchlist category is derived from the row itself (cash lines Cash, options Option, fund units Fund, everything else Equity). Cash cushions keep the provider's negative weight and value
  • SPRE holds foreign listings; their exchange-suffixed tickers (for example GMG AU) are kept as published
  • Returns are the official month-end NAV figures as published (3Y/5Y/10Y annualized); cumulative figures are derived from them. A tenor longer than the fund's age at the table date (for example 3 years for SPTE and SPWO, which began in November and December 2023) is published by the site as 0.00 or - and is left empty here, never 0: every tenor is checked as inception + N years <= table date
  • Daily history, closing prices beyond the page's own close and dividends are Yahoo Finance market data; the distribution calendar, frequency and indicated yield are derived estimates, not issuer-reported yields. Official NAV, net assets and the 30-day SEC yield come from the fund page with their as-of dates
  • HISTORY_RANGE narrows the Yahoo request window (explicit period1/period2; period2 is now + 1 day so the latest bar is included). Published rows older than the window are kept and only the window is refreshed
  • Fund-level consistency: every fund is either fully updated or fully kept. If the fund page, the holdings (CSV, then an N-PORT-P filing that is not older than the published holdings) or the Yahoo history fails for a fund that already has published files, that fund stays exactly as published and the run reports it; when every processed fund is kept this way the run exits non-zero. A fund page that loads without one of its tables keeps the published values of that section as a unit (returns together with performanceAsOf and returnsBasis); a table that is present with a blank cell publishes the blank
  • Writes are atomic (temp file + rename) and ordered: pages, then meta.json, then the index; stale pages are removed after the new meta.json. A rerun with identical upstream data writes nothing (generatedAt moves only with content). The run stops taking new funds after 25 minutes and still writes the index; unprocessed funds without files have dataFile: null and an all-null metrics object. New lineup tickers print NEW FUNDS: ... and go to the step summary
  • Published shapes: history rows are oldest first with Sep 30 2026 dates, holdings Weight (percent) and Market Value (USD) are plain numbers, generatedAt has no milliseconds, index rows carry closePriceValue, premiumDiscountValue and terGrossValue. terValue is the net expense ratio (the only one SP Funds publishes), terGrossValue stays null unless a gross figure appears on the page. A semi-annual distribution frequency means 2 payments per year. secYield may legitimately be a published 0.00
  • No ticker exclusions: the five ETFs are the lineup; the site's six target-date mutual funds are out of scope

Each fund carries a derived metrics object that powers the catalog columns shared with the sibling sites:

  • ytd / tr1y - official NAV YTD and 1-year returns -> YTD Return, TR 1Y

  • cagr3y / cagr5y / cagr10y - official annualized 3Y/5Y/10Y figures -> CAGR 3Y/5Y/10Y

  • tr3y / tr5y / tr10y - cumulative 3Y/5Y/10Y figures (1 + CAGR)^n - 1 -> TR 3Y/5Y/10Y

  • siAnn - since-inception annualized NAV return when the site publishes it

  • dividendYield - indicated distribution rate (latest distribution × frequency ÷ NAV) when the page publishes no explicit rate; — otherwise

  • dividendYieldBasis - code of the definition behind dividendYield, null exactly when dividendYield is null; here only indicated (SP Funds publishes no distribution yield). The other codes of the shared standard are not produced:

    Code Meaning Used here
    official-trailing-12m provider-published trailing 12-month distribution yield no
    official-distribution-rate provider-published distribution rate no
    official-other provider-published yield with another or unclear definition no
    computed-trailing-12m updater sums the last 12 months of distributions over price/NAV no
    indicated latest distribution x inferred payments per year / NAV yes
  • secYield - 30-day SEC yield when published; — otherwise

  • returnsBasis - mandatory non-empty label of how the returns are computed: here always the official SP Funds month-end NAV total returns from the fund page performance table, with the cumulative 3/5/10-year figures derived from the published annualized ones

  • performanceAsOf - mandatory ISO YYYY-MM-DD date of that performance table (the month-end Date column), not the NAV date; null only when the table date is unknown. Both fields are the last two keys of metrics

Update controls

Defaults below are from scripts/update-data.config.json; blank Actions inputs do not override them. The individual workflow inputs cover every control except SEC_UA, EDGAR_FALLBACK, VERBOSE and USE_SYSTEM_CA, which are set through advanced.

Environment variable Default Meaning
MAX_FETCHES 0 Batch evaluation size: positive resumes the scoped cursor in api/spfunds/update-state.json; 0 is a full selected pass. A failed fund prevents cursor advancement
REQUEST_SLEEP 1 Minimum seconds between outgoing request starts in each independent lane, including retries
CONCURRENCY 2 Parallel fund workers (integer >= 1), each with its own paced request lane. CONCURRENCY=15 ./scripts/update-data.ts fetches up to 15 funds at once; the lineup has five funds, so at most five run together
TICKERS all Space/comma/semicolon allowlist, e.g. SPUS SPSK SPTE. Unknown requested tickers fail before writes
AUM : Net Assets range: USD amounts or K/M/B/T suffixes; nano/micro/small/mid/large presets; inclusive min:max
TER : Net expense ratio (terValue) range in % (strict min:max); funds without a published ratio do not pass an active range
DIVIDEND_YIELD : Distribution-yield range in %, min:max; missing values do not pass an active range
SEC_YIELD : Official 30-day SEC-yield range in %, min:max; missing values do not pass
HOLDINGS_PAGE_SIZE 250 Current holdings rows per JSON page
HISTORY_PAGE_SIZE 1000 Daily history rows per JSON page
MAX_RETRIES 2 Retries after the initial request, integer >= 1 (transient HTTP/network failures only)
HISTORY_RANGE max Yahoo Finance history window: max or <N>y (for example 5y); applied to the request as explicit period1/period2 and replaces the published daily history with that window
PERFORMANCE_YTD : YTD performance percent min:max (3Y/5Y/10Y annualized)
PERFORMANCE_1Y : 1Y performance percent min:max (3Y/5Y/10Y annualized)
PERFORMANCE_3Y : 3Y performance percent min:max (3Y/5Y/10Y annualized)
PERFORMANCE_5Y : 5Y performance percent min:max (3Y/5Y/10Y annualized)
PERFORMANCE_10Y : 10Y performance percent min:max (3Y/5Y/10Y annualized)
TOTAL_RETURN_YTD : YTD cumulative total return percent min:max
TOTAL_RETURN_1Y : 1Y cumulative total return percent min:max
TOTAL_RETURN_3Y : 3Y cumulative total return percent min:max
TOTAL_RETURN_5Y : 5Y cumulative total return percent min:max
TOTAL_RETURN_10Y : 10Y cumulative total return percent min:max
SEC_UA daggerok ETF feed daggerok@gmail.com SEC EDGAR User-Agent contact; redacted in logs. The repository Actions variable SEC_UA overrides it in the workflow
SKIP_SPFUNDS false Skip the official sp-funds.com fund pages (keeps published facts); alias SKIP_SP_FUNDS
SKIP_YAHOO false Skip Yahoo Finance (keeps published history/dividends)
EDGAR_FALLBACK true Use SEC EDGAR N-PORT-P when the issuer's daily holdings file is unavailable
VERBOSE false Provider, fallback and retry detail; the compact fund reporter always retains real zero/false values and omits missing fields
USE_SYSTEM_CA auto TLS trust store: auto restarts the updater once with Bun's --use-system-ca when a request fails with an untrusted-certificate error; true always uses the system CA store; false never restarts. Not an individual workflow input: use advanced, the config file or the CLI environment.

TICKERS combines with AUM, TER, yield and return filters using AND logic; it does not override them. Funds not selected for a successful update keep their prior published metadata and data files.

Examples

MAX_FETCHES=10 ./scripts/update-data.ts
TICKERS="SPUS SPSK SPTE" ./scripts/update-data.ts
AUM="1B:" TER=":0.5" ./scripts/update-data.ts
PERFORMANCE_1Y="15:" ./scripts/update-data.ts
HISTORY_RANGE=5y CONCURRENCY=15 ./scripts/update-data.ts

TypeScript and verification

The browser app lives in src/: index.html carries the markup and bootstrap, main.tsx is the TypeScript app and index.css holds Tailwind v4 and the component styles. Parcel bundles them into dist (bun run build) - no tsconfig.json is needed. Bun runs the updater TypeScript out of the box.

Verification before every publish:

bun install --frozen-lockfile
bun test
bun build --target=bun scripts/update-data.ts --outfile=/dev/null
git diff --check

Brands table

Brand Where to get the data
AAM aamlive.com | AAM
abrdn (Aberdeen) aberdeeninvestments.com | aberdeen
Amplify amplifyetfs.com | Amplify
ARK Invest ark-funds.com | ARK
Capital Group capitalgroup.com | Capital-Group
Fidelity fidelity.com | Fidelity
First Trust ftportfolios.com | First-Trust
Franklin Templeton franklintempleton.com | Franklin
Global X globalxetfs.com/explore | Global-X
Goldman Sachs am.gs.com | Goldman-Sachs
Invesco invesco.com | Invesco
iShares ishares.com | iShares
JPMorgan am.jpmorgan.com | JPMorgan
NEOS neosfunds.com | Neos
Northern Trust etfs.ntam.northerntrust.com | Northern-Trust
Pacer ETFs paceretfs.com | Pacer
Parametric eatonvance.com | Parametric
ProShares proshares.com | ProShares
Schwab schwabassetmanagement.com | Schwab
SP Funds sp-funds.com | SP-Funds
SPDR ssga.com | SPDR
Sprott ETFs sprottetfs.com | Sprott
Tema ETFs temaetfs.com | Tema
Themes ETFs themesetfs.com/etfs | Themes
VanEck vaneck.com | VanEck
Vanguard investor.vanguard.com | Vanguard
VictoryShares vcm.com VictoryShares ETFs | VictoryShares
WisdomTree wisdomtree.com | WisdomTree
Xtrackers etf.dws.com | Xtrackers

Sibling applications

Application Data provider Repository
AAM Official AAM catalog/detail HTML + full holdings XLS + SEC N-PORT holdings fallback + Yahoo market history/dividends AAM
abrdn (Aberdeen) Official Aberdeen gateway + SEC N-PORT holdings fallback + Yahoo history/dividends aberdeen
Amplify Amplify ETFs Firestore data feed + SEC EDGAR N-PORT-P holdings fallback + Yahoo Finance history/dividends Amplify
ARK Invest ark-funds.com fund pages + overview/NAV-history/performance JSON + official daily holdings CSV + SEC EDGAR N-PORT-P holdings fallback + Yahoo Finance distributions/history fallback ARK
Capital Group Official Capital Group fund data + SEC N-PORT holdings fallback + Yahoo history fallback Capital-Group
Fidelity SEC EDGAR N-PORT-P + Yahoo Finance Fidelity
First Trust ftportfolios.com official ETF list + fund summary, holdings, distribution and price-history export pages + SEC EDGAR N-PORT-P holdings fallback + Yahoo Finance history fallback First-Trust
Franklin Templeton franklintempleton.com ETF listings + product pages + SEC EDGAR N-PORT-P Franklin
Global X globalxetfs.com Next.js catalog and fund pages + dated full-holdings CSV Global-X
Goldman Sachs am.gs.com fund finder + detail pages + SEC EDGAR N-PORT-P Goldman-Sachs
Invesco invesco.com fund pages and sitemap + official Invesco fund API (monthly returns, NAV, AUM, yields, daily holdings, expense ratio) + SEC EDGAR N-PORT-P holdings fallback + Yahoo Finance history/dividends Invesco
iShares iShares (BlackRock) product workbooks iShares
JPMorgan am.jpmorgan.com fund explorer + product-data JSON JPMorgan
NEOS neosfunds.com lineup table + official fund pages + daily holdings CSV Neos
Northern Trust etfs.ntam.northerntrust.com funds list + per-fund CSV/JSON downloads Northern-Trust
Pacer ETFs paceretfs.com product catalog and fund pages (Cloudflare WAF; r.jina.ai proxy fallback) + SEC EDGAR N-PORT-P (Pacer Funds Trust) + Yahoo Finance history/dividends Pacer
Parametric eatonvance.com ETF catalog and Parametric product pages + SEC EDGAR N-PORT-P holdings + Yahoo Finance history/dividends Parametric
ProShares proshares.com ETF finder + fund pages + official data host ProShares
Schwab schwabassetmanagement.com product pages + CSV exports Schwab
SP Funds sp-funds.com homepage catalog, fund pages and daily holdings CSV + SEC EDGAR N-PORT-P holdings fallback + Yahoo Finance history/dividends SP-Funds
SPDR SSGA / State Street public feeds SPDR
Sprott ETFs sprottetfs.com fund pages + SEC EDGAR N-PORT-P (Sprott Funds Trust) + Yahoo Finance history/dividends Sprott
Tema ETFs Tema official fund pages + dated daily holdings CSV; SEC EDGAR N-PORT-P holdings fallback only + Yahoo Finance price/history/dividend fallback Tema
Themes ETFs themesetfs.com catalog + daily holdings CSV + Yahoo Finance history/dividends + SEC N-PORT-P holdings fallback Themes
VanEck vaneck.com ETF finder + product pages VanEck
Vanguard Vanguard product pages + SEC EDGAR N-PORT-P Vanguard
VictoryShares VCM VictoryShares catalog and product JSON + SEC EDGAR N-PORT-P holdings fallback + Yahoo Finance adjusted-market-price history VictoryShares
WisdomTree WisdomTree product table + SEC EDGAR N-PORT-P + Yahoo Finance WisdomTree
Xtrackers Official DWS catalog/US sitemap + PDP/XLSX + SEC N-PORT-P holdings fallback + Yahoo Finance daily prices/history/dividends Xtrackers

License

MIT — same as all sibling ETF repositories.

SP Funds, the fund names and tickers referenced here, and all index names are the property of their respective owners. This is an independent, unofficial tool; it is not affiliated with, endorsed by, or sponsored by SP Funds or the funds' trusts. All data is reproduced from sp-funds.com's own public fund pages and downloads, public SEC EDGAR filings and Yahoo Finance for research purposes. All other trademarks are the property of their respective owners.

About

SP Funds ETF holdings-to-watchlist: static ./api/spfunds feed generated by a Bun updater from sp-funds.com homepage catalog, fund pages and daily holdings CSV, SEC EDGAR N-PORT-P holdings fallback, Yahoo Finance history/dividends

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages