diff --git a/src/routes/etf/[tickerID]/+layout.svelte b/src/routes/etf/[tickerID]/+layout.svelte index 5830a20a..d2c8a626 100644 --- a/src/routes/etf/[tickerID]/+layout.svelte +++ b/src/routes/etf/[tickerID]/+layout.svelte @@ -280,14 +280,10 @@ if ($page?.url?.pathname && typeof window !== "undefined") { const parts = $page?.url?.pathname?.split("/"); const sectionMap = { - statistics: "statistics", - financials: "financials", + holdings: "holdings", options: "options", - metrics: "metrics", insider: "insider", dividends: "dividends", - forecast: "forecast", - news: "news", }; displaySection = sectionMap[ @@ -688,49 +684,16 @@ Overview changeSection("financials")} + href={`/etf/${$etfTicker}/holdings`} + on:click={() => changeSection("holdings")} class="p-2 px-5 cursor-pointer {displaySection === - 'financials' + 'holdings' ? '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]'}" > - Financials + Holdings - changeSection("statistics")} - class="p-2 px-5 cursor-pointer {displaySection === - 'statistics' - ? '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]'}" - >Statistics - {#if ["amd", "save", "ba", "adbe", "nflx", "pltr", "msft", "meta", "tsla", "nvda", "aapl", "gme"]?.includes($etfTicker?.toLowerCase())} - changeSection("metrics")} - class="p-2 px-5 cursor-pointer {displaySection === - 'metrics' - ? '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]'}" - >Metrics - {/if} - - {#if Object?.keys(data?.getAnalystRating ?? {})?.length > 0} - changeSection("forecast")} - class="p-2 px-5 cursor-pointer {displaySection === - 'forecast' - ? '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]'}" - > - Forecast - - {/if} changeSection("options")} diff --git a/src/routes/etf/[tickerID]/+page.svelte b/src/routes/etf/[tickerID]/+page.svelte index 3b7b68f8..aaf1af0b 100644 --- a/src/routes/etf/[tickerID]/+page.svelte +++ b/src/routes/etf/[tickerID]/+page.svelte @@ -1351,12 +1351,20 @@ class="flex flex-col border-b border-gray-600 py-1 sm:table-row sm:py-0" >Short % of Shares OutInception Date {stockDeck?.shortOutStandingPercent !== null - ? stockDeck?.shortOutStandingPercent + "%" + >{stockDeck?.inceptionDate !== null + ? new Date(stockDeck?.inceptionDate)?.toLocaleString( + "en-US", + { + month: "short", + day: "numeric", + year: "numeric", + daySuffix: "2-digit", + }, + ) : "n/a"} diff --git a/src/routes/etf/[tickerID]/congress-trading/+page.svelte b/src/routes/etf/[tickerID]/congress-trading/+page.svelte deleted file mode 100644 index c30dbfca..00000000 --- a/src/routes/etf/[tickerID]/congress-trading/+page.svelte +++ /dev/null @@ -1,607 +0,0 @@ - - - - - - - {$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ""} - {$displayCompanyName} ({$etfTicker}) US Congress & Senate Trading 路 - stocknear - - - - - - - - - - - - - - - - -
-
-
-
-
-

- Congress Trading -

- -
- - Get detailed insights of Corrupt US Politician 馃嚭馃嚫 who bought or sold - {$displayCompanyName} and the amounts involved! -
-
- {#if isLoaded} - {#if senateTradingList?.length !== 0} -

- Congress Statistics -

- -
-
- -
-
- Buy/Sell - - {buySellRatio?.toFixed(3)} - -
- -
- - - - - - = 0 - ? 100 - (buySellRatio * 100)?.toFixed(2) - : 0} - > - - - -
- {buySellRatio?.toFixed(2)} -
-
- -
- - -
-
- Dem/Rep - - {partyRatio?.toFixed(3)} - -
- -
- - - - - - = 0 - ? 100 - (partyRatio * 100)?.toFixed(2) - : 0} - > - - - -
- {partyRatio?.toFixed(2)} -
-
- -
- -
-
- - - - - {#if displayStructure === "Card"} -
- - - - - - - - - - - {#each senateTradingList as item} - - - - - - - - - {/each} - -
- Person - - Transaction Date - - Amount - Type
-
-
- -
- -
- -
- {new Date(item?.transactionDate)?.toLocaleString( - "en-US", - { - month: "short", - day: "numeric", - year: "numeric", - daySuffix: "2-digit", - }, - )} - - {item?.amount} - - {#if item?.type === "Bought"} - Bought - {:else if item?.type === "Sold"} - Sold - {:else if item?.type === "Exchange"} - Exchange - {/if} -
-
- {:else} -
- {#each senateTradingList as item} -
-
- {#if item?.party === "Republican"} - - {:else if item?.party === "Democratic"} - - {:else} - - {/if} -
-
- -
- - {item?.representative?.replace("_", " ")} - - - {item?.party ?? "n/a"} / {district[ - item?.representative - ] ?? "n/a"} - -
- -
-
- Owner - - {item?.owner?.length !== 0 ? item?.owner : "-"} - -
-
- -
- -
-
- Transaction Date - - {new Date(item?.transactionDate)?.toLocaleString( - "en-US", - { - month: "short", - day: "numeric", - year: "numeric", - daySuffix: "2-digit", - }, - )} - -
- -
- Disclosure Date - - {new Date(item?.disclosureDate)?.toLocaleString( - "en-US", - { - month: "short", - day: "numeric", - year: "numeric", - daySuffix: "2-digit", - }, - )} - -
-
- -
- -
-
- Amount - - {item?.amount?.replace( - "$1,000,001 - $5,000,000", - "$1Mio - $5Mio", - )} - -
- -
- Type - - {#if item?.type === "Bought"} - Bought - {:else if item?.type === "Sold"} - Sold - {:else if item?.type === "Exchange"} - Exchange - {/if} - -
-
-
-
- {/each} -
- - {#if rawData?.length >= 20} - - {/if} - {/if} - {:else} -

- No trading history available for {$displayCompanyName}. Likely no - corrupt politican has interest in this stock. -

- {/if} - {:else} -
-
- -
-
- {/if} -
-
-
-
diff --git a/src/routes/etf/[tickerID]/dividends/+layout.server.ts b/src/routes/etf/[tickerID]/dividends/+layout.server.ts new file mode 100644 index 00000000..49c34a91 --- /dev/null +++ b/src/routes/etf/[tickerID]/dividends/+layout.server.ts @@ -0,0 +1,28 @@ +export const load = async ({ locals, params }) => { + + const getSimilarStocks = async () => { + const { apiKey, apiURL } = locals; + const postData = { + ticker: params.tickerID, + }; + + // make the POST request to the endpoint + const response = await fetch(apiURL + "/similar-stocks", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-API-KEY": apiKey, + }, + body: JSON.stringify(postData), + }); + + const output = await response.json(); + + return output; + }; + + // Make sure to return a promise + return { + getSimilarStocks: await getSimilarStocks(), + }; +}; diff --git a/src/routes/etf/[tickerID]/dividends/+layout.svelte b/src/routes/etf/[tickerID]/dividends/+layout.svelte new file mode 100644 index 00000000..1dab400b --- /dev/null +++ b/src/routes/etf/[tickerID]/dividends/+layout.svelte @@ -0,0 +1,87 @@ + + +
+
+
+
+
+ +
+ +
+
+
+
+
diff --git a/src/routes/etf/[tickerID]/dividends/+page.server.ts b/src/routes/etf/[tickerID]/dividends/+page.server.ts index fdb462a0..b5f8e493 100644 --- a/src/routes/etf/[tickerID]/dividends/+page.server.ts +++ b/src/routes/etf/[tickerID]/dividends/+page.server.ts @@ -1,6 +1,9 @@ -export const load = async ({ locals, params }) => { +export const load = async ({ params, locals }) => { const getStockDividend = async () => { - const { apiKey, apiURL } = locals; + let newsList; + + const { apiURL, apiKey } = locals; + const postData = { ticker: params.tickerID, }; @@ -15,9 +18,9 @@ export const load = async ({ locals, params }) => { body: JSON.stringify(postData), }); - const output = await response.json(); + newsList = await response.json(); - return output; + return newsList; }; // Make sure to return a promise diff --git a/src/routes/etf/[tickerID]/dividends/+page.svelte b/src/routes/etf/[tickerID]/dividends/+page.svelte index 89fb19ce..10f84d7b 100644 --- a/src/routes/etf/[tickerID]/dividends/+page.svelte +++ b/src/routes/etf/[tickerID]/dividends/+page.svelte @@ -81,6 +81,9 @@ data: dividendList, type: "bar", smooth: true, + itemStyle: { + color: "#fff", + }, }, ], }; @@ -132,21 +135,19 @@ -
+
-

+

Dividends

+ No dividend history available for {$displayCompanyName}. +
{/if}
{#if rawData?.history?.length !== 0}
@@ -349,7 +352,7 @@ - ${item?.adjDividend?.toFixed(3)} + {item?.adjDividend?.toFixed(3)} { - const { apiKey, apiURL } = locals; + const { apiKey, apiURL, user } = locals; const getETFHoldings = async () => { const postData = { ticker: params.tickerID, }; - + // make the POST request to the endpoint const response = await fetch(apiURL + "/etf-holdings", { method: "POST", diff --git a/src/routes/etf/[tickerID]/holdings/+page.svelte b/src/routes/etf/[tickerID]/holdings/+page.svelte index 6c0e9d18..47eef5c6 100644 --- a/src/routes/etf/[tickerID]/holdings/+page.svelte +++ b/src/routes/etf/[tickerID]/holdings/+page.svelte @@ -4,140 +4,29 @@ numberOfUnreadNotification, displayCompanyName, } from "$lib/store"; - import { Button } from "$lib/components/shadcn/button/index.js"; - import { screenWidth } from "$lib/store"; - import { abbreviateNumber, formatString } from "$lib/utils"; - import { onMount } from "svelte"; - import { goto } from "$app/navigation"; - import TableHeader from "$lib/components/Table/TableHeader.svelte"; - import HoverStockChart from "$lib/components/HoverStockChart.svelte"; + import { formatString } from "$lib/utils"; + import Table from "$lib/components/Table/Table.svelte"; export let data; let rawData = data?.getETFHoldings; - let holdings = rawData?.slice(0, 50); + const excludedRules = new Set([ + "price", + "changesPercentage", + "sharesNumber", + "weightPercentage", + ]); - async function handleScroll() { - const scrollThreshold = document.body.offsetHeight * 0.8; // 80% of the website height - const isBottom = window.innerHeight + window.scrollY >= scrollThreshold; - if (isBottom && holdings?.length !== rawData?.length) { - const nextIndex = holdings?.length; - const filteredNewResults = rawData?.slice(nextIndex, nextIndex + 25); - holdings = [...holdings, ...filteredNewResults]; - } - } - - onMount(() => { - window.addEventListener("scroll", handleScroll); - return () => { - window.removeEventListener("scroll", handleScroll); - }; - }); - - $: charNumber = $screenWidth < 640 ? 20 : 20; - - let columns = [ - { key: "asset", label: "Symbol", align: "left" }, - { key: "name", label: "Name", align: "left" }, - { key: "price", label: "Price", align: "right" }, - { key: "changesPercentage", label: "Change", align: "right" }, - { key: "sharesNumber", label: "Shares", align: "right" }, - { key: "weightPercentage", label: "% Weight", align: "right" }, + const defaultList = [ + { name: "Price", rule: "price" }, + { name: "% Change", rule: "changesPercentage" }, + { name: "Shares", rule: "sharesNumber" }, + { name: "% Weight", rule: "weightPercentage" }, ]; - let sortOrders = { - asset: { order: "none", type: "string" }, - name: { order: "none", type: "string" }, - price: { order: "none", type: "number" }, - changesPercentage: { order: "none", type: "number" }, - sharesNumber: { order: "none", type: "number" }, - weightPercentage: { order: "none", type: "number" }, - }; - - const sortData = (key) => { - // Reset all other keys to 'none' except the current key - for (const k in sortOrders) { - if (k !== key) { - sortOrders[k].order = "none"; - } - } - - // Cycle through 'none', 'asc', 'desc' for the clicked key - const orderCycle = ["none", "asc", "desc"]; - - let originalData = rawData; - - const currentOrderIndex = orderCycle.indexOf(sortOrders[key].order); - sortOrders[key].order = - orderCycle[(currentOrderIndex + 1) % orderCycle.length]; - const sortOrder = sortOrders[key].order; - - // Reset to original data when 'none' and stop further sorting - if (sortOrder === "none") { - holdings = [...originalData]?.slice(0, 50); // Reset to original data (spread to avoid mutation) - return; - } - - // Define a generic comparison function - const compareValues = (a, b) => { - const { type } = sortOrders[key]; - let valueA, valueB; - - switch (type) { - case "date": - valueA = new Date(a[key]); - valueB = new Date(b[key]); - break; - case "string": - valueA = a[key].toUpperCase(); - valueB = b[key].toUpperCase(); - return sortOrder === "asc" - ? valueA.localeCompare(valueB) - : valueB.localeCompare(valueA); - case "number": - default: - valueA = parseFloat(a[key]); - valueB = parseFloat(b[key]); - break; - } - - if (sortOrder === "asc") { - return valueA < valueB ? -1 : valueA > valueB ? 1 : 0; - } else { - return valueA > valueB ? -1 : valueA < valueB ? 1 : 0; - } - }; - - // Sort using the generic comparison function - holdings = [...originalData].sort(compareValues)?.slice(0, 50); - }; - - const exportData = (format = "csv") => { - if (data?.user?.tier === "Pro") { - // Add headers row - const csvRows = []; - csvRows.push("Symbol,Name,Price, Change, Shares,Weight"); - - // Add data rows - rawData.forEach((item) => { - const csvRow = `${item?.asset},${item?.name},${item?.price},${item?.changesPercentage},${item?.sharesNumber},${item?.weightPercentage}`; - csvRows.push(csvRow); - }); - - // Create CSV blob and trigger download - const csv = csvRows.join("\n"); - const blob = new Blob([csv], { type: "text/csv" }); - const url = window.URL.createObjectURL(blob); - const a = document.createElement("a"); - a.setAttribute("hidden", ""); - a.setAttribute("href", url); - a.setAttribute("download", `${$etfTicker}_holdings.csv`); - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - } else { - goto("/pricing"); - } - }; + const specificRows = [ + { name: "% Weight", rule: "weightPercentage", type: "float" }, + { name: "Shares", rule: "sharesNumber", type: "int" }, + ]; @@ -215,103 +104,14 @@
- {#if holdings?.length !== 0} -
- -
- -
- - - - - - {#each holdings as item} - - {#if item?.asset !== null} - - - - - - - - - - - - - - {/if} - {/each} - -
- - - {item?.name?.length > charNumber - ? formatString(item?.name?.slice(0, charNumber)) + - "..." - : formatString(item?.name)} - - {item?.price} - - {#if item?.changesPercentage >= 0} - +{item?.changesPercentage >= 1000 - ? abbreviateNumber(item?.changesPercentage) - : item?.changesPercentage?.toFixed(2)}% - {:else if item?.changesPercentage < 0} - {item?.changesPercentage <= -1000 - ? abbreviateNumber(item?.changesPercentage) - : item?.changesPercentage?.toFixed(2)}% - - {:else} - - - {/if} - - {abbreviateNumber(item?.sharesNumber)} - - {item?.weightPercentage >= 0.01 - ? item?.weightPercentage?.toFixed(2) - : "< 0.01"}% -
-
+ {#if rawData?.length !== 0} + {:else}

+ import { etfTicker } from "$lib/store"; + import ArrowLogo from "lucide-svelte/icons/move-up-right"; + + export let data; + + let newsList = data?.getNews ?? []; + + const formatDate = (dateString) => { + // Create a date object for the input dateString + const inputDate = new Date(dateString); + + // Create a date object for the current time in New York City + const nycTime = new Date().toLocaleString("en-US", { + timeZone: "America/New_York", + }); + const currentNYCDate = new Date(nycTime); + + // Calculate the difference in milliseconds + const difference = inputDate.getTime() - currentNYCDate.getTime(); + + // Convert the difference to minutes + const minutes = Math.abs(Math.round(difference / (1000 * 60))); + + if (minutes < 60) { + return `${minutes} minutes`; + } else if (minutes < 1440) { + const hours = Math.round(minutes / 60); + return `${hours} hour${hours !== 1 ? "s" : ""}`; + } else { + const days = Math.round(minutes / 1440); + return `${days} day${days !== 1 ? "s" : ""}`; + } + }; + + +
+
+
+
+
+ +
+ + +
+
+
+
+ + diff --git a/src/routes/etf/[tickerID]/congress-trading/+page.server.ts b/src/routes/etf/[tickerID]/insider/+page.server.ts similarity index 100% rename from src/routes/etf/[tickerID]/congress-trading/+page.server.ts rename to src/routes/etf/[tickerID]/insider/+page.server.ts diff --git a/src/routes/etf/[tickerID]/insider/+page.svelte b/src/routes/etf/[tickerID]/insider/+page.svelte new file mode 100644 index 00000000..3f9f54da --- /dev/null +++ b/src/routes/etf/[tickerID]/insider/+page.svelte @@ -0,0 +1,427 @@ + + + + + + + {$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ""} + {$displayCompanyName} ({$etfTicker}) US Congress & Senate Trading 路 + stocknear + + + + + + + + + + + + + + + + +
+
+
+
+
+

+ Congress Trading +

+ + {#if isLoaded} + {#if senateTradingList?.length !== 0} + +
+
+ +
+
+ Buy/Sell + + {buySellRatio?.toFixed(3)} + +
+ +
+ + + + + + = 0 + ? 100 - (buySellRatio * 100)?.toFixed(2) + : 0} + > + + + +
+ {buySellRatio?.toFixed(2)} +
+
+ +
+ + +
+
+ Dem/Rep + + {partyRatio?.toFixed(3)} + +
+ +
+ + + + + + = 0 + ? 100 - (partyRatio * 100)?.toFixed(2) + : 0} + > + + + +
+ {partyRatio?.toFixed(2)} +
+
+ +
+ +
+
+ + +
+

+ + + + + + + + + + {#each senateTradingList as item} + + + + + + + + + {/each} + +
+ Person + + Transaction Date + + Amount + Type
+ + + + {new Date(item?.transactionDate)?.toLocaleString( + "en-US", + { + month: "short", + day: "numeric", + year: "numeric", + daySuffix: "2-digit", + }, + )} + + {item?.amount} + + {#if item?.type === "Bought"} + Bought + {:else if item?.type === "Sold"} + Sold + {:else if item?.type === "Exchange"} + Exchange + {/if} +
+
+ + {#if rawData?.length >= 20} + + {/if} + {:else} +

+ No trading history available for {$displayCompanyName}. Likely + no corrupt politican has interest in this stock. +

+ {/if} + {:else} +
+
+ +
+
+ {/if} + + + + +
diff --git a/src/routes/etf/[tickerID]/options/+layout.svelte b/src/routes/etf/[tickerID]/options/+layout.svelte new file mode 100644 index 00000000..0a43674d --- /dev/null +++ b/src/routes/etf/[tickerID]/options/+layout.svelte @@ -0,0 +1,80 @@ + + +
+ +
diff --git a/src/routes/etf/[tickerID]/options/+page.svelte b/src/routes/etf/[tickerID]/options/+page.svelte index 76164f61..7280b9df 100644 --- a/src/routes/etf/[tickerID]/options/+page.svelte +++ b/src/routes/etf/[tickerID]/options/+page.svelte @@ -3,7 +3,7 @@ numberOfUnreadNotification, displayCompanyName, screenWidth, - stockTicker, + etfTicker, setCache, getCache, } from "$lib/store"; @@ -474,7 +474,7 @@ isLoaded = false; optionDetailsDesktopModal?.showModal(); - rawDataHistory = await getDailyTransactions($stockTicker + "-" + date); + rawDataHistory = await getDailyTransactions($etfTicker + "-" + date); rawDataHistory?.forEach((item) => { item.dte = daysLeft(item?.date_expiration); @@ -538,21 +538,21 @@ {$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ""} - {$displayCompanyName} ({$stockTicker}) Options Activity 路 stocknear + {$displayCompanyName} ({$etfTicker}) Options Activity 路 stocknear @@ -561,11 +561,11 @@ @@ -877,7 +877,7 @@ handleViewData(item?.date)} on:mouseover={() => - getDailyTransactions($stockTicker + "+" + item?.date)} + getDailyTransactions($etfTicker + "+" + item?.date)} class="cursor-pointer sm:hover:bg-[#245073] sm:hover:bg-opacity-[0.2] odd:bg-[#27272A] border-b-[#09090B] {index + 1 === optionList?.slice(0, 3)?.length &&