update insider page
This commit is contained in:
parent
3b28daf0c9
commit
d8edb418ce
@ -40,13 +40,19 @@ export const load = async ({ locals, params }) => {
|
|||||||
|
|
||||||
let output = await response.json();
|
let output = await response.json();
|
||||||
|
|
||||||
output = output?.map((item) => ({
|
output = output?.reduce((acc, item) => {
|
||||||
...item,
|
const newTransactionType =
|
||||||
transactionType:
|
|
||||||
typeof transactionTypeMap[item?.transactionType] === "function"
|
typeof transactionTypeMap[item?.transactionType] === "function"
|
||||||
? transactionTypeMap[item?.transactionType](item)
|
? transactionTypeMap[item?.transactionType](item)
|
||||||
: transactionTypeMap[item?.transactionType] || "n/a",
|
: transactionTypeMap[item?.transactionType];
|
||||||
}));
|
|
||||||
|
// Only include items with 'Bought' or 'Sold'
|
||||||
|
if (newTransactionType === "Bought" || newTransactionType === "Sold") {
|
||||||
|
acc.push({ ...item, transactionType: newTransactionType });
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
};
|
};
|
||||||
@ -71,42 +77,9 @@ export const load = async ({ locals, params }) => {
|
|||||||
return output;
|
return output;
|
||||||
};
|
};
|
||||||
|
|
||||||
async function historicalPrice() {
|
|
||||||
const postData = {
|
|
||||||
ticker: params.tickerID,
|
|
||||||
timePeriod: "max",
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await fetch(apiURL + "/historical-price", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-API-KEY": apiKey,
|
|
||||||
},
|
|
||||||
body: JSON.stringify(postData),
|
|
||||||
});
|
|
||||||
|
|
||||||
const output = (await response?.json()) ?? [];
|
|
||||||
|
|
||||||
//Adding this would create a bug hence I cant use the historicalPrice endpoint such as in +page.svelte but rather need to call
|
|
||||||
// it again without modification.
|
|
||||||
/*
|
|
||||||
output= (data) => map(({ time, open, high, low, close }) => ({
|
|
||||||
time: Date.parse(time),
|
|
||||||
open,
|
|
||||||
high,
|
|
||||||
low,
|
|
||||||
close
|
|
||||||
}));
|
|
||||||
*/
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure to return a promise
|
// Make sure to return a promise
|
||||||
return {
|
return {
|
||||||
getInsiderTrading: await getInsiderTrading(),
|
getInsiderTrading: await getInsiderTrading(),
|
||||||
getInsiderTradingStatistics: await getInsiderTradingStatistics(),
|
getInsiderTradingStatistics: await getInsiderTradingStatistics(),
|
||||||
getHistoricalPrice: await historicalPrice(),
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,19 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { displayCompanyName, numberOfUnreadNotification, stockTicker } from '$lib/store';
|
import { displayCompanyName, numberOfUnreadNotification, stockTicker } from '$lib/store';
|
||||||
import InfiniteLoading from '$lib/components/InfiniteLoading.svelte';
|
|
||||||
import { formatString, abbreviateNumber } from '$lib/utils';
|
import { formatString, abbreviateNumber } from '$lib/utils';
|
||||||
import InfoModal from '$lib/components/InfoModal.svelte';
|
|
||||||
import UpgradeToPro from "$lib/components/UpgradeToPro.svelte";
|
import UpgradeToPro from "$lib/components/UpgradeToPro.svelte";
|
||||||
import { Chart } from 'svelte-echarts'
|
import { onMount } from 'svelte';
|
||||||
import { init, use } from 'echarts/core'
|
|
||||||
import { LineChart, BarChart } from 'echarts/charts'
|
|
||||||
import { GridComponent } from 'echarts/components'
|
|
||||||
import { CanvasRenderer } from 'echarts/renderers'
|
|
||||||
use([LineChart, BarChart, GridComponent, CanvasRenderer])
|
|
||||||
|
|
||||||
|
|
||||||
import { onMount } from 'svelte';
|
|
||||||
|
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
let isLoaded = false;
|
let isLoaded = false;
|
||||||
@ -25,20 +16,13 @@ use([LineChart, BarChart, GridComponent, CanvasRenderer])
|
|||||||
let buySharesPercentage = Math.floor(buyShares/(buyShares+soldShares)*100);
|
let buySharesPercentage = Math.floor(buyShares/(buyShares+soldShares)*100);
|
||||||
let soldSharesPercentage = 100 - buySharesPercentage;
|
let soldSharesPercentage = 100 - buySharesPercentage;
|
||||||
|
|
||||||
let options = {};
|
|
||||||
|
|
||||||
let rawData = [];
|
let rawData = data?.getInsiderTrading?.sort(
|
||||||
let insiderTradingList = [];
|
(a, b) => new Date(b?.transactionDate) - new Date(a?.transactionDate)
|
||||||
let dataPoints = [];
|
);
|
||||||
let dates = [];
|
let insiderTradingList = rawData?.slice(0,20)
|
||||||
let soldList = [];
|
|
||||||
let boughtList = [];
|
|
||||||
let grantList = [];
|
|
||||||
let exerciseList = [];
|
|
||||||
|
|
||||||
|
|
||||||
//Find Latest date to filter historicalPrice:
|
|
||||||
const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
||||||
|
|
||||||
function backToTop() {
|
function backToTop() {
|
||||||
window.scrollTo({
|
window.scrollTo({
|
||||||
@ -65,222 +49,32 @@ function extractOfficeInfo(inputString) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleScroll() {
|
||||||
|
const scrollThreshold = document.body.offsetHeight * 0.8; // 80% of the website height
|
||||||
async function infiniteHandler({ detail: { loaded, complete } })
|
const isBottom = window.innerHeight + window.scrollY >= scrollThreshold;
|
||||||
{
|
if (isBottom && insiderTradingList?.length !== rawData?.length) {
|
||||||
if (insiderTradingList?.length === rawData?.length) {
|
const nextIndex = insiderTradingList?.length;
|
||||||
complete();
|
const filteredNewResults = rawData?.slice(nextIndex, nextIndex + 50);
|
||||||
} else {
|
insiderTradingList = [...insiderTradingList, ...filteredNewResults];
|
||||||
const nextIndex = insiderTradingList?.length;
|
|
||||||
const newArticles = rawData?.slice(nextIndex, nextIndex + 20);
|
|
||||||
insiderTradingList = [...insiderTradingList, ...newArticles];
|
|
||||||
loaded();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function normalizer(value) {
|
|
||||||
if (Math?.abs(value) >= 1e12) {
|
|
||||||
return { unit: 'T', denominator: 1e12 };
|
|
||||||
} else if (Math?.abs(value) >= 1e9) {
|
|
||||||
return { unit: 'B', denominator: 1e9 };
|
|
||||||
} else if (Math?.abs(value) >= 1e6) {
|
|
||||||
return { unit: 'M', denominator: 1e6 };
|
|
||||||
} else if (Math?.abs(value) >= 1e5) {
|
|
||||||
return { unit: 'K', denominator: 1e5 };
|
|
||||||
} else {
|
|
||||||
return { unit: '', denominator: 1 };
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let syncWorker: Worker | undefined = undefined;
|
onMount(() => {
|
||||||
|
isLoaded = true;
|
||||||
// Handling messages from the worker
|
window.addEventListener('scroll', handleScroll);
|
||||||
const handleMessage = async (event) => {
|
return () => {
|
||||||
const finalData = event.data?.finalData
|
window.removeEventListener('scroll', handleScroll);
|
||||||
|
|
||||||
rawData = finalData?.rawData;
|
|
||||||
insiderTradingList = rawData?.slice(0,20) ?? [];
|
|
||||||
dataPoints = finalData?.dataPoints;
|
|
||||||
dates = finalData?.dates;
|
|
||||||
soldList = finalData?.barChartData?.sold;
|
|
||||||
grantList = finalData?.barChartData?.grant;
|
|
||||||
exerciseList = finalData?.barChartData?.exercise;
|
|
||||||
boughtList = finalData?.barChartData?.bought;
|
|
||||||
|
|
||||||
if(dataPoints?.length !== 0 && dates?.length !==0) {
|
|
||||||
|
|
||||||
const maxBought = Math.max(...boughtList) ?? 0;
|
|
||||||
const maxSold = Math.max(...soldList) ?? 0;
|
|
||||||
const maxGrant = Math.max(...grantList) ?? 0;
|
|
||||||
const maxExercise = Math.max(...exerciseList) ?? 0;
|
|
||||||
|
|
||||||
const maxAmongAll = Math.max(maxBought, maxSold, maxGrant, maxExercise);
|
|
||||||
const { unit, denominator } = normalizer(maxAmongAll);
|
|
||||||
|
|
||||||
|
|
||||||
options = {
|
|
||||||
silent: true,
|
|
||||||
animation: false,
|
|
||||||
grid: {
|
|
||||||
left: '0%',
|
|
||||||
right: '2%',
|
|
||||||
top: '10%',
|
|
||||||
bottom: '10%',
|
|
||||||
containLabel: true,
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
type: 'category',
|
|
||||||
boundaryGap: false,
|
|
||||||
data: dates,
|
|
||||||
axisLabel: {
|
|
||||||
color: '#fff',
|
|
||||||
formatter: function (value) {
|
|
||||||
// Assuming dates are in the format 'yyyy-mm-dd'
|
|
||||||
const dateParts = value.split('-');
|
|
||||||
const monthIndex = parseInt(dateParts[1]) - 1; // Months are zero-indexed in JavaScript Date objects
|
|
||||||
const year = parseInt(dateParts[0]);
|
|
||||||
return `${monthNames[monthIndex]} ${year}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
yAxis: [
|
|
||||||
{
|
|
||||||
type: 'value',
|
|
||||||
axisLabel: {
|
|
||||||
color: '#fff',
|
|
||||||
formatter: function (value, index) {
|
|
||||||
// Display every second tick
|
|
||||||
if (index % 2 === 0) {
|
|
||||||
return '$'+`${value}` // Format value in millions
|
|
||||||
} else {
|
|
||||||
return ''; // Hide this tick
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
splitLine: {
|
|
||||||
show: false, // Disable x-axis grid lines
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'value',
|
|
||||||
axisLabel: {
|
|
||||||
formatter: function (value, index) {
|
|
||||||
// Display every second tick
|
|
||||||
if (index % 2 === 0) {
|
|
||||||
value = Math.max(value, 0);
|
|
||||||
return (value / denominator)?.toFixed(0) + unit; // Format value in millions
|
|
||||||
} else {
|
|
||||||
return ''; // Hide this tick
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
splitLine: {
|
|
||||||
show: false, // Disable x-axis grid lines
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'value',
|
|
||||||
axisLabel: {
|
|
||||||
formatter: '{value} %',
|
|
||||||
},
|
|
||||||
splitLine: {
|
|
||||||
show: false, // Disable x-axis grid lines
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: 'Price',
|
|
||||||
type: 'line',
|
|
||||||
// prettier-ignore
|
|
||||||
data: dataPoints,
|
|
||||||
itemStyle: {
|
|
||||||
color: "#fff"
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
markArea: {
|
|
||||||
data: markAreaData
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '',
|
|
||||||
data: soldList,
|
|
||||||
type: 'bar',
|
|
||||||
areaStyle: {opacity: 1},
|
|
||||||
yAxisIndex: 1,
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
// Set color based on positive or negative value
|
|
||||||
return params.data >= 0 ? 'rgb(15, 192, 8)' : 'rgb(255, 47, 31)';
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '',
|
|
||||||
data: boughtList,
|
|
||||||
type: 'bar',
|
|
||||||
areaStyle: {opacity: 1},
|
|
||||||
yAxisIndex: 1,
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
// Set color based on positive or negative value
|
|
||||||
return params.data >= 0 ? '#37C97D' : '';
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '',
|
|
||||||
data: grantList,
|
|
||||||
type: 'bar',
|
|
||||||
yAxisIndex: 1,
|
|
||||||
areaStyle: {opacity: 1},
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
// Set color based on positive or negative value
|
|
||||||
return params.data >= 0 ? '#8f95a1' : '';
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '',
|
|
||||||
data: exerciseList,
|
|
||||||
type: 'bar',
|
|
||||||
areaStyle: {opacity: 1},
|
|
||||||
yAxisIndex: 1,
|
|
||||||
itemStyle: {
|
|
||||||
color: (params) => {
|
|
||||||
// Set color based on positive or negative value
|
|
||||||
return params.data >= 0 ? '#F8901E' : '';
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
isLoaded = true;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadWorker = async () => {
|
|
||||||
const SyncWorker = await import('./workers/insiderWorker?worker');
|
|
||||||
syncWorker = new SyncWorker.default();
|
|
||||||
syncWorker.postMessage({ message: data?.getInsiderTrading, historicalPrice: data?.getHistoricalPrice});
|
|
||||||
syncWorker.onmessage = handleMessage;
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
onMount(async() => {
|
|
||||||
await loadWorker()
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const transactionStyles = {
|
||||||
|
'Bought': { text: 'Bought', class: 'text-[#37C97D]', border: 'border-[#37C97D]' },
|
||||||
|
'Grant': { text: 'Grant', class: 'text-[#F8901E]', border: 'border-[#F8901E]'},
|
||||||
|
'Sold': { text: 'Sold', class: 'text-[#FF2F1F]', border: 'border-[#FF2F1F]'},
|
||||||
|
'Exercise': { text: 'Exercise', class: 'text-[#F8901E]', border: 'border-[#v]'},
|
||||||
|
'n/a': { text: 'n/a', class: 'text-gray-300'}
|
||||||
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -333,6 +127,7 @@ onMount(async() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if insiderTradingList?.length !== 0}
|
{#if insiderTradingList?.length !== 0}
|
||||||
|
<!--
|
||||||
<div class="text-white text-[1rem] text-center m-auto w-full pb-3">
|
<div class="text-white text-[1rem] text-center m-auto w-full pb-3">
|
||||||
We can divide four types of insider transactions:
|
We can divide four types of insider transactions:
|
||||||
|
|
||||||
@ -357,7 +152,8 @@ onMount(async() => {
|
|||||||
/>.
|
/>.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -366,6 +162,7 @@ onMount(async() => {
|
|||||||
|
|
||||||
{#if insiderTradingList?.length !== 0}
|
{#if insiderTradingList?.length !== 0}
|
||||||
|
|
||||||
|
<!--
|
||||||
{#if Object?.keys(options)?.length !== 0}
|
{#if Object?.keys(options)?.length !== 0}
|
||||||
<div class="app w-full">
|
<div class="app w-full">
|
||||||
<Chart {init} options={options} class="chart" />
|
<Chart {init} options={options} class="chart" />
|
||||||
@ -413,9 +210,11 @@ onMount(async() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/if}
|
{/if}
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
{#if Object?.keys(statistics)?.length !== 0 }
|
{#if Object?.keys(statistics)?.length !== 0 }
|
||||||
<h3 class="text-white text-xl font-semibold pt-5">
|
<h3 class="text-white text-lg font-semibold pt-5">
|
||||||
Q{statistics?.quarter} {statistics?.year} Insider Statistics
|
Q{statistics?.quarter} {statistics?.year} Insider Statistics
|
||||||
</h3>
|
</h3>
|
||||||
<!--Start Widget-->
|
<!--Start Widget-->
|
||||||
@ -521,10 +320,10 @@ onMount(async() => {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr class="bg-[#09090B] shadow-md">
|
<tr class="bg-[#09090B] shadow-md">
|
||||||
<th class="text-start bg-[#09090B] text-white text-[1rem] font-semibold">
|
<th class="text-start bg-[#09090B] text-white text-[1rem] font-semibold">
|
||||||
Person
|
Name
|
||||||
</th>
|
</th>
|
||||||
<th class="text-end bg-[#09090B] text-white text-[1rem] font-semibold">
|
<th class="text-end bg-[#09090B] text-white text-[1rem] font-semibold">
|
||||||
Transaction Date
|
Date
|
||||||
</th>
|
</th>
|
||||||
<th class="text-end bg-[#09090B] text-white text-[1rem] font-semibold">
|
<th class="text-end bg-[#09090B] text-white text-[1rem] font-semibold">
|
||||||
Shares
|
Shares
|
||||||
@ -532,7 +331,7 @@ onMount(async() => {
|
|||||||
<th class="text-end bg-[#09090B] text-white text-[1rem] font-semibold">
|
<th class="text-end bg-[#09090B] text-white text-[1rem] font-semibold">
|
||||||
Price
|
Price
|
||||||
</th>
|
</th>
|
||||||
<th class="text-white font-semibold text-end text-[1rem]">Type</th>
|
<th class="text-white font-semibold text-end text-[1rem]">Value</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -542,10 +341,10 @@ onMount(async() => {
|
|||||||
<td class="text-white text-sm sm:text-[1rem] border-b border-[#09090B] whitespace-nowrap">
|
<td class="text-white text-sm sm:text-[1rem] border-b border-[#09090B] whitespace-nowrap">
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<span class="">{formatString(item?.reportingName)?.replace('/de/','')}</span>
|
<span class="">{formatString(item?.reportingName)?.replace('/de/','')}</span>
|
||||||
<span class="text-sm">{extractOfficeInfo(item?.typeOfOwner)}</span>
|
<span class="text-sm text-white/80">{extractOfficeInfo(item?.typeOfOwner)}</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="text-end text-sm sm:text-[1rem] whitespace-nowrap text-white border-b border-[#09090B]">
|
<td class="text-end text-sm sm:text-[1rem] whitespace-nowrap text-white border-b border-[#09090B]">
|
||||||
{new Date(item?.transactionDate)?.toLocaleString('en-US', { month: 'short', day: 'numeric', year: 'numeric', daySuffix: '2-digit' })}
|
{new Date(item?.transactionDate)?.toLocaleString('en-US', { month: 'short', day: 'numeric', year: 'numeric', daySuffix: '2-digit' })}
|
||||||
</td>
|
</td>
|
||||||
@ -557,17 +356,17 @@ onMount(async() => {
|
|||||||
${item?.price?.toFixed(2)}
|
${item?.price?.toFixed(2)}
|
||||||
</td>
|
</td>
|
||||||
<td class="font-medium text-end text-sm sm:text-[1rem] whitespace-nowrap text-white border-b border-[#09090B]">
|
<td class="font-medium text-end text-sm sm:text-[1rem] whitespace-nowrap text-white border-b border-[#09090B]">
|
||||||
{#if item?.transactionType === 'Bought'}
|
|
||||||
<span class="text-[#37C97D]">Bought</span>
|
<div class="flex flex-row items-center justify-end">
|
||||||
{:else if item?.transactionType === 'Grant'}
|
{#if transactionStyles[item?.transactionType]}
|
||||||
<span class="text-white">Grant</span>
|
<div class="{transactionStyles[item?.transactionType]?.class}">{abbreviateNumber(item?.securitiesTransacted * item?.price, true)}</div>
|
||||||
{:else if item?.transactionType === 'Sold'}
|
<div class="{transactionStyles[item?.transactionType]?.class} {transactionStyles[item?.transactionType]?.border} ml-2 px-1.5 py-1.5 border text-center rounded-lg text-xs font-semibold">
|
||||||
<span class="text-[#FF2F1F]">Sold</span>
|
{transactionStyles[item?.transactionType].text}
|
||||||
{:else if item?.transactionType === 'Exercise'}
|
</div>
|
||||||
<span class="text-[#F8901E]">Exercise</span>
|
{:else}
|
||||||
{:else if item?.transactionType === 'n/a'}
|
|
||||||
<span class="text-gray-300">n/a</span>
|
<span class="text-gray-300">n/a</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
@ -583,10 +382,6 @@ onMount(async() => {
|
|||||||
</label>
|
</label>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if data?.user?.tier === 'Pro'}
|
|
||||||
<InfiniteLoading on:infinite={infiniteHandler} />
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<UpgradeToPro data={data} title="Access {$displayCompanyName}'s insider transactions to track executive selling and purchasing activity"/>
|
<UpgradeToPro data={data} title="Access {$displayCompanyName}'s insider transactions to track executive selling and purchasing activity"/>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,81 +0,0 @@
|
|||||||
function findLatestDateInPast(data) {
|
|
||||||
if (!data || !Array?.isArray(data) || data?.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let latestDate = new Date(data?.at(0)?.transactionDate);
|
|
||||||
|
|
||||||
for (let i = 1; i < data?.length; i++) {
|
|
||||||
const currentDate = new Date(data[i]?.transactionDate);
|
|
||||||
const currentTransactionDate = currentDate.getDate();
|
|
||||||
const currentTransactionDay = currentDate.getDay(); // Sunday is 0, Monday is 1, ..., Saturday is 6
|
|
||||||
|
|
||||||
// If the current date is a weekday, reduce it by one month
|
|
||||||
if (currentTransactionDay !== 0 && currentTransactionDay !== 6) {
|
|
||||||
currentDate.setMonth(currentDate.getMonth() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentDate < latestDate) {
|
|
||||||
latestDate = currentDate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return latestDate?.toISOString()?.split("T")?.at(0); // Format to yyyy-mm-dd
|
|
||||||
}
|
|
||||||
|
|
||||||
function getBarChart(data, dates) {
|
|
||||||
let soldList = Array(dates?.length)?.fill(0);
|
|
||||||
let boughtList = Array(dates?.length)?.fill(0);
|
|
||||||
let grantList = Array(dates?.length)?.fill(0);
|
|
||||||
let exerciseList = Array(dates?.length)?.fill(0);
|
|
||||||
|
|
||||||
// Group transactions by date and transaction type
|
|
||||||
const groupedTransactions = {};
|
|
||||||
data?.forEach((item) => {
|
|
||||||
const { transactionDate, securitiesTransacted, transactionType } = item;
|
|
||||||
const key = `${transactionDate}_${transactionType}`;
|
|
||||||
|
|
||||||
if (!groupedTransactions[key]) {
|
|
||||||
groupedTransactions[key] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update transaction type specific lists
|
|
||||||
if (transactionType === "Sold") {
|
|
||||||
soldList[dates?.indexOf(transactionDate)] -= securitiesTransacted;
|
|
||||||
} else if (transactionType === "Bought") {
|
|
||||||
boughtList[dates?.indexOf(transactionDate)] += securitiesTransacted;
|
|
||||||
} else if (transactionType === "Grant") {
|
|
||||||
grantList[dates?.indexOf(transactionDate)] += securitiesTransacted;
|
|
||||||
} else if (transactionType === "Exercise") {
|
|
||||||
exerciseList[dates?.indexOf(transactionDate)] += securitiesTransacted;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
sold: soldList,
|
|
||||||
bought: boughtList,
|
|
||||||
grant: grantList,
|
|
||||||
exercise: exerciseList,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
onmessage = async (event: MessageEvent) => {
|
|
||||||
const data = event.data?.message;
|
|
||||||
const rawData = data?.sort(
|
|
||||||
(a, b) => new Date(b?.transactionDate) - new Date(a?.transactionDate)
|
|
||||||
);
|
|
||||||
const latestDate = findLatestDateInPast(rawData);
|
|
||||||
|
|
||||||
let historicalPrice = event.data?.historicalPrice?.filter(
|
|
||||||
(item) => new Date(item?.time) >= new Date(latestDate)
|
|
||||||
);
|
|
||||||
|
|
||||||
const dataPoints = historicalPrice?.map(({ close }) => close);
|
|
||||||
const dates = historicalPrice?.map(({ time }) => time);
|
|
||||||
const barChartData = getBarChart(rawData, dates);
|
|
||||||
|
|
||||||
let finalData = { rawData, dataPoints, dates, barChartData };
|
|
||||||
postMessage({ message: "success", finalData });
|
|
||||||
};
|
|
||||||
|
|
||||||
export {};
|
|
||||||
Loading…
x
Reference in New Issue
Block a user