diff --git a/src/lib/components/RatingsChart.svelte b/src/lib/components/RatingsChart.svelte index 0edadf7d..6e524e0b 100644 --- a/src/lib/components/RatingsChart.svelte +++ b/src/lib/components/RatingsChart.svelte @@ -23,6 +23,7 @@ export let ratingsList; export let numOfRatings = 0; export let title = "Ratings"; + export let addToLast = false; //if date value not found at mark point to the last value date. let isLoaded = false; let optionsData = null; @@ -103,40 +104,53 @@ // Prepare markPoints for ratings const markPoints = ratingsList ?.filter((rating) => { - // Ensure date format is correct and matches - return dates.includes(rating?.date) && rating?.ticker === symbol; + // Ensure date format is correct and matches the ticker symbol + return rating?.ticker === symbol; }) - ?.map((rating) => ({ - // Marker at the rating's date - type: "max", // Marking the rating date - name: rating?.type, - coord: [ - rating?.date, - closeValues[dates?.indexOf(rating?.date)], // Find the close value corresponding to the rating date - ], - label: { - formatter: rating?.type //rating.rating_current - ?.replace("Bought", "Buy") - ?.replace("Sold", "Sell") - ?.replace("Sector Perform", "Hold") - ?.replace("Equal-Weight", "Hold") - ?.replace("Market Perform", "Hold") - ?.replace("Overweight", "Buy") - ?.replace("Market Outperform", "Buy") - ?.replace("Outperform", "Buy") - ?.replace("Market Underperform", "Sell") - ?.replace("Underperform", "Sell") - ?.replace("Underweight", "Sell"), // Display the rating_current text - position: "top", // Position the label above the point - color: "white", // Set label color (can be customized) - fontSize: 14, // Set font size (increase for better visibility) - }, - symbol: "rectangle", // Symbol type (can be customized) - symbolSize: 12, // Increase symbol size for better visibility - itemStyle: { - color: "red", // Set symbol color to red for better visibility - }, - })); + ?.map((rating) => { + let dateIndex; + + if (addToLast) { + // If addToLast is true, use fallback logic for the last date + dateIndex = dates.includes(rating?.date) + ? dates.indexOf(rating?.date) + : dates.length - 1; + } else { + // If addToLast is false, use the original logic + dateIndex = dates.indexOf(rating?.date); + } + + return { + type: "max", // Marking the rating date + name: rating?.type, + coord: [ + dates[dateIndex], // Use the found date or the last date + closeValues[dateIndex], // Close value corresponding to the date + ], + label: { + formatter: rating?.type + ?.replace("Bought", "Buy") + ?.replace("Sold", "Sell") + ?.replace("Sector Perform", "Hold") + ?.replace("Equal-Weight", "Hold") + ?.replace("Market Perform", "Hold") + ?.replace("Overweight", "Buy") + ?.replace("Market Outperform", "Buy") + ?.replace("Outperform", "Buy") + ?.replace("Market Underperform", "Sell") + ?.replace("Underperform", "Sell") + ?.replace("Underweight", "Sell"), + position: "top", // Position the label above the point + color: "white", // Set label color (can be customized) + fontSize: 14, // Set font size (increase for better visibility) + }, + symbol: "rectangle", // Symbol type (can be customized) + symbolSize: 12, // Increase symbol size for better visibility + itemStyle: { + color: "red", // Set symbol color to red for better visibility + }, + }; + }); const series = [ { diff --git a/src/routes/insider-tracker/+page.server.ts b/src/routes/insider-tracker/+page.server.ts index 60cf122a..91bc72d1 100644 --- a/src/routes/insider-tracker/+page.server.ts +++ b/src/routes/insider-tracker/+page.server.ts @@ -12,7 +12,7 @@ export const load = async ({ locals }) => { }); let output = await response.json(); - output = user?.tier !== "Pro" ? output?.reverse()?.slice(0, 6) : output; + output = user?.tier !== "Pro" ? output?.slice(0, 6) : output; return output; }; diff --git a/src/routes/insider-tracker/+page.svelte b/src/routes/insider-tracker/+page.svelte index 894ac206..26daed60 100644 --- a/src/routes/insider-tracker/+page.svelte +++ b/src/routes/insider-tracker/+page.svelte @@ -3,31 +3,43 @@ import { abbreviateNumber } from "$lib/utils"; import { onMount } from "svelte"; import UpgradeToPro from "$lib/components/UpgradeToPro.svelte"; - import ArrowLogo from "lucide-svelte/icons/move-up-right"; import TableHeader from "$lib/components/Table/TableHeader.svelte"; import HoverStockChart from "$lib/components/HoverStockChart.svelte"; + import RatingsChart from "$lib/components/RatingsChart.svelte"; export let data; let isLoaded = true; - let rawData = data?.getInsiderTracker ?? []; + let rawData = processTickerData(data?.getInsiderTracker) ?? []; let stockList = rawData?.slice(0, 50) ?? []; - isLoaded = true; - function formatDateTime(dateTimeStr) { - const date = new Date(dateTimeStr); + function processTickerData(data) { + const symbolMap = new Map(); - const options = { hour: "numeric", minute: "numeric", hour12: true }; + data.forEach((item) => { + const { symbol } = item; - // Extract month and day - const month = (date.getMonth() + 1).toString().padStart(2, "0"); - const day = date.getDate().toString().padStart(2, "0"); + if (!symbol) return; // Skip if symbol is not defined - // Format the time as 12-hour format with AM/PM - const time = date.toLocaleTimeString("en-US", options); + if (!symbolMap.has(symbol)) { + // Add the item and initialize count + symbolMap.set(symbol, { ...item, ratings: 1 }); + } else { + const existing = symbolMap.get(symbol); - return `${month}/${day} ${time}`; + // Increment the ratings count + existing.ratings += 1; + + // Keep the item with the latest date + if (new Date(item.filingDate) > new Date(existing.filingDate)) { + symbolMap.set(symbol, { ...item, ratings: existing.ratings }); + } + } + }); + + // Convert the Map back to an array + return Array.from(symbolMap.values()); } async function handleScroll() { @@ -49,7 +61,10 @@ } }); - let columns = [ + $: columns = [ + ...($screenWidth > 1024 + ? [{ key: "chart", label: "", align: "right" }] + : []), { key: "symbol", label: "Symbol", align: "left" }, { key: "name", label: "Name", align: "left" }, { key: "reportingName", label: "Member", align: "left" }, @@ -61,6 +76,7 @@ ]; let sortOrders = { + chart: { order: "none", type: "string" }, filingDate: { order: "none", type: "date" }, symbol: { order: "none", type: "string" }, name: { order: "none", type: "string" }, @@ -130,6 +146,16 @@ stockList = [...originalData].sort(compareValues)?.slice(0, 50); }; $: charNumber = $screenWidth < 640 ? 20 : 25; + + $: checkedSymbol = ""; + function openGraph(symbol) { + // Clear all existing symbols + if (checkedSymbol === symbol) { + checkedSymbol = ""; + } else { + checkedSymbol = symbol; + } + } @@ -186,21 +212,32 @@ {#if isLoaded} -
- +
- We update our data in real time to bring you the latest insights on - unusual insider trading, sourced from SEC filings with a minimum transaction - value of $100,000. + +
+
+
+ We update our data in real time to bring you the latest + insights on unusual insider trading, sourced from SEC + filings with a minimum transaction value of $100,000. +
+
+
+
@@ -222,6 +259,27 @@ ? 'opacity-[0.1]' : ''}" > + + @@ -290,6 +348,41 @@
+ {#if checkedSymbol === item?.symbol} +
+
+
+
+
+ ({ + ...item, + type: item?.transactionType, + date: item?.filingDate, + ticker: item?.symbol, + }), + )} + symbol={item?.symbol} + numOfRatings={item?.ratings} + title={"Insider Trading"} + addToLast={true} + /> +
+
+
+
+
+ + {/if} {/each}