diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 122d0fda..f02a12f6 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -20,7 +20,7 @@ type FlyAndScaleParams = { export const flyAndScale = ( node: Element, - params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 0 } + params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 0 }, ): TransitionConfig => { const style = getComputedStyle(node); const transform = style.transform === "none" ? "" : style.transform; @@ -28,7 +28,7 @@ export const flyAndScale = ( const scaleConversion = ( valueA: number, scaleA: [number, number], - scaleB: [number, number] + scaleB: [number, number], ) => { const [minA, maxA] = scaleA; const [minB, maxB] = scaleB; @@ -40,7 +40,7 @@ export const flyAndScale = ( }; const styleToString = ( - style: Record + style: Record, ): string => { return Object.keys(style).reduce((str, key) => { if (style[key] === undefined) return str; @@ -268,7 +268,7 @@ export function sumQuarterlyResultsByYear(quarterlyResults, namingList) { // Filter out years with less than 4 quarters const validYears = Object?.keys(quarterCounts)?.filter( - (year) => quarterCounts[year] === 4 + (year) => quarterCounts[year] === 4, ); const annualResults = validYears?.map((year) => yearlySummaries[year]); @@ -476,7 +476,7 @@ export function formatETFName(inputString) { // Capitalize the first letter of each word const capitalizedWords = words?.map( - (word) => word.charAt(0)?.toUpperCase() + word?.slice(1) + (word) => word.charAt(0)?.toUpperCase() + word?.slice(1), ); // Join the words back together with a space between them @@ -498,7 +498,7 @@ export function addDays(data, days, state) { } else { const differenceInTime = result - createdDate; const differenceInDays = Math.round( - differenceInTime / (1000 * 60 * 60 * 24) + differenceInTime / (1000 * 60 * 60 * 24), ); return Math.abs(differenceInDays); } @@ -1280,3 +1280,55 @@ export const monthNames = [ "Nov", "Dec", ]; + +export const holidays = [ + "2024-01-01", + "2024-01-15", + "2024-02-19", + "2024-03-29", + "2024-05-27", + "2024-06-19", + "2024-07-04", + "2024-09-02", + "2024-11-28", + "2024-12-25", +]; + +export const getLastTradingDay = () => { + const etTimeZone = "America/New_York"; + + // Helper function to check if a date (in NY time) is a holiday + const isHoliday = (date) => { + return holidays.includes(date.toISOString().split("T")[0]); + }; + let date = new Date(); + + // Convert current date to NY timezone + const nyDate = new Date( + date.toLocaleString("en-US", { timeZone: etTimeZone }), + ); + const currentHour = nyDate.getHours(); + const currentMinutes = nyDate.getMinutes(); + const isMarketClosed = + currentHour < 9 || + (currentHour === 9 && currentMinutes < 30) || + currentHour >= 16; + + // If market is closed, move to the previous day + if (isMarketClosed) { + nyDate.setDate(nyDate.getDate() - 1); + } + + // Loop backwards to find the most recent trading day + while (true) { + const dayOfWeek = nyDate.getUTCDay(); + + // Check if it's a weekday and not a holiday + if (dayOfWeek !== 0 && dayOfWeek !== 6 && !isHoliday(nyDate)) { + return nyDate.toISOString().split("T")[0]; + } + + // Move back one day + nyDate.setDate(nyDate.getDate() - 1); + } +}; diff --git a/src/routes/market-mover/+page.server.ts b/src/routes/market-mover/+layout.server.ts similarity index 66% rename from src/routes/market-mover/+page.server.ts rename to src/routes/market-mover/+layout.server.ts index df5cd05e..61d2b1db 100644 --- a/src/routes/market-mover/+page.server.ts +++ b/src/routes/market-mover/+layout.server.ts @@ -1,7 +1,7 @@ -export const load = async ({ locals, setHeaders }) => { +export const load = async ({ locals }) => { const { apiURL, apiKey } = locals; - const getDailyGainerLoserActive = async () => { + const getMarketMover = async () => { const response = await fetch(apiURL + "/market-movers", { method: "GET", headers: { @@ -14,7 +14,7 @@ export const load = async ({ locals, setHeaders }) => { return output; }; - + /* const getMiniPlotsIndex = async () => { const response = await fetch(apiURL + "/mini-plots-index", { method: "GET", @@ -28,11 +28,8 @@ export const load = async ({ locals, setHeaders }) => { return output; }; - - setHeaders({ "cache-control": "public, max-age=3000" }); - // Make sure to return a promise + */ return { - getDailyGainerLoserActive: await getDailyGainerLoserActive(), - getMiniPlotsIndex: await getMiniPlotsIndex(), + getMarketMover: await getMarketMover(), }; }; diff --git a/src/routes/market-mover/+layout.svelte b/src/routes/market-mover/+layout.svelte new file mode 100644 index 00000000..6ad17db2 --- /dev/null +++ b/src/routes/market-mover/+layout.svelte @@ -0,0 +1,181 @@ + + + + + + + + +
+ + +
+
+ +
+
+
diff --git a/src/routes/market-mover/+page.svelte b/src/routes/market-mover/+page.svelte index 64070ed4..57766bef 100644 --- a/src/routes/market-mover/+page.svelte +++ b/src/routes/market-mover/+page.svelte @@ -1,176 +1,40 @@ @@ -261,7 +120,7 @@ {$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ""} Today's - Top Stock Gainers, Losers and Most Active · stocknear + Top Stock Gainers · stocknear - + -
- +
+
+
+
+ -
-
-
-
-

- Market Movers -

- - {#if isLoaded} -
- Stock Indexes - {getCurrentDateFormatted()} -
- -
+
    +
  • selectTimeInterval("1D")} + class="p-2 px-5 cursor-pointer {timePeriod === '1D' + ? 'text-white bg-[#27272A] sm:hover:bg-opacity-[0.95]' + : 'text-gray-400 sm:hover:text-white sm:hover:bg-[#27272A] sm:hover:bg-opacity-[0.95]'}" > + Today +
  • +
  • selectTimeInterval("1W")} + class="p-2 px-5 cursor-pointer {timePeriod === '1W' + ? 'text-white bg-[#27272A] sm:hover:bg-opacity-[0.95]' + : 'text-gray-400 sm:hover:text-white sm:hover:bg-[#27272A] sm:hover:bg-opacity-[0.95]'}" + > + Week +
  • +
  • selectTimeInterval("1M")} + class="p-2 px-5 cursor-pointer {timePeriod === '1M' + ? 'text-white bg-[#27272A] sm:hover:bg-opacity-[0.95]' + : 'text-gray-400 sm:hover:text-white sm:hover:bg-[#27272A] sm:hover:bg-opacity-[0.95]'}" + > + Month +
  • +
  • selectTimeInterval("1Y")} + class="p-2 px-5 cursor-pointer {timePeriod === '1Y' + ? 'text-white bg-[#27272A] sm:hover:bg-opacity-[0.95]' + : 'text-gray-400 sm:hover:text-white sm:hover:bg-[#27272A] sm:hover:bg-opacity-[0.95]'}" + > + Year +
  • +
  • selectTimeInterval("3Y")} + class="p-2 px-5 cursor-pointer {timePeriod === '3Y' + ? 'text-white bg-[#27272A] sm:hover:bg-opacity-[0.95]' + : 'text-gray-400 sm:hover:text-white sm:hover:bg-[#27272A] sm:hover:bg-opacity-[0.95]'}" + > + 3 Years +
  • +
  • selectTimeInterval("5Y")} + class="p-2 px-5 cursor-pointer {timePeriod === '3Y' + ? 'text-white bg-[#27272A] sm:hover:bg-opacity-[0.95]' + : 'text-gray-400 sm:hover:text-white sm:hover:bg-[#27272A] sm:hover:bg-opacity-[0.95]'}" + > + 5 Years +
  • +
+ + +
+
+
+

+ {displayTitle[timePeriod]} +

+
- - - - + + {lastTradingDay}
+
- - - - - -
+ -
-
- - - - - {#each gainerLoserActive as item} - + + + + {#each stockList as item} + + + + - + {item?.symbol} + + + - + {:else} + {item?.changesPercentage <= -1000 + ? abbreviateNumber(item?.changesPercentage) + : item?.changesPercentage?.toFixed(2)}% + + {/if} + - + - + - - - {/each} - -
+ {item?.rank} + + - - - {item?.symbol} - - - {item?.name?.length > charNumber - ? item?.name?.slice(0, charNumber) + "..." - : item?.name} - + {item?.name?.length > charNumber + ? item?.name?.slice(0, charNumber) + "..." + : item?.name} + + {#if item?.changesPercentage >= 0} + +{item?.changesPercentage >= 1000 + ? abbreviateNumber(item?.changesPercentage) + : item?.changesPercentage?.toFixed(2)}% - {#if item?.changesPercentage >= 0} - +{item?.changesPercentage >= 1000 - ? abbreviateNumber(item?.changesPercentage) - : item?.changesPercentage?.toFixed(2)}% - {:else} - {item?.changesPercentage <= -1000 - ? abbreviateNumber(item?.changesPercentage) - : item?.changesPercentage?.toFixed(2)}% - - {/if} - - {item?.price?.toFixed(2)} - + {item?.price?.toFixed(2)} + - {item?.marketCap !== null - ? abbreviateNumber(item?.marketCap) - : "-"} - + {item?.marketCap !== null + ? abbreviateNumber(item?.marketCap) + : "-"} + - {item?.volume !== null - ? abbreviateNumber(item?.volume) - : "-"} -
-
-
- {:else} -
-
- -
-
- {/if} -
- - -
+
+
diff --git a/src/routes/market-mover/active/+page.svelte b/src/routes/market-mover/active/+page.svelte new file mode 100644 index 00000000..fffd196c --- /dev/null +++ b/src/routes/market-mover/active/+page.svelte @@ -0,0 +1,315 @@ + + + + + + + {$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ""} + Top Stock Active · stocknear + + + + + + + + + + + + + + + + +
+
+
+
+ + + + +
+
+
+

+ {displayTitle[timePeriod]} +

+ +
+ + {lastTradingDay} +
+
+
+ +
+ + + + + + {#each stockList as item} + + + + + + + + + + + + + + + {/each} + +
+ {item?.rank} + + + {item?.symbol} + + + {item?.name?.length > charNumber + ? item?.name?.slice(0, charNumber) + "..." + : item?.name} + + {#if item?.changesPercentage >= 0} + +{item?.changesPercentage >= 1000 + ? abbreviateNumber(item?.changesPercentage) + : item?.changesPercentage?.toFixed(2)}% + {:else} + {item?.changesPercentage <= -1000 + ? abbreviateNumber(item?.changesPercentage) + : item?.changesPercentage?.toFixed(2)}% + + {/if} + + {item?.price?.toFixed(2)} + + {item?.marketCap !== null + ? abbreviateNumber(item?.marketCap) + : "-"} + + {item?.volume !== null + ? abbreviateNumber(item?.volume) + : "-"} +
+
+
+
+
+
+
diff --git a/src/routes/market-mover/losers/+page.svelte b/src/routes/market-mover/losers/+page.svelte new file mode 100644 index 00000000..0d4b5e41 --- /dev/null +++ b/src/routes/market-mover/losers/+page.svelte @@ -0,0 +1,315 @@ + + + + + + + {$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ""} + Top Stock Losers · stocknear + + + + + + + + + + + + + + + + +
+
+
+
+ + + + +
+
+
+

+ {displayTitle[timePeriod]} +

+ +
+ + {lastTradingDay} +
+
+
+ +
+ + + + + + {#each stockList as item} + + + + + + + + + + + + + + + {/each} + +
+ {item?.rank} + + + {item?.symbol} + + + {item?.name?.length > charNumber + ? item?.name?.slice(0, charNumber) + "..." + : item?.name} + + {#if item?.changesPercentage >= 0} + +{item?.changesPercentage >= 1000 + ? abbreviateNumber(item?.changesPercentage) + : item?.changesPercentage?.toFixed(2)}% + {:else} + {item?.changesPercentage <= -1000 + ? abbreviateNumber(item?.changesPercentage) + : item?.changesPercentage?.toFixed(2)}% + + {/if} + + {item?.price?.toFixed(2)} + + {item?.marketCap !== null + ? abbreviateNumber(item?.marketCap) + : "-"} + + {item?.volume !== null + ? abbreviateNumber(item?.volume) + : "-"} +
+
+
+
+
+
+
diff --git a/src/routes/market-news/+layout.svelte b/src/routes/market-news/+layout.svelte index 13476ff4..d2d308ba 100644 --- a/src/routes/market-news/+layout.svelte +++ b/src/routes/market-news/+layout.svelte @@ -1,8 +1,6 @@