From 08e40a87df39ff63eed5ff461859cadd46e1a033 Mon Sep 17 00:00:00 2001 From: Seth Trowbridge Date: Sat, 17 Jan 2026 16:21:36 -0500 Subject: [PATCH] added date prompt --- README.md | 2 +- deno.json | 2 +- scraper.ts | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 128 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 92e1e86..ea70150 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ in console, run: -`deno task scrape` \ No newline at end of file +`deno task scraper` \ No newline at end of file diff --git a/deno.json b/deno.json index 12fad0c..87ecfb7 100644 --- a/deno.json +++ b/deno.json @@ -1,5 +1,5 @@ { "tasks": { - "scrape": "deno run -A scraper.ts" + "scraper": "deno run -A scraper.ts" } } diff --git a/scraper.ts b/scraper.ts index 8867fed..e62d335 100644 --- a/scraper.ts +++ b/scraper.ts @@ -62,10 +62,103 @@ async function fetchSP500Tickers(): Promise { // ------------------------------------------------------------ // Yahoo Finance fetch // ------------------------------------------------------------ -const now = Math.floor(Date.now() / 1000); -const thirtyDaysAgo = now - 30 * 24 * 60 * 60; -const args = `?period1=${thirtyDaysAgo}&period2=${now}&interval=1d`; -const closingEndpoint =(ticker:string)=>`https://query1.finance.yahoo.com/v8/finance/chart/${ticker}${args}`; +function parseDate(dateStr: string): number { + const date = new Date(dateStr); + if (isNaN(date.getTime())) { + throw new Error(`Invalid date format: ${dateStr}. Use YYYY-MM-DD`); + } + return Math.floor(date.getTime() / 1000); +} + +async function getDateRange(): Promise<{ period1: number; period2: number }> { + const args = Deno.args; + + // If command line args provided, use them + if (args.length === 1) { + const days = parseInt(args[0], 10); + if (isNaN(days) || days <= 0) { + throw new Error(`Invalid number of days: ${args[0]}`); + } + const now = Math.floor(Date.now() / 1000); + const daysAgo = now - days * 24 * 60 * 60; + console.log(`Using last ${days} days.`); + return { period1: daysAgo, period2: now }; + } + + if (args.length >= 2) { + const startDate = parseDate(args[0]); + const endDate = parseDate(args[1]); + + if (startDate > endDate) { + throw new Error("Start date must be before end date"); + } + + console.log(`Using date range from ${args[0]} to ${args[1]}.`); + return { period1: startDate, period2: endDate }; + } + + // Interactive mode + console.log("\n=== Date Range Selection ==="); + console.log("1. Last 30 days (default)"); + console.log("2. Last N days (custom)"); + console.log("3. Custom date range (YYYY-MM-DD)"); + + const choice = prompt("Choose an option (1-3):", "1"); + + if (choice === "1" || choice === null) { + // Default: last 30 days + const now = Math.floor(Date.now() / 1000); + const thirtyDaysAgo = now - 30 * 24 * 60 * 60; + console.log("Using last 30 days."); + return { period1: thirtyDaysAgo, period2: now }; + } + + if (choice === "2") { + const daysStr = prompt("Enter number of days:", "30"); + if (!daysStr) { + throw new Error("No days specified"); + } + const days = parseInt(daysStr, 10); + if (isNaN(days) || days <= 0) { + throw new Error(`Invalid number of days: ${daysStr}`); + } + const now = Math.floor(Date.now() / 1000); + const daysAgo = now - days * 24 * 60 * 60; + console.log(`Using last ${days} days.`); + return { period1: daysAgo, period2: now }; + } + + if (choice === "3") { + const dateRangeStr = prompt("Enter date range (YYYY-MM-DD YYYY-MM-DD):", "2025-12-18 2026-01-17"); + if (!dateRangeStr) { + throw new Error("No date range specified"); + } + + const dates = dateRangeStr.trim().split(/\s+/); + if (dates.length !== 2) { + throw new Error("Please provide two dates in format: YYYY-MM-DD YYYY-MM-DD"); + } + + const startStr = dates[0]; + const endStr = dates[1]; + + const startDate = parseDate(startStr); + const endDate = parseDate(endStr); + + if (startDate > endDate) { + throw new Error("Start date must be before end date"); + } + + console.log(`Using date range from ${startStr} to ${endStr}.`); + return { period1: startDate, period2: endDate }; + } + + throw new Error("Invalid option selected"); +} + +let dateRange: { period1: number; period2: number }; +let args: string; +let closingEndpoint: (ticker: string) => string; async function fetchLast30Closes(ticker: string): Promise { @@ -122,6 +215,7 @@ async function throttle( function buildHtmlTable( results: { ticker: string; model: ReturnType }[], + dateRangeLabel: string, ): string { const rows = results.map(({ ticker, model }) => { const link = `${ticker}`; @@ -153,6 +247,7 @@ function buildHtmlTable(

S&P 500 Regression Results

+

Date Range: ${dateRangeLabel}

@@ -160,7 +255,7 @@ function buildHtmlTable( - + @@ -177,7 +272,31 @@ function buildHtmlTable( // ------------------------------------------------------------ // Main Dump // ------------------------------------------------------------ +function getDateRangeLabel(): string { + const args = Deno.args; + + if (args.length === 0) { + return "Last 30 days"; + } + + if (args.length === 1) { + const days = parseInt(args[0], 10); + return `Last ${days} days`; + } + + if (args.length >= 2) { + return `${args[0]} to ${args[1]}`; + } + + return "Unknown range"; +} + async function Dump() { + // Initialize date range interactively + dateRange = await getDateRange(); + args = `?period1=${dateRange.period1}&period2=${dateRange.period2}&interval=1d`; + closingEndpoint = (ticker: string) => `https://query1.finance.yahoo.com/v8/finance/chart/${ticker}${args}`; + const spx = await ComputeTicker("^GSPC"); if (!spx) { console.error("Could not get S&P Index data"); @@ -215,11 +334,11 @@ async function Dump() { results.sort((a, b) => b.model.growth - a.model.growth); // Build HTML - const html = buildHtmlTable(results); + const dateRangeLabel = getDateRangeLabel(); + const html = buildHtmlTable(results, dateRangeLabel); await Deno.writeTextFile("sp500_regression.html", html); console.log("Dumped output to sp500_regression.html"); } Dump(); -//console.log(closingEndpoint("^GSPC"))
Slope Intercept Growth (30d)Growth