Stock Watchlist

While it's no bloomberg terminal, I'd like to share my first kit script 🥳

Thanks @johnlindquist for building this!

stonks-demo-zz

Code

Click to expand
// Menu: Kit Stocks
// Description: Display Stocks
// Author: Zach Zeleznick
// Twitter: @zzxiv
const {focusTab} = await kit('chrome')
const defaultSymbols = ["GME", "AMC", "SNAP"];
const apiUrl = `https://query1.finance.yahoo.com/v7/finance/quote?lang=en-US&region=US&corsDomain=finance.yahoo.com&symbols=`
const populateFrom = (symbols) => symbols.map((v, i) => {return {symbol: v, id: `id-${i}` }})
const tickersDB = db("tickers", { tickers: populateFrom(defaultSymbols) });
const tickersRef = tickersDB.get("tickers");
// helper in the case all tickers are removed – we should reinit or return empty result
const initDB = () => {
const tickers = populateFrom(defaultSymbols);
tickersDB.set("tickers", tickers).write();
}
const urlToOpen = (ticker) => {
return `https://finance.yahoo.com/quote/${ticker}?p=${ticker}`
}
const getTickers = () => tickersRef.value()
const tickersToSymbols = () => getTickers().map(({symbol}) => symbol)
const tickersToChoices = () => {
return getTickers().map(({symbol, id}) => {
return {
name: symbol,
value: id,
}
});
}
const getStocks = async (stocks) => {
stocks = stocks ? stocks : defaultSymbols;
stocks = Array.isArray(stocks) ? stocks.join(",") : stocks;
const response = await get(`${apiUrl}${stocks}`);
const { quoteResponse: { result, error } } = response.data;
// TODO: handle errors
// console.log(JSON.stringify(result, null, 2));
return result;
}
const buildHtml = ({price, percentChange}) => {
let color = 'gray';
const significance = Math.abs(percentChange) > 0.25; // arbitray 0.25% cutoff
// TODO: should filter on significance based on volatility
const pct = percentChange.toFixed(2);
color = significance ? (Math.sign(percentChange) === -1 ? "red" : "green") : color;
return `<div class="h-full w-full p-1 text-xs flex flex-col justify-center items-center font-bold">
<div>${price}</div>
<div style="color:${color}">${pct}%</div>
</div>`
}
const quoteResponseToChoice = (quoteResponse) => {
const { symbol, displayName, regularMarketPrice,
regularMarketChange, regularMarketChangePercent,
} = quoteResponse;
try {
return {
name: symbol,
value: symbol,
description: displayName,
html: buildHtml({price: regularMarketPrice, percentChange: regularMarketChangePercent}),
}
} catch(e) {
console.error(e);
return null
}
}
const listTickers = async () => {
let symbols = tickersToSymbols();
if (!symbols || !symbols.length) {
await arg("Search stocks:", [{
name: "No Results",
value: "__empty__",
description: "Hit enter to reinit default stocks"
}]);
initDB();
return await listTickers();
}
const stocks = await getStocks(symbols);
const choices = stocks.map(quoteResponseToChoice).filter(x => x);
const selectedTicker = await arg("Search stocks:", choices);
focusTab(urlToOpen(selectedTicker)); // open tab for quote
}
const addTicker = async () => {
const symbol = await arg("Select stock to add:");
tickersRef.insert({ symbol }).write();
return await addTicker();
};
const removeTicker = async () => {
const choices = tickersToChoices();
const id = await arg("Select stock to remove:", choices);
tickersRef.remove({ id }).write();
return await removeTicker();
};
onTab("List", listTickers)
onTab("Add", addTicker)
onTab("Remove", removeTicker)