@@ -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}
-
- |
-
- |
-
-
- {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}
- {/each}
-
-
-
+ {#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)}
+
+
+
+
+
+
+
+ {buySellRatio?.toFixed(2)}
+
+
+
+
+
+
+
+
+ Dem/Rep
+
+ {partyRatio?.toFixed(3)}
+
+
+
+
+
+
+
+ {partyRatio?.toFixed(2)}
+
+
+
+
+
+
+
+
+
+
+
+ {#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 &&