Skip to content

Instantly share code, notes, and snippets.

@Beliavsky
Last active May 1, 2018 19:19
Show Gist options
  • Save Beliavsky/16acfafe60b3fb1c5e1f41298497c79d to your computer and use it in GitHub Desktop.
Save Beliavsky/16acfafe60b3fb1c5e1f41298497c79d to your computer and use it in GitHub Desktop.
Closed-End Fund quotes
<!-- 05/01/2018 10:31 AM display quotes for all CEFs -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="utf-8">
<title>Closed-End Funds</title>
<link rel="icon" href="https://d3v3cbxkdlyonc.cloudfront.net/stocks/favicon.ico">
<meta name="description" content="A free, lightweight, blazing-fast page to get stock quotes using the IEX API">
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
"Oxygen", "Ubuntu", "Helvetica Neue", Arial, sans-serif;
}
table { font-family: Courier, monospace; }
.stocks-container {
margin-bottom: 1.5em;
width: 100%;
max-width: 600px;
}
.stocks-container a { text-decoration: none; }
table {
border-collapse: collapse;
width: 100%;
font-size: 1.1em ;
}
.stock-symbol {
width: 12%;
padding: 2px 4px 2px 0px;
}
.stock-price, .stock-change, .stock-change-pct, .stock-mkt-cap {
width: 22%;
text-align: right;
padding: 2px 4px;
}
@media (max-width: 576px) {
table { margin-bottom: 3em; }
.stock-mkt-cap { display: none; }
.stock-symbol { width: 16%; }
.stock-price, .stock-change, .stock-change-pct { width: 28%; }
td.stock-symbol, td.stock-price, td.stock-change, td.stock-change-pct {
padding-top: 1em;
padding-bottom: 1em;
}
}
summary:hover { cursor: pointer; }
summary::-webkit-details-marker { display: none; }
</style>
</head>
<body>
<div class="stocks-container"></div>
<p class="attribution">
Data provided subject to <a href="https://iextrading.com/api-exhibit-a/" target="_blank">IEX Exhibit A</a>.
</p>
<p class="updated-timestamp"></p>
<p>Customize <a href="https://github.com/toddwschneider/stocks" target="_blank">on GitHub</p>
<script>
'use strict';
// categories below are from CEF Connect 2018-05-01
const PORTFOLIOS = [
{'name': 'ETFs', 'symbols': ['SPY', 'TLT', 'MUB']},
{'name': 'Non-US/Other-Asia Equity', 'symbols': ['APB', 'APF', 'CAF', 'CHN', 'GCH', 'GRR', 'IAE', 'IF', 'IFN', 'IIF', 'JEQ', 'JOF', 'KF', 'SGF', 'TDF', 'TWN']},
{'name': 'Non-US/Other-Emerging Market Equity', 'symbols': ['ABE', 'CEE', 'EMF', 'IHD', 'MSF']},
{'name': 'Non-US/Other-Emerging Market Income', 'symbols': ['BGIO', 'EDD', 'EDF', 'EDI', 'EMD', 'JEMD', 'MSD', 'TEI']},
{'name': 'Non-US/Other-Global Equity', 'symbols': ['BST', 'GDL', 'GGT', 'GGZ', 'GLQ', 'IDE', 'INF', 'RGT']},
{'name': 'Non-US/Other-Global Equity Dividend', 'symbols': ['AGD', 'AOD', 'EOD', 'HEQ', 'IID']},
{'name': 'Non-US/Other-Global Growth & Income', 'symbols': ['CGO', 'CHW', 'DEX', 'EGIF', 'FEO', 'GLO', 'GLV', 'JDD', 'LCM', 'LGI', 'LOR', 'SCD', 'ZF']},
{'name': 'Non-US/Other-Global Income', 'symbols': ['BWG', 'EHI', 'FAM', 'FAX', 'FCO', 'GIM', 'JGH', 'KMM', 'KST', 'MCR', 'MIN', 'MMT', 'PPT', 'RCS']},
{'name': 'Non-US/Other-Latin American Equity', 'symbols': ['CH', 'CUBA', 'LAQ', 'LDF', 'MXE', 'MXF']},
{'name': 'Non-US/Other-Other Non-US Equity', 'symbols': ['EEA', 'FDEU', 'GF', 'IAF', 'IRL', 'ISL', 'SWZ']},
{'name': 'Non-US/Other-undefined', 'symbols': ['VCF']},
{'name': 'Tax-Free Income-Arizona', 'symbols': ['MZA', 'NAZ']},
{'name': 'Tax-Free Income-California', 'symbols': ['AKP', 'BFZ', 'BJZ', 'CCA', 'CEV', 'EIA', 'EVM', 'MCA', 'MUC', 'MYC', 'NAC', 'NBW', 'NCA', 'NCB', 'NKX', 'NXC', 'PCK', 'PCQ', 'PZC', 'VCV']},
{'name': 'Tax-Free Income-Connecticut', 'symbols': ['NTC']},
{'name': 'Tax-Free Income-Florida', 'symbols': ['BFO']},
{'name': 'Tax-Free Income-Georgia', 'symbols': ['NKG']},
{'name': 'Tax-Free Income-High Yield', 'symbols': ['CMU', 'CXE', 'MAV', 'MFM', 'MHI', 'NMZ']},
{'name': 'Tax-Free Income-Maryland', 'symbols': ['BZM', 'NMY']},
{'name': 'Tax-Free Income-Massachusetts', 'symbols': ['MAB', 'MHE', 'MMV', 'NMT']},
{'name': 'Tax-Free Income-Michigan', 'symbols': ['EMI', 'MIW', 'MIY', 'NUM']},
{'name': 'Tax-Free Income-Minnesota', 'symbols': ['NMS', 'VMM']},
{'name': 'Tax-Free Income-Missouri', 'symbols': ['NOM']},
{'name': 'Tax-Free Income-National', 'symbols': ['AFB', 'BAF', 'BBF', 'BBK', 'BFK', 'BKK', 'BKN', 'BLE', 'BPK', 'BSD', 'BTA', 'BTT', 'BYM', 'CXH', 'DMB', 'DMF', 'DSM', 'DTF', 'EIM', 'EIV', 'EOT', 'ETX', 'EVN', 'FMN', 'IIM', 'IQI', 'KSM', 'KTF', 'LEO', 'MEN', 'MFL', 'MFT', 'MHD', 'MHF', 'MMD', 'MMU', 'MNP', 'MQT', 'MQY', 'MTT', 'MUA', 'MUE', 'MUH', 'MUI', 'MUS', 'MVF', 'MVT', 'MYD', 'MYF', 'MYI', 'MZF', 'NAD', 'NBH', 'NEA', 'NEV', 'NHA', 'NID', 'NIM', 'NIQ', 'NMI', 'NUV', 'NUW', 'NVG', 'NXP', 'NXQ', 'NXR', 'NZF', 'OIA', 'PMF', 'PML', 'PMM', 'PMO', 'PMX', 'SBI', 'VFL', 'VGM', 'VKI', 'VKQ', 'VMO']},
{'name': 'Tax-Free Income-New Jersey', 'symbols': ['BLJ', 'BNJ', 'EMJ', 'EVJ', 'MUJ', 'MYJ', 'NJV', 'NXJ']},
{'name': 'Tax-Free Income-New York', 'symbols': ['BFY', 'BLH', 'BNY', 'BQH', 'BSE', 'ENX', 'EVY', 'MHN', 'MNE', 'MYN', 'NAN', 'NBO', 'NNY', 'NRK', 'NXN', 'NYH', 'NYV', 'PNF', 'PNI', 'PYN', 'VTN']},
{'name': 'Tax-Free Income-North Carolina', 'symbols': ['NNC']},
{'name': 'Tax-Free Income-Ohio', 'symbols': ['EIO', 'EVO', 'NUO']},
{'name': 'Tax-Free Income-Pennsylvania', 'symbols': ['EIP', 'EVP', 'MPA', 'NPN', 'NQP', 'VPV']},
{'name': 'Tax-Free Income-Texas', 'symbols': ['NTX']},
{'name': 'Tax-Free Income-Virginia', 'symbols': ['BHV', 'NPV']},
{'name': 'Taxable Income-Convertible', 'symbols': ['AGC', 'AVK', 'BCV', 'ECF']},
{'name': 'Taxable Income-Government', 'symbols': ['EXD', 'WIA', 'WIW']},
{'name': 'Taxable Income-High Yield', 'symbols': ['AIF', 'AWF', 'BGH', 'CIF', 'CIK', 'DHF', 'DHY', 'EAD', 'EHT', 'FHY', 'FSD', 'GHY', 'HIO', 'HIX', 'HNW', 'HYB', 'HYI', 'HYT', 'ISD', 'IVH', 'JCO', 'JHA', 'JHB', 'JHD', 'JHY', 'KIO', 'MCI', 'MPV', 'NHS', 'PCF', 'PHT', 'VLT']},
{'name': 'Taxable Income-Investment Grade', 'symbols': ['BTZ', 'DUC', 'GDO', 'GGM', 'ICB', 'IGI', 'INSI', 'JHI', 'JHS', 'PAI', 'PCN', 'PIM', 'PTY', 'VBF', 'WEA']},
{'name': 'Taxable Income-Limited Duration', 'symbols': ['BLW', 'ERC', 'EVG', 'EVV', 'FTF']},
{'name': 'Taxable Income-Mortgage Bond', 'symbols': ['BKT', 'DMO', 'EGF', 'FMY', 'IHIT', 'IHTA', 'JLS', 'JMM', 'JMT', 'PCM', 'RA']},
{'name': 'Taxable Income-Multi-Sector', 'symbols': ['ACV', 'BHK', 'BIT', 'CBH', 'CCD', 'CHI', 'CHY', 'DBL', 'DCF', 'DSL', 'FT', 'GFY', 'MGF', 'NCV', 'NCZ', 'OPP', 'PCI', 'PDI', 'PFL', 'PFN', 'PGP', 'PHK', 'PKO', 'TSI', 'VGI']},
{'name': 'Taxable Income-Municipal', 'symbols': ['BBN', 'GBAB', 'NBB', 'NBD']},
{'name': 'Taxable Income-Preferreds', 'symbols': ['DFP', 'FFC', 'FLC', 'FPF', 'HPF', 'HPI', 'HPS', 'JPC', 'JPI', 'JPS', 'JPT', 'LDP', 'PDT', 'PFD', 'PFO', 'PSF']},
{'name': 'Taxable Income-Senior Loan', 'symbols': ['ACP', 'AFT', 'ARDC', 'BGB', 'BGT', 'BGX', 'BSL', 'DSU', 'ECC', 'EFF', 'EFL', 'EFR', 'EFT', 'EVF', 'FCT', 'FIV', 'FRA', 'HFRO', 'JFR', 'JQC', 'JRO', 'JSD', 'NSL', 'OXLC', 'PHD', 'PPR', 'TLI', 'TSLF', 'VTA', 'VVR', 'XFLT']},
{'name': 'US Equity-Commodities', 'symbols': ['CEF', 'PHYS', 'PSLV', 'SPPP']},
{'name': 'US Equity-Covered Call', 'symbols': ['BDJ', 'BGY', 'BOE', 'BXMX', 'CII', 'DIAX', 'EOI', 'EOS', 'ETB', 'ETJ', 'ETV', 'ETW', 'ETY', 'EXG', 'FFA', 'GNT', 'GPM', 'IGA', 'IGD', 'INB', 'IRR', 'MCN', 'MSP', 'NFJ', 'QQQX', 'SPXX', 'STK']},
{'name': 'US Equity-Dividend Equity', 'symbols': ['FOF', 'HIE']},
{'name': 'US Equity-Energy/Resources', 'symbols': ['ASA', 'BCX', 'BGR', 'FIF', 'GGN', 'PEO', 'SZC', 'TTP']},
{'name': 'US Equity-Equity Tax-Advantaged', 'symbols': ['ETG', 'ETO', 'EVT', 'GDV', 'HTD', 'HTY', 'JTA', 'JTD']},
{'name': 'US Equity-Finance', 'symbols': ['BTO', 'FGB']},
{'name': 'US Equity-General Equity', 'symbols': ['ADX', 'ASG', 'BIF', 'CET', 'CLM', 'CRF', 'FUND', 'FXBY', 'GAB', 'GAM', 'GRF', 'JCE', 'RMT', 'RVT', 'SOR', 'SPE', 'TY', 'USA']},
{'name': 'US Equity-Growth & Income', 'symbols': ['CSQ', 'DDF', 'DNI', 'DNP', 'GCV', 'GGO', 'GOF', 'MFV', 'NHF', 'NIE', 'RCG', 'RIV', 'TPZ', 'ZTR']},
{'name': 'US Equity-Health/Biotech', 'symbols': ['BME', 'GRX', 'HQH', 'HQL', 'THQ', 'THW']},
{'name': 'US Equity-MLP', 'symbols': ['CBA', 'CEM', 'CEN', 'CTR', 'DSE', 'EMO', 'FEI', 'FEN', 'FMO', 'FPL', 'GER', 'GMZ', 'JMF', 'JMLP', 'KED', 'KMF', 'KYE', 'KYN', 'MIE', 'NDP', 'NML', 'NTG', 'SMM', 'SRF', 'SRV', 'TYG']},
{'name': 'US Equity-Real Estate (Global)', 'symbols': ['AWP', 'IGR', 'JRI']},
{'name': 'US Equity-Real Estate (US)', 'symbols': ['JRS', 'NRO', 'PGZ', 'RFI', 'RIF', 'RNP', 'RQI']},
{'name': 'US Equity-Utilities', 'symbols': ['BUI', 'DPG', 'ERH', 'GLU', 'GUT', 'MFD', 'MGU', 'UTF', 'UTG']}
];
const REFRESH_SECONDS = 10;
const BATCH_SIZE = 100;
const BASE_URL = 'https://api.iextrading.com/1.0/stock/market/batch';
let symbols = [];
let containerDiv = document.querySelector('.stocks-container');
let updatedDiv = document.querySelector('.updated-timestamp');
PORTFOLIOS.forEach((p, i) => addPortfolio(p, i === 0));
symbols = symbols.filter((s, i) => symbols.indexOf(s) === i);
updateData('addTitle');
setInterval(updateData, REFRESH_SECONDS * 1000);
function addPortfolio(portfolio, includeHeader) {
let tableHeaderHtml = '';
if (includeHeader) {
tableHeaderHtml = `
<thead>
<tr>
<th></th>
<th class="stock-price">Last</th>
<th class="stock-change">Change</th>
<th class="stock-change-pct">Change%</th>
<th class="stock-mkt-cap">Mkt Cap</th>
</tr>
</thead>
`
}
let tableBodyHtml = portfolio.symbols.map(symbol => {
symbol = symbol.toUpperCase();
symbols.push(symbol);
let html = `
<tr data-symbol="${symbol}">
<td class="stock-symbol"><a href="${symbolUrl(symbol)}" target="_blank">${symbol}</a></td>
<td class="stock-price"></td>
<td class="stock-change"></td>
<td class="stock-change-pct"></td>
<td class="stock-mkt-cap"></td>
</tr>
`
return html;
}).join('');
let portfolioDiv = document.createElement('div');
portfolioDiv.innerHTML = `
<details open>
<summary><h2>${portfolio.name}</h2></summary>
<table>${tableHeaderHtml}<tbody>${tableBodyHtml}</tbody></table>
</details>
`;
containerDiv.appendChild(portfolioDiv);
}
function updateData(addTitle) {
let numberOfBatches = Math.ceil(symbols.length / BATCH_SIZE);
for (let i = 0; i < numberOfBatches; i++) {
let symbolsBatch = symbols.slice(i * BATCH_SIZE, (i + 1) * BATCH_SIZE);
updateDataForBatch(symbolsBatch, addTitle);
}
updatedDiv.innerHTML = `Data updated at ${(new Date()).toLocaleString()}`;
}
function updateDataForBatch(symbols, addTitle) {
let filters = ['latestPrice', 'change', 'changePercent', 'marketCap'];
if (addTitle) filters.push('companyName');
let url = `${BASE_URL}?types=quote&symbols=${symbols.join(',')}&filter=${filters.join(',')}`;
fetch(url).then(response => response.json()).then(json => {
symbols.forEach(symbol => {
let data = json[symbol];
if (typeof(data) === 'undefined') return;
let formattedPrice = formatQuote(data.quote.latestPrice);
let formattedChange = data.quote.change.toLocaleString('en', {'minimumFractionDigits': 2});
let formattedChangePercent = (data.quote.changePercent * 100).toFixed(1) + '%';
let formattedMarketCap = formatMarketCap(data.quote.marketCap);
let rgbColor = data.quote.changePercent > 0 ? '0,255,0' : '255,0,0';
let rgbOpacity = Math.min(Math.abs(data.quote.changePercent) * 20, 1);
document.querySelectorAll(`[data-symbol="${symbol}"] .stock-price`).forEach(e => {
e.innerHTML = formattedPrice;
e.setAttribute('style', `background-color: rgba(${rgbColor}, ${rgbOpacity})`);
});
document.querySelectorAll(`[data-symbol="${symbol}"] .stock-change`).forEach(e => {
e.innerHTML = formattedChange;
e.setAttribute('style', `background-color: rgba(${rgbColor}, ${rgbOpacity})`);
});
document.querySelectorAll(`[data-symbol="${symbol}"] .stock-change-pct`).forEach(e => {
e.innerHTML = formattedChangePercent;
e.setAttribute('style', `background-color: rgba(${rgbColor}, ${rgbOpacity})`);
});
document.querySelectorAll(`[data-symbol="${symbol}"] .stock-mkt-cap`).forEach(e => {
e.innerHTML = formattedMarketCap;
e.setAttribute('style', `background-color: rgba(${rgbColor}, ${rgbOpacity})`);
});
if (addTitle) {
document.querySelectorAll(`[data-symbol="${symbol}"] .stock-symbol a`).forEach(e => {
e.setAttribute('title', data.quote.companyName);
});
}
});
});
}
function symbolUrl(symbol) {
return `https://iextrading.com/apps/stocks/#/${symbol}`;
}
function formatQuote(value) {
let options = {
'minimumFractionDigits': 2,
'style': 'currency',
'currency': 'USD'
};
return value.toLocaleString('en', options);
}
function formatMarketCap(marketCap) {
let value, suffix;
if (marketCap >= 1e12) {
value = marketCap / 1e12;
suffix = 'T';
} else if (marketCap >= 1e9) {
value = marketCap / 1e9;
suffix = 'B';
} else {
value = marketCap / 1e6;
suffix = 'M';
}
let digits = value < 10 ? 1 : 0;
return '$' + value.toFixed(digits) + suffix;
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment