Skip to content

Latest commit

 

History

20 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NEOS

One of the app's features lets you select NEOS 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 reading the generated ./api/neos static feed (NEOS ETF catalog, per-fund pages, official daily full-holdings CSV, NAV / market price / premium-discount, performance and distribution history, SEC EDGAR N-PORT-P and Yahoo Finance as fallbacks) into a searchable ETF/asset-class 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/Neos#main ./12345 && cd $_ && bunx serve . -p 1234
open http://0:1234

The published application is available at https://daggerok.github.io/Neos/. No build step, no bundler, no server — index.html carries the markup and a Babel-standalone bootstrap that transpiles app.tsx in the browser. The app reads only relative ./api/neos/** files generated by scripts/update-data.ts; the committed feed is complete, so the site works offline with no update run at all.

Updating the static NEOS data

Run the updater with Bun:

bun test scripts/update-data.test.ts
./scripts/update-data.ts

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

The Update NEOS ETF data GitHub Actions workflow (.github/workflows/update-data.yml, workflow_dispatch only) exposes the same settings as manual inputs and commits api/neos/** and nothing else — a data run never rewrites app code or CI config. All supplied filters use AND logic.

Data sources

Block Source Notes
Fund universe, ticker, fund name, declared distribution frequency, distribution rate, 30-Day SEC Yield, management fee, net assets, inception date, asset class neosfunds.com/#explore-etfs The server-rendered "Explore Our ETFs" table, which is also what fills the site's own tabs: Equity High Income, Boosted High Income, High Income Alternatives, Hedged Equity Income, Enhanced Fixed Income
CUSIP, ISIN, NAV, market price, premium / discount, 30-Day Median Bid-Ask Spread, primary exchange, shares outstanding, underlying exposure, management fee, total annual fund operating expenses https://neosfunds.com/<ticker>/ The page's own Fund Details, Closing NAV Price, Closing Market Price and Premium / Discount panels. Underlying Exposure is absent on five funds (see below).
Holdings (daily) https://neosfunds.com/wp-admin/admin-ajax.php?action=download_holdings_csv&ticker=<TICKER> The exact URL the fund page's Download Full Holdings button calls (etf-pages.js → etf_ajax.ajax_url), so the file is the official daily holdings sheet: Date, Account, StockTicker, Cusip, SecurityName, Shares, Price, MarketValue, Weightings, NetAssets, SharesOutstanding, CreationUnits, MoneyMarketFlag
NAV-basis performance (1 Mo/3 Mo/6 Mo/YTD/1 Yr/3 Yr/5 Yr/10 Yr/Inception, cumulative and annualized), monthly and quarter-end, plus the closing-market-price row and the benchmark row The fund page Performance tables (#monthly-performance, #quarterly-performance) This feed publishes the NAV Performance row for every return metric, exactly as every other column-heading in the app assumes
Distribution history (declaration, ex-dividend, record, payable dates + amount per share) and the Distribution Information block (Distribution Rate, 12-Month Trailing Distribution Rate, Distribution Amount / Share in $ and %) The fund page Distributions tabs Copied verbatim from the year-tabbed calendar, newest first; a month that is declared but not yet paid keeps NEOS's empty amount in the JSON and the app prints — for that cell rather than a blank
Growth-of-$10,000-at-NAV series since inception The fund page Growth of $10,000 at NAV Since Inception chart (const dates / const navValues / const indexValues2) Stored as series metadata (start, end, point count); the app plots its own charts
Fund documents (prospectus, summary prospectus, SAI, annual and semi-annual report, fiscal Q1/Q3 portfolio holdings, tax insert, Form 8937) The fund page Documents table and Form 8937 tab One row per published document; a document NEOS has not published yet stays null and the app prints —
Daily price history (Date / Close / Adj Close / Volume) Yahoo Finance public chart API Used for the price-history sheets; the fund's own official NAV and market price are always preferred where NEOS publishes them
Holdings fallback SEC EDGAR Form N-PORT-P, NEOS ETF Trust CIK 0001848758 (file 811-23645) Used only when a holdings CSV is unavailable (EDGAR_FALLBACK=1); off by default because SEC EDGAR answers GitHub runner IPs with 403
Dividend-history fallback Yahoo Finance dividend events Only for a fund with no published Distribution History rows; the feed marks those rows distributions.fallback: true

neosfunds.com throttles bursts (the TLS connection is reset), so requests are globally paced by REQUEST_SLEEP (default 1.5s) with bounded retries; the runner defaults to 2s.

The daily holdings CSV

The download is a real CSV served by WordPress, and it is the same file a visitor gets from the fund page. scripts/update-data.ts includes a small, dependency-free RFC-4180 reader (parseCsv) that tolerates the BOM, CRLF and quoted fields.

NEOS publishes no asset-class column, so the Watchlist's Asset Category is derived from each row's own fields — Cash (MoneyMarketFlag=Y or the Cash&Other line), Option (an OCC contract code such as SPXW 261001P07075000), Treasury (a T-bill or note), Fund (an ETF/trust the fund holds) and otherwise Equity — and that derivation is documented, tested and never applied to any other value. Every other cell is the provider's own text, including a written option's negative market value and share count.

Cusip is the identifier the CSV publishes, so it fills the shared Identifier column; option and T-bill rows key on it because they have no exchange ticker. The per-row NetAssets, SharesOutstanding and CreationUnits columns (identical on every row) are lifted to fund level.

Return arithmetic

The page publishes annualized 3 Yr / 5 Yr / 10 Yr figures and a cumulative Inception figure. The catalog carries both readings, computed from the published one with (1 + r)^n - 1 or (1 + TR)^(1/n) - 1, and returns.derivedFrom records that the basis is NEOS's own NAV Performance table. Nothing is extrapolated past a tenor the fund has not lived through: a fund younger than its 5-year mark publishes null, and the app prints —.

Known value limitations

Metric Status Reason
YTD / 1 Yr / 3 Yr / 5 Yr / 10 Yr returns published from NEOS's own performance tables; — only for a tenor the fund is too young to report XBCI, XQQI and XSPI (Feb 2026 inception) have no YTD, 1 Yr or SI-annualized figure yet; 14 funds have no 3 Yr; 17 have no 5 Yr; SPYI is the 10 Yr case. This is exactly what the pages themselves print as -.
Underlying Exposure — for HYBI, IAUI, NIHI, QQQH and SPYH Those five fund pages carry an Investment Objective paragraph instead of the Underlying Exposure row; the feed publishes null rather than guessing an index from the fund's name
FY-Q3 Portfolio Holdings — for 9 funds Not published on the fund page yet
Annual Report / Supplemental Tax Information — for XBCI, XQQI and XSPI The three February-2026 funds have not filed a first annual report yet
Form 8937 — for IAUI, NIHI, XBCI, XQQI and XSPI Those five 8937 tabs are empty — NEOS has not filed one for them yet
12-Month Trailing Distribution Rate — for IAUI, MLPI, NEHI, NIHI, NLSI, SPYH, XBCI, XQQI and XSPI Their Distribution Information block publishes the Distribution Rate and the 30-Day SEC Yield only; the trailing row is absent even where the fund is older than 12 months (IAUI), so it is never inferred from the payout calendar
SEDOL / FIGI always — NEOS publishes neither (the holdings CSV carries a CUSIP, and the fund page CUSIP/ISIN), so the app shows the documented data limitation
Coupon / Maturity always — NEOS publishes no fixed-income holdings sheet with coupon or maturity columns
SEC Yield (30-day) official fund page figure, — where the page prints -- Every NEOS fund currently publishes one; negative values (e.g. QQQI -0.05%) are kept verbatim
Inception Date the lineup table's figure; the fund page's own panel date is kept as inceptionDateFundPage and explained by inceptionNote HYBI is the one fund where the two differ: the ETF converted from the WSTCM Credit Select Risk-Managed Fund on 09/27/2024 and assumed its performance, so the lineup table keeps 09/30/2014 — the date its published 5Y and 10Y returns are measured from
Premium / Discount the fund page's own Premium Discount (%) row The (Market Price - NAV) / NAV quotient is stored alongside as premiumDiscountDerivedValue, and is used only if a page ever omits the row

Update controls

Environment variable Default Meaning
MAX_FETCHES 0 Funds to process; 0 = full pass. A positive value resumes after the cursor in api/neos/update-state.json.
TICKERS "" Space/comma separated ticker allowlist; ANDed with the other filters, never overriding them.
CATEGORY "" Substring match on the NEOS asset-class group.
AUM / TER / DIVIDEND_YIELD / SEC_YIELD "" min:max ranges; AUM also accepts nano/micro/small/mid/large.
PERFORMANCE_YTD … PERFORMANCE_10Y / TOTAL_RETURN_YTD … TOTAL_RETURN_10Y "" min:max filters on the official NAV return, or on the derived cumulative total return.
CONCURRENCY / REQUEST_SLEEP 2 / 1.5 Politeness; neosfunds.com throttles bursts.
HOLDINGS_PAGE_SIZE / HISTORY_PAGE_SIZE 250 / 1000 Rows per generated JSON page.
MAX_RETRIES 3 Retries after the initial request for network errors and HTTP 408/425/429/5xx.
HISTORY_RANGE max Yahoo chart range for the price-history sheets (max, 10y, 5y, …).
EDGAR_FALLBACK off Use N-PORT-P when a holdings CSV is unavailable.
SKIP_YAHOO / SKIP_NEOS off Skip the Yahoo Finance or neosfunds.com fetch stages.
STORE_RAW_DOWNLOADS off Keep one raw sample under api/neos/raw.
SEC_UA repo URL Declared User-Agent for SEC EDGAR requests.

TICKERS combines with the AUM/TER/yield/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, so a bounded or partly failed run can never empty the site.

Examples

MAX_FETCHES=10 ./scripts/update-data.ts
TICKERS="SPYI QQQI CSHI" ./scripts/update-data.ts
AUM="1B:" TER=":0.70" ./scripts/update-data.ts
CATEGORY="Fixed Income" ./scripts/update-data.ts

TypeScript

The browser app is intentionally build-free: index.html carries the markup, styles and bootstrap, and app.tsx is TypeScript compiled in the browser with Babel standalone — no build step, no bundler, no tsconfig.json needed. Bun runs TypeScript out of the box.

Verification before every publish: bun install --frozen-lockfile, bun test, and git diff --check.

Brands table

Brand Where to get the data
abrdn (Aberdeen) aberdeeninvestments.com | aberdeen
Amplify amplifyetfs.com | Amplify
Fidelity fidelity.com | Fidelity
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
ProShares proshares.com | ProShares
Schwab schwabassetmanagement.com | Schwab
SPDR ssga.com | SPDR
VanEck vaneck.com | VanEck
Vanguard investor.vanguard.com | Vanguard
WisdomTree wisdomtree.com | WisdomTree

Sibling applications

Application Data provider Repository
abrdn (Aberdeen) Official Aberdeen gateway + SEC N-PORT holdings fallback + Yahoo history/dividends aberdeen
Amplify Amplify ETFs (Firestore data feed) Amplify
Fidelity SEC EDGAR N-PORT-P + Yahoo Finance Fidelity
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 CSV downloads + Yahoo Finance 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
ProShares proshares.com ETF finder + fund pages + official data host ProShares
Schwab schwabassetmanagement.com product pages + CSV exports Schwab
SPDR SSGA / State Street public feeds SPDR
VanEck vaneck.com ETF finder + product pages VanEck
Vanguard Vanguard product pages + SEC EDGAR N-PORT-P Vanguard
WisdomTree WisdomTree product table + SEC EDGAR N-PORT-P + Yahoo Finance WisdomTree

License

MIT — same as all sibling ETF repositories.

NEOS® and the fund names/tickers referenced here are trademarks of NEOS Investment Management LLC. This is an independent, unofficial tool; it is not affiliated with, endorsed by, or sponsored by NEOS. All data is reproduced from NEOS's own public pages and downloads, with public SEC EDGAR filings and Yahoo Finance as fallbacks, for research purposes. All other trademarks, including index names, are the property of their respective owners.

About

NEOS ETF. A single-file client-side tool reading ./api/neos (NEOS catalog, fund pages, official daily full-holdings CSV, NAV performance, SEC EDGAR and Yahoo Finance fallbacks) into a searchable ETF/asset-class catalog with per-fund tabs, watchlist, CSV/TXT export. TailwindCSS, dark mode, Bun updater.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages