From c74b4ec4afc26436015278fb0d6e5b3f35baf51f Mon Sep 17 00:00:00 2001 From: MuslemRahimi Date: Tue, 13 Aug 2024 12:43:22 +0200 Subject: [PATCH] updating income statement page --- src/lib/utils.ts | 25 +-- .../[tickerID]/stats/employees/+page.svelte | 80 ++++---- .../[tickerID]/stats/income/+page.svelte | 179 +++++++++++++----- 3 files changed, 183 insertions(+), 101 deletions(-) diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 82c19e39..47901796 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -138,12 +138,12 @@ export const validateData = async (formData, schema) => { export function sumQuarterlyResultsByYear(quarterlyResults, namingList) { const yearlySummaries = {}; const quarterCounts = {}; - //FMP sucks since these keys are up to date only by the last quarter value + // FMP sucks since these keys are up to date only by the last quarter value const lastQuarterKeys = new Set([namingList]); // Keys that need last quarter values // Define a Set of keys to exclude from summing - //FMP sucks since these keys are up to date for every quarter hence no summation required - const excludeKeys = new Set(['priceToEarnings','weightedAverageShsOut', 'weightedAverageShsOutDil']); + // FMP sucks since these keys are up to date for every quarter hence no summation required + const excludeKeys = new Set(['priceToEarnings', 'weightedAverageShsOut', 'weightedAverageShsOutDil']); // Function to get the quarter number from the period string function getQuarterNumber(period) { @@ -166,19 +166,22 @@ export function sumQuarterlyResultsByYear(quarterlyResults, namingList) { if (!yearlySummaries[year]) { yearlySummaries[year] = { calendarYear: `${year}`, // Use end of the year date - lastQuarterProcessed: 0 // Keep track of the last quarter processed + lastQuarterProcessed: 0, // Keep track of the last quarter processed + date: quarter?.date // Copy the 'date' field unchanged }; quarterCounts[year] = 0; } - + // Increment the quarter count for the year quarterCounts[year]++; - - // Update last quarter processed if current quarter is greater + + // Update last quarter processed if the current quarter is greater if (quarterNum > yearlySummaries[year].lastQuarterProcessed) { yearlySummaries[year].lastQuarterProcessed = quarterNum; + // Update the date to the latest quarter's date if applicable + yearlySummaries[year].date = quarter?.date; } - + // Sum up the numeric fields for the year, excluding specific keys Object?.keys(quarter)?.forEach(key => { if (typeof quarter[key] === 'number' && !excludeKeys?.has(key) && !lastQuarterKeys.has(key)) { @@ -192,14 +195,14 @@ export function sumQuarterlyResultsByYear(quarterlyResults, namingList) { } }); }); - + // Filter out years with less than 4 quarters const validYears = Object?.keys(quarterCounts)?.filter(year => quarterCounts[year] === 4); const annualResults = validYears?.map(year => yearlySummaries[year]); - + // Sort the results by year in descending order annualResults.sort((a, b) => b?.calendarYear?.localeCompare(a?.calendarYear)); - + return annualResults; } diff --git a/src/routes/stocks/[tickerID]/stats/employees/+page.svelte b/src/routes/stocks/[tickerID]/stats/employees/+page.svelte index 78c4f7ea..f9cb99b6 100644 --- a/src/routes/stocks/[tickerID]/stats/employees/+page.svelte +++ b/src/routes/stocks/[tickerID]/stats/employees/+page.svelte @@ -64,7 +64,7 @@ function selectSortingMethod(state:string) { const options = { grid: { left: '0%', - right: '2%', + right: '0%', top: '10%', bottom: '20%', containLabel: true, @@ -72,11 +72,15 @@ function selectSortingMethod(state:string) { xAxis: { data: dateList, type: 'category', + axisLabel: { + color: '#fff', + } }, yAxis: [ { type: 'value', axisLabel: { + color: '#fff', formatter: '{value}', }, splitLine: { @@ -94,12 +98,7 @@ function selectSortingMethod(state:string) { type: 'bar', barWidth: '80%', smooth: true, - itemStyle: { - // Define colors based on positive/negative values - color: function(params) { - return '#F8901E'; - } - }, + }, ], @@ -142,11 +141,15 @@ function selectSortingMethod(state:string) { xAxis: { data: dateList, type: 'category', + axisLabel: { + color: '#fff' + } }, yAxis: [ { type: 'value', axisLabel: { + color: '#fff', formatter: '{value}', }, splitLine: { @@ -219,11 +222,15 @@ function plotGrowth() { xAxis: { data: dateList, type: 'category', + axisLabel: { + color: '#fff' + } }, yAxis: [ { type: 'value', axisLabel: { + color:'#fff', formatter: '{value} %', }, splitLine: { @@ -452,20 +459,20 @@ optionsGrowth = plotGrowth(); {/if} -
- +
+
- - - - @@ -473,28 +480,26 @@ optionsGrowth = plotGrowth(); {#each historyList as item, index} - - - -
+ Date + Employees Change + Growth
+ {new Date(item?.filingDate)?.toLocaleString('en-US', { month: 'short', day: 'numeric', year: 'numeric', daySuffix: '2-digit' })} + {new Intl.NumberFormat("en").format(item?.employeeCount)} {abbreviateNumber(item?.employeeCount-historyList[index+1]?.employeeCount)} - {#if index+1-historyList?.length == 0} + + {#if index+1-historyList?.length === 0} 0.00% {:else} {#if (item?.employeeCount- historyList[index+1]?.employeeCount) > 0} - +{(((item?.employeeCount-historyList[index+1]?.employeeCount) / item?.employeeCount) * 100 )?.toFixed(2)}% {:else if (item?.employeeCount - historyList[index+1]?.employeeCount ) < 0} - - {(((historyList[index+1]?.employeeCount - item?.employeeCount) / item?.employeeCount) * 100 )?.toFixed(2)}% + -{(((historyList[index+1]?.employeeCount - item?.employeeCount) / item?.employeeCount) * 100 )?.toFixed(2)}% {:else} 0.00% @@ -625,21 +630,20 @@ optionsGrowth = plotGrowth(); - - \ No newline at end of file + @media (max-width: 560px) { + .app { + width: 100%; + height: 300px; + } + } + + .chart { + width: 100%; + } + \ No newline at end of file diff --git a/src/routes/stocks/[tickerID]/stats/income/+page.svelte b/src/routes/stocks/[tickerID]/stats/income/+page.svelte index 9742d8c3..b3175e9b 100644 --- a/src/routes/stocks/[tickerID]/stats/income/+page.svelte +++ b/src/routes/stocks/[tickerID]/stats/income/+page.svelte @@ -6,24 +6,30 @@ import { Chart } from 'svelte-echarts' import { init, use } from 'echarts/core' import { LineChart, BarChart } from 'echarts/charts' - import { GridComponent } from 'echarts/components' + import { GridComponent, TooltipComponent } from 'echarts/components' import { CanvasRenderer } from 'echarts/renderers' - use([LineChart, BarChart, GridComponent, CanvasRenderer]) + import { onMount } from 'svelte'; + use([LineChart, BarChart, GridComponent,TooltipComponent, CanvasRenderer]) export let data; + let isLoaded = false; let optionsData; - + let tableList = []; + let income = []; let fullStatement = []; let filterRule = 'annual'; let displayStatement = 'revenue'; - let mode = false; + let mode = true; let timeFrame = '10Y'; + onMount(async () => { + isLoaded = true; + }) const statementConfig = [ @@ -163,14 +169,14 @@ function normalizer(value) { } } - function plotData() +function plotData() { let labelName = '-'; let xList = []; let valueList = []; let growthList = []; - + tableList = []; const index = statementConfig?.findIndex((item) => item?.propertyName === displayStatement); @@ -180,22 +186,30 @@ function normalizer(value) { const year = statement?.calendarYear?.slice(-2); const quarter = statement?.period; + // Determine the label based on filterRule if (filterRule === 'annual') { - xList?.push('FY'+year); + xList.push('FY' + year); } else { - xList?.push('FY'+year+' '+quarter); + xList.push('FY' + year + ' ' + quarter); } - - if (!['eps', 'epsdiluted']?.includes(statementConfig[index]?.propertyName)) - { - valueList.push((Number(statement[statementConfig[index]?.propertyName]) )?.toFixed(2)); - } - else { - valueList.push((Number(statement[statementConfig[index]?.propertyName]))?.toFixed(2)); - } - growthList.push((Number(statement[statementConfig[index]?.growthPropertyName]) * 100)?.toFixed(2)); - } + // Calculate the value and growth + const value = (Number(statement[statementConfig[index]?.propertyName]))?.toFixed(2); + const growth = (Number(statement[statementConfig[index]?.growthPropertyName]) * 100)?.toFixed(2); + + valueList.push(value); + growthList.push(growth); + + // Add the entry to tableList + tableList.push({ + 'date': statement?.date, + 'value': value, + }); + } + + //sort tableList by date + tableList?.sort((a, b) => new Date(b?.date) - new Date(a?.date)); + if(['growthSellingGeneralAndAdministrativeExpenses']?.includes(statementConfig[index]?.growthPropertyName)) { growthList = []; @@ -213,7 +227,18 @@ function normalizer(value) { const {unit, denominator } = normalizer(Math.max(...valueList) ?? 0) const options = { + animation: false, + grid: { + left: '0%', + right: '0%', + bottom: '2%', + top: '10%', + containLabel: true + }, xAxis: { + axisLabel: { + color: '#fff', + }, data: xList, type: 'category', }, @@ -224,7 +249,7 @@ function normalizer(value) { show: false, // Disable x-axis grid lines }, axisLabel: { - color: '#6E7079', // Change label color to white + color: '#fff', // Change label color to white formatter: function (value) { value = Math.max(value, 0); return '$'+(value / denominator)?.toFixed(1) + unit; // Format value in millions @@ -237,40 +262,18 @@ function normalizer(value) { show: false, // Disable x-axis grid lines }, }, - { - type: 'value', - axisLabel: { - formatter: '{value} %', - }, - splitLine: { - show: false, // Disable x-axis grid lines - }, - }, ], series: [ { name: labelName, data: valueList, - type: 'line', - smooth: true, - }, - { - name: 'Growth Rate [%]', - data: growthList, type: 'bar', smooth: true, - yAxisIndex: 1, - itemStyle: { - color: (params) => { - // Set color based on positive or negative value - return params.data >= 0 ? '#10DB06' : '#FF2F1F'; - }, - }, }, ], tooltip: { trigger: 'axis', - hideDelay: 100, // Set the delay in milliseconds + hideDelay: 100, }, }; @@ -329,8 +332,8 @@ const exportData = (format = 'csv') => { timeFrame = '10Y'; income = fullStatement?.slice(0,10); displayStatement = 'revenue'; - + $: { if (timeFrame || displayStatement || filterRule) { @@ -387,9 +390,11 @@ const exportData = (format = 'csv') => { -
-
-
+
+
+
+ + {#if isLoaded}
@@ -508,9 +513,70 @@ const exportData = (format = 'csv') => {
-
+
+ + +

+ {statementConfig?.find((item) => item?.propertyName === displayStatement)?.label} History +

+ + +
+ + + + + + + + + + + {#each tableList as item, index} + + + + + + + + + + + + + {/each} + + +
{filterRule === 'annual' ? 'Fiscal Year End' : 'Quarter Ends'}RevenueChangeGrowth
+ {item?.date} + + {abbreviateNumber(item?.value,true)} + + {item?.value-tableList[index+1]?.value !== 0 ? abbreviateNumber(item?.value-tableList[index+1]?.value) : '-'} + + {#if index+1-tableList?.length === 0} + - + {:else} + {#if (item?.value- tableList[index+1]?.value) > 0} + + +{(((item?.value-tableList[index+1]?.value) / item?.value) * 100 )?.toFixed(2)}% + + {:else if (item?.value - tableList[index+1]?.value ) < 0} + + -{(((tableList[index+1]?.value - item?.value) / item?.value) * 100 )?.toFixed(2)}% + + {:else} + - + {/if} + {/if} +
+ + +
+ {:else} @@ -653,6 +719,15 @@ const exportData = (format = 'csv') => {
+ {:else} +
+
+ +
+
+ {/if}
@@ -826,14 +901,14 @@ const exportData = (format = 'csv') => {