diff --git a/src/lib/components/FailToDeliver.svelte b/src/lib/components/FailToDeliver.svelte index b0a6b6c3..b365858f 100644 --- a/src/lib/components/FailToDeliver.svelte +++ b/src/lib/components/FailToDeliver.svelte @@ -5,9 +5,8 @@ assetType, etfTicker, } from "$lib/store"; - import InfoModal from "$lib/components/InfoModal.svelte"; import { Chart } from "svelte-echarts"; - import { abbreviateNumber, formatDateRange, monthNames } from "$lib/utils"; + import { abbreviateNumber, monthNames } from "$lib/utils"; import { init, use } from "echarts/core"; import { LineChart } from "echarts/charts"; @@ -22,38 +21,76 @@ let isLoaded = false; let optionsData; let avgFailToDeliver; - let lowestPrice; - let highestPrice; let weightedFTD; - function findLowestAndHighestPrice(data, lastDateStr) { - const lastDate = new Date(lastDateStr); - const firstDate = new Date(lastDate.getFullYear(), lastDate.getMonth(), 1); + let activeIdx = 0; + const tabs = [ + { + title: "Annual", + }, + { + title: "Quarterly", + }, + ]; - const filteredData = data?.filter((item) => { - const currentDate = new Date(item?.date); - return currentDate >= firstDate && currentDate <= lastDate; - }); + let tableList = rawData?.sort( + (a, b) => new Date(b?.date) - new Date(a?.date), + ); - let prices = filteredData?.map((item) => parseFloat(item.price)); + tableList = filterByPeriod([...tableList], activeIdx); - lowestPrice = Math.min(...prices); - highestPrice = Math.max(...prices); + function changeTimePeriod(i) { + activeIdx = i; + tableList = rawData?.sort((a, b) => new Date(b?.date) - new Date(a?.date)); + + tableList = filterByPeriod([...tableList], i); + } + + function filterByPeriod(data, period) { + if (!Array.isArray(data) || data.length === 0) return []; + + if (period === 0) { + // Annual: one result per year. + const seenYears = new Set(); + return data.filter((item) => { + const dt = new Date(item.date); + const year = dt.getFullYear(); + if (!seenYears.has(year)) { + seenYears.add(year); + return true; + } + return false; + }); + } else if (period === 1) { + // Quarterly: one result per year-quarter. + const seenPeriods = new Set(); + return data.filter((item) => { + const dt = new Date(item.date); + const year = dt.getFullYear(); + const quarter = Math.floor(dt.getMonth() / 3) + 1; // Quarter 1 to 4 + const key = `${year}-Q${quarter}`; + if (!seenPeriods.has(key)) { + seenPeriods.add(key); + return true; + } + return false; + }); + } + + return []; } function getPlotOptions() { let dates = []; let priceList = []; let failToDeliverList = []; - + rawData?.sort((a, b) => new Date(a?.date) - new Date(b?.date)); rawData?.forEach((item) => { dates?.push(item?.date); priceList?.push(item?.price); failToDeliverList?.push(item?.failToDeliver); }); - findLowestAndHighestPrice(rawData, rawData?.slice(-1)?.at(0)?.date); - const totalNumber = failToDeliverList?.reduce((acc, item) => acc + item, 0); avgFailToDeliver = (totalNumber / failToDeliverList?.length)?.toFixed(0); @@ -188,9 +225,7 @@
-

- Historical {$stockTicker} Chart -

+

FTD Chart

{#if isLoaded} @@ -250,51 +285,128 @@ -

- Latest Information -

+

+ FTD History +

-
- +
+
+ {#each tabs as item, i} + {#if data?.user?.tier !== "Pro" && i > 0} + + {:else} + + {/if} + {/each} +
+
+ + +
+
+ + + + + + + - - - Date - - - - - - - - - - - + + + + + + + {/each}
DateFTD Shares% Change
+
- {formatDateRange(rawData?.slice(-1)?.at(0)?.date)} -
- Price Range - - {lowestPrice + "-" + highestPrice} -
- Latest FTD - - {abbreviateNumber(rawData?.slice(-1)?.at(0)?.failToDeliver)} -
+ {item?.date} + + {abbreviateNumber(item?.failToDeliver)} + + {#if index === tableList?.length - 1} + n/a + {:else if item?.failToDeliver > tableList[index + 1]?.failToDeliver} + + +{( + ((item?.failToDeliver - + tableList[index + 1]?.failToDeliver) / + tableList[index + 1]?.failToDeliver) * + 100 + )?.toFixed(2)}% + + {:else if item?.failToDeliver < tableList[index + 1]?.failToDeliver} + + -{( + Math.abs( + (item?.failToDeliver - + tableList[index + 1]?.failToDeliver) / + tableList[index + 1]?.failToDeliver, + ) * 100 + )?.toFixed(2)}% + + {:else} + n/a + {/if} +
diff --git a/src/routes/affiliate-program/+page.svelte b/src/routes/affiliate-program/+page.svelte index c9a70ec1..d777504f 100644 --- a/src/routes/affiliate-program/+page.svelte +++ b/src/routes/affiliate-program/+page.svelte @@ -25,7 +25,7 @@ "name": "How much can I earn?", "acceptedAnswer": { "@type": "Answer", - "text": "You can earn up to $18.00 commission on every referral that results in a successful sale. There's no limit to how much you can make by promoting us." + "text": "You can earn up to $24.00 commission on every referral that results in a successful sale. There's no limit to how much you can make by promoting us." } }, { @@ -184,7 +184,7 @@

- You can earn up to $18.00 commission on every referral + You can earn up to $24.00 commission on every referral that results in a successful sale. There's no limit to how much you can make by promoting us.
diff --git a/src/routes/stocks/[tickerID]/statistics/fail-to-deliver/+page.svelte b/src/routes/stocks/[tickerID]/statistics/fail-to-deliver/+page.svelte index 5b321bec..722feed7 100644 --- a/src/routes/stocks/[tickerID]/statistics/fail-to-deliver/+page.svelte +++ b/src/routes/stocks/[tickerID]/statistics/fail-to-deliver/+page.svelte @@ -1,10 +1,6 @@