diff options
author | sotech117 <michael_foiani@brown.edu> | 2025-08-27 00:08:59 -0400 |
---|---|---|
committer | sotech117 <michael_foiani@brown.edu> | 2025-08-27 00:08:59 -0400 |
commit | 9d2ebe6baff737bc88a35c9b64b914825e20275a (patch) | |
tree | 5b7af5df4c85be900b1c9dc7ef57e850521833f1 /screener.js | |
parent | 61e0e4fefc8508448642502748e0885fef574767 (diff) |
fix fetch import, update server.js, git ignore gen file, remove screener.js and upload.js
Diffstat (limited to 'screener.js')
-rw-r--r-- | screener.js | 353 |
1 files changed, 0 insertions, 353 deletions
diff --git a/screener.js b/screener.js deleted file mode 100644 index 85e6183..0000000 --- a/screener.js +++ /dev/null @@ -1,353 +0,0 @@ -import fs from 'fs'; - -// this gets the HTML page of the SP500 list from slickcharts.com -const getSPHTML = async () => { - const response = await fetch('https://www.slickcharts.com/sp500'); - if (!response.ok) { - throw new Error('Network response was not ok'); - } - const text = await response.text(); - return text; -} - -// this parses the HTML of the SP500 list to tickers -const parseHTMLToTickers = (html) => { - // get the tickers by slicing on all `/symbol/` occurrences - // (with the special format before it to only get one occurance) - const tickers = []; - html.split('nowrap;"><a href="/symbol/').slice(1).forEach(item => { - // get the ticker from the item (before the next ")" - const ticker = item.split('"')[0]; - const name = item.split('">')[1].split('</a>')[0]; - - // get weight of item using the % - let weight = item.search(/<td>([\d.]+)%<\/td>/); - if (weight === -1) { - console.warn(`Ticker ${ticker} does not have a valid weight, skipping.`); - return; - } - weight = parseFloat(item.slice(weight + 4, item.indexOf('</td>', weight))); - - if (ticker && name && weight) { - tickers.push({ name, symbol: ticker, weight }); - } - }); - - // update the tickers file with the new tickers - if (fs.existsSync('sp500_tickers.json')) { - fs.unlinkSync('sp500_tickers.json'); - } - fs.writeFileSync('sp500_tickers.json', JSON.stringify(tickers, null, 2)); - // console.log(`Saved ${tickers.length} tickers to sp500_tickers.json`); - return tickers; -} - -const getTickersFromFile = () => { - if (!fs.existsSync('sp500_tickers.json')) { - console.error('sp500_tickers.json file does not exist. Please run the script to fetch tickers first.'); - return []; - } - const data = fs.readFileSync('sp500_tickers.json', 'utf8'); - return JSON.parse(data); -} - -// get the JSON history data from the symbol -const getSymbolHistory = async (symbol) => { - // clean the symbol (reaplce . with dash) - symbol = symbol.replace(/\./g, '-'); - const parameters = 'interval=1d&includePrePost=true&events=div%7Csplit%7Cearn&lang=en-US®ion=US&range=6mo'; - const response = await fetch(`https://query1.finance.yahoo.com/v8/finance/chart/${symbol}?${parameters}`); - if (!response.ok) { - console.error(`Network response was not ok for symbol: ${symbol}. Status: ${response.status}`); - return {}; - } - const data = await response.json(); - - return data; -} - -const getSectorMap = () => { - // pull from the sector map tsv file - if (!fs.existsSync('sector-map.tsv')) { - console.error('sector-map.tsv file does not exist. Please run the script to fetch sectors first.'); - return 'Unknown'; - } - const sectorMap = fs.readFileSync('sector-map.tsv', 'utf8'); - const lines = sectorMap.split('\n'); - const sectorMapObj = {}; - lines.forEach((line, index) => { - if (index === 0) return; // skip the header line - // split the line by comma and get the name, ticker, sector, and subSector - const [name, ticker, sector, subSector] = line.split('\t'); - sectorMapObj[ticker.trim()] = [sector.trim(), subSector.trim(), name.trim()]; - }); - return sectorMapObj; -} - -const getHistoriesForEachTicker = async (tickers) => { - // use Promise.all to fetch all histories concurrently - const histories = await Promise.all(tickers.map(ticker => getSymbolHistory(ticker.symbol))); - - // zip the histories with the tickers - const zippedHistories = histories.map((history, index) => ({ - ...tickers[index], - history - })); - return zippedHistories; -} - -// test getting the histories for all the symbols -const testGetHistories = async () => { - try { - // const html = await getSPHTML(); - // const tickers = parseHTMLToTickers(html); - // console.log('SP500 Tickers:', tickers); - - // get tickers from file - const tickers = getTickersFromFile(); - if (tickers.length === 0) { - console.error('No tickers found. Please ensure sp500_tickers.json exists and is populated.'); - return; - } - console.log(`Found ${tickers.length} tickers in sp500_tickers.json`); - - // get histories for each symbol - const histories = await getHistoriesForEachTicker(tickers); - console.log('Histories fetched for all symbols:', histories.length); - - // save histories to a file - if (fs.existsSync('sp500_histories.json')) { - fs.unlinkSync('sp500_histories.json'); - } - fs.writeFileSync('sp500_histories.json', JSON.stringify(histories, null, 2)); - console.log(`Saved ${histories.length} histories to sp500_histories.json`); - - // print first 5 and last 5 histories - console.log('First 5 histories:'); - histories.slice(0, 5).forEach(h => console.log(h.symbol, h.history)); - // console.log('Last 5 histories:', histories.slice(-5)); - } catch (error) { - console.error('Error fetching SP500 histories:', error); - } -}; - -const formatDataFromHistories = (histories) => { - // format the data from the histories to a more readable format - const csv_headers = ['Ticker', 'Name', '% Weight', 'Sector', 'SubSector', 'RSI (14)', 'MACD (Histogram Value)', '1W', '1M', '3M', '6M']; - const csv_final = [csv_headers]; - - // get the sector map - const sectorMap = getSectorMap(); - - histories.forEach(history => { - - // Tickern, name weight, sector from html pull - const { symbol, webName, weight } = history; - const sector = sectorMap[symbol] ? sectorMap[symbol][0] : 'Unknown'; - const subSector = sectorMap[symbol] ? sectorMap[symbol][1] : 'Unknown'; - const name = sectorMap[symbol] ? sectorMap[symbol][2] : webName || 'Unknown'; - - // Get RSI, MACD from helper - const timestamps = history.history.chart.result[0].timestamp; - const prices = history.history.chart.result[0].indicators.quote[0].close; - const rsi = calculateRSI(prices); - const macd = calculateMACD(prices); - - // print first 5 timestamps and prices for debugging - // console.log('First 5 timestamps:', timestamps.slice(0, 5).map(ts => new Date(ts * 1000).toLocaleDateString())); - // console.log('First 5 prices:', prices.slice(0, 5)); - - const currentPrice = prices[prices.length - 1]; - // Directly calculate the percentage changes for 1W, 1M, 3M, and 6M\ - const oneWeekAgoPrice = prices[prices.length - 6]; // 5 days of trading - const oneWeekChange = ((currentPrice - oneWeekAgoPrice) / oneWeekAgoPrice) * 100; - - const oneMonthAgoPrice = prices[prices.length - 21]; // 20 days of trading (4 weeks) - const oneMonthChange = ((currentPrice - oneMonthAgoPrice) / oneMonthAgoPrice) * 100; - - const threeMonthsAgoPrice = prices[parseInt(prices.length / 2) - 1]; // 3 months is half the length of the prices array - const threeMonthChange = ((currentPrice - threeMonthsAgoPrice) / threeMonthsAgoPrice) * 100; - - const sixMonthsAgoPrice = prices[0]; // last 6 months is the first price in the array - const sixMonthChange = ((currentPrice - sixMonthsAgoPrice) / sixMonthsAgoPrice) * 100; - - const mappedValues = { - Ticker: symbol, - Name: name, - '% Weight': weight, - 'Sector': sector, - 'Subsector': subSector, - 'RSI (14)': rsi.toFixed(3), - 'MACD (Histogram Value)': macd.toFixed(3), - '1W': oneWeekChange.toFixed(3) + '%', - '1M': oneMonthChange.toFixed(3) + '%', - '3M': threeMonthChange.toFixed(3) + '%', - '6M': sixMonthChange.toFixed(3) + '%' - }; - - // pushed the mapped values to the formatted data - csv_final.push(Object.values(mappedValues)); - }); - - // write the formatted data to a CSV file - const csvContent = csv_final.map(e => e.join('\t')).join('\n'); - if (fs.existsSync('sp500_formatted_data.tsv')) { - fs.unlinkSync('sp500_formatted_data.tsv'); - } - fs.writeFileSync('sp500_formatted_data.tsv', csvContent); - // console.log('Formatted data saved to sp500_formatted_data.tsv'); - return csv_final; -}; -// testGetHistories(); - -const calculateMACD = (prices, shortPeriod = 12, longPeriod = 26, signalPeriod = 9) => { - // Helper function to calculate the Exponential Moving Average (EMA) - const exponentialMovingAverage = (data, period) => { - const k = 2 / (period + 1); - let ema = [data[0]]; // Start with the first price as the initial EMA - - for (let i = 1; i < data.length; i++) { - const currentEma = (data[i] * k) + (ema[i - 1] * (1 - k)); - ema.push(currentEma); - } - return ema; - } - - // Calculate the short and long periods - const ema12 = exponentialMovingAverage(prices, shortPeriod); - const ema26 = exponentialMovingAverage(prices, longPeriod); - - // Calcualte the MACD line - const macdLine = ema12.map((value, index) => value - ema26[index]) - - // Calculate the signal line - const signalLine = exponentialMovingAverage(macdLine, signalPeriod); - - // Calculate the MACD histogram - const macdHistogram = macdLine.map((value, index) => value - signalLine[index]); - - // Return the last value of the MACD histogram - return macdHistogram[macdHistogram.length - 1]; -} - - -const calculateRSI = (prices, period = 14) => { - // calculate the first RSI within our period - let gains = []; - let losses = []; - - for (let i = 0; i < period; i++) { - const change = prices[i + 1] - prices[i]; - if (change > 0) { - gains.push(change); - losses.push(0); - } else { - losses.push(Math.abs(change)); - gains.push(0); - } - } - - const averageGain = gains.reduce((a, b) => a + b, 0) / period; - const averageLoss = losses.reduce((a, b) => a + b, 0) / period; - if (averageLoss === 0) { - console.log('No losses in the period, RSI is 100'); - return 100; // RSI is 100 if there are no losses - } - const firstRSI = 100 - (100 / (1 + (averageGain / averageLoss))); - if (isNaN(firstRSI)) { - console.error('Calculated RSI is NaN, returning 0'); - return 0; // Return 0 if RSI calculation fails - } - - const RSIs = [firstRSI]; - // `Initial RSI for the first ${period} data points: ${firstRSI}`); - - // Calculate the RSI for the rest of the data points - let previousAverageGain = averageGain; - let previousAverageLoss = averageLoss; - - for (let i = period; i < prices.length - 1; i++) { - const change = prices[i + 1] - prices[i]; - let gain = 0; - let loss = 0; - - if (change > 0) { - gain = change; - } else { - loss = Math.abs(change); - } - - // Calculate the new average gain and loss - previousAverageGain = (previousAverageGain * (period - 1) + gain) / period; - previousAverageLoss = (previousAverageLoss * (period - 1) + loss) / period; - if (previousAverageLoss === 0) { - console.log('No losses in the period, RSI is 100'); - return 100; // RSI is 100 if there are no losses - } - - // add this RSI to the list - const rsi = 100 - (100 / (1 + (previousAverageGain / previousAverageLoss))); - RSIs.push(rsi); - } - - // Return the last calculated RSI - return RSIs[RSIs.length - 1]; -} - -// test rsi calculation on one stock -const testRSI = async () => { - const symbol = 'NVDA'; - const history = await getSymbolHistory(symbol); - if (!history.chart || !history.chart.result || history.chart.result.length === 0) { - console.error(`No data found for symbol: ${symbol}`); - return; - } - const prices = history.chart.result[0].indicators.quote[0].close; - const timestamps = history.chart.result[0].timestamp; - // print the last 10 prices and dates - const rsi = calculateRSI(prices); - console.log(`RSI for ${symbol}:`, rsi); -} - -const testGetSector = async () => { - // pull for each ticker in the sp500_tickers.json file - const tickers = getTickersFromFile(); - if (tickers.length === 0) { - console.error('No tickers found. Please ensure sp500_tickers.json exists and is populated.'); - return; - } - // get the sector map - const sectorMap = getSectorMap(); - tickers.forEach(async (ticker) => { - const sector = sectorMap[ticker.symbol] ? sectorMap[ticker.symbol][0] : 'Unknown'; - console.log(`Ticker: ${ticker.symbol}, Sector: ${sector}`); - }); -} - -export const runScreener = async () => { - try { - // gt the test histories from the file - // const histories = fs.readFileSync('sp500_histories.json', 'utf8'); - // const parsedHistories = JSON.parse(histories); - // console.log(`Loaded ${parsedHistories.length} histories from sp500_histories.json`); - - // get tickers from file - const tickers = getTickersFromFile(); - if (tickers.length === 0) { - console.error('No tickers found. Please ensure sp500_tickers.json exists and is populated.'); - return; - } - // console.log(`Found ${tickers.length} tickers in sp500_tickers.json`); - // get histories for each symbol - const parsedHistories = await getHistoriesForEachTicker(tickers); - // console.log(`Fetched histories for ${parsedHistories.length} symbols.`); - - - // format the data from the histories - const formattedData = formatDataFromHistories(parsedHistories); - // console.log('Formatted data:', formattedData.slice(0, 5)); // Print first 5 entries for brevity - - } catch (error) { - console.error('Error in main function:', error); - } -} |