add earnings suprise
This commit is contained in:
parent
f1c2ec0a13
commit
23761e042c
81
src/lib/components/EarningsSurprise.svelte
Normal file
81
src/lib/components/EarningsSurprise.svelte
Normal file
@ -0,0 +1,81 @@
|
||||
<script lang='ts'>
|
||||
import { stockTicker, displayCompanyName } from "$lib/store";
|
||||
import InfoModal from '$lib/components/InfoModal.svelte';
|
||||
import { abbreviateNumber } from "$lib/utils";
|
||||
export let data;
|
||||
|
||||
let rawData = {};
|
||||
|
||||
function latestInfoDate(inputDate) {
|
||||
// Convert the input date string to milliseconds since epoch
|
||||
const inputDateMs = Date?.parse(inputDate);
|
||||
|
||||
// Get today's date in milliseconds since epoch
|
||||
const todayMs = Date?.now();
|
||||
|
||||
// Calculate the difference in milliseconds
|
||||
const differenceInMs = todayMs - inputDateMs;
|
||||
|
||||
// Convert milliseconds to days
|
||||
const differenceInDays = Math?.floor(differenceInMs / (1000 * 60 * 60 * 24));
|
||||
|
||||
// Return the difference in days
|
||||
return differenceInDays <=3;
|
||||
}
|
||||
|
||||
|
||||
$: {
|
||||
if($stockTicker && typeof window !== 'undefined') {
|
||||
rawData = data?.getEarningsSurprise;
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
{#if Object?.keys(rawData)?.length !== 0}
|
||||
|
||||
<div class="space-y-3 overflow-hidden">
|
||||
<!--Start Content-->
|
||||
<div class="w-auto lg:w-full p-1 flex flex-col m-auto">
|
||||
|
||||
<div class="flex flex-col items-center w-full mb-6">
|
||||
<div class="flex flex-row justify-start mr-auto items-center">
|
||||
<!--<img class="h-10 inline-block mr-2" src={copilotIcon} />-->
|
||||
<div class="flex flex-row items-center">
|
||||
<label for="earningsSurprise" class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-3xl font-bold">
|
||||
Earnings Surprise
|
||||
</label>
|
||||
<InfoModal
|
||||
title={"Earnings Surprise"}
|
||||
content={`The earnings surprise is when a company's actual EPS differs from analysts' forecasts. Positive surprises boost stock prices; negative ones can lower them.`}
|
||||
id={"earningsSurprise"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if data?.user?.tier === 'Pro'}
|
||||
<div class="text-white text-[1rem] {latestInfoDate(rawData?.date) ? 'bg-[#F9AB00] bg-opacity-[0.1] p-3 rounded-lg' : 'bg-[#27272A]'} ">
|
||||
<div class="mt-1">{$displayCompanyName} has released their quartely earnings on {new Date(rawData?.date)?.toLocaleString('en-US', { month: 'short', day: 'numeric', year: 'numeric', daySuffix: '2-digit' })}:</div>
|
||||
|
||||
<li class="ml-[20px] sm:ml-[30px]" style="color: #fff; line-height: 22px; margin-top:20px; margin-bottom: 15px; list-style-type: disc;">
|
||||
Revenue of {abbreviateNumber(rawData?.revenue,true)} {rawData?.revenueSurprise > 0 ? 'exceeds' : 'misses'} estimates by {abbreviateNumber(Math.abs(rawData?.revenueSurprise),true)}, with {((rawData?.revenue/rawData?.revenuePrior-1)*100)?.toFixed(2)}% YoY {(rawData?.revenue/rawData?.revenuePrior-1) < 0 ? 'decline' : 'growth'}.
|
||||
</li>
|
||||
<li class="ml-[20px] sm:ml-[30px]" style="color: #fff; line-height: 22px; margin-top:0px; margin-bottom: 15px; list-style-type: disc;">
|
||||
EPS of ${rawData?.eps} {rawData?.epsSurprise > 0 ? 'exceeds' : 'misses'} estimates by ${rawData?.epsSurprise?.toFixed(2)}, with {(((rawData?.eps - rawData?.epsPrior) / Math.abs(rawData?.epsPrior)) * 100)?.toFixed(2)}% YoY {((rawData?.eps - rawData?.epsPrior) / Math.abs(rawData?.epsPrior)) < 0 ? 'decline' : 'growth'}.
|
||||
</li>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="shadow-lg shadow-bg-[#000] bg-[#111112] sm:bg-opacity-[0.5] text-sm sm:text-[1rem] rounded-md w-full p-4 min-h-24 mt-4 text-white m-auto flex justify-center rawDatas-center text-center font-semibold">
|
||||
<svg class="mr-1.5 w-5 h-5 inline-block"xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#A3A3A3" d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"/></svg>
|
||||
Unlock content with <a class="inline-block ml-2 text-blue-400 hover:sm:text-white" href="/pricing">Pro Subscription</a>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
{/if}
|
||||
@ -1,30 +1,40 @@
|
||||
const cleanString = (input) => {
|
||||
const substringsToRemove = [
|
||||
'Depositary', 'Inc.', 'Incorporated', 'Holdings', 'Corporation', 'Corporations',
|
||||
'LLC', 'Holdings plc American Depositary Shares', 'Holding Corporation', 'Oyj',
|
||||
'Company', 'The', 'plc',
|
||||
"Depositary",
|
||||
"Inc.",
|
||||
"Incorporated",
|
||||
"Holdings",
|
||||
"Corporation",
|
||||
"Corporations",
|
||||
"LLC",
|
||||
"Holdings plc American Depositary Shares",
|
||||
"Holding Corporation",
|
||||
"Oyj",
|
||||
"Company",
|
||||
"The",
|
||||
"plc",
|
||||
];
|
||||
const pattern = new RegExp(`\\b(${substringsToRemove.join('|')})\\b|,`, 'gi');
|
||||
return input?.replace(pattern, '').trim();
|
||||
const pattern = new RegExp(`\\b(${substringsToRemove.join("|")})\\b|,`, "gi");
|
||||
return input?.replace(pattern, "").trim();
|
||||
};
|
||||
|
||||
const fetchData = async (apiURL, apiKey, endpoint, ticker) => {
|
||||
const response = await fetch(`${apiURL}${endpoint}`, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-API-KEY": apiKey
|
||||
"X-API-KEY": apiKey,
|
||||
},
|
||||
body: JSON.stringify({ ticker })
|
||||
body: JSON.stringify({ ticker }),
|
||||
});
|
||||
return response.json();
|
||||
};
|
||||
|
||||
const fetchFromFastify = async (fastifyURL, endpoint, userId) => {
|
||||
const response = await fetch(`${fastifyURL}${endpoint}`, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ userId })
|
||||
body: JSON.stringify({ userId }),
|
||||
});
|
||||
const { items } = await response.json();
|
||||
return items;
|
||||
@ -32,16 +42,18 @@ const fetchFromFastify = async (fastifyURL, endpoint, userId) => {
|
||||
|
||||
const fetchCommunitySentiment = async (pb, ticker, cookies) => {
|
||||
const cookieVote = cookies.get(`community-sentiment-${ticker}`);
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
const tomorrow = new Date(new Date().setDate(new Date().getDate() + 1)).toISOString().split('T')[0];
|
||||
const today = new Date().toISOString().split("T")[0];
|
||||
const tomorrow = new Date(new Date().setDate(new Date().getDate() + 1))
|
||||
.toISOString()
|
||||
.split("T")[0];
|
||||
|
||||
const output = await pb.collection("sentiment").getFullList({
|
||||
filter: `ticker="${ticker}" && created >= "${today}" && created < "${tomorrow}"`
|
||||
filter: `ticker="${ticker}" && created >= "${today}" && created < "${tomorrow}"`,
|
||||
});
|
||||
|
||||
return {
|
||||
alreadyVoted: cookieVote || null,
|
||||
sentimentData: output?.at(0) || {}
|
||||
sentimentData: output?.at(0) || {},
|
||||
};
|
||||
};
|
||||
|
||||
@ -50,24 +62,43 @@ export const load = async ({ params, locals, cookies, setHeaders }) => {
|
||||
const { tickerID } = params;
|
||||
|
||||
const endpoints = [
|
||||
'/similar-stocks', '/stockdeck', '/analyst-summary-rating', '/stock-quote',
|
||||
'/bull-bear-say','/wiim', '/top-etf-ticker-holder', '/one-day-price','/next-earnings'
|
||||
"/similar-stocks",
|
||||
"/stockdeck",
|
||||
"/analyst-summary-rating",
|
||||
"/stock-quote",
|
||||
"/bull-bear-say",
|
||||
"/wiim",
|
||||
"/top-etf-ticker-holder",
|
||||
"/one-day-price",
|
||||
"/next-earnings",
|
||||
"/earnings-surprise",
|
||||
];
|
||||
|
||||
const promises = [
|
||||
...endpoints.map(endpoint => fetchData(apiURL, apiKey, endpoint, tickerID)),
|
||||
fetchFromFastify(fastifyURL, '/all-watchlists', user?.id),
|
||||
...endpoints.map((endpoint) =>
|
||||
fetchData(apiURL, apiKey, endpoint, tickerID)
|
||||
),
|
||||
fetchFromFastify(fastifyURL, "/all-watchlists", user?.id),
|
||||
//fetchFromFastify(fastifyURL, '/get-portfolio-data', user?.id),
|
||||
fetchCommunitySentiment(pb, tickerID, cookies)
|
||||
fetchCommunitySentiment(pb, tickerID, cookies),
|
||||
];
|
||||
|
||||
const [
|
||||
getSimilarStock, getStockDeck, getAnalystRating, getStockQuote,
|
||||
getBullBearSay, getWhyPriceMoved, getTopETFHolder, getOneDayPrice, getNextEarnings,
|
||||
getUserWatchlist, getCommunitySentiment
|
||||
getSimilarStock,
|
||||
getStockDeck,
|
||||
getAnalystRating,
|
||||
getStockQuote,
|
||||
getBullBearSay,
|
||||
getWhyPriceMoved,
|
||||
getTopETFHolder,
|
||||
getOneDayPrice,
|
||||
getNextEarnings,
|
||||
getEarningsSurprise,
|
||||
getUserWatchlist,
|
||||
getCommunitySentiment,
|
||||
] = await Promise.all(promises);
|
||||
|
||||
setHeaders({ 'cache-control': 'public, max-age=300' });
|
||||
setHeaders({ "cache-control": "public, max-age=300" });
|
||||
|
||||
return {
|
||||
getSimilarStock,
|
||||
@ -79,8 +110,9 @@ export const load = async ({ params, locals, cookies, setHeaders }) => {
|
||||
getTopETFHolder,
|
||||
getOneDayPrice,
|
||||
getNextEarnings,
|
||||
getEarningsSurprise,
|
||||
getUserWatchlist,
|
||||
getCommunitySentiment,
|
||||
companyName: cleanString(getStockDeck?.at(0)?.companyName),
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@ -37,6 +37,8 @@
|
||||
import { onDestroy } from "svelte";
|
||||
import BullBearSay from "$lib/components/BullBearSay.svelte";
|
||||
import NextEarnings from "$lib/components/NextEarnings.svelte";
|
||||
import EarningsSurprise from "$lib/components/EarningsSurprise.svelte";
|
||||
|
||||
import CommunitySentiment from "$lib/components/CommunitySentiment.svelte";
|
||||
import Lazy from "$lib/components/Lazy.svelte";
|
||||
import { convertTimestamp } from '$lib/utils';
|
||||
@ -1062,6 +1064,10 @@ async function exportData(timePeriod:string) {
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="w-full mt-10 sm:mt-0 m-auto sm:pl-6 sm:pb-6 sm:pt-6 {Object?.keys(data?.getEarningsSurprise || {})?.length !== 0 ? '' : 'hidden'}">
|
||||
<EarningsSurprise {data} />
|
||||
</div>
|
||||
|
||||
<div class="w-full mt-10 sm:mt-0 m-auto sm:pl-6 sm:pb-6 sm:pt-6 {Object?.keys(data?.getNextEarnings || {})?.length !== 0 ? '' : 'hidden'}">
|
||||
<NextEarnings {data} />
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user