clean code

This commit is contained in:
MuslemRahimi 2025-01-12 11:29:54 +01:00
parent 46e5d91b7f
commit e97b356d27
19 changed files with 297 additions and 2785 deletions

View File

@ -1,147 +0,0 @@
<script lang 'ts'></script>
<section class="overflow-hidden text-white h-full pb-8">
<main class="overflow-hidden">
<div class="flex flex-row items-center">
<label
for="borrowedShareInfo"
class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-3xl font-bold"
>
Borrowed Share
</label>
<InfoModal
title={"Borrowed Share Statistics"}
content={"At Interactive Brokers, borrowed shares refer to shares lent by other investors for short selling. Borrowers pay a fee to lenders, aiming to profit from declining stock prices. Lenders earn interest on their lent shares, enhancing returns while still owning the stock."}
id={"borrowedShareInfo"}
/>
</div>
{#if isLoaded}
{#if rawData?.length !== 0}
<div class="w-full flex flex-col items-start">
<div class="text-white text-[1rem] mt-2 mb-2 w-full">
Over the past six months, Interactive Brokers had {abbreviateNumber(
totalAvailableShares,
)} shares available for borrowing, with an average fee of {avgFee}%.
</div>
</div>
<div class="pb-2 rounded-md bg-default">
<div class="app w-full h-[300px] mt-5">
<Chart {init} options={optionsData} class="chart" />
</div>
</div>
<div
class="flex flex-row items-center justify-between mx-auto mt-5 w-full sm:w-11/12"
>
<div
class="mt-3.5 sm:mt-0 flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center"
>
<div
class="h-full transform -translate-x-1/2"
aria-hidden="true"
></div>
<div
class="w-3 h-3 bg-[#fff] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2"
aria-hidden="true"
></div>
<span
class="mt-2 sm:mt-0 text-white text-center sm:text-start text-xs sm:text-md inline-block"
>
Available Shares
</span>
</div>
<div
class="flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center"
>
<div
class="h-full transform -translate-x-1/2"
aria-hidden="true"
></div>
<div
class="w-3 h-3 bg-[#22C55E] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2"
aria-hidden="true"
></div>
<span
class="mt-2 sm:mt-0 text-white text-xs sm:text-md sm:font-medium inline-block"
>
Fee
</span>
</div>
</div>
<h2
class="mt-10 mr-1 flex flex-row items-center text-white text-xl sm:text-2xl font-bold mb-3"
>
Latest Information
</h2>
<div class="flex justify-start items-center w-full m-auto">
<table class="w-full" data-test="statistics-table">
<tbody>
<tr class="border-y border-gray-800 odd:bg-odd">
<td class="px-[5px] py-1.5 xs:px-2.5 xs:py-2">
<span>Date</span>
</td>
<td
class="px-[5px] py-1.5 text-right whitespace-nowrap font-medium xs:px-2.5 xs:py-2"
>
{formatDateRange(rawData?.slice(-1)?.at(0)?.date)}
</td>
</tr>
<tr class="border-y border-gray-800 odd:bg-odd">
<td class="px-[5px] py-1.5 xs:px-2.5 xs:py-2">
<span>Fee Range</span>
</td>
<td
class="px-[5px] py-1.5 text-right font-medium xs:px-2.5 xs:py-2"
>
{lowestFee + "%" + "-" + highestFee + "%"}
</td>
</tr>
<tr class="border-y border-gray-800 odd:bg-odd">
<td class="px-[5px] py-1.5 xs:px-2.5 xs:py-2">
<span>Total Available Shares</span>
</td>
<td
class="px-[5px] py-1.5 text-right font-medium xs:px-2.5 xs:py-2"
>
{abbreviateNumber(monthlyAvailableShares)}
</td>
</tr>
</tbody>
</table>
</div>
{/if}
{:else}
<div class="flex justify-center items-center h-80">
<div class="relative">
<label
class="bg-secondary rounded-md h-14 w-14 flex justify-center items-center absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
>
<span class="loading loading-spinner loading-md text-gray-400"
></span>
</label>
</div>
</div>
{/if}
</main>
</section>
<style>
.app {
height: 300px;
max-width: 100%; /* Ensure chart width doesn't exceed the container */
}
@media (max-width: 640px) {
.app {
height: 210px;
}
}
.chart {
width: 100%;
}
</style>

View File

@ -1,159 +0,0 @@
<script lang="ts">
import { etfTicker, screenWidth } from "$lib/store";
import { formatString } from "$lib/utils";
import InfoModal from "$lib/components/InfoModal.svelte";
import { countryList } from "$lib/country-list.ts";
export let geographicList;
$: {
if (
$etfTicker &&
typeof window !== "undefined" &&
typeof geographicList !== "undefined" &&
geographicList?.length !== 0
) {
showFullStats = false;
// Create an index for quick lookups
const countryIndex = countryList?.reduce((acc, country) => {
acc[country.long] = country.short;
return acc;
}, {});
// Update the originalList with the "code" property
geographicList = geographicList?.map((item) => ({
...item,
code: countryIndex[item.country]?.toLowerCase() || "xx",
}));
geographicList = [...geographicList];
}
}
let showFullStats = false;
let charNumber = 40;
$: {
if ($screenWidth < 640) {
charNumber = 25;
} else {
charNumber = 40;
}
}
</script>
<section class="bg-default overflow-hidden text-white h-full sm:mb-0">
<div class="flex justify-center m-auto h-full overflow-hidden">
<div
class="relative flex justify-center items-center overflow-hidden w-full"
>
<div class="w-full">
<div class="flex flex-row items-center">
<label
for="countryWeightingInfo"
class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-3xl font-bold"
>
Country Weighting Breakdown
</label>
<InfoModal
title={"Country Weighting Breakdown"}
content={"Explore the degree to which your ETF's performance is influenced by individual countries."}
id={"countryWeightingInfo"}
/>
</div>
{#if geographicList?.length !== 0}
<div class="text-white text-md mt-3">
The ETF is build on top of {geographicList?.length} regions:
</div>
<div class="w-full rounded-full flex justify-center items-center">
<div class="flex flex-col items-center w-full">
<!--Start Progress-->
{#each showFullStats ? geographicList : geographicList?.slice(0, 3) as item, index}
<div
class="shadow-lg bg-primary w-full rounded-md p-4 sm:p-3 mb-5 flex flex-row items-center {index ===
0
? 'mt-4'
: ''} {index === 2 &&
!showFullStats &&
geographicList?.length > 2
? 'opacity-[0.3]'
: ''}"
>
<div
class="flex-shrink-0 mr-3 rounded-full w-10 h-10 relative bg-default"
>
<img
class="flex-shrink-0 rounded-full w-7 h-7 absolute inset-1/2 transform -translate-x-1/2 -translate-y-1/2"
src={`https://hatscripts.github.io/circle-flags/flags/${item?.code}.svg`}
alt="Country Logo"
/>
</div>
<div class="flex flex-col -mt-3 sm:-mt-5 w-full">
<div class="flex flex-row items-center w-full">
<span
class="text-white text-sm sm:text-md font-medium text-start mb-2 mr-auto mt-2"
>
{item?.country?.length > charNumber
? formatString(item?.country)?.slice(0, charNumber) +
"..."
: formatString(item?.country)}
</span>
<span
class="text-white text-sm sm:text-md font-medium ml-auto"
>
{item?.weightPercentage <= 0.01
? "< 0.01%"
: item?.weightPercentage?.toFixed(2) + "%"}
</span>
</div>
<progress
class="progress [&::-webkit-progress-value]:bg-[#00FC50] [&::-moz-progress-bar]:bg-[#00FC50]"
value={item?.weightPercentage}
max="100"
></progress>
</div>
</div>
{/each}
<!--End Progress-->
</div>
</div>
{#if geographicList?.length > 2}
<label
on:click={() => (showFullStats = !showFullStats)}
class="cursor-pointer m-auto flex justify-center items-center mt-5"
>
<svg
class="w-10 h-10 transform {showFullStats ? 'rotate-180' : ''} "
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><path
fill="#2A323C"
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10s10-4.48 10-10S17.52 2 12 2zm0 13.5L7.5 11l1.42-1.41L12 12.67l3.08-3.08L16.5 11L12 15.5z"
/></svg
>
</label>
{/if}
{:else}
<h2
class="mt-10 justify-center items-center text-3xl font-bold text-slate-700 mb-5 m-auto"
>
No data available
<svg
class="w-10 sm:w-12 inline-block"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><path
fill="#334155"
d="M18.68 12.32a4.49 4.49 0 0 0-6.36.01a4.49 4.49 0 0 0 0 6.36a4.508 4.508 0 0 0 5.57.63L21 22.39L22.39 21l-3.09-3.11c1.13-1.77.87-4.09-.62-5.57m-1.41 4.95c-.98.98-2.56.97-3.54 0c-.97-.98-.97-2.56.01-3.54c.97-.97 2.55-.97 3.53 0c.97.98.97 2.56 0 3.54M10.9 20.1a6.527 6.527 0 0 1-1.48-2.32C6.27 17.25 4 15.76 4 14v3c0 2.21 3.58 4 8 4c-.4-.26-.77-.56-1.1-.9M4 9v3c0 1.68 2.07 3.12 5 3.7v-.2c0-.93.2-1.85.58-2.69C6.34 12.3 4 10.79 4 9m8-6C7.58 3 4 4.79 4 7c0 2 3 3.68 6.85 4h.05c1.2-1.26 2.86-2 4.6-2c.91 0 1.81.19 2.64.56A3.215 3.215 0 0 0 20 7c0-2.21-3.58-4-8-4Z"
/></svg
>
</h2>
{/if}
</div>
</div>
</div>
</section>

View File

@ -1,288 +0,0 @@
<script lang="ts">
//import ProgressBar from 'progressbar.js';
//import { onMount } from 'svelte';
import { stockTicker, screenWidth } from "$lib/store";
export let stockDeck;
let info;
let esgScore;
let socialScore;
let environmentalScore;
let governanceScore;
let esgRiskRating;
$: {
if (
$stockTicker &&
typeof window !== "undefined" &&
typeof stockDeck !== "undefined" &&
stockDeck?.length !== 0
) {
info = stockDeck?.at(0);
esgScore =
info?.esgScore !== "n/a"
? Math.round(info?.esgScore.toFixed(1))
: "n/a";
socialScore =
info?.socialScore !== "n/a"
? Math.round(info?.socialScore.toFixed(1))
: "n/a";
environmentalScore =
info?.environmentalScore !== "n/a"
? Math.round(info?.environmentalScore?.toFixed(1))
: "n/a";
governanceScore =
info?.governanceScore !== "n/a"
? Math.round(info?.governanceScore?.toFixed(1))
: "n/a";
esgRiskRating = info?.esgRiskRating;
}
}
</script>
<!--Start ESG Card -->
<div
class="space-y-3 lg:pt-5 hidden lg:block lg:{esgScore &&
esgRiskRating &&
environmentalScore &&
governanceScore !== 'n/a'
? ''
: 'hidden'}"
>
<div
class="sm:rounded-md shadow-lg bg-[#000] sm:bg-default sm:border sm:border-gray-600 h-auto sm:h-[470px] {$screenWidth <
640
? 'w-screen pt-16'
: ''} md:w-[420px] xl:w-[450px] -mx-1 sm:mx-0"
>
<!--Start Content-->
<div
class="w-auto lg:w-full p-1 flex flex-col m-auto pb-14 sm:pb-10 px-2 sm:px-0"
>
<h2 class="text-start text-2xl font-semibold text-white p-3 mt-3 ml-1">
ESG Score
</h2>
{#if esgScore && esgRiskRating && environmentalScore && governanceScore !== "n/a"}
<p class="text-white mb-5 ml-4 mr-1">
Gain valuable insights into a company's sustainability by evaluating
its ESG (Environmental, Social, and Governance) scores.
</p>
<div class="flex flex-col m-auto items-center rounded-md w-full mb-16">
<div class="flex flex-col items-center w-full">
<div class="flex flex-row items-center w-11/12 mt-2 mb-2">
<span class="text-white font-medium text-start mr-auto">
Total
</span>
<span class="text-white text-md font-medium ml-auto">
{esgScore}
</span>
</div>
<progress
class="progress bg-[#3B3D3F] w-11/12 {esgScore >= 50
? '[&::-webkit-progress-value]:bg-[#00FC50] [&::-moz-progress-bar]:bg-[#00FC50]'
: '[&::-webkit-progress-value]:bg-[#FF2F1F] [&::-moz-progress-bar]:bg-[#FF2F1F]'}"
value={esgScore}
max="100"
></progress>
</div>
<div class="flex flex-col items-center w-full mt-2">
<div class="flex flex-row items-center w-11/12 mt-5 mb-2">
<span class="text-white font-medium text-start mr-auto">
Environment
</span>
<span class="text-white text-md font-medium ml-auto">
{environmentalScore}
</span>
</div>
<progress
class="progress bg-[#3B3D3F] w-11/12 {environmentalScore >= 50
? '[&::-webkit-progress-value]:bg-[#00FC50] [&::-moz-progress-bar]:bg-[#00FC50]'
: '[&::-webkit-progress-value]:bg-[#FF2F1F] [&::-moz-progress-bar]:bg-[#FF2F1F]'}"
value={environmentalScore}
max="100"
></progress>
</div>
<div class="flex flex-col items-center w-full">
<div class="flex flex-row items-center w-11/12 mt-5 mb-2">
<span class="text-white font-medium text-start mr-auto">
Social
</span>
<span class="text-white text-md font-medium ml-auto">
{socialScore}
</span>
</div>
<progress
class="progress bg-[#3B3D3F] w-11/12 {socialScore >= 50
? '[&::-webkit-progress-value]:bg-[#00FC50] [&::-moz-progress-bar]:bg-[#00FC50]'
: '[&::-webkit-progress-value]:bg-[#FF2F1F] [&::-moz-progress-bar]:bg-[#FF2F1F]'}"
value={socialScore}
max="100"
></progress>
</div>
<div class="flex flex-col items-center w-full">
<div class="flex flex-row items-center w-11/12 mt-5 mb-2">
<span class="text-white font-medium text-start mr-auto">
Governance
</span>
<span class="text-white text-md font-medium ml-auto">
{governanceScore}
</span>
</div>
<progress
class="progress bg-[#3B3D3F] w-11/12 {governanceScore >= 50
? '[&::-webkit-progress-value]:bg-[#00FC50] [&::-moz-progress-bar]:bg-[#00FC50]'
: '[&::-webkit-progress-value]:bg-[#FF2F1F] [&::-moz-progress-bar]:bg-[#FF2F1F]'}"
value={governanceScore}
max="100"
></progress>
</div>
</div>
{:else}
<h2
class="mt-20 justify-center items-center text-3xl font-bold text-slate-700 mb-20 m-auto"
>
No data available
</h2>
{/if}
</div>
</div>
</div>
<!--End ESG Card-->
<!--Start Mobile ESG Card-->
<div class="lg:hidden space-y-3 sm:pt-5">
<div class="bg-[#000] h-auto w-screen">
<!--Start Header-->
<div class="w-full p-1 flex flex-col items-center pb-5 h-auto">
<h2
class="text-center m-auto text-[1.1rem] font-semibold text-white mt-5"
>
ESG Score
</h2>
<div class="flex flex-col items-center mt-10 mb-5 w-full px-3">
<span class="text-white text-center text-md">
Gain valuable insights into a company's sustainability by evaluating
its ESG (Environmental, Social, and Governance) scores.
</span>
</div>
</div>
<!--End Header-->
{#if esgScore && esgRiskRating && environmentalScore && governanceScore !== "n/a"}
<div
class="mt-5 flex flex-col m-auto items-center rounded-md w-full mb-16 p-3"
>
<div
class="shadow-lg bg-primary w-full rounded-md p-4 mb-5 flex flex-row items-center"
>
<div class="flex flex-col -mt-2 w-full">
<div class="flex flex-row items-center w-full">
<span
class="text-white text-md font-medium text-start mb-2 mr-auto mt-2"
>
Total
</span>
<span class="text-white text-md font-medium ml-auto">
{esgScore}
</span>
</div>
<progress
class="progress w-full {esgScore >= 50
? '[&::-webkit-progress-value]:bg-[#00FC50] [&::-moz-progress-bar]:bg-[#00FC50]'
: '[&::-webkit-progress-value]:bg-[#FF2F1F] [&::-moz-progress-bar]:bg-[#FF2F1F]'}"
value={esgScore}
max="100"
></progress>
</div>
</div>
<div
class="shadow-lg bg-primary w-full rounded-md p-4 mb-5 flex flex-row items-center"
>
<div class="flex flex-col -mt-2 w-full">
<div class="flex flex-row items-center w-full">
<span
class="text-white text-md font-medium text-start mb-2 mr-auto mt-2"
>
Environment
</span>
<span class="text-white text-md font-medium ml-auto">
{environmentalScore}
</span>
</div>
<progress
class="progress w-full {environmentalScore >= 50
? '[&::-webkit-progress-value]:bg-[#00FC50] [&::-moz-progress-bar]:bg-[#00FC50]'
: '[&::-webkit-progress-value]:bg-[#FF2F1F] [&::-moz-progress-bar]:bg-[#FF2F1F]'}"
value={environmentalScore}
max="100"
></progress>
</div>
</div>
<div
class="shadow-lg bg-primary w-full rounded-md p-4 mb-5 flex flex-row items-center"
>
<div class="flex flex-col -mt-2 w-full">
<div class="flex flex-row items-center w-full">
<span
class="text-white text-md font-medium text-start mb-2 mr-auto mt-2"
>
Social
</span>
<span class="text-white text-md font-medium ml-auto">
{socialScore}
</span>
</div>
<progress
class="progress w-full {socialScore >= 50
? '[&::-webkit-progress-value]:bg-[#00FC50] [&::-moz-progress-bar]:bg-[#00FC50]'
: '[&::-webkit-progress-value]:bg-[#FF2F1F] [&::-moz-progress-bar]:bg-[#FF2F1F]'}"
value={socialScore}
max="100"
></progress>
</div>
</div>
<div
class="shadow-lg bg-primary w-full rounded-md p-4 mb-5 flex flex-row items-center"
>
<div class="flex flex-col -mt-2 w-full">
<div class="flex flex-row items-center w-full">
<span
class="text-white text-md font-medium text-start mb-2 mr-auto mt-2"
>
Governance
</span>
<span class="text-white text-md font-medium ml-auto">
{governanceScore}
</span>
</div>
<progress
class="progress w-full {governanceScore >= 50
? '[&::-webkit-progress-value]:bg-[#00FC50] [&::-moz-progress-bar]:bg-[#00FC50]'
: '[&::-webkit-progress-value]:bg-[#FF2F1F] [&::-moz-progress-bar]:bg-[#FF2F1F]'}"
value={governanceScore}
max="100"
></progress>
</div>
</div>
</div>
{:else}
<div
class=" mt-20 flex justify-center items-center text-2xl font-bold text-slate-700 mb-20 m-auto"
>
No data available
</div>
{/if}
</div>
</div>
<!--End Mobile ESG Card-->

View File

@ -1,145 +0,0 @@
<script lang 'ts'></script>
<section class="overflow-hidden text-white h-full pb-8 sm:pb-2">
<main class="overflow-hidden">
<div class="flex flex-row items-center">
<label
for="enterpriseValueInfo"
class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-3xl font-bold"
>
Enterprise Value
</label>
<InfoModal
title={"Enterprise Value"}
content={"Enterprise value (EV) is a comprehensive measure of a company's total value in the stock market, considering market capitalization, debt, cash, and minority interests. It helps in M&A analysis, comparative analysis, valuation metrics, and capital structure assessment, aiding investors, analysts, and companies in financial decision-making."}
id={"enterpriseValueInfo"}
/>
</div>
{#if isLoaded}
{#if rawData?.length !== 0}
<div class="mt-2 pb-4">
<div class="w-full flex flex-col items-start">
<div class="text-white text-[1rem] mt-1 sm:mt-3 mb-1 w-full">
{$displayCompanyName}'s' enterprise value provides a comprehensive
snapshot of its total worth, crucial for assessing its financial
health and making informed investment decisions.
</div>
</div>
<a
href={"/stocks/" + $stockTicker + "/stats/income"}
class="text-blue-400 hover:text-white flex justify-end mt-3 text-sm sm:text-[1rem]"
>
Full report
</a>
<div class="app w-full h-[300px] mt-5">
<Chart {init} options={optionsData} class="chart" />
</div>
<div
class="flex flex-row items-center justify-between mx-auto mt-5 w-full sm:w-11/12"
>
<div
class="mt-3.5 sm:mt-0 flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center"
>
<div
class="h-full transform -translate-x-1/2"
aria-hidden="true"
></div>
<div
class="w-3 h-3 bg-[#22C55E] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2"
aria-hidden="true"
></div>
<span
class="mt-2 sm:mt-0 text-white text-center sm:text-start text-xs sm:text-md inline-block"
>
Enterprise Value
</span>
</div>
<div
class="flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center"
>
<div
class="h-full transform -translate-x-1/2"
aria-hidden="true"
></div>
<div
class="w-3 h-3 bg-[#E11D48] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2"
aria-hidden="true"
></div>
<span
class="mt-2 sm:mt-0 text-white text-xs sm:text-md sm:font-medium inline-block"
>
Mkt Cap
</span>
</div>
<div
class="flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center"
>
<div
class="h-full transform -translate-x-1/2"
aria-hidden="true"
></div>
<div
class="w-3 h-3 bg-[#3B82F6] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2"
aria-hidden="true"
></div>
<span
class="mt-2 sm:mt-0 text-white text-xs sm:text-md inline-block"
>
Debt
</span>
</div>
<div
class="mt-3.5 sm:mt-0 flex flex-col sm:flex-row items-center sm:items-center ml-3 sm:ml-0 w-1/2 justify-center"
>
<div
class="h-full transform -translate-x-1/2"
aria-hidden="true"
></div>
<div
class="w-3 h-3 bg-[#fff] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2"
aria-hidden="true"
></div>
<span
class="mt-2 sm:mt-0 text-white text-center sm:text-start text-xs sm:text-md inline-block"
>
Cash Equivalents
</span>
</div>
</div>
</div>
{/if}
{:else}
<div class="flex justify-center items-center h-80">
<div class="relative">
<label
class="bg-secondary rounded-md h-14 w-14 flex justify-center items-center absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
>
<span class="loading loading-spinner loading-md text-gray-400"
></span>
</label>
</div>
</div>
{/if}
</main>
</section>
<style>
.app {
height: 300px;
max-width: 100%; /* Ensure chart width doesn't exceed the container */
}
@media (max-width: 640px) {
.app {
height: 230px;
}
}
.chart {
width: 100%;
}
</style>

View File

@ -1,356 +0,0 @@
<script lang="ts">
import {
fomcImpactComponent,
displayCompanyName,
stockTicker,
assetType,
etfTicker,
getCache,
setCache,
} from "$lib/store";
import InfoModal from "$lib/components/InfoModal.svelte";
import { Chart } from "svelte-echarts";
import { init, use } from "echarts/core";
import { LineChart } from "echarts/charts";
import {
GridComponent,
MarkLineComponent,
TooltipComponent,
} from "echarts/components";
import { CanvasRenderer } from "echarts/renderers";
use([
LineChart,
GridComponent,
MarkLineComponent,
TooltipComponent,
CanvasRenderer,
]);
let isLoaded = false;
let rawData = [];
let tableList = [];
let optionsData;
let showFullStats = false;
function getPlotOptions() {
const xAxisData = rawData?.history.map((item) => item.date);
// Extract yAxis data from history (close prices)
const seriesData = rawData?.history?.map((item) => [item.date, item.close]);
// Create markLine data for FOMC dates based on xAxis index
const markLineData = rawData.fomcData
?.map((item) => {
const index = xAxisData?.indexOf(item?.date);
if (index !== -1) {
return { xAxis: index }; // Mark the index in xAxis where FOMC date exists
}
return null;
})
?.filter((item) => item !== null); // Filter out null values in case FOMC date is not in history
tableList = rawData.fomcData?.sort(
(a, b) => new Date(b?.date) - new Date(a?.date),
);
const option = {
silent: true,
animation: false,
tooltip: {
trigger: "axis",
hideDelay: 100,
},
grid: {
left: "3%",
right: "3%",
bottom: "2%",
top: "5%",
containLabel: true,
},
xAxis: {
type: "category",
boundaryGap: false,
axisLabel: {
color: "#fff",
},
data: xAxisData,
},
yAxis: [
{
type: "value",
splitLine: {
show: false,
},
axisLabel: {
show: false,
},
},
],
series: [
{
name: "Stock Price",
type: "line",
symbol: "none",
lineStyle: {
color: "#fff",
},
markLine: {
symbol: ["none", "none"],
label: { show: false },
lineStyle: {
color: "orange", // Set the color to orange
},
data: markLineData, // Use dynamically created markLine data for FOMC dates
},
data: seriesData, // Populate series with [date, close] data
},
],
};
return option;
}
const getFOMCImpact = async (ticker: string) => {
// Get cached data for the specific tickerID
const cachedData = getCache(ticker, "getFOMCImpact");
if (cachedData) {
rawData = cachedData;
} else {
const postData = { ticker: ticker, path: "fomc-impact" };
const response = await fetch("/api/ticker-data", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
});
rawData = await response?.json();
// Cache the data for this specific tickerID with a specific name 'getFOMCImpact'
setCache(ticker, rawData, "getFOMCImpact");
}
if (Object?.keys(rawData)?.length !== 0) {
$fomcImpactComponent = true;
} else {
$fomcImpactComponent = false;
}
};
$: {
if (
$assetType === "stock"
? $stockTicker
: $etfTicker && typeof window !== "undefined"
) {
isLoaded = false;
showFullStats = false;
tableList = [];
const asyncFunctions = [
getFOMCImpact($assetType === "stock" ? $stockTicker : $etfTicker),
];
Promise.all(asyncFunctions)
.then(() => {
if (Object?.keys(rawData)?.length !== 0) {
optionsData = getPlotOptions();
}
})
.catch((error) => {
console.error("An error occurred:", error);
});
isLoaded = true;
}
}
</script>
<section class="overflow-hidden text-white h-full">
<main class="overflow-hidden">
<div class="flex flex-row items-center">
<label
for="fomcInfo"
class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-3xl font-bold"
>
FOMC Impact
</label>
<InfoModal
title={"Federal Open Market Committee Meeting"}
content={"FOMC meetings influence stock prices by setting interest rates and signaling monetary policy, affecting investor sentiment and market liquidity."}
id={"fomcInfo"}
/>
</div>
{#if isLoaded}
{#if Object?.keys(rawData)?.length !== 0}
<div class="mt-2 pb-8 sm:pb-2 rounded-md bg-default sm:bg-default">
<div class="w-full flex flex-col items-start">
<div class="text-white text-[1rem] mt-1 sm:mt-3 mb-1 w-full">
Examine how sensitive {$displayCompanyName}'s stock price is to
the decisions made during FOMC meetings. The vertical line marks
the interest rate decision.
</div>
</div>
<div class="app w-full h-[300px]">
<Chart {init} options={optionsData} class="chart" />
</div>
<h2
class="mt-10 mr-1 flex flex-row items-center text-white text-2xl font-bold mb-3"
>
Latest FED Interest Rate
</h2>
<div class="w-full text-white text-[1rem] mt-6">
The latest list of previous, forecasted, and actual interest rate
decisions made by the FED.
<br />
The "% Price Change" column reflects the performance for each interval
leading up to the next FOMC meeting.
</div>
<div
class="flex justify-start items-center w-full m-auto mt-6 overflow-x-scroll"
>
<table class="table table-sm table-compact w-full">
<thead>
<tr class="border-b border-[#27272A]">
<th
class="text-white font-semibold text-sm sm:text-[1rem] text-start bg-default"
>Date</th
>
<th
class="text-white font-semibold text-sm sm:text-[1rem] text-end bg-default"
>Previous</th
>
<th
class="text-white font-semibold text-sm sm:text-[1rem] text-end bg-default"
>Forecast</th
>
<th
class="text-white font-semibold text-sm sm:text-[1rem] text-end bg-default"
>Actual</th
>
<th
class="text-white font-semibold text-sm sm:text-[1rem] text-end bg-default"
>% Price Change</th
>
</tr>
</thead>
<tbody>
{#each showFullStats ? tableList?.slice(0, 10) : tableList?.slice(0, 3) as item, index}
<tr
class="border-y border-gray-800 odd:bg-odd {index === 2 &&
!showFullStats &&
tableList?.length > 3
? 'opacity-[0.5]'
: ''} sm:hover:bg-[#245073] sm:hover:bg-opacity-[0.2] bg-default border-b-[#09090B]"
>
<td
class="text-white font-medium text-sm sm:text-[1rem] whitespace-nowrap"
>
{new Date(item?.date ?? null)?.toLocaleString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
})}
</td>
<td
class="text-white text-end font-medium text-sm sm:text-[1rem] whitespace-nowrap"
>
{item?.previous}%
</td>
<td
class="text-white text-end font-medium text-sm sm:text-[1rem] whitespace-nowrap"
>
{item?.estimate}%
</td>
<td
class="text-white text-end font-medium text-sm sm:text-[1rem] whitespace-nowrap"
>
{item?.actual}%
</td>
<td
class="text-white font-normal text-end text-sm sm:text-[1rem] whitespace-nowrap"
>
{#if item?.changePercentage >= 0}
<span class="text-[#00FC50]"
>+{item?.changePercentage?.toFixed(2)}%</span
>
{:else}
<span class="text-[#FF2F1F]"
>{item?.changePercentage?.toFixed(2)}%
</span>
{/if}
</td>
</tr>
{/each}
</tbody>
</table>
</div>
<label
on:click={() => (showFullStats = !showFullStats)}
class="cursor-pointer flex justify-center items-center mt-5"
>
<svg
class="w-10 h-10 transform {showFullStats ? 'rotate-180' : ''} "
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><path
fill="#2A323C"
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10s10-4.48 10-10S17.52 2 12 2zm0 13.5L7.5 11l1.42-1.41L12 12.67l3.08-3.08L16.5 11L12 15.5z"
/></svg
>
</label>
</div>
{:else}
<h2
class="mt-10 mb-5 flex justify-center items-center text-3xl font-bold text-slate-700 m-auto"
>
No data available
<svg
class="w-10 sm:w-12 inline-block"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><path
fill="#334155"
d="M18.68 12.32a4.49 4.49 0 0 0-6.36.01a4.49 4.49 0 0 0 0 6.36a4.508 4.508 0 0 0 5.57.63L21 22.39L22.39 21l-3.09-3.11c1.13-1.77.87-4.09-.62-5.57m-1.41 4.95c-.98.98-2.56.97-3.54 0c-.97-.98-.97-2.56.01-3.54c.97-.97 2.55-.97 3.53 0c.97.98.97 2.56 0 3.54M10.9 20.1a6.527 6.527 0 0 1-1.48-2.32C6.27 17.25 4 15.76 4 14v3c0 2.21 3.58 4 8 4c-.4-.26-.77-.56-1.1-.9M4 9v3c0 1.68 2.07 3.12 5 3.7v-.2c0-.93.2-1.85.58-2.69C6.34 12.3 4 10.79 4 9m8-6C7.58 3 4 4.79 4 7c0 2 3 3.68 6.85 4h.05c1.2-1.26 2.86-2 4.6-2c.91 0 1.81.19 2.64.56A3.215 3.215 0 0 0 20 7c0-2.21-3.58-4-8-4Z"
/></svg
>
</h2>
{/if}
{:else}
<div class="flex justify-center items-center h-80">
<div class="relative">
<label
class="bg-secondary rounded-md h-14 w-14 flex justify-center items-center absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
>
<span class="loading loading-spinner loading-md"></span>
</label>
</div>
</div>
{/if}
</main>
</section>
<style>
.app {
height: 300px;
max-width: 100%; /* Ensure chart width doesn't exceed the container */
}
@media (max-width: 640px) {
.app {
height: 230px;
}
}
.chart {
width: 100%;
}
</style>

View File

@ -172,7 +172,6 @@
const ticker = $assetType === "stock" ? $stockTicker : $etfTicker;
if (ticker) {
isLoaded = false;
console.log(rawData);
if (rawData?.length > 0) {
weightedFTD = (
(rawData?.slice(-1)?.at(0)?.failToDeliver /

View File

@ -1,266 +0,0 @@
<script lang="ts">
import {
governmentContractComponent,
displayCompanyName,
stockTicker,
screenWidth,
getCache,
setCache,
} from "$lib/store";
import InfoModal from "$lib/components/InfoModal.svelte";
import { Chart } from "svelte-echarts";
import { abbreviateNumber } from "$lib/utils";
import { init, use } from "echarts/core";
import { BarChart } from "echarts/charts";
import { GridComponent } from "echarts/components";
import { CanvasRenderer } from "echarts/renderers";
use([BarChart, GridComponent, CanvasRenderer]);
let isLoaded = false;
let rawData = [];
let optionsData;
let avgNumberOfContracts = 0;
let displayMaxContracts = 0;
let displayYear = "n/a";
let totalAmount;
let totalContract;
function getPlotOptions() {
let dates = [];
let amountList = [];
let numList = [];
rawData?.forEach((item) => {
const fiscalYear = "FY" + item?.year?.slice(2);
dates?.push(fiscalYear);
amountList?.push(item?.amount);
numList?.push(item?.numOfContracts);
});
totalContract = rawData?.reduce(
(sum, item) => sum + item?.numOfContracts,
0,
);
totalAmount = rawData?.reduce((sum, item) => sum + item?.amount, 0);
avgNumberOfContracts = Math.floor(totalContract / rawData?.length);
const { year: yearWithMaxContracts, numOfContracts: maxContracts } =
rawData?.reduce(
(max, contract) =>
contract?.numOfContracts > max?.numOfContracts ? contract : max,
rawData?.at(0),
);
displayYear = yearWithMaxContracts;
displayMaxContracts = maxContracts;
const option = {
silent: true,
tooltip: {
trigger: "axis",
hideDelay: 100, // Set the delay in milliseconds
},
animation: false,
grid: {
left: "2%",
right: $screenWidth < 640 ? "0%" : "2%",
bottom: "0%",
top: "10%",
containLabel: true,
},
xAxis: {
data: dates,
type: "category",
axisLabel: {
color: "#fff",
},
},
yAxis: [
{
type: "value",
splitLine: {
show: false, // Disable x-axis grid lines
},
axisLabel: {
show: false, // Hide y-axis labels
},
},
{
type: "value",
splitLine: {
show: false, // Disable x-axis grid lines
},
axisLabel: {
show: false, // Hide y-axis labels
},
position: "right",
},
],
series: [
{
name: "# of Contracts",
data: numList,
type: "line",
yAxisIndex: 1,
itemStyle: {
color: "#fff", // Change bar color to white
},
},
{
name: "Amount",
data: amountList,
type: "bar",
itemStyle: {
color: "#fff", // Change bar color to orange
},
},
],
};
return option;
}
const getGovernmentContract = async (ticker) => {
const cachedData = getCache(ticker, "getGovernmentContract");
if (cachedData) {
rawData = cachedData;
} else {
const postData = { ticker: ticker, path: "government-contract" };
const response = await fetch("/api/ticker-data", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
});
rawData = await response?.json();
setCache(ticker, rawData, "getGovernmentContract");
}
governmentContractComponent.set(rawData?.length !== 0);
};
$: {
if ($stockTicker && typeof window !== "undefined") {
isLoaded = false;
const ticker = $stockTicker;
const asyncFunctions = [getGovernmentContract(ticker)];
Promise.all(asyncFunctions)
.then(() => {
if (rawData?.length !== 0) {
optionsData = getPlotOptions();
}
})
.catch((error) => {
console.error("An error occurred:", error);
});
isLoaded = true;
}
}
</script>
<section class="overflow-hidden text-white h-full pb-8">
<main class="overflow-hidden">
<div class="flex flex-row items-center">
<label
for="governmentContractInfo"
class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-3xl font-bold"
>
US Government Contract
</label>
<InfoModal
title={"Government Contract"}
content={"Government contracts are agreements between the local government and companies for goods or services. They can be substantial revenue sources for companies, particularly in sectors like defense, technology, and infrastructure. Winning contracts can enhance a company's stability and credibility, but it often involves competitive bidding and compliance with strict regulations."}
id={"governmentContractInfo"}
/>
</div>
{#if isLoaded}
{#if rawData?.length !== 0}
<div class="w-full flex flex-col items-start">
<div class="text-white text-sm sm:text-[1rem] mt-2 mb-2 w-full">
Since 2015, {$displayCompanyName} has secured a total of {totalContract}
government contracts, amassing {abbreviateNumber(totalAmount, true)}
in revenue. The company has averaged {avgNumberOfContracts} contracts
per year, with a peak of {displayMaxContracts} contracts in {displayYear}.
</div>
</div>
<div class="pb-2 rounded-md bg-default">
<div class="app w-full h-[300px] mt-5">
<Chart {init} options={optionsData} class="chart" />
</div>
</div>
<div
class="flex flex-row items-center justify-between mx-auto mt-5 w-full sm:w-11/12"
>
<div
class="mt-3.5 sm:mt-0 flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center"
>
<div
class="h-full transform -translate-x-1/2"
aria-hidden="true"
></div>
<div
class="w-3 h-3 bg-[#fff] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2"
aria-hidden="true"
></div>
<span
class="mt-2 sm:mt-0 text-white text-center sm:text-start text-xs sm:text-md inline-block"
>
# of Contracts
</span>
</div>
<div
class="flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center"
>
<div
class="h-full transform -translate-x-1/2"
aria-hidden="true"
></div>
<div
class="w-3 h-3 bg-[#FFAD24] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2"
aria-hidden="true"
></div>
<span
class="mt-2 sm:mt-0 text-white text-xs sm:text-md sm:font-medium inline-block"
>
Amount
</span>
</div>
</div>
{/if}
{:else}
<div class="flex justify-center items-center h-80">
<div class="relative">
<label
class="bg-secondary rounded-md h-14 w-14 flex justify-center items-center absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
>
<span class="loading loading-spinner loading-md text-gray-400"
></span>
</label>
</div>
</div>
{/if}
</main>
</section>
<style>
.app {
height: 300px;
max-width: 100%; /* Ensure chart width doesn't exceed the container */
}
@media (max-width: 640px) {
.app {
height: 210px;
}
}
.chart {
width: 100%;
}
</style>

View File

@ -1,258 +0,0 @@
<script lang 'ts'></script>
<section class="overflow-hidden text-white h-full pb-8">
<main class="overflow-hidden">
<div class="flex flex-row items-center">
<label
for="marketMakerInfo"
class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-3xl font-bold"
>
Market Maker Activity
</label>
<InfoModal
title={"Market Maker Activity"}
content={"Market makers provide liquidity by quoting buy and sell prices, stabilizing markets. For retail traders, understanding this helps navigate tight spreads, execute trades effectively, and gauge market sentiment."}
id={"marketMakerInfo"}
/>
</div>
{#if isLoaded}
{#if historyData?.length !== 0}
<div class="w-full flex flex-col items-start">
<div class="text-white text-[1rem] mt-2 mb-2 w-full">
Over the past year, {$displayCompanyName} has seen a weekly average of
<span class="font-semibold">{abbreviateNumber(avgTradeCount)}</span>
trades, involving an average of
<span class="font-semibold"
>{abbreviateNumber(avgShareQuantity)}</span
>
shares bought and sold. This activity sums up to an average total
notional value of
<span class="font-semibold"
>{abbreviateNumber(avgNotionalSum, true)}</span
>.
</div>
</div>
<div class="pb-2 rounded-md bg-default">
<div class="app w-full h-[300px] mt-5">
<Chart {init} options={optionsData} class="chart" />
</div>
</div>
<div
class="flex flex-row items-center justify-between mx-auto mt-5 w-full sm:w-11/12"
>
<div
class="mt-3.5 sm:mt-0 flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center"
>
<div
class="h-full transform -translate-x-1/2"
aria-hidden="true"
></div>
<div
class="w-3 h-3 bg-[#fff] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2"
aria-hidden="true"
></div>
<span
class="mt-2 sm:mt-0 text-white text-center sm:text-start text-xs sm:text-md inline-block"
>
Notional Sum
</span>
</div>
<div
class="flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center"
>
<div
class="h-full transform -translate-x-1/2"
aria-hidden="true"
></div>
<div
class="w-3 h-3 bg-[#3B82F6] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2"
aria-hidden="true"
></div>
<span
class="mt-2 sm:mt-0 text-white text-xs sm:text-md sm:font-medium inline-block"
>
Share Quantity
</span>
</div>
</div>
<h2
class="mt-10 mr-1 flex flex-row items-center text-white text-xl sm:text-2xl font-bold mb-3"
>
Latest Information
</h2>
<div class="flex justify-start items-center w-full m-auto">
<table class="w-full" data-test="statistics-table">
<tbody>
<tr class="border-y border-gray-800 odd:bg-odd">
<td class="px-[5px] py-1.5 xs:px-2.5 xs:py-2">
<span>Date</span>
</td>
<td
class="px-[5px] py-1.5 text-right whitespace-nowrap font-medium xs:px-2.5 xs:py-2"
>
{formatDateRange(historyData?.slice(-1)?.at(0)?.date)}
</td>
</tr>
<tr class="border-y border-gray-800 odd:bg-odd">
<td class="px-[5px] py-1.5 xs:px-2.5 xs:py-2">
<span>Total Notional Sum</span>
</td>
<td
class="px-[5px] py-1.5 text-right font-medium xs:px-2.5 xs:py-2"
>
${abbreviateNumber(
historyData?.slice(-1)?.at(0)?.totalNotionalSum,
)}
</td>
</tr>
<tr class="border-y border-gray-800 odd:bg-odd">
<td class="px-[5px] py-1.5 xs:px-2.5 xs:py-2">
<span>Total Trade Count</span>
</td>
<td
class="px-[5px] py-1.5 text-right font-medium xs:px-2.5 xs:py-2"
>
{abbreviateNumber(
historyData?.slice(-1)?.at(0)?.totalWeeklyTradeCount,
)}
</td>
</tr>
<tr class="border-y border-gray-800 odd:bg-odd">
<td class="px-[5px] py-1.5 xs:px-2.5 xs:py-2">
<span>Total Share Quantity</span>
</td>
<td
class="px-[5px] py-1.5 text-right font-medium xs:px-2.5 xs:py-2"
>
{abbreviateNumber(
historyData?.slice(-1)?.at(0)?.totalWeeklyShareQuantity,
)}
</td>
</tr>
</tbody>
</table>
</div>
<h3
class="mt-10 mr-1 flex flex-row items-center text-white text-xl sm:text-2xl font-bold mb-3"
>
Top 10 Market Makers Activity
</h3>
These market makers represent the highest average trading activity for {$displayCompanyName}
over the past 12 months, calculated on a weekly basis.
<div
class="flex justify-start items-center w-full m-auto mt-6 overflow-x-scroll no-scrollbar"
>
<table class="table table-sm table-compact w-full">
<thead>
<tr class="">
<th
class="text-white shadow-md font-semibold text-sm text-start bg-default"
>Name</th
>
<th
class="text-white shadow-md font-semibold text-sm text-end bg-default"
>Trade Count</th
>
<th
class="text-white shadow-md font-semibold text-sm text-end bg-default"
>Share Quantity</th
>
<th
class="text-white shadow-md font-semibold text-sm text-end bg-default"
>Notional Sum</th
>
</tr>
</thead>
<tbody>
{#each showFullStats ? topMarketMakers?.slice(0, 10) : topMarketMakers?.slice(0, 3) as item, index}
<tr
class="border-y border-gray-800 odd:bg-odd {index ===
2 &&
!showFullStats &&
topMarketMakers?.length > 3
? 'opacity-[0.5]'
: ''} sm:hover:bg-[#245073] sm:hover:bg-opacity-[0.2] bg-default border-b-[#09090B]"
>
<td
class="text-white text-sm sm:text-[1rem] font-medium whitespace-nowrap"
>
{item?.name?.length > charNumber
? formatString(item?.name?.slice(0, charNumber)) + "..."
: formatString(item?.name)}
</td>
<td
class="text-white text-end text-sm sm:text-[1rem] font-medium whitespace-nowrap"
>
{abbreviateNumber(Math.floor(item?.avgWeeklyTradeCount))}
</td>
<td
class="text-white text-end text-sm sm:text-[1rem] font-medium whitespace-nowrap"
>
{abbreviateNumber(Math.floor(item?.avgWeeklyShareQuantity))}
</td>
<td
class="text-white text-end text-sm sm:text-[1rem] font-medium whitespace-nowrap"
>
{abbreviateNumber(item?.avgNotionalSum, true)}
</td>
</tr>
{/each}
</tbody>
</table>
</div>
<label
on:click={() => (showFullStats = !showFullStats)}
class="cursor-pointer flex justify-center items-center mt-5"
>
<svg
class="w-10 h-10 transform {showFullStats ? 'rotate-180' : ''} "
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><path
fill="#2A323C"
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10s10-4.48 10-10S17.52 2 12 2zm0 13.5L7.5 11l1.42-1.41L12 12.67l3.08-3.08L16.5 11L12 15.5z"
/></svg
>
</label>
{/if}
{:else}
<div class="flex justify-center items-center h-80">
<div class="relative">
<label
class="bg-secondary rounded-md h-14 w-14 flex justify-center items-center absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
>
<span class="loading loading-spinner loading-md"></span>
</label>
</div>
</div>
{/if}
</main>
</section>
<style>
.app {
height: 300px;
max-width: 100%; /* Ensure chart width doesn't exceed the container */
}
@media (max-width: 640px) {
.app {
height: 210px;
}
}
.chart {
width: 100%;
}
</style>

View File

@ -1,135 +0,0 @@
<script lang="ts">
import { isOpen, screenWidth } from "$lib/store";
</script>
<div class="flex flex-row items-center">
<label for={$screenWidth > 640 ? "marketHour" : ""} class="cursor-pointer">
{#if $isOpen}
<div
class="sm:border sm:border-gray-800 b sm:hover:bg-[#333333] sm:ease-in-out sm:duration-100 rounded-2xl pl-3 pr-3 pt-1 pb-1 flex flex-row items-center"
>
<span class="text-xs py-0.5 text-slate-200 font-semibold">
Market Open
</span>
<span class="relative flex h-1.5 w-1.5 sm:h-2 sm:w-2 ml-2">
<span
class="animate-ping absolute inline-flex h-full w-full rounded-full bg-[#00FC50] opacity-75"
></span>
<span
class="relative inline-flex rounded-full h-1.5 w-1.5 sm:h-2 sm:w-2 bg-[#00FC50]"
></span>
</span>
</div>
{:else}
<div
class="sm:border sm:border-gray-800 sm:hover:bg-[#333333] sm:ease-in-out sm:duration-100 rounded-2xl pl-3 pr-3 pt-1 pb-1 flex flex-row items-center"
>
<span class="text-xs py-0.5 text-slate-200 font-semibold">
Market Closed
</span>
<span class="relative flex h-1.5 w-1.5 sm:h-2 sm:w-2 ml-2">
<span
class="animate-ping absolute inline-flex h-full w-full rounded-full bg-[#FF2F1F] opacity-75"
></span>
<span
class="relative inline-flex rounded-full h-1.5 w-1.5 sm:h-2 sm:w-2 bg-[#FF2F1F]"
></span>
</span>
</div>
{/if}
</label>
</div>
<!-- Put this part before </body> tag -->
<input type="checkbox" id="marketHour" class="modal-toggle" />
<label
for="marketHour"
class="modal modal-bottom sm:modal-middle cursor-pointer"
>
<label for="marketHour" class="cursor-pointer modal-backdrop"></label>
<!-- svelte-ignore a11y-label-has-associated-control -->
<label
class="modal-box w-full relative bg-primary border border-gray-800 h-auto"
>
<label
for="marketHour"
class="cursor-pointer absolute right-5 top-2 bg-primary text-2xl text-white"
>
</label>
<h3 class="text-2xl font-bold text-white">Opening Hours</h3>
<p class="py-4 text-gray-200 bg-primary w-full">
The New York Stock Exchange, one of the largest stock exchanges in the
world, operates on a regular trading schedule. Its trading hours are from <span
class="text-white font-semibold underline">9:30</span
>
ET in the morning to
<span class="text-white font-semibold underline">16:00</span>
ET in the afternoon. Monday through Friday.
<br />
<br />
The market is closed on the following holidays:
</p>
<table
class="table table-sm table-compact bg-primary w-full mt-5 mb-10 text-white"
>
<!-- head -->
<thead>
<tr class="border-b border-slate-700 odd:bg-odd">
<th class="bg-primary text-white text-sm font-semibold">
Exchange holidays
</th>
<th class="bg-primary text-white text-sm font-semibold"> Date </th>
</tr>
</thead>
<tbody>
<!-- row 1 -->
<tr class="border-b border-slate-700 odd:bg-odd">
<td class="font-semibold"> New Years Day</td>
<td class="">01.01.2024</td>
</tr>
<!-- row 2 -->
<tr class="border-b border-slate-700 odd:bg-odd">
<td class="font-semibold">Martin Luther King, Jr. Day</td>
<td class="">15.01.2024</td>
</tr>
<tr class="border-b border-slate-700 odd:bg-odd">
<td class="font-semibold">Washington's Birthday</td>
<td class="">19.02.2024</td>
</tr>
<tr class="border-b border-slate-700 odd:bg-odd">
<td class="font-semibold"> Good Friday </td>
<td class="bg-primary">29.03.2024</td>
</tr>
<tr class="border-b border-slate-700 odd:bg-odd">
<td class="font-semibold">Memorial Day</td>
<td class="">27.05.2024</td>
</tr>
<tr class="border-b border-slate-700 odd:bg-odd">
<td class="font-semibold">Juneteenth National Independence Day</td>
<td class="">19.06.2024</td>
</tr>
<tr class="border-b border-slate-700 odd:bg-odd">
<td class="font-semibold">Independence Day</td>
<td class="">04.07.2024</td>
</tr>
<tr class="border-b border-slate-700 odd:bg-odd">
<td class="font-semibold">Labor Day</td>
<td class="">02.09.2024</td>
</tr>
<tr class="border-b border-slate-700 odd:bg-odd">
<td class="font-semibold">Thanksgiving Day</td>
<td class="">28.11.2024</td>
</tr>
<tr class="odd:bg-odd">
<td class="font-semibold">Christmas</td>
<td class="">25.12.2024</td>
</tr>
</tbody>
</table>
</label>
</label>

View File

@ -1,378 +0,0 @@
<script lang="ts">
import { onMount, onDestroy } from "svelte";
import toast from "svelte-french-toast";
import { commentAdded } from "$lib/store";
import { pb } from "$lib/pocketbase";
export let data;
export let postId = "";
let post = data?.getOnePost;
let imageId = "image-input";
let expandField = false;
//let characterCount = 0;
let inputValue = "";
let imageInput = "";
let imageComment;
const showPreview = (event) => {
const target = event.target;
const files = target.files;
if (files.length > 0) {
const src = URL.createObjectURL(files[0]);
const preview = document.getElementById("image-preview");
preview.src = src;
}
};
const createComment = async (event) => {
event.preventDefault(); // prevent the default form submission behavior
let output;
let newComment;
const userId = data?.user?.id;
if (inputValue.length !== 0) {
const postData = {
comment: inputValue,
post: postId,
image: imageInput.length !== 0 ? imageInput[0] : "",
user: userId,
};
/*
const response = await fetch(fastifyURL+'/create-comment', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(postData),
}); // make a POST request to the server with the FormData object
output = await response.json()
}
*/
//Create comment in User Browser because i need to learn how to transfer images.
try {
newComment = await pb.collection("comments").create(postData);
newComment = await pb.collection("comments").getOne(newComment?.id, {
expand: `user`,
});
//User always upvotes their post in the intial state
await pb.collection("comments").update(newComment.id, {
"vote+": 1,
});
const opPost = await pb.collection("posts").getOne(postId);
//create new record for notifications collections
if (userId !== opPost?.user) {
let formDataNotifications = new FormData();
formDataNotifications.append("opUser", opPost?.user);
formDataNotifications.append("user", userId);
formDataNotifications.append("post", postId);
formDataNotifications.append("comment", newComment?.id);
formDataNotifications.append("notifyType", "comment");
await pb.collection("notifications").create(formDataNotifications);
}
output = "success";
} catch (e) {
console.log(e);
output = "failure";
}
if (output === "success") {
toast.success("Commented successfully", {
style: "border-radius: 200px; background: #2A2E39; color: #fff;",
});
$commentAdded = newComment;
//console.log("comment added: ", $commentAdded)
inputValue = "";
imageInput = "";
handleCancel();
} else {
toast.error("Something went wrong. Please try again...", {
style: "border-radius: 200px; background: #2A2E39; color: #fff;",
});
}
}
};
function handleInput(event) {
inputValue = event.target.value;
const textarea = event.target;
textarea.style.height = "auto";
textarea.style.height = Math.min(textarea.scrollHeight, 140) + "px";
}
function handleCancel() {
if (editor) {
editor.innerHTML = "";
}
inputValue = "";
imageInput = "";
const closePopup = document.getElementById("mobileTextEditorModal");
closePopup?.dispatchEvent(new MouseEvent("click"));
const textarea = document.querySelector("textarea");
textarea.style.height = "36px"; // 48px are h-12 in tailwindcss
}
function handleImageInput(event) {
imageInput = event.target.files;
expandField = true;
}
const toolbarOptions = {
container: [
["bold", "italic", "underline", "strike"],
[{ list: "ordered" }, { list: "bullet" }],
["link"],
[{ header: [1, 2, 3, 4, 5, 6, false] }],
],
};
let quill;
let editor;
const regex = /^(\s*<p><br><\/p>\s*)*$/;
let loadingEditor = false;
onMount(async () => {
const { default: Quill } = await import("quill");
quill = new Quill(".quill-editor", {
modules: {
toolbar: toolbarOptions,
},
theme: "snow",
placeholder: "Leave a comment...",
keepFocus: true,
});
quill.on("text-change", function () {
editor = quill.root;
// Add text-4xl class to all h1 elements
editor.querySelectorAll("h1").forEach((h1) => {
h1.classList.add("text-4xl");
});
editor.querySelectorAll("h2").forEach((h2) => {
h2.classList.add("text-3xl");
});
editor.querySelectorAll("h3").forEach((h3) => {
h3.classList.add("text-xl");
});
editor.querySelectorAll("h4").forEach((h4) => {
h4.classList.add("text-md");
});
editor.querySelectorAll("h5").forEach((h5) => {
h5.classList.add("text-sm");
});
editor.querySelectorAll("h6").forEach((h6) => {
h6.classList.add("text-xs");
});
editor.querySelectorAll("a").forEach((a) => {
a.classList.add("text-blue-400", "hover:text-white", "underline");
});
editor.querySelectorAll("ol").forEach((ol) => {
ol.classList.add("list-decimal", "ml-10", "text-sm");
});
editor.querySelectorAll("ul").forEach((ul) => {
ul.classList.add("list-disc", "ml-10", "text-sm");
});
/*
editor.querySelectorAll('pre').forEach((pre) => {
pre.classList.add('bg-[#23241F]', 'w-auto','h-auto','max-w-6xl','breaks-all');
});
*/
const contents = editor.innerHTML;
inputValue = contents;
console.log(inputValue);
if (regex?.test(inputValue)) {
editor.innerHTML = "";
inputValue = "";
}
//console.log(inputValue)
// Force Svelte to update the DOM
$: {
}
});
loadingEditor = true;
});
onDestroy(() => {
if (quill) {
quill.off("text-change");
quill = null;
}
});
1;
</script>
<svelte:head>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0"
/>
</svelte:head>
<div class="drawer drawer-end overflow-hidden" style="z-index: 9999;">
<input id="mobileTextEditorModal" type="checkbox" class="drawer-toggle" />
<div class="drawer-side overflow-hidden">
<div
class="rounded-xl bg-[#000] min-h-screen w-screen pb-20 overflow-y-auto overflow-hidden"
>
<!--
<h1 class="text-white sm:hidden font-medium text-xl mt-10 pl-2">
{@html post?.title}
</h1>
-->
<h1 class="text-white text-lg pl-7 pt-16">Share your opinion</h1>
<!--Start Quill Editor-->
<div class="{!loadingEditor ? 'hidden' : ''} mt-3 pl-7 pr-7">
<div
class="quill-editor min-h-[96px] h-[120px] resize-none focus-none ring-none rounded-none bg-secondary text-white"
>
<select class="ql-header" aria-label="Header" title="Header">
<option selected></option>
</select>
</div>
</div>
<textarea
placeholder="Loading editor..."
class="{loadingEditor
? 'hidden'
: ''} min-h-[96px] h-[120px] text-sm italic w-full resize-none focus-none ring-none rounded-none bg-secondary text-white"
></textarea>
<!--End Quill Editor-->
<textarea
class="hidden"
type="text"
value={inputValue}
on:input={handleInput}
/>
<div class="flex flex-row justify-start mt-4 pb-16 pl-7 pr-7">
<div class="relative">
<label
for={imageId}
class="{imageInput.length !== 0
? 'hidden'
: ''} inline-flex mr-auto items-center py-2.5 px-4 text-xs font-medium text-center text-white rounded-md hover:bg-gray-800 cursor-pointer"
>
<svg
class="w-5 h-5 inline-block"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g
id="SVGRepo_tracerCarrier"
stroke-linecap="round"
stroke-linejoin="round"
></g><g id="SVGRepo_iconCarrier">
<path
opacity="0.4"
d="M22.0206 16.8198L18.8906 9.49978C18.3206 8.15978 17.4706 7.39978 16.5006 7.34978C15.5406 7.29978 14.6106 7.96978 13.9006 9.24978L12.0006 12.6598C11.6006 13.3798 11.0306 13.8098 10.4106 13.8598C9.78063 13.9198 9.15063 13.5898 8.64063 12.9398L8.42063 12.6598C7.71063 11.7698 6.83063 11.3398 5.93063 11.4298C5.03063 11.5198 4.26063 12.1398 3.75063 13.1498L2.02063 16.5998C1.40063 17.8498 1.46063 19.2998 2.19063 20.4798C2.92063 21.6598 4.19063 22.3698 5.58063 22.3698H18.3406C19.6806 22.3698 20.9306 21.6998 21.6706 20.5798C22.4306 19.4598 22.5506 18.0498 22.0206 16.8198Z"
fill="#A6ADBB"
></path>
<path
d="M6.96984 8.38012C8.83657 8.38012 10.3498 6.86684 10.3498 5.00012C10.3498 3.13339 8.83657 1.62012 6.96984 1.62012C5.10312 1.62012 3.58984 3.13339 3.58984 5.00012C3.58984 6.86684 5.10312 8.38012 6.96984 8.38012Z"
fill="#E5E7EB"
></path>
</g></svg
>
<input
class="hidden rounded text-gray-200"
type="file"
id={imageId}
name={imageId}
on:input={handleImageInput}
on:change={showPreview}
/>
</label>
{#if imageInput.length !== 0}
<div class="ml-2">
<label
on:click={() => (imageInput = "")}
class="btn btn-xs btn-circle bg-red-600 absolute -top-2 -right-2"
>
</label>
<img
class="object-contain w-10 h-10 mx-auto rounded"
alt="Image preview"
id="image-preview"
/>
</div>
{/if}
</div>
<div class="relative flex justify-end items-center ml-auto mr-2">
<label
on:click={handleCancel}
class="inline-flex justify-end items-center py-2.5 px-4 text-xs font-medium text-center text-white rounded-md hover:bg-gray-800 cursor-pointer mr-3"
>
Cancel
</label>
<label
on:click={createComment}
class="inline-flex justify-end items-center bg-[#fff] {inputValue.length !==
0
? 'opacity-100 cursor-pointer'
: 'opacity-60'} py-2.5 px-4 text-xs font-medium text-center text-white rounded-md focus:ring-purple-300"
>
Post
</label>
</div>
</div>
<label on:click={() => handleCancel()} class="absolute left-6 top-4">
<svg
class="w-6 h-6 inline-block mb-0.5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><path
fill="#fff"
d="M9.125 21.1L.7 12.7q-.15-.15-.213-.325T.425 12q0-.2.063-.375T.7 11.3l8.425-8.425q.35-.35.875-.35t.9.375q.375.375.375.875t-.375.875L3.55 12l7.35 7.35q.35.35.35.863t-.375.887q-.375.375-.875.375t-.875-.375Z"
/></svg
>
<span class="text-white text-md font-medium"> Return </span>
</label>
</div>
</div>
</div>
<style>
@import "/src/lib/assets/style_quill.css";
.quill-editor,
textarea {
touch-action: none; /* or 'none' if manipulation doesn't work */
}
</style>

View File

@ -1,214 +0,0 @@
<script lang="ts">
import { onMount } from "svelte";
import InfoModal from "$lib/components/InfoModal.svelte";
import { Chart } from "svelte-echarts";
import { init, use } from "echarts/core";
import { BarChart } from "echarts/charts";
import { GridComponent, TooltipComponent } from "echarts/components";
import { CanvasRenderer } from "echarts/renderers";
import { monthNames } from "$lib/utils";
export let rawData: Array<{
date: string;
price: number;
netCall: number;
netPut: number;
}>;
use([BarChart, GridComponent, TooltipComponent, CanvasRenderer]);
let isLoaded = false;
let optionsData;
let sentiment;
function getPlotOptions() {
const dates = [];
const priceList = [];
const netCallList = [];
const netPutList = [];
if (rawData && rawData.length) {
// Populate arrays with data
rawData.forEach((item) => {
dates.push(item.date);
priceList.push(item.price);
netCallList.push(item.netCall);
netPutList.push(item.netPut);
});
// Determine sentiment
sentiment =
netCallList.at(-1) > netPutList.at(-1) ? "bullish" : "bearish";
return {
silent: true,
tooltip: {
trigger: "axis",
hideDelay: 100, // Tooltip delay in milliseconds
},
animation: false,
grid: {
left: "3%",
right: "3%",
bottom: "0%",
top: "10%",
containLabel: true,
},
xAxis: {
type: "category",
boundaryGap: false,
data: dates,
axisLabel: {
color: "#fff",
formatter: (value, index) => {
if (index % 2 === 0) {
const [year, month, day] = value.split("-");
return `${day} ${monthNames[parseInt(month, 10) - 1]}`;
}
return "";
},
},
},
yAxis: [
{
type: "value",
splitLine: { show: false },
axisLabel: { show: false },
},
],
series: [
{
name: "Net Call",
data: netCallList,
type: "bar",
stack: "NetFlow",
itemStyle: { color: "#2256FF" },
showSymbol: false,
},
{
name: "Net Put",
data: netPutList,
type: "bar",
stack: "NetFlow",
itemStyle: { color: "#FF2256" },
showSymbol: false,
},
],
};
} else {
console.warn("No raw data available to populate chart options");
return {};
}
}
onMount(() => {
optionsData = getPlotOptions();
isLoaded = true;
});
</script>
<section class="overflow-hidden text-white h-full pb-8">
<main class="overflow-hidden">
<div class="flex flex-row items-center">
<label
for="optionsNetFlowInfo"
class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-2xl font-bold"
>
Options Net Flow
</label>
<InfoModal
title={"Options Net Flow"}
content={"An Options Net Flow of XY% means the market expects significant price fluctuations for the stock, with an annualized potential range of ±XY% from its current price. This indicates high uncertainty and risk, leading to more expensive options but doesn't predict price direction."}
id={"optionsNetFlowInfo"}
/>
</div>
{#if isLoaded}
{#if rawData?.length !== 0}
<div class="w-full flex flex-col items-start">
<div class="text-white text-[1rem] mt-2 mb-2 w-full">
The options net flow demonstrates a {sentiment} trend in the last 2 trading
hours, characterized by the {sentiment === "bullish"
? "Net Call Flow exceeding the Net Put Flow"
: "Net Put Flow exceeding the Net Call Flow"}.
</div>
</div>
<div class="pb-2 rounded-md bg-default">
<div class="app w-full h-[300px] mt-5">
<Chart {init} options={optionsData} class="chart" />
</div>
</div>
<div
class="flex flex-row items-center justify-between mx-auto mt-5 w-full sm:w-11/12"
>
<div
class="flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center"
>
<div
class="h-full transform -translate-x-1/2"
aria-hidden="true"
></div>
<div
class="w-3 h-3 bg-[#2256FF] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2"
aria-hidden="true"
></div>
<span
class="mt-2 sm:mt-0 text-white text-xs sm:text-md sm:font-medium inline-block"
>
Net Call
</span>
</div>
<div
class="flex flex-col sm:flex-row items-center ml-3 sm:ml-0 w-1/2 justify-center"
>
<div
class="h-full transform -translate-x-1/2"
aria-hidden="true"
></div>
<div
class="w-3 h-3 bg-[#FF2F1F] border-4 box-content border-[#27272A] rounded-full transform sm:-translate-x-1/2"
aria-hidden="true"
></div>
<span
class="mt-2 sm:mt-0 text-white text-xs sm:text-md sm:font-medium inline-block"
>
Net Put
</span>
</div>
</div>
{/if}
{:else}
<div class="flex justify-center items-center h-80">
<div class="relative">
<label
class="bg-secondary rounded-md h-14 w-14 flex justify-center items-center absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
>
<span class="loading loading-spinner loading-md text-gray-400"
></span>
</label>
</div>
</div>
{/if}
</main>
</section>
<style>
.app {
height: 300px;
max-width: 100%; /* Ensure chart width doesn't exceed the container */
}
@media (max-width: 640px) {
.app {
height: 210px;
}
}
.chart {
width: 100%;
}
</style>

View File

@ -1,233 +0,0 @@
<script lang='ts'>
import {stockTicker, etfTicker, cryptoTicker, assetType} from '$lib/store';
import { Chart } from 'svelte-echarts'
import { init, use } from 'echarts/core'
import { BarChart } from 'echarts/charts'
import { GridComponent, TooltipComponent } from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers'
use([BarChart, GridComponent, TooltipComponent, CanvasRenderer])
export let quantData;
let firstElementKey;
let monthlyReturnsData;
let benchmarkMonthlyReturnsData;
const plotAnnualReturn = () => {
const annualReturnList = [];
const annualReturnListBenchmark = [];
// Find the maximum year in monthlyReturnsData
// Find the maximum year in monthlyReturnsData
let maxYear = Math?.max(...Object?.keys(monthlyReturnsData)?.map(year => parseInt(year)));
// Iterate over the last 4 years
for (let year = maxYear; year > maxYear - 4; year--) {
if (monthlyReturnsData.hasOwnProperty(year.toString())) {
const values = monthlyReturnsData[year.toString()];
annualReturnList.push(values[values.length - 1]);
}
}
// Find the maximum year in benchmarkMonthlyReturnsData
maxYear = Math?.max(...Object?.keys(benchmarkMonthlyReturnsData)?.map(year => parseInt(year)));
// Iterate over the last 4 years
for (let year = maxYear; year > maxYear - 4; year--) {
if (benchmarkMonthlyReturnsData.hasOwnProperty(year.toString())) {
const values = benchmarkMonthlyReturnsData[year.toString()];
annualReturnListBenchmark.push(values[values.length - 1]);
}
}
const options = {
silent: true,
animation: false,
tooltip: {
trigger: 'axis',
hideDelay: 100, // Set the delay in milliseconds
},
grid: {
left: "0%",
right: "0%",
bottom: '0%',
height: "90%",
containLabel: true,
},
xAxis: {
data: ['2020', '2021', '2022', '2023','2024'],
type: 'category',
axisTick: {
alignWithLabel: true,
},
axisLabel: {
color: '#fff',
}
},
yAxis: [
{
type: 'value',
splitLine: {
show: false, // Disable y-axis grid lines
},
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
}
}
}
},
{
type: 'value',
axisLabel: {
formatter: '{value} %', // Display value with a percent sign
},
splitLine: {
show: false, // Disable y-axis grid lines
},
},
],
series: [
{
name: $assetType === 'etf' ? $etfTicker : $assetType === 'crypto' ? $cryptoTicker : $stockTicker,
data: annualReturnList,
type: 'bar',
barWidth: '30%',
smooth: true,
itemStyle: {
color: '#398EC2',
},
},
{
name: 'S&P500',
data: annualReturnListBenchmark,
type: 'bar',
barWidth: '30%',
smooth: true,
itemStyle: {
color: '#FEDD78',
},
},
],
};
return options;
}
let optionsAnnualReturn;
$: {
if (($assetType === 'etf' ? $etfTicker : $assetType === 'crypto' ? $cryptoTicker : $stockTicker) && typeof window !== 'undefined')
{
try {
firstElementKey = Object?.keys(quantData)[0];
monthlyReturnsData = quantData[firstElementKey]['Monthly Return'];
benchmarkMonthlyReturnsData = quantData['SPY']['Monthly Return']
}
catch(e) {
monthlyReturnsData = [];
benchmarkMonthlyReturnsData = [];
}
optionsAnnualReturn = plotAnnualReturn()
}
}
</script>
<section class="mt-4 overflow-hidden text-white h-full">
<div class="w-full m-auto h-full overflow-hidden">
<main>
<div class="rounded-2xl pl-3 sm:pr-0 pt-3 w-full mt-4">
<h1 class="text-white m-auto font-bold text-lg text-center">
Annual Return (%)
</h1>
<div class="text-white flex flex-row items-center justify-center m-auto font-medium text-md text-center mt-3">
<div class="flex flex-row items-center">
<div class="h-full bg-gray-800 transform -translate-x-1/2 " aria-hidden="true"></div>
<div class="w-4 h-4 bg-[#3E9CD5] border-4 box-content border-gray-900 rounded-full transform -translate-x-1/2" aria-hidden="true"></div>
<span class="text-white font-medium inline-block">
{$assetType === 'etf' ? $etfTicker : $assetType === 'crypto' ? $cryptoTicker : $stockTicker}
</span>
</div>
<div class="flex flex-row items-center ml-10">
<div class="h-full bg-gray-800 transform -translate-x-1/2 " aria-hidden="true"></div>
<div class="w-4 h-4 bg-[#FFF384] border-4 box-content border-gray-900 rounded-full transform -translate-x-1/2" aria-hidden="true"></div>
<span class="text-white font-medium inline-block">
S&P500
</span>
</div>
</div>
<div class="app w-full h-[300px] mt-5 mb-16">
<Chart {init} options={optionsAnnualReturn} class="chart" />
</div>
</div>
</main>
</div>
</section>
<style>
.app {
height: 300px;
max-width: 100%; /* Ensure chart width doesn't exceed the container */
}
@media (max-width: 640px) {
.app {
height: 210px;
}
}
.chart {
width: 100%;
}
</style>

View File

@ -4,12 +4,12 @@
displayCompanyName,
stockTicker,
etfTicker,
cryptoTicker,
assetType,
getCache,
setCache,
} from "$lib/store";
import InfoModal from "$lib/components/InfoModal.svelte";
import Infobox from "$lib/components/Infobox.svelte";
import { Chart } from "svelte-echarts";
@ -19,6 +19,7 @@
import { CanvasRenderer } from "echarts/renderers";
export let data;
export let rawData = {};
use([LineChart, GridComponent, TooltipComponent, CanvasRenderer]);
@ -26,17 +27,18 @@
let rating: string | undefined;
let outlook: string | undefined;
let valueAtRisk: number | string | undefined;
let varDict: Record<string, any> = {};
let optionsData: any;
let monthlyVarAvg: string | undefined;
function getPlotOptions() {
const dates: string[] = [];
const varList: number[] = [];
const priceList = [];
varDict?.history?.forEach((item: { date: string; var: number }) => {
dates.push(item.date);
varList.push(item.var);
rawData?.history?.forEach((item: { date: string; var: number }) => {
dates.push(item?.date);
varList.push(item?.var);
priceList.push(item?.price);
});
const sum = varList.reduce((acc, curr) => acc + curr, 0);
@ -77,62 +79,44 @@
splitLine: { show: false },
axisLabel: { show: false },
},
{
type: "value",
splitLine: { show: false },
axisLabel: { show: false },
},
],
series: [
{
name: "VaR",
data: varList,
type: "line",
areaStyle: { opacity: 0.8 },
itemStyle: { color: "#E11D48" },
showSymbol: false,
},
{
name: "Stock Price",
data: priceList,
type: "line",
yAxisIndex: 1,
itemStyle: { color: "#fff" },
showSymbol: false,
},
],
};
return option;
}
const getVaR = async (ticker: string) => {
const cachedData = getCache(ticker, "getVaR");
if (cachedData) {
varDict = cachedData;
} else {
const postData = { ticker, path: "value-at-risk" };
const response = await fetch("/api/ticker-data", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(postData),
});
varDict = await response.json();
setCache(ticker, varDict, "getVaR");
}
$varComponent = Object.keys(varDict).length !== 0;
};
$: {
const ticker =
$assetType === "stock"
? $stockTicker
: $assetType === "etf"
? $etfTicker
: $cryptoTicker;
if (ticker && typeof window !== "undefined") {
const ticker = $assetType === "stock" ? $stockTicker : "etf";
if (ticker) {
isLoaded = false;
getVaR(ticker)
.then(() => {
rating = varDict.rating;
outlook = varDict.outlook;
valueAtRisk = varDict.history?.slice(-1)?.at(0)?.var ?? "n/a";
optionsData = getPlotOptions();
})
.catch((error) => console.error("An error occurred:", error))
.finally(() => {
isLoaded = true;
});
rating = rawData.rating;
outlook = rawData.outlook;
valueAtRisk = rawData.history?.slice(-1)?.at(0)?.var ?? "n/a";
optionsData = getPlotOptions();
}
}
</script>
@ -142,7 +126,7 @@
<div class="flex flex-row items-center">
<label
for="varInfo"
class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-3xl font-bold"
class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-2xl font-bold"
>
Value at Risk
</label>
@ -153,7 +137,7 @@
/>
</div>
{#if Object?.keys(varDict)?.length !== 0}
{#if Object?.keys(rawData)?.length !== 0}
<div class="pb-4 w-full mt-5">
<div
class="w-auto p-4 sm:p-6 bg-default sm:bg-default rounded-md relative"
@ -223,13 +207,7 @@
class="font-semibold">95%</span
>
probability that
<span class="text-blue-400"
>${$assetType === "stock"
? $stockTicker
: $assetType === "etf"
? $etfTicker
: $cryptoTicker}</span
>
{$assetType === "stock" ? $stockTicker : $etfTicker}
will incur a maximum loss of
<span class="text-[#FF2F1F] font-semibold">{valueAtRisk}%</span>
in the upcoming week.
@ -244,18 +222,15 @@
</h2>
<div class="text-white text-[1rem] mt-3">
Based on historical price data, the company experienced an average
monthly Value at Risk (VaR) of {monthlyVarAvg}%.
monthly Value-at-Risk <span class="font-semibold">{monthlyVarAvg}%</span
>.
</div>
<div class="app w-full h-[300px] mt-5">
<Chart {init} options={optionsData} class="chart" />
</div>
{:else}
<h2
class="mt-10 mb-5 flex justify-center items-center text-2xl font-bold text-slate-700 m-auto"
>
No data available
</h2>
<Infobox text="No data available" />
{/if}
</main>
</section>

View File

@ -1,145 +0,0 @@
<script lang='ts'>
import { onMount, onDestroy} from 'svelte';
import { browser } from '$app/environment';
import { v4 as uuidv4 } from 'uuid';
import { screenWidth } from '$lib/store';
let id = uuidv4();
export let src;
export let hideProgressbar = false;
let container;
let observer;
let video;
let progress = -1;
onMount(async () => {
video = document.getElementById(id);
if (typeof window !== 'undefined' && browser) {
const handleIntersect = (entries, observer) => {
entries.forEach((entry) => {
if (entry?.isIntersecting) {
//console.log("video id:", id, "should play now");
playVideo();
} else {
pauseVideo();
//console.log("video id:", id, "should stop now");
}
});
};
const options = { threshold: 0.4, rootMargin: '-0px 0px' };
observer = new IntersectionObserver(handleIntersect, options);
if (container instanceof Element) {
observer.observe(container);
}
}
});
function updateProgress() {
if (video && video?.duration && !video?.paused) {
const { currentTime, duration } = video;
progress = (currentTime / duration) * 100;
// Use decimal values for smoother progress
progress = parseFloat(progress.toFixed(2));
}
}
$: {
if (video) {
video.ontimeupdate = updateProgress;
}
};
onDestroy(() => {
if (observer) {
observer?.disconnect();
}
});
function playVideo() {
if (video) {
video.play();
video.muted = true;
;
}
}
function pauseVideo() {
if (video) {
video.pause();
video.muted = true;
isVideoMuted[id] = video.muted;
}
}
let isVideoMuted = {};
isVideoMuted[id] = true;
function toggleMute(event) {
event.preventDefault();
if (video?.paused)
{
video?.play();
isVideoMuted[id] = false;
}
else {
video.muted = !video?.muted;
isVideoMuted[id] = video?.muted;
}
}
</script>
<div class="absolute inset-0 bg-cover object-fill bg-center bg-[#000]">
</div>
<video
id={id}
playsinline
loop
class="relative m-auto w-full max-w-screen max-h-[520px] sm:max-h-[700px]"
src={src}
on:click={toggleMute}
bind:this={container}
on:loadedmetadata={updateProgress}
/>
<div class="z-20 bg-center bg-[#000] rounded-full w-11 h-11 bg-opacity-60 absolute bottom-2 left-2 flex items-center justify-center">
{#if isVideoMuted[id]}
<svg class="w-6 h-6 m-auto" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#ffffff" stroke="#ffffff"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <title>sound-off-filled</title> <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="icon" fill="#fff" transform="translate(42.666667, 59.581722)"> <path d="M47.0849493,-1.42108547e-14 L298.668,251.583611 L304.101001,257.015597 L304.101,257.016 L353.573532,306.488791 C353.573732,306.488458 353.573933,306.488124 353.574133,306.48779 L384.435257,337.348961 L384.434,337.349 L409.751616,362.666662 L379.581717,392.836561 L191.749,205.003 L191.749973,369.105851 L81.0208,283.647505 L7.10542736e-15,283.647505 L7.10542736e-15,112.980838 L80.8957867,112.980838 L91.433,104.688 L16.9150553,30.169894 L47.0849493,-1.42108547e-14 Z M361.298133,28.0146513 C429.037729,103.653701 443.797162,209.394226 405.578884,298.151284 L372.628394,265.201173 C396.498256,194.197542 381.626623,113.228555 328.013013,54.642278 L361.298133,28.0146513 Z M276.912853,95.5237713 C305.539387,127.448193 318.4688,168.293162 315.701304,208.275874 L266.464558,159.040303 C261.641821,146.125608 254.316511,133.919279 244.488548,123.156461 L243.588693,122.182545 L276.912853,95.5237713 Z M191.749973,25.7516113 L191.749,84.3256113 L158.969,51.5456113 L191.749973,25.7516113 Z" id="Combined-Shape"> </path> </g> </g> </g></svg>
{:else}
<svg class="w-6 h-6 m-auto" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="#fff"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <title>sound-loud-filled</title> <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> <g id="icon" fill="#ffffff" transform="translate(42.666667, 85.333333)"> <path d="M361.299413,341.610667 L328.014293,314.98176 C402.206933,233.906133 402.206933,109.96608 328.013013,28.8906667 L361.298133,2.26304 C447.910187,98.97536 447.908907,244.898347 361.299413,341.610667 Z M276.912853,69.77216 L243.588693,96.4309333 C283.38432,138.998613 283.38304,204.87488 243.589973,247.44256 L276.914133,274.101333 C329.118507,215.880107 329.118507,127.992107 276.912853,69.77216 Z M191.749973,1.42108547e-14 L80.8957867,87.2292267 L7.10542736e-15,87.2292267 L7.10542736e-15,257.895893 L81.0208,257.895893 L191.749973,343.35424 L191.749973,1.42108547e-14 L191.749973,1.42108547e-14 Z" id="Shape"> </path> </g> </g> </g></svg>
{/if}
</div>
{#if !hideProgressbar}
<progress class="progress [&::-webkit-progress-value]:bg-[#FF0000] [&::-moz-progress-bar]:bg-[#FF0000] w-full bg-white" value={progress} max="100" style="height: 1.5px"></progress>
{/if}

View File

@ -13,10 +13,8 @@
if (state !== "overview" && subSectionMap[state]) {
displaySubSection = state;
//goto(`/stocks/${$stockTicker}${subSectionMap[state]}`);
} else {
displaySubSection = state;
//goto(`/stocks/${$stockTicker}/statistics`);
}
}
@ -77,6 +75,7 @@
>
Employees
</a>
<a
href={`/stocks/${$stockTicker}/statistics/fail-to-deliver`}
on:click={() => changeSubSection("fail-to-deliver")}

View File

@ -0,0 +1,27 @@
export const load = async ({ locals, params }) => {
const getSimilarStocks = async () => {
const { apiKey, apiURL } = locals;
const postData = {
ticker: params.tickerID,
};
// make the POST request to the endpoint
const response = await fetch(apiURL + "/similar-stocks", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
// Make sure to return a promise
return {
getSimilarStocks: await getSimilarStocks(),
};
};

View File

@ -0,0 +1,92 @@
<script lang="ts">
import { abbreviateNumber } from "$lib/utils";
import ArrowLogo from "lucide-svelte/icons/move-up-right";
export let data;
const similarStocks = data?.getSimilarStocks?.sort((a, b) => b?.var - a?.var);
</script>
<section class="w-full overflow-hidden">
<div class="w-full overflow-hidden m-auto">
<div class="sm:p-0 flex justify-center w-full m-auto overflow-hidden">
<div
class="relative flex justify-center items-start overflow-hidden w-full"
>
<main class="w-full lg:w-3/4">
<slot />
</main>
<aside class="hidden lg:block relative fixed w-1/4 ml-4">
{#if data?.user?.tier !== "Pro" || data?.user?.freeTrial}
<div
class="w-full text-white border border-gray-600 rounded-md h-fit pb-4 mt-4 cursor-pointer bg-primary sm:hover:bg-secondary transition ease-out duration-100"
>
<a
href="/pricing"
class="w-auto lg:w-full p-1 flex flex-col m-auto px-2 sm:px-0"
>
<div class="w-full flex justify-between items-center p-3 mt-3">
<h2 class="text-start text-xl font-semibold text-white ml-3">
Pro Subscription
</h2>
<ArrowLogo class="w-8 h-8 mr-3 flex-shrink-0" />
</div>
<span class="text-white p-3 ml-3 mr-3">
Upgrade now for unlimited access to all data and tools.
</span>
</a>
</div>
{/if}
{#if similarStocks?.length > 0}
<div
class="w-full p-2 text-white border border-gray-600 bg-primary rounded-md h-fit pb-4 mt-4 cursor-pointer"
>
<h3 class="p-2 pt-4 text-2xl font-semibold">Related Stocks</h3>
<table class="table table-sm table-compact w-full text-white">
<thead class="text-white"
><tr
><th
class="whitespace-nowrap border-b font-semibold text-[1rem] text-left"
>Company</th
>
<th
class="whitespace-nowrap border-b font-semibold text-[1rem] text-right"
>Value-at-Risk</th
></tr
></thead
>
<tbody>
{#each similarStocks?.slice(0, 8) as item, index}
<tr
class="border-gray-600 text-[1rem] {index !==
similarStocks?.slice(0, 8).length - 1
? 'border-b'
: ''}"
><td class="text-left text-[1rem]"
><a
href={`/stocks/${item?.symbol}`}
class="sm:hover:text-white text-blue-400"
>{item?.symbol}</a
></td
>
<td class="text-right cursor-normal text-[1rem]"
>{abbreviateNumber(item?.var) + "%"}</td
>
</tr>
{/each}
</tbody>
</table>
<a
href="/list/most-ftd-shares"
class="flex justify-center items-center rounded cursor-pointer w-full py-2 mt-3 text-[1rem] text-center font-semibold text-black m-auto sm:hover:bg-gray-300 bg-[#fff] transition duration-100"
>
Fail-to-Deliver Ranks
</a>
</div>
{/if}
</aside>
</div>
</div>
</div>
</section>

View File

@ -0,0 +1,46 @@
export const load = async ({ locals, params }) => {
const { apiKey, apiURL } = locals;
const getVaR = async () => {
const postData = {
ticker: params.tickerID,
};
// make the POST request to the endpoint
const response = await fetch(apiURL + "/value-at-risk", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-KEY": apiKey,
},
body: JSON.stringify(postData),
});
const output = await response.json();
return output;
};
const getHistoricalPrice = async () => {
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();
return output;
};
// Make sure to return a promise
return {
getVaR: await getVaR(),
getHistoricalPrice: await getHistoricalPrice(),
};
};

View File

@ -0,0 +1,98 @@
<script lang="ts">
import {
numberOfUnreadNotification,
displayCompanyName,
stockTicker,
} from "$lib/store";
import Infobox from "$lib/components/Infobox.svelte";
import VaR from "$lib/components/VaR.svelte";
export let data;
let rawData = data?.getVaR || {};
if (!rawData?.history || !data?.getHistoricalPrice) {
console.error(
"Missing required data: rawData.history or data.getHistoricalPrice is undefined.",
);
} else {
for (let i = 0; i < rawData.history.length; i++) {
const entry = rawData.history[i];
const entryMonth = entry?.date; // "YYYY-MM" format
let matchingData = null;
// Find the first price at the beginning of the month
for (let j = 0; j < data.getHistoricalPrice.length; j++) {
const priceData = data.getHistoricalPrice[j];
// Extract year and month from the `time` field
const priceDate = priceData?.time?.slice(0, 7); // "YYYY-MM" format
if (priceDate === entryMonth) {
matchingData = priceData;
break; // Use the first matching entry
}
}
// Add the 'close' price to the entry if matching data is found
if (matchingData) {
entry.price = matchingData.close;
} else {
console.warn(`No matching data found for month: ${entry?.date}`);
}
}
}
console.log(rawData);
</script>
<svelte:head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>
{$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ""}
{$displayCompanyName} ({$stockTicker}) Value-at-Risk Stocknear
</title>
<meta
name="description"
content={`Historical Value-at-Risk of ${$stockTicker}.`}
/>
<meta
property="og:title"
content={`${$displayCompanyName} (${$stockTicker}) Value-at-Risk · Stocknear`}
/>
<meta
property="og:description"
content={`Historical Value-at-Risk of ${$stockTicker}.`}
/>
<meta property="og:type" content="website" />
<meta name="twitter:card" content="summary_large_image" />
<meta
name="twitter:title"
content={`${$displayCompanyName} (${$stockTicker}) Value-at-Risk · Stocknear`}
/>
<meta
name="twitter:description"
content={`Historical Value-at-Risk of ${$stockTicker}.`}
/>
</svelte:head>
<section
class="bg-default w-full overflow-hidden min-h-screen text-white h-full"
>
<div class="w-full flex justify-center w-full sm-auto h-full overflow-hidden">
<div
class="w-full relative flex justify-center items-center overflow-hidden"
>
<main class="w-full">
<div class="sm:p-7 m-auto mt-2 sm:mt-0">
{#if rawData?.length !== 0}
<div class="grid grid-cols-1 gap-2">
<VaR {data} {rawData} />
</div>
{:else}
<Infobox text="No data available" />
{/if}
</div>
</main>
</div>
</div>
</section>