This commit is contained in:
MuslemRahimi 2025-02-10 13:31:06 +01:00
parent d800bd9084
commit f1e1ecc2ac
3 changed files with 244 additions and 258 deletions

View File

@ -7,8 +7,8 @@
</script> </script>
<SEO <SEO
title="Data Disclaimer" title="Data Disclaimer | StockNear - Financial Data Sources & Accuracy"
description="All data shown on this website is for informational purposes only and should not be relied upon to make trading or investing decisions." description="Comprehensive information about our financial data sources, accuracy standards, and usage guidelines for stock market data, analyst estimates, and financial metrics."
/> />
<section <section
@ -36,11 +36,14 @@
<div class="w-full bg-default m-auto"> <div class="w-full bg-default m-auto">
<div> <div>
<p class="text-white mb-5 text-[1rem] sm:text-lg"> <p class="text-white mb-5 text-[1rem] sm:text-lg">
The data on this site is for informational purposes only and The financial data presented on Stocknear is provided for
should not be used for trading or investment decisions. informational and educational purposes only. This information
should not be construed as financial advice or used as the sole
basis for trading or investment decisions.
<br /><br /> <br /><br />
We strive for accuracy and quality. If you spot errors, please contact We maintain rigorous standards for data accuracy and quality assurance.
us via If you identify any discrepancies or require clarification, please
reach out to us via
<a <a
href={discordURL} href={discordURL}
target="_blank" target="_blank"
@ -49,7 +52,7 @@
> >
Discord Discord
</a> </a>
or email us at or contact our support team at
<a <a
href="mailto:{emailAddress}" href="mailto:{emailAddress}"
class="text-blue-400 hover:underline" class="text-blue-400 hover:underline"
@ -59,86 +62,103 @@
</p> </p>
<h2 class="mb-1 text-white text-2xl sm:text-3xl font-bold"> <h2 class="mb-1 text-white text-2xl sm:text-3xl font-bold">
Stock Charts Market Data & Stock Charts
</h2> </h2>
<p class="text-white mb-10 mt-5 text-[1rem] sm:text-lg"> <p class="text-white mb-10 mt-5 text-[1rem] sm:text-lg">
Intraday and historical stock chart data are provided by Our comprehensive market data and real-time stock charts are
powered by
<a <a
href="https://site.financialmodelingprep.com/pricing-plans?couponCode=stocknear" href="https://site.financialmodelingprep.com/pricing-plans?couponCode=stocknear"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="text-blue-400 hover:underline" class="text-blue-400 hover:underline"
> >
FMP Financial Modeling Prep (FMP)
</a>. </a>, a leading provider of financial data services. This
includes intraday pricing, historical data, and technical
indicators.
</p> </p>
<h2 class="mb-1 text-white text-2xl sm:text-3xl font-bold"> <h2 class="mb-1 text-white text-2xl sm:text-3xl font-bold">
Stocks Covered Market Coverage
</h2> </h2>
<p class="text-white mb-10 mt-5 text-[1rem] sm:text-lg"> <p class="text-white mb-10 mt-5 text-[1rem] sm:text-lg">
Stocknear covers all stocks and ETFs listed on major US Stocknear provides comprehensive coverage of securities listed
exchanges (NASDAQ, NYSE, NYSE American, NYSE Arca) and many OTC on major U.S. exchanges, including NASDAQ, NYSE, NYSE American,
stocks. We also include international companies trading on US and NYSE Arca. Our database encompasses stocks, ETFs, ADRs, and
exchanges and aim to add global stock exchanges soon. select OTC securities. We are actively expanding our coverage to
include additional global exchanges and financial instruments.
</p> </p>
<h2 class="mb-1 text-white text-2xl sm:text-3xl font-bold"> <h2 class="mb-1 text-white text-2xl sm:text-3xl font-bold">
Historical Financials Financial Fundamentals
</h2> </h2>
<p class="text-white mb-10 mt-5 text-[1rem] sm:text-lg"> <p class="text-white mb-10 mt-5 text-[1rem] sm:text-lg">
Fundamental financial data is sourced from Our fundamental financial data is sourced from
<a <a
href="https://site.financialmodelingprep.com/pricing-plans?couponCode=stocknear" href="https://site.financialmodelingprep.com/pricing-plans?couponCode=stocknear"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="text-blue-400 hover:underline" class="text-blue-400 hover:underline"
> >
FMP Financial Modeling Prep
</a>, based on official SEC filings (10-K, 10-Q). Data is </a>
updated daily, but delays may occur if companies file late. All and is derived from official SEC filings (Forms 10-K, 10-Q, and 8-K).
displayed data is official, audited, and SEC-submitted. While our systems update this information daily, there may be inherent
delays due to standard SEC filing windows and corporate reporting
schedules. All financial data presented is based on official, audited
company submissions to regulatory authorities.
</p> </p>
<h2 class="mb-1 text-white text-2xl sm:text-3xl font-bold"> <h2 class="mb-1 text-white text-2xl sm:text-3xl font-bold">
Price Targets and Analyst Estimates Analyst Coverage & Market Projections
</h2> </h2>
<p class="text-white mb-10 mt-5 text-[1rem] sm:text-lg"> <p class="text-white mb-10 mt-5 text-[1rem] sm:text-lg">
Price targets and ratings are from Benzinga, sourced from Wall Our analyst coverage integrates data from multiple authoritative
Street analysts. Revenue and EPS forecasts come from Benzinga sources. Price targets and analyst ratings are provided by
and <a Benzinga, representing consensus views from leading Wall Street
analysts. Revenue projections and EPS forecasts are aggregated
from both Benzinga and
<a
href="https://site.financialmodelingprep.com/pricing-plans?couponCode=stocknear" href="https://site.financialmodelingprep.com/pricing-plans?couponCode=stocknear"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="text-blue-400 hover:underline" class="text-blue-400 hover:underline"
> >
FMP Financial Modeling Prep
</a>. Analyst accuracy and performance metrics are also provided </a>. While we strive to present the most accurate forecasts,
by Benzinga. Differences across sources may occur but are users should note that market predictions are inherently
directionally consistent. Use this information cautiously as uncertain and should be considered as part of a broader
predictions are often inaccurate. investment research process.
</p> </p>
<h2 class="mb-1 text-white text-2xl sm:text-3xl font-bold"> <h2 class="mb-1 text-white text-2xl sm:text-3xl font-bold">
ETF Holdings ETF Analytics
</h2> </h2>
<p class="text-white mb-10 mt-5 text-[1rem] sm:text-lg"> <p class="text-white mb-10 mt-5 text-[1rem] sm:text-lg">
Most ETF holdings data is provided by <a Our ETF holdings data and analytics are provided through our
partnership with
<a
href="https://site.financialmodelingprep.com/pricing-plans?couponCode=stocknear" href="https://site.financialmodelingprep.com/pricing-plans?couponCode=stocknear"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="text-blue-400 hover:underline" class="text-blue-400 hover:underline"
> >
FMP Financial Modeling Prep
</a>. </a>, ensuring accurate and timely information on fund
compositions, allocations, and performance metrics.
</p> </p>
<h2 class="mb-1 text-white text-2xl sm:text-3xl font-bold"> <h2 class="mb-1 text-white text-2xl sm:text-3xl font-bold">
Options Data Options Market Data
</h2> </h2>
<p class="text-white mb-10 mt-5 text-[1rem] sm:text-lg"> <p class="text-white mb-10 mt-5 text-[1rem] sm:text-lg">
Options flow data is sourced from Benzinga via OPRA. Individual Our options market data infrastructure combines real-time
ticker options data is from Intrinio, also sourced from OPRA. options flow information from Benzinga and individual options
chain data from Intrinio, both sourced through the Options Price
Reporting Authority (OPRA). This provides comprehensive coverage
of options market activity and pricing across all major
exchanges.
</p> </p>
</div> </div>
</div> </div>

View File

@ -18,9 +18,11 @@
let filterList = []; let filterList = [];
let weekdayFiltered = []; let weekdayFiltered = [];
let weekday; // Added declaration let weekday = []; // our unordered week data
let syncWorker: Worker | undefined; let syncWorker: Worker | undefined;
let pagePathName = $page?.url?.pathname; let pagePathName: string = "";
// reassign pagePathName reactively
$: pagePathName = $page?.url?.pathname || "";
const maxWeeksChange = 6; const maxWeeksChange = 6;
const today = new Date(); const today = new Date();
@ -31,21 +33,27 @@
let sortMode = false; let sortMode = false;
$: testList = []; $: testList = [];
// Get calendar data from our load function
$: economicCalendar = data?.getEconomicCalendar; $: economicCalendar = data?.getEconomicCalendar;
// Calculate the week days
$: daysOfWeek = getDaysOfWeek(currentWeek); $: daysOfWeek = getDaysOfWeek(currentWeek);
// Format days for header labels
$: formattedWeekday = daysOfWeek.map((day) => format(day.date, "EEE, MMM d")); $: formattedWeekday = daysOfWeek.map((day) => format(day.date, "EEE, MMM d"));
$: {
if (!sortMode) {
weekday = getWeekdayData(economicCalendar, daysOfWeek);
rawData = weekday;
}
// Reapply filters whenever weekday data changes // Recalculate weekday data when the economicCalendar or days change but only when not sorting
if (filterList.length > 0 && syncWorker) { $: if (!sortMode) {
loadWorker(); weekday = getWeekdayData(economicCalendar, daysOfWeek);
} rawData = weekday;
} }
// Whenever filters are applied and the worker exists, trigger filtering
$: if (filterList.length > 0 && syncWorker) {
loadWorker();
}
// Create a consolidated derived value for our header and table rendering
$: displayWeekData = filterList.length === 0 ? weekday : weekdayFiltered;
const startBoundary = subWeeks( const startBoundary = subWeeks(
startOfWeek(today, { weekStartsOn: 1 }), startOfWeek(today, { weekStartsOn: 1 }),
maxWeeksChange, maxWeeksChange,
@ -54,13 +62,13 @@
startOfWeek(today, { weekStartsOn: 1 }), startOfWeek(today, { weekStartsOn: 1 }),
maxWeeksChange, maxWeeksChange,
); );
$: previousMax = currentWeek <= startBoundary; $: previousMax = currentWeek <= startBoundary;
$: nextMax = currentWeek >= endBoundary; $: nextMax = currentWeek >= endBoundary;
let currentDate = new Date(); let currentDate = new Date();
let selectedWeekday = Math.min((currentDate.getDay() + 6) % 7, 4); let selectedWeekday = Math.min((currentDate.getDay() + 6) % 7, 4);
// Returns an array of weekdays (Monday - Friday) for a given week.
function getDaysOfWeek(week) { function getDaysOfWeek(week) {
const startDate = startOfWeek(week, { weekStartsOn: 1 }); const startDate = startOfWeek(week, { weekStartsOn: 1 });
return Array.from({ length: 5 }, (_, i) => ({ return Array.from({ length: 5 }, (_, i) => ({
@ -69,23 +77,27 @@
})); }));
} }
// Retrieves and sorts calendar data for each day.
function getWeekdayData(calendar, days) { function getWeekdayData(calendar, days) {
if (!calendar) return []; if (!calendar) return [];
return days.map((day) => { return days.map((day) => {
const dayData = calendar.filter( const dayData = calendar.filter(
(item) => item.date === format(day.date, "yyyy-MM-dd"), (item) => item.date === format(day.date, "yyyy-MM-dd"),
); );
return dayData?.sort( return dayData.sort(
(a, b) => (a, b) =>
new Date(`1970-01-01T${a.time}`) - new Date(`1970-01-01T${b.time}`), new Date(`1970-01-01T${a.time}`).getTime() -
new Date(`1970-01-01T${b.time}`).getTime(),
); );
}); });
} }
// Handle messages from our filtering web worker.
const handleMessage = (event) => { const handleMessage = (event) => {
weekdayFiltered = event.data?.finalData?.output ?? []; weekdayFiltered = event.data?.finalData?.output ?? [];
}; };
// Tell the web worker to filter our data
const loadWorker = async () => { const loadWorker = async () => {
syncWorker?.postMessage({ rawData, filterList }); syncWorker?.postMessage({ rawData, filterList });
}; };
@ -115,7 +127,6 @@
state === "previous" state === "previous"
? subWeeks(currentWeek, 1) ? subWeeks(currentWeek, 1)
: addWeeks(currentWeek, 1); : addWeeks(currentWeek, 1);
if (newWeek >= startBoundary && newWeek <= endBoundary) { if (newWeek >= startBoundary && newWeek <= endBoundary) {
currentWeek = newWeek; currentWeek = newWeek;
} }
@ -123,21 +134,21 @@
function saveRules() { function saveRules() {
try { try {
// Save the version along with the rules localStorage?.setItem(pagePathName, JSON.stringify(filterList));
localStorage?.setItem(pagePathName, JSON?.stringify(filterList));
} catch (e) { } catch (e) {
console.log("Failed saving filterlist: ", e); console.error("Failed saving filterlist:", e);
} }
} }
// Notice we now initialize checkedItems just once instead of using a reactive assignment.
let checkedItems: Set<any> = new Set();
onMount(async () => { onMount(async () => {
try { try {
const savedRules = localStorage?.getItem(pagePathName); const savedRules = localStorage?.getItem(pagePathName);
if (savedRules) { if (savedRules) {
filterList = JSON.parse(savedRules); filterList = JSON.parse(savedRules);
} }
checkedItems = new Set(filterList); checkedItems = new Set(filterList);
if (!syncWorker) { if (!syncWorker) {
@ -146,29 +157,23 @@
syncWorker.onmessage = handleMessage; syncWorker.onmessage = handleMessage;
} }
} catch (e) { } catch (e) {
console.log(e); console.error(e);
} }
}); });
function handleInput(event) { // Update the global searchQuery (avoid shadowing) and debounce the filtering.
const searchQuery = event.target.value?.toLowerCase() || ""; function handleInput(event: InputEvent) {
searchQuery = (event.target as HTMLInputElement)?.value.toLowerCase() || "";
setTimeout(() => { setTimeout(() => {
testList = []; testList = [];
if (searchQuery.length > 0) { if (searchQuery.length > 0) {
const rawList = listOfRelevantCountries; testList = listOfRelevantCountries.filter((item) =>
testList = item.toLowerCase().startsWith(searchQuery),
rawList?.filter((item) => { );
const index = item?.toLowerCase();
return index?.startsWith(searchQuery);
}) || [];
} }
}, 50); }, 50);
} }
$: checkedItems = new Set();
async function handleChangeValue(value) { async function handleChangeValue(value) {
if (checkedItems.has(value)) { if (checkedItems.has(value)) {
checkedItems.delete(value); checkedItems.delete(value);
@ -190,23 +195,20 @@
function handleReset() { function handleReset() {
filterList = []; filterList = [];
checkedItems = new Set(); checkedItems = new Set();
economicCalendar = data?.getEconomicCalendar; economicCalendar = data?.getEconomicCalendar;
daysOfWeek = getDaysOfWeek(currentWeek); daysOfWeek = getDaysOfWeek(currentWeek);
formattedWeekday = daysOfWeek.map((day) => format(day.date, "EEE, MMM d")); formattedWeekday = daysOfWeek.map((day) => format(day.date, "EEE, MMM d"));
weekday = getWeekdayData(economicCalendar, daysOfWeek); weekday = getWeekdayData(economicCalendar, daysOfWeek);
rawData = weekday; rawData = weekday;
currentWeek = startOfWeek(today, { weekStartsOn: 1 }); currentWeek = startOfWeek(today, { weekStartsOn: 1 });
selectedWeekday = Math.min((currentDate.getDay() + 6) % 7, 4); selectedWeekday = Math.min((currentDate.getDay() + 6) % 7, 4);
previousMax = currentWeek <= startBoundary; previousMax = currentWeek <= startBoundary;
nextMax = currentWeek >= endBoundary; nextMax = currentWeek >= endBoundary;
saveRules(); saveRules();
} }
let columns = [ // Static columns (do not change across renders)
const columns = [
{ key: "time", label: "Time", align: "left" }, { key: "time", label: "Time", align: "left" },
{ key: "country", label: "Country", align: "left" }, { key: "country", label: "Country", align: "left" },
{ key: "event", label: "Event", align: "left" }, { key: "event", label: "Event", align: "left" },
@ -228,86 +230,70 @@
const sortData = (key) => { const sortData = (key) => {
sortMode = true; sortMode = true;
for (const k in sortOrders) { Object.keys(sortOrders).forEach((k) => {
if (k !== key) { if (k !== key) sortOrders[k].order = "none";
sortOrders[k].order = "none"; });
}
}
// Cycle through 'none', 'asc', 'desc' for the clicked key // Cycle through "none", "asc", "desc"
const orderCycle = ["none", "asc", "desc"]; const orderCycle = ["none", "asc", "desc"];
const currentOrderIndex = orderCycle.indexOf( const currentOrderIndex = orderCycle.indexOf(
sortOrders[key]?.order || "none", sortOrders[key]?.order || "none",
); );
sortOrders[key] = { sortOrders[key] = {
...(sortOrders[key] || {}), ...sortOrders[key],
order: orderCycle[(currentOrderIndex + 1) % orderCycle.length], order: orderCycle[(currentOrderIndex + 1) % orderCycle.length],
}; };
const sortOrder = sortOrders[key]?.order;
// Reset to original data when 'none' and stop further sorting const sortOrder = sortOrders[key].order;
if (sortOrder === "none") { if (sortOrder === "none") {
sortMode = false; sortMode = false;
return; return;
} }
// Generic comparison function
const compareValues = (a, b) => { const compareValues = (a, b) => {
const { type } = sortOrders[key]; const { type } = sortOrders[key];
let valueA, valueB; let valueA, valueB;
switch (type) { switch (type) {
case "date": case "date":
valueA = new Date(a[key]); valueA = new Date(a[key]).getTime();
valueB = new Date(b[key]); valueB = new Date(b[key]).getTime();
break; break;
case "rating":
case "string": case "string":
// Retrieve values valueA = a[key] ? a[key].toUpperCase() : "";
valueA = a[key]; valueB = b[key] ? b[key].toUpperCase() : "";
valueB = b[key];
// Handle null or undefined values, always placing them at the bottom
if (valueA == null && valueB == null) {
return 0; // Both are null/undefined, no need to change the order
} else if (valueA == null) {
return 1; // null goes to the bottom
} else if (valueB == null) {
return -1; // null goes to the bottom
}
// Convert the values to uppercase for case-insensitive comparison
valueA = valueA?.toUpperCase();
valueB = valueB?.toUpperCase();
// Perform the sorting based on ascending or descending order
return sortOrder === "asc" return sortOrder === "asc"
? valueA?.localeCompare(valueB) ? valueA.localeCompare(valueB)
: valueB?.localeCompare(valueA); : valueB.localeCompare(valueA);
case "number": case "number":
default: default:
valueA = parseFloat(a[key]); valueA = parseFloat(a[key]);
valueB = parseFloat(b[key]); valueB = parseFloat(b[key]);
break; break;
} }
return sortOrder === "asc"
if (sortOrder === "asc") { ? valueA < valueB
return valueA < valueB ? -1 : valueA > valueB ? 1 : 0; ? -1
} else { : valueA > valueB
return valueA > valueB ? -1 : valueA < valueB ? 1 : 0; ? 1
} : 0
: valueA > valueB
? -1
: valueA < valueB
? 1
: 0;
}; };
// Sort and update the originalData and stockList // Create a new array copy to trigger reactivity (instead of in-place mutation)
weekday[selectedWeekday] = [...rawData[selectedWeekday]].sort( weekday = [
compareValues, ...weekday.slice(0, selectedWeekday),
); [...rawData[selectedWeekday]].sort(compareValues),
...weekday.slice(selectedWeekday + 1),
];
}; };
</script> </script>
<SEO <SEO
title="Worldwide title="Worldwide Economic Calendar"
Economic Calendar"
description="A list of upcoming economic events on the US stock market, with dates, times and estimation." description="A list of upcoming economic events on the US stock market, with dates, times and estimation."
/> />
@ -337,11 +323,10 @@
<div class="flex justify-center w-full m-auto h-full overflow-hidden"> <div class="flex justify-center w-full m-auto h-full overflow-hidden">
<!-- Content area --> <!-- Content area -->
<div class="relative flex flex-col flex-1 overflow-hidden"> <div class="relative flex flex-col flex-1 overflow-hidden">
<!-- Cards --> <!-- Header Dates -->
<div <div
class=" w-full flex flex-row justify-center m-auto items-center" class="w-full flex flex-row justify-center m-auto items-center"
> >
<!-- Start Columns -->
<label <label
on:click={() => changeWeek("previous")} on:click={() => changeWeek("previous")}
class="{previousMax class="{previousMax
@ -352,13 +337,14 @@
class="w-6 h-6 m-auto rotate-180" class="w-6 h-6 m-auto rotate-180"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"
><path >
<path
fill="white" fill="white"
d="M8.025 22L6.25 20.225L14.475 12L6.25 3.775L8.025 2l10 10l-10 10Z" d="M8.025 22L6.25 20.225L14.475 12L6.25 3.775L8.025 2l10 10l-10 10Z"
/></svg />
> </svg>
</label> </label>
{#each filterList?.length === 0 ? weekday : weekdayFiltered as day, index} {#each displayWeekData as day, index (formattedWeekday[index])}
<div <div
class="w-full text-white {index === selectedWeekday class="w-full text-white {index === selectedWeekday
? '' ? ''
@ -366,13 +352,13 @@
> >
<label <label
on:click={() => toggleDate(index)} on:click={() => toggleDate(index)}
class=" m-auto w-full cursor-pointer h-16 {index === class="m-auto w-full cursor-pointer h-16 {index ===
selectedWeekday selectedWeekday
? 'bg-white text-black font-semibold' ? 'bg-white text-black font-semibold'
: ''} rounded sm:rounded-none flex bg-default border border-gray-600 mb-3" : ''} rounded sm:rounded-none flex bg-default border border-gray-600 mb-3"
> >
<div <div
class=" flex flex-row justify-center items-center w-full" class="flex flex-row justify-center items-center w-full"
> >
<label <label
on:click={() => clickWeekday("previous", index)} on:click={() => clickWeekday("previous", index)}
@ -382,18 +368,19 @@
class="w-8 h-8 inline-block rotate-180" class="w-8 h-8 inline-block rotate-180"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"
><path >
<path
fill="black" fill="black"
d="M8.025 22L6.25 20.225L14.475 12L6.25 3.775L8.025 2l10 10l-10 10Z" d="M8.025 22L6.25 20.225L14.475 12L6.25 3.775L8.025 2l10 10l-10 10Z"
/></svg />
> </svg>
</label> </label>
<div <div
class="flex flex-col items-center truncate m-auto p-1" class="flex flex-col items-center truncate m-auto p-1"
> >
<span class="text-md">{formattedWeekday[index]}</span> <span class="text-md">{formattedWeekday[index]}</span>
<span class="text-[1rem] sm:text-sm m-auto pt-1 pb-1"> <span class="text-[1rem] sm:text-sm m-auto pt-1 pb-1"
{day?.length} Events</span >{day?.length} Events</span
> >
</div> </div>
<label <label
@ -404,11 +391,12 @@
class="w-8 h-8 inline-block" class="w-8 h-8 inline-block"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"
><path >
<path
fill="black" fill="black"
d="M8.025 22L6.25 20.225L14.475 12L6.25 3.775L8.025 2l10 10l-10 10Z" d="M8.025 22L6.25 20.225L14.475 12L6.25 3.775L8.025 2l10 10l-10 10Z"
/></svg />
> </svg>
</label> </label>
</div> </div>
</label> </label>
@ -424,14 +412,16 @@
class="w-6 h-6 m-auto" class="w-6 h-6 m-auto"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"
><path >
<path
fill="white" fill="white"
d="M8.025 22L6.25 20.225L14.475 12L6.25 3.775L8.025 2l10 10l-10 10Z" d="M8.025 22L6.25 20.225L14.475 12L6.25 3.775L8.025 2l10 10l-10 10Z"
/></svg />
> </svg>
</label> </label>
</div> </div>
<!-- Dropdown Filters -->
<div <div
class="flex flex-row items-center w-full sm:w-fit m-auto sm:m-0 pt-6 pb-3" class="flex flex-row items-center w-full sm:w-fit m-auto sm:m-0 pt-6 pb-3"
> >
@ -467,32 +457,28 @@
class="relative sticky z-40 focus:outline-none -top-1" class="relative sticky z-40 focus:outline-none -top-1"
tabindex="0" tabindex="0"
role="menu" role="menu"
style=""
> >
<input <input
bind:value={searchQuery} bind:value={searchQuery}
on:input={handleInput} on:input={handleInput}
autocomplete="off" autocomplete="off"
class=" absolute fixed sticky w-full border-0 bg-default border-b border-gray-200 class="absolute sticky w-full border-0 bg-default border-b border-gray-200 focus:border-gray-200 focus:ring-0 text-white placeholder:text-gray-300"
focus:border-gray-200 focus:ring-0 text-white placeholder:text-gray-300"
type="search" type="search"
placeholder="Search..." placeholder="Search..."
/> />
</div> </div>
<DropdownMenu.Group> <DropdownMenu.Group>
{#each testList.length > 0 && searchQuery?.length > 0 ? testList : searchQuery?.length > 0 && testList?.length === 0 ? [] : listOfRelevantCountries as item} {#each searchQuery.length > 0 ? testList : listOfRelevantCountries as item}
<DropdownMenu.Item class="sm:hover:bg-primary"> <DropdownMenu.Item class="sm:hover:bg-primary">
<div class="flex items-center"> <div class="flex items-center">
<label <label
on:click={() => { on:click={() => handleChangeValue(item)}
handleChangeValue(item);
}}
class="cursor-pointer text-white" class="cursor-pointer text-white"
for={item} for={item}
> >
<input <input
type="checkbox" type="checkbox"
checked={checkedItems?.has(item)} checked={checkedItems.has(item)}
/> />
<span class="ml-2">{item}</span> <span class="ml-2">{item}</span>
</label> </label>
@ -507,7 +493,7 @@
<DropdownMenu.Trigger asChild let:builder> <DropdownMenu.Trigger asChild let:builder>
<Button <Button
builders={[builder]} builders={[builder]}
class="border-gray-600 border bg-default sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-2 text-white rounded-md truncate" class="border-gray-600 border bg-default sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-2 text-white rounded-md truncate"
> >
<span class="truncate text-white" <span class="truncate text-white"
>Filter Importance</span >Filter Importance</span
@ -534,26 +520,23 @@
class="relative sticky z-40 focus:outline-none -top-1" class="relative sticky z-40 focus:outline-none -top-1"
tabindex="0" tabindex="0"
role="menu" role="menu"
style=""
></div> ></div>
<DropdownMenu.Group> <DropdownMenu.Group>
{#each [1, 2, 3] as i} {#each [1, 2, 3] as i}
<DropdownMenu.Item class="sm:hover:bg-primary"> <DropdownMenu.Item class="sm:hover:bg-primary">
<div class="flex items-center"> <div class="flex items-center">
<label <label
on:click={() => { on:click={() => handleChangeValue(i)}
handleChangeValue(i);
}}
class="flex flex-row items-center cursor-pointer text-white" class="flex flex-row items-center cursor-pointer text-white"
for={i} for={i}
> >
<input <input
type="checkbox" type="checkbox"
checked={checkedItems?.has(i)} checked={checkedItems.has(i)}
/> />
<div class="ml-2 flex flex-row items-center"> <div class="ml-2 flex flex-row items-center">
{#if i > 0} {#if i > 0}
{#each Array(i).fill() as _, index} {#each Array(i) as _}
<svg <svg
class="w-4 h-4 text-[#FBCE3C]" class="w-4 h-4 text-[#FBCE3C]"
aria-hidden="true" aria-hidden="true"
@ -566,19 +549,6 @@
/> />
</svg> </svg>
{/each} {/each}
{#each Array(3 - i).fill() as _}
<svg
class="w-4 h-4 text-gray-300 dark:text-gray-500"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 22 20"
>
<path
d="M20.924 7.625a1.523 1.523 0 0 0-1.238-1.044l-5.051-.734-2.259-4.577a1.534 1.534 0 0 0-2.752 0L7.365 5.847l-5.051.734A1.535 1.535 0 0 0 1.463 9.2l3.656 3.563-.863 5.031a1.532 1.532 0 0 0 2.226 1.616L11 17.033l4.518 2.375a1.534 1.534 0 0 0 2.226-1.617l-.863-5.03L20.537 9.2a1.523 1.523 0 0 0 .387-1.575Z"
/>
</svg>
{/each}
{/if} {/if}
</div> </div>
</label> </label>
@ -589,34 +559,35 @@
</DropdownMenu.Content> </DropdownMenu.Content>
</DropdownMenu.Root> </DropdownMenu.Root>
{#if filterList?.length !== 0} {#if filterList.length !== 0}
<Button <Button
on:click={() => handleReset()} on:click={handleReset}
class="w-fit border-gray-600 border bg-default sm:hover:bg-primary ease-out flex flex-row justify-start items-center px-3 py-2 text-white rounded-md truncate" class="w-fit border-gray-600 border bg-default sm:hover:bg-primary ease-out flex flex-row justify-start items-center px-3 py-2 text-white rounded-md truncate"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
class="inline-block w-4 h-4 mr-2" class="inline-block w-4 h-4 mr-2"
viewBox="0 0 21 21" viewBox="0 0 21 21"
><g >
<g
fill="none" fill="none"
fill-rule="evenodd"
stroke="currentColor" stroke="currentColor"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
><path d="M3.578 6.487A8 8 0 1 1 2.5 10.5" /><path >
d="M7.5 6.5h-4v-4" <path d="M3.578 6.487A8 8 0 1 1 2.5 10.5" />
/></g <path d="M7.5 6.5h-4v-4" />
></svg </g>
> </svg>
Reset All Reset All
</Button> </Button>
{/if} {/if}
</div> </div>
</div> </div>
<!-- Events Table -->
<div class="z-0 mb-40"> <div class="z-0 mb-40">
{#each filterList?.length === 0 ? weekday : weekdayFiltered as day, index} {#each displayWeekData as day, index}
{#if index === selectedWeekday} {#if index === selectedWeekday}
{#if day?.length !== 0} {#if day?.length !== 0}
<div class="flex flex-row items-center mt-5"> <div class="flex flex-row items-center mt-5">
@ -624,19 +595,15 @@
{formattedWeekday[index]?.split(", ")[1]} · {day?.length} {formattedWeekday[index]?.split(", ")[1]} · {day?.length}
Events Events
</h2> </h2>
{#if filterList?.length !== 0} {#if filterList.length !== 0}
<div <div
class="ml-auto text-[1rem] sm:text-lg flex flex-row items-center relative block rounded-md px-2 py-1 focus:outline-none" class="ml-auto text-[1rem] sm:text-lg flex flex-row items-center relative block rounded-md px-2 py-1 focus:outline-none"
> >
<span class="text-white">Filters</span> <span class="text-white">Filters</span>
<span <span
class="ml-2 rounded-full avatar w-5 h-5 text-xs font-semibold text-white text-center flex-shrink-0 class="ml-2 rounded-full avatar w-5 h-5 text-xs font-semibold text-white text-center flex-shrink-0 flex items-center justify-center bg-red-500"
flex items-center justify-center bg-red-500 {filterList?.length !==
0
? 'bg-red-500'
: 'bg-gray-600'}"
> >
{filterList?.length} {filterList.length}
</span> </span>
</div> </div>
{/if} {/if}
@ -651,16 +618,14 @@
</thead> </thead>
<tbody> <tbody>
{#each day as item} {#each day as item}
<!-- row -->
<tr <tr
class="sm:hover:bg-[#245073] sm:hover:bg-opacity-[0.2] odd:bg-odd border border-gray-800" class="sm:hover:bg-[#245073] sm:hover:bg-opacity-[0.2] odd:bg-odd border border-gray-800"
> >
<td class="text-white text-sm sm:text-[1rem]"> <td class="text-white text-sm sm:text-[1rem]">
<label class="p-1.5 rounded-md"> <label class="p-1.5 rounded-md"
{item?.time} >{item?.time}</label
</label> >
</td> </td>
<td <td
class="flex flex-row items-center text-sm sm:text-[1rem] whitespace-nowrap" class="flex flex-row items-center text-sm sm:text-[1rem] whitespace-nowrap"
> >
@ -670,93 +635,98 @@
class="w-4 h-4 sm:w-6 sm:h-6" class="w-4 h-4 sm:w-6 sm:h-6"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512" viewBox="0 0 512 512"
><mask id="circleFlagsEu0" >
><circle <mask id="circleFlagsEu0">
<circle
cx="256" cx="256"
cy="256" cy="256"
r="256" r="256"
fill="#fff" fill="#fff"
/></mask />
><g mask="url(#circleFlagsEu0)" </mask>
><path <g mask="url(#circleFlagsEu0)">
<path
fill="#0052b4" fill="#0052b4"
d="M0 0h512v512H0z" d="M0 0h512v512H0z"
/><path />
<path
fill="#ffda44" fill="#ffda44"
d="m256 100.2l8.3 25.5H291l-21.7 15.7l8.3 25.6l-21.7-15.8l-21.7 15.8l8.3-25.6l-21.7-15.7h26.8zm-110.2 45.6l24 12.2l18.9-19l-4.2 26.5l23.9 12.2l-26.5 4.2l-4.2 26.5l-12.2-24l-26.5 4.3l19-19zM100.2 256l25.5-8.3V221l15.7 21.7l25.6-8.3l-15.8 21.7l15.8 21.7l-25.6-8.3l-15.7 21.7v-26.8zm45.6 110.2l12.2-24l-19-18.9l26.5 4.2l12.2-23.9l4.2 26.5l26.5 4.2l-24 12.2l4.3 26.5l-19-19zM256 411.8l-8.3-25.5H221l21.7-15.7l-8.3-25.6l21.7 15.8l21.7-15.8l-8.3 25.6l21.7 15.7h-26.8zm110.2-45.6l-24-12.2l-18.9 19l4.2-26.5l-23.9-12.2l26.5-4.2l4.2-26.5l12.2 24l26.5-4.3l-19 19zM411.8 256l-25.5 8.3V291l-15.7-21.7l-25.6 8.3l15.8-21.7l-15.8-21.7l25.6 8.3l15.7-21.7v26.8zm-45.6-110.2l-12.2 24l19 18.9l-26.5-4.2l-12.2 23.9l-4.2-26.5l-26.5-4.2l24-12.2l-4.3-26.5l19 19z" d="m256 100.2l8.3 25.5H291l-21.7 15.7l8.3 25.6l-21.7-15.8l-21.7 15.8l8.3-25.6l-21.7-15.7h26.8zm-110.2 45.6l24 12.2l18.9-19l-4.2 26.5l23.9 12.2l-26.5 4.2l-4.2 26.5l-12.2-24l-26.5 4.3l19-19zM100.2 256l25.5-8.3V221l15.7 21.7l25.6-8.3l-15.8 21.7l15.8 21.7l-25.6-8.3l-15.7 21.7v-26.8zm45.6 110.2l12.2-24l-19-18.9l26.5 4.2l12.2-23.9l4.2 26.5l26.5 4.2l-24 12.2l4.3 26.5l-19-19zM256 411.8l-8.3-25.5H221l21.7-15.7l-8.3-25.6l21.7 15.8l21.7-15.8l-8.3 25.6l21.7 15.7h-26.8zm110.2-45.6l-24-12.2l-18.9 19l4.2-26.5l-23.9-12.2l26.5-4.2l4.2-26.5l12.2 24l26.5-4.3l-19 19zM411.8 256l-25.5 8.3V291l-15.7-21.7l-25.6 8.3l15.8-21.7l-15.8-21.7l25.6 8.3l15.7-21.7v26.8zm-45.6-110.2l-12.2 24l19 18.9l-26.5-4.2l-12.2 23.9l-4.2-26.5l-26.5-4.2l24-12.2l-4.3-26.5l19 19z"
/></g />
></svg </g>
> </svg>
{:else if item?.country === "UK"} {:else if item?.country === "UK"}
<svg <svg
style="clip-path: circle(50%);" style="clip-path: circle(50%);"
class="w-4 h-4 sm:w-6 sm:h-6" class="w-4 h-4 sm:w-6 sm:h-6"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512" viewBox="0 0 512 512"
><mask id="circleFlagsUk0" >
><circle <mask id="circleFlagsUk0">
<circle
cx="256" cx="256"
cy="256" cy="256"
r="256" r="256"
fill="#fff" fill="#fff"
/></mask />
><g mask="url(#circleFlagsUk0)" </mask>
><path <g mask="url(#circleFlagsUk0)">
<path
fill="#eee" fill="#eee"
d="m0 0l8 22l-8 23v23l32 54l-32 54v32l32 48l-32 48v32l32 54l-32 54v68l22-8l23 8h23l54-32l54 32h32l48-32l48 32h32l54-32l54 32h68l-8-22l8-23v-23l-32-54l32-54v-32l-32-48l32-48v-32l-32-54l32-54V0l-22 8l-23-8h-23l-54 32l-54-32h-32l-48 32l-48-32h-32l-54 32L68 0z" d="m0 0l8 22l-8 23v23l32 54l-32 54v32l32 48l-32 48v32l32 54l-32 54v68l22-8l23 8h23l54-32l54 32h32l48-32l48 32h32l54-32l54 32h68l-8-22l8-23v-23l-32-54l32-54v-32l-32-48l32-48v-32l-32-54l32-54V0l-22 8l-23-8h-23l-54 32l-54-32h-32l-48 32l-48-32h-32l-54 32L68 0z"
/><path />
<path
fill="#0052b4" fill="#0052b4"
d="M336 0v108L444 0Zm176 68L404 176h108zM0 176h108L0 68ZM68 0l108 108V0Zm108 512V404L68 512ZM0 444l108-108H0Zm512-108H404l108 108Zm-68 176L336 404v108z" d="M336 0v108L444 0Zm176 68L404 176h108zM0 176h108L0 68ZM68 0l108 108V0Zm108 512V404L68 512ZM0 444l108-108H0Zm512-108H404l108 108Zm-68 176L336 404v108z"
/><path />
<path
fill="#d80027" fill="#d80027"
d="M0 0v45l131 131h45zm208 0v208H0v96h208v208h96V304h208v-96H304V0zm259 0L336 131v45L512 0zM176 336L0 512h45l131-131zm160 0l176 176v-45L381 336z" d="M0 0v45l131 131h45zm208 0v208H0v96h208v208h96V304h208v-96H304V0zm259 0L336 131v45L512 0zM176 336L0 512h45l131-131zm160 0l176 176v-45L381 336z"
/></g />
></svg </g>
> </svg>
{:else} {:else}
<img <img
style="clip-path: circle(50%);" style="clip-path: circle(50%);"
class="w-4 h-4 sm:w-6 sm:h-6" class="w-4 h-4 sm:w-6 sm:h-6"
src={`https://hatscripts.github.io/circle-flags/flags/${item?.countryCode}.svg`} src={`https://hatscripts.github.io/circle-flags/flags/${item?.countryCode}.svg`}
loading="lazy"
alt="{item?.country} flag"
/> />
{/if} {/if}
<span class="text-white ml-2"> <span class="text-white ml-2"
{item?.country} >{item?.country}</span
</span> >
</td> </td>
<td <td
class="text-start text-white text-sm sm:text-[1rem] whitespace-nowrap" class="text-start text-white text-sm sm:text-[1rem] whitespace-nowrap"
> >
{item?.event?.length > 40 {item?.event?.length > 40
? item?.event?.slice(0, 40) + "..." ? item?.event.slice(0, 40) + "..."
: item?.event} : item?.event}
</td> </td>
<td <td
class="text-white text-end text-sm sm:text-[1rem] whitespace-nowrap" class="text-white text-end text-sm sm:text-[1rem] whitespace-nowrap"
> >
{item?.actual !== (null || "") {item?.actual !== null && item?.actual !== ""
? abbreviateNumber(item?.actual) ? abbreviateNumber(item?.actual)
: "-"} : "-"}
</td> </td>
<td <td
class="text-white text-end text-sm sm:text-[1rem] whitespace-nowrap" class="text-white text-end text-sm sm:text-[1rem] whitespace-nowrap"
> >
{item?.consensus !== (null || "") {item?.consensus !== null &&
item?.consensus !== ""
? abbreviateNumber(item?.consensus) ? abbreviateNumber(item?.consensus)
: "-"} : "-"}
</td> </td>
<td <td
class="text-white text-end text-sm sm:text-[1rem] whitespace-nowrap" class="text-white text-end text-sm sm:text-[1rem] whitespace-nowrap"
> >
{item?.prior !== (null || "") {item?.prior !== null && item?.prior !== ""
? abbreviateNumber(item?.prior) ? abbreviateNumber(item?.prior)
: "-"} : "-"}
</td> </td>
<td <td
class="text-white text-start text-sm sm:text-[1rem] whitespace-nowrap" class="text-white text-start text-sm sm:text-[1rem] whitespace-nowrap"
> >
@ -818,7 +788,7 @@
class="w-full text-white border border-gray-600 rounded-md h-fit pb-4 mt-4 cursor-pointer bg-inherit sm:hover:bg-secondary transition ease-out duration-100" class="w-full text-white border border-gray-600 rounded-md h-fit pb-4 mt-4 cursor-pointer bg-inherit sm:hover:bg-secondary transition ease-out duration-100"
> >
<a <a
href={"/earnings-calendar"} href="/earnings-calendar"
class="w-auto lg:w-full p-1 flex flex-col m-auto px-2 sm:px-0" class="w-auto lg:w-full p-1 flex flex-col m-auto px-2 sm:px-0"
> >
<div class="w-full flex justify-between items-center p-3 mt-3"> <div class="w-full flex justify-between items-center p-3 mt-3">
@ -827,9 +797,9 @@
</h2> </h2>
<ArrowLogo class="w-8 h-8 mr-3 flex-shrink-0" /> <ArrowLogo class="w-8 h-8 mr-3 flex-shrink-0" />
</div> </div>
<span class="text-white p-3 ml-3 mr-3"> <span class="text-white p-3 ml-3 mr-3"
Get the latest Earnings of companies >Get the latest Earnings of companies</span
</span> >
</a> </a>
</div> </div>
@ -837,7 +807,7 @@
class="w-full text-white border border-gray-600 rounded-md h-fit pb-4 mt-4 cursor-pointer bg-inherit sm:hover:bg-secondary transition ease-out duration-100" class="w-full text-white border border-gray-600 rounded-md h-fit pb-4 mt-4 cursor-pointer bg-inherit sm:hover:bg-secondary transition ease-out duration-100"
> >
<a <a
href={"/dividends-calendar"} href="/dividends-calendar"
class="w-auto lg:w-full p-1 flex flex-col m-auto px-2 sm:px-0" class="w-auto lg:w-full p-1 flex flex-col m-auto px-2 sm:px-0"
> >
<div class="w-full flex justify-between items-center p-3 mt-3"> <div class="w-full flex justify-between items-center p-3 mt-3">
@ -846,9 +816,9 @@
</h2> </h2>
<ArrowLogo class="w-8 h-8 mr-3 flex-shrink-0" /> <ArrowLogo class="w-8 h-8 mr-3 flex-shrink-0" />
</div> </div>
<span class="text-white p-3 ml-3 mr-3"> <span class="text-white p-3 ml-3 mr-3"
Get the latest dividend announcement >Get the latest dividend announcement</span
</span> >
</a> </a>
</div> </div>
</aside> </aside>

View File

@ -3,7 +3,7 @@ import { convertToSlug } from "$lib/utils";
const pages = [ const pages = [
{ title: "/" }, { title: "/" },
{ title: "/reddit-tracker" }, { title: "/reddit-tracker" },
{ title: "/list/most-shorted-stocks"}, { title: "/list/most-shorted-stocks" },
{ title: "/stocks" }, { title: "/stocks" },
{ title: "/etf" }, { title: "/etf" },
{ title: "/etf/etf-providers" }, { title: "/etf/etf-providers" },
@ -29,10 +29,10 @@ const pages = [
{ title: "/list/market-cap/small-cap-stocks" }, { title: "/list/market-cap/small-cap-stocks" },
{ title: "/list/market-cap/micro-cap-stocks" }, { title: "/list/market-cap/micro-cap-stocks" },
{ title: "/list/market-cap/nano-cap-stocks" }, { title: "/list/market-cap/nano-cap-stocks" },
{ title: "/list/highest-open-interest" }, { title: "/list/highest-open-interest" },
{ title: "/list/highest-open-interest-change" }, { title: "/list/highest-open-interest-change" },
{ title: "/list/highest-option-iv-rank" }, { title: "/list/highest-option-iv-rank" },
{ title: "/list/highest-option-premium" }, { title: "/list/highest-option-premium" },
{ title: "/list/bitcoin-etfs" }, { title: "/list/bitcoin-etfs" },
{ title: "/stock-screener" }, { title: "/stock-screener" },
{ title: "/market-news" }, { title: "/market-news" },
@ -66,7 +66,7 @@ const pages = [
{ title: "/analysts" }, { title: "/analysts" },
{ title: "/analysts/top-stocks" }, { title: "/analysts/top-stocks" },
{ title: "/heatmap" }, { title: "/heatmap" },
{ title: "/market-flow" }, { title: "/market-flow" },
]; ];
const website = "https://stocknear.com"; const website = "https://stocknear.com";
@ -76,8 +76,6 @@ export async function GET({ locals }) {
//get all posts; //get all posts;
const { apiKey, apiURL, pb } = locals; const { apiKey, apiURL, pb } = locals;
const rawData = await fetch(apiURL + "/full-searchbar", { const rawData = await fetch(apiURL + "/full-searchbar", {
method: "GET", method: "GET",
headers: { headers: {
@ -92,16 +90,15 @@ export async function GET({ locals }) {
type: item?.type, type: item?.type,
})); }));
const articles = await pb.collection("articles").getFullList({
sort: "-created",
});
const articles = await pb.collection("articles").getFullList({ const tutorials = await pb.collection("tutorials").getFullList({
sort: "-created", sort: "-created",
}); });
const tutorials = await pb.collection("tutorials").getFullList({ const body = sitemap(stocks, articles, pages);
sort: "-created",
});
const body = sitemap(stocks, articles, pages, tutorials);
const response = new Response(body); const response = new Response(body);
response.headers.set("Cache-Control", "max-age=0, s-maxage=3600"); response.headers.set("Cache-Control", "max-age=0, s-maxage=3600");
response.headers.set("Content-Type", "application/xml"); response.headers.set("Content-Type", "application/xml");
@ -113,7 +110,6 @@ const sitemap = (
stocks, stocks,
articles, articles,
pages, pages,
tutorials,
) => `<?xml version="1.0" encoding="UTF-8" ?> ) => `<?xml version="1.0" encoding="UTF-8" ?>
<urlset <urlset
xmlns="https://www.sitemaps.org/schemas/sitemap/0.9" xmlns="https://www.sitemaps.org/schemas/sitemap/0.9"
@ -133,23 +129,23 @@ const sitemap = (
) )
.join("")} .join("")}
${articles ${articles
.map( .map(
(item) => ` (item) => `
<url> <url>
<loc>${website}/blog/article/${convertToSlug(item?.title)}</loc> <loc>${website}/blog/article/${convertToSlug(item?.title)}</loc>
</url> </url>
`, `,
) )
.join("")} .join("")}
${tutorials ${tutorials
.map( .map(
(item) => ` (item) => `
<url> <url>
<loc>${website}/learning-center/article/${convertToSlug(item?.title)}</loc> <loc>${website}/learning-center/article/${convertToSlug(item?.title)}</loc>
</url> </url>
`, `,
) )
.join("")} .join("")}
${stocks ${stocks
.map((ticker) => { .map((ticker) => {
// Determine the path based on the type of the ticker // Determine the path based on the type of the ticker
@ -158,7 +154,7 @@ const sitemap = (
? "/stocks/" ? "/stocks/"
: ticker.type === "ETF" : ticker.type === "ETF"
? "/etf/" ? "/etf/"
: "/index/"; : "/crypto/";
return ` return `
<url> <url>
<loc>${website}${path}${ticker.id}</loc> <loc>${website}${path}${ticker.id}</loc>