added date prompt

This commit is contained in:
Seth Trowbridge 2026-01-17 16:21:36 -05:00
parent 7d78598129
commit 08e40a87df
3 changed files with 128 additions and 9 deletions

View File

@ -1,2 +1,2 @@
in console, run:
`deno task scrape`
`deno task scraper`

View File

@ -1,5 +1,5 @@
{
"tasks": {
"scrape": "deno run -A scraper.ts"
"scraper": "deno run -A scraper.ts"
}
}

View File

@ -62,10 +62,103 @@ async function fetchSP500Tickers(): Promise<string[]> {
// ------------------------------------------------------------
// 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<number[]> {
@ -122,6 +215,7 @@ async function throttle<T>(
function buildHtmlTable(
results: { ticker: string; model: ReturnType<typeof linearRegression> }[],
dateRangeLabel: string,
): string {
const rows = results.map(({ ticker, model }) => {
const link = `<a href="https://www.tradingview.com/symbols/NASDAQ-${ticker}/?timeframe=6M" target="_blank">${ticker}</a>`;
@ -153,6 +247,7 @@ function buildHtmlTable(
</head>
<body>
<h1>S&P 500 Regression Results</h1>
<p><em>Date Range: ${dateRangeLabel}</em></p>
<table>
<thead>
<tr>
@ -160,7 +255,7 @@ function buildHtmlTable(
<th class="extra">Slope</th>
<th class="extra">Intercept</th>
<th class="extra">R²</th>
<th>Growth (30d)</th>
<th>Growth</th>
</tr>
</thead>
<tbody>
@ -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"))