From 5b500ae3f937a5406f302945a652bd0ee7d992fb Mon Sep 17 00:00:00 2001 From: MuslemRahimi Date: Sun, 9 Feb 2025 22:17:13 +0100 Subject: [PATCH] add index --- src/lib/components/IndexSidecard.svelte | 218 ++++ src/lib/components/Searchbar.svelte | 6 +- src/lib/store.ts | 2 + src/routes/index/[tickerID]/+layout.server.ts | 124 ++ src/routes/index/[tickerID]/+layout.svelte | 1078 +++++++++++++++ src/routes/index/[tickerID]/+page.server.ts | 146 +++ src/routes/index/[tickerID]/+page.svelte | 1156 +++++++++++++++++ .../index/[tickerID]/history/+page.server.ts | 25 + .../index/[tickerID]/history/+page.svelte | 594 +++++++++ .../index/[tickerID]/holdings/+page.server.ts | 27 + .../index/[tickerID]/holdings/+page.svelte | 133 ++ .../index/[tickerID]/options/+layout.svelte | 182 +++ .../index/[tickerID]/options/+page.server.ts | 209 +++ .../index/[tickerID]/options/+page.svelte | 665 ++++++++++ .../[tickerID]/options/dex/+layout.svelte | 94 ++ .../[tickerID]/options/dex/+page.server.ts | 51 + .../index/[tickerID]/options/dex/+page.svelte | 33 + .../options/dex/expiry/+page.server.ts | 36 + .../options/dex/expiry/+page.svelte | 35 + .../options/dex/strike/+page.server.ts | 36 + .../options/dex/strike/+page.svelte | 35 + .../[tickerID]/options/gex/+layout.svelte | 94 ++ .../[tickerID]/options/gex/+page.server.ts | 51 + .../index/[tickerID]/options/gex/+page.svelte | 32 + .../options/gex/expiry/+page.server.ts | 36 + .../options/gex/expiry/+page.svelte | 35 + .../options/gex/strike/+page.server.ts | 36 + .../options/gex/strike/+page.svelte | 35 + .../options/hottest-contracts/+page.server.ts | 52 + .../options/hottest-contracts/+page.svelte | 1084 ++++++++++++++++ .../[tickerID]/options/oi/+layout.svelte | 83 ++ .../[tickerID]/options/oi/+page.server.ts | 34 + .../index/[tickerID]/options/oi/+page.svelte | 35 + .../options/oi/expiry/+page.server.ts | 34 + .../[tickerID]/options/oi/expiry/+page.svelte | 35 + .../options/unusual-activity/+page.server.ts | 49 + .../options/unusual-activity/+page.svelte | 37 + .../options/volatility/+layout.svelte | 15 + .../options/volatility/+page.server.ts | 31 + .../options/volatility/+page.svelte | 35 + src/routes/sitemap.xml/+server.ts | 2 +- src/routes/sitemap/+page.svelte | 218 ++-- 42 files changed, 6828 insertions(+), 120 deletions(-) create mode 100644 src/lib/components/IndexSidecard.svelte create mode 100644 src/routes/index/[tickerID]/+layout.server.ts create mode 100644 src/routes/index/[tickerID]/+layout.svelte create mode 100644 src/routes/index/[tickerID]/+page.server.ts create mode 100644 src/routes/index/[tickerID]/+page.svelte create mode 100644 src/routes/index/[tickerID]/history/+page.server.ts create mode 100644 src/routes/index/[tickerID]/history/+page.svelte create mode 100644 src/routes/index/[tickerID]/holdings/+page.server.ts create mode 100644 src/routes/index/[tickerID]/holdings/+page.svelte create mode 100644 src/routes/index/[tickerID]/options/+layout.svelte create mode 100644 src/routes/index/[tickerID]/options/+page.server.ts create mode 100644 src/routes/index/[tickerID]/options/+page.svelte create mode 100644 src/routes/index/[tickerID]/options/dex/+layout.svelte create mode 100644 src/routes/index/[tickerID]/options/dex/+page.server.ts create mode 100644 src/routes/index/[tickerID]/options/dex/+page.svelte create mode 100644 src/routes/index/[tickerID]/options/dex/expiry/+page.server.ts create mode 100644 src/routes/index/[tickerID]/options/dex/expiry/+page.svelte create mode 100644 src/routes/index/[tickerID]/options/dex/strike/+page.server.ts create mode 100644 src/routes/index/[tickerID]/options/dex/strike/+page.svelte create mode 100644 src/routes/index/[tickerID]/options/gex/+layout.svelte create mode 100644 src/routes/index/[tickerID]/options/gex/+page.server.ts create mode 100644 src/routes/index/[tickerID]/options/gex/+page.svelte create mode 100644 src/routes/index/[tickerID]/options/gex/expiry/+page.server.ts create mode 100644 src/routes/index/[tickerID]/options/gex/expiry/+page.svelte create mode 100644 src/routes/index/[tickerID]/options/gex/strike/+page.server.ts create mode 100644 src/routes/index/[tickerID]/options/gex/strike/+page.svelte create mode 100644 src/routes/index/[tickerID]/options/hottest-contracts/+page.server.ts create mode 100644 src/routes/index/[tickerID]/options/hottest-contracts/+page.svelte create mode 100644 src/routes/index/[tickerID]/options/oi/+layout.svelte create mode 100644 src/routes/index/[tickerID]/options/oi/+page.server.ts create mode 100644 src/routes/index/[tickerID]/options/oi/+page.svelte create mode 100644 src/routes/index/[tickerID]/options/oi/expiry/+page.server.ts create mode 100644 src/routes/index/[tickerID]/options/oi/expiry/+page.svelte create mode 100644 src/routes/index/[tickerID]/options/unusual-activity/+page.server.ts create mode 100644 src/routes/index/[tickerID]/options/unusual-activity/+page.svelte create mode 100644 src/routes/index/[tickerID]/options/volatility/+layout.svelte create mode 100644 src/routes/index/[tickerID]/options/volatility/+page.server.ts create mode 100644 src/routes/index/[tickerID]/options/volatility/+page.svelte diff --git a/src/lib/components/IndexSidecard.svelte b/src/lib/components/IndexSidecard.svelte new file mode 100644 index 00000000..05d9d57f --- /dev/null +++ b/src/lib/components/IndexSidecard.svelte @@ -0,0 +1,218 @@ + + +
+

+ About {$indexTicker} +

+

+ {description} +

+ +
+
+ Asset Class + {assetClass} +
+
+ Ticker Symbol + {$indexTicker} +
+ + + + +
+
+ + {#if topSectorList?.length !== 0} +
+
+ +
+

+ Top Sectors +

+ +
+ + + + + + + + + + {#each topSectorList as item} + {#if item?.weightPercentage > 0} + + + + + + {/if} + {/each} + +
SectorWeight %
+ listItem?.title === item?.sector, + )?.link} + class="sm:hover:underline sm:hover:underline-offset-4 text-white truncate" + > + {item?.sector} + + + {abbreviateNumber(item?.weightPercentage?.toFixed(2))}% +
+
+ + + All Sectors + +
+
+
+ {/if} + + {#if topHoldingList?.length !== 0} +
+
+ +
+

+ Top Holdings + + {totalAssetPercentage}% of assets + +

+ +
+ + + + + + + + + + {#each topHoldingList?.slice(0, 5) as item} + {#if item?.symbol !== null} + stockSelector(item?.symbol)} + class="lg:shake-ticker sm:hover:text-white text-blue-400 cursor-pointer lg:hover:bg-[#245073] lg:hover:bg-opacity-[0.2] [#09090B] border-b border-[#27272A]" + > + + + + + {/if} + {/each} + +
CompanyPortfolio
+
+
+ {item?.symbol ?? "n/a"} + + {#if typeof item?.name !== "undefined"} + {item?.name?.length > 20 + ? formatString(item?.name?.slice(0, 20)) + "..." + : formatString(item?.name)?.replace( + "Usd", + "USD", + )} + {:else} + n/a + {/if} + +
+
+
+ {abbreviateNumber(item?.weightPercentage?.toFixed(2))}% +
+
+ + + All Holdings + +
+
+
+ {/if} + + \ No newline at end of file diff --git a/src/lib/components/Searchbar.svelte b/src/lib/components/Searchbar.svelte index 8c9fe8eb..03868ce2 100644 --- a/src/lib/components/Searchbar.svelte +++ b/src/lib/components/Searchbar.svelte @@ -67,7 +67,7 @@ nextPage = true; goto( - `/${assetType === "ETF" ? "etf" : assetType === "Crypto" ? "crypto" : "stocks"}/${symbol}`, + `/${assetType === "ETF" ? "etf" : assetType === "Index" ? "index" : "stocks"}/${symbol}`, ); } @@ -498,7 +498,7 @@ {#each searchHistory?.length > 0 ? searchHistory : popularList as item}
  • popularTicker(item?.symbol)} class="mb-2 {item?.symbol === focusedSuggestion ? 'cursor-pointer flex justify-start items-center p-2 text-white bg-primary rounded group' @@ -530,7 +530,7 @@ searchBarTicker(item?.symbol)} class="mb-2 {item?.symbol === focusedSuggestion ? 'shake-ticker cursor-pointer flex justify-start items-center p-2 text-white bg-primary rounded group' diff --git a/src/lib/store.ts b/src/lib/store.ts index f46c9681..af252bd8 100644 --- a/src/lib/store.ts +++ b/src/lib/store.ts @@ -146,6 +146,8 @@ export const searchBarData = writable([]); export const stockTicker = writable(""); export const etfTicker = writable(""); +export const indexTicker = writable(""); + export const cryptoTicker = writable(""); export const assetType = writable(""); diff --git a/src/routes/index/[tickerID]/+layout.server.ts b/src/routes/index/[tickerID]/+layout.server.ts new file mode 100644 index 00000000..792b0269 --- /dev/null +++ b/src/routes/index/[tickerID]/+layout.server.ts @@ -0,0 +1,124 @@ +const cleanString = (input) => { + const substringsToRemove = [ + "Depositary", + "Inc.", + "Incorporated", + "Holdings", + "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 fetchData = async (apiURL, apiKey, endpoint, ticker) => { + try { + const response = await fetch(`${apiURL}${endpoint}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-API-KEY": apiKey, + }, + body: JSON.stringify({ ticker }), + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + return data; + } catch (error) { + console.error(`Error fetching ${endpoint}:`, error); + return []; + } +}; + +const fetchWatchlist = async (pb, userId) => { + let output; + try { + output = await pb.collection("watchlist").getFullList({ + filter: `user="${userId}"`, + }); + } catch (e) { + //console.log(e) + output = []; + } + return output; +}; + +export const load = async ({ params, locals }) => { + const { apiURL, apiKey, pb, user } = locals; + const { tickerID } = params; + + try { + const endpoints = [ + "/index-profile", + "/etf-holdings", + "/etf-sector-weighting", + "/stock-quote", + "/pre-post-quote", + "/wiim", + "/one-day-price", + "/stock-news", + ]; + + const promises = endpoints.map((endpoint) => { + // Use SPY for specific endpoints when tickerID is ^SPC or ^spc + const useSpyTicker = tickerID?.toLowerCase() === "^spc" && + ["/etf-holdings", "/etf-sector-weighting", "/wiim", "/stock-news"].includes(endpoint); + + return fetchData(apiURL, apiKey, endpoint, useSpyTicker ? "SPY" : tickerID); + }); + + // Add watchlist promise + promises.push(fetchWatchlist(pb, user?.id)); + + const [ + getIndexProfile, + getIndexHolding, + getIndexSectorWeighting, + getStockQuote, + getPrePostQuote, + getWhyPriceMoved, + getOneDayPrice, + getNews, + getUserWatchlist, + ] = await Promise.all(promises); + + return { + getIndexProfile: getIndexProfile || [], + getIndexHolding: getIndexHolding || [], + getIndexSectorWeighting: getIndexSectorWeighting || [], + getStockQuote: getStockQuote || [], + getPrePostQuote: getPrePostQuote || [], + getWhyPriceMoved: getWhyPriceMoved || [], + getOneDayPrice: getOneDayPrice || [], + getNews: getNews || [], + getUserWatchlist: getUserWatchlist || [], + companyName: cleanString(getIndexProfile?.at(0)?.name), + getParams: params.tickerID, + }; + } catch (error) { + console.error('Error in load function:', error); + return { + getIndexProfile: [], + getIndexHolding: [], + getIndexSectorWeighting: [], + getStockQuote: [], + getPrePostQuote: [], + getWhyPriceMoved: [], + getOneDayPrice: [], + getNews: [], + getUserWatchlist: [], + companyName: '', + getParams: params.tickerID, + }; + } +}; \ No newline at end of file diff --git a/src/routes/index/[tickerID]/+layout.svelte b/src/routes/index/[tickerID]/+layout.svelte new file mode 100644 index 00000000..531321dd --- /dev/null +++ b/src/routes/index/[tickerID]/+layout.svelte @@ -0,0 +1,1078 @@ + + + + + + +
    +
    +
    +
    +
    + + + + +
    +
    + +
    +
    + + +
    + + + + + +
    +
    +
    +
    +

    + {$displayCompanyName?.length > charNumber + ? $displayCompanyName?.slice(0, charNumber) + + "..." + : $displayCompanyName} + +

    +
    + +
    +
    +
    + {displayLegend?.close} +
    +
    + = 0 + ? "before:content-['+'] text-[#00FC50]" + : "text-[#FF2F1F]"} + > + {displayLegend?.change} + + = 0 + ? "text-[#00FC50]" + : "text-[#FF2F1F]"} + > + ({displayLegend?.changesPercentage}%) + +
    +
    + {#if !$isOpen} + At close: + {/if} + {displayLegend?.date} + {#if $isOpen} + - Market open + {/if} +
    +
    + {#if Object?.keys(prePostData)?.length !== 0 && !$isOpen} +
    +
    + {prePostData?.price?.toFixed(2)} +
    +
    + {prePostData?.changesPercentage?.toFixed( + 2, + )}% +
    +
    + + {#if prePostData?.time?.includes("AM")} + + {:else} + + {/if} + {prePostData?.time?.includes("AM") + ? "Pre-market" + : "After-hours"} + {prePostData?.time} +
    +
    + {/if} +
    +
    +
    +
    +
    + + + + +
    + + + + + +
    +
    +
    +
    +
    +
    +
    +
    +
    + + + +{#if LoginPopup} + +{/if} + + + + + + + + + + + + + + + + + diff --git a/src/routes/index/[tickerID]/+page.server.ts b/src/routes/index/[tickerID]/+page.server.ts new file mode 100644 index 00000000..5fe339a3 --- /dev/null +++ b/src/routes/index/[tickerID]/+page.server.ts @@ -0,0 +1,146 @@ +import { error, fail, redirect } from "@sveltejs/kit"; +import { validateData } from "$lib/utils"; +import { loginUserSchema, registerUserSchema } from "$lib/schemas"; + +export const actions = { + login: async ({ request, locals }) => { + const { formData, errors } = await validateData( + await request.formData(), + loginUserSchema, + ); + + if (errors) { + return fail(400, { + data: formData, + errors: errors.fieldErrors, + }); + } + + try { + await locals.pb + .collection("users") + .authWithPassword(formData.email, formData.password); + + /* + if (!locals.pb?.authStore?.model?.verified) { + locals.pb.authStore.clear(); + return { + notVerified: true, + }; + } + */ + } catch (err) { + console.log("Error: ", err); + error(err.status, err.message); + } + + redirect(302, "/"); + }, + + register: async ({ locals, request }) => { + const { formData, errors } = await validateData( + await request.formData(), + registerUserSchema, + ); + + if (errors) { + return fail(400, { + data: formData, + errors: errors.fieldErrors, + }); + } + + try { + let newUser = await locals.pb.collection("users").create(formData); + /* +await locals.pb?.collection('users').update( + newUser?.id, { + 'freeTrial' : true, + 'tier': 'Pro', //Give new users a free trial for the Pro Subscription + }); +*/ + + await locals.pb.collection("users").requestVerification(formData.email); + } catch (err) { + console.log("Error: ", err); + error(err.status, err.message); + } + + try { + await locals.pb + .collection("users") + .authWithPassword(formData.email, formData.password); + } catch (err) { + console.log("Error: ", err); + error(err.status, err.message); + } + + redirect(303, "/"); + }, + + oauth2: async ({ url, locals, request, cookies }) => { + const authMethods = (await locals?.pb + ?.collection("users") + ?.listAuthMethods())?.oauth2; + + + const data = await request?.formData(); + const providerSelected = data?.get("provider"); + + if (!authMethods) { + return { + authProviderRedirect: "", + authProviderState: "", + }; + } + const redirectURL = `${url.origin}/oauth`; + + const targetItem = authMethods?.providers?.findIndex( + (item) => item?.name === providerSelected, + ); + //console.log("==================") + //console.log(authMethods.authProviders) + //console.log('target item is: ', targetItem) + + const provider = authMethods.providers[targetItem]; + const authProviderRedirect = `${provider.authUrl}${redirectURL}`; + const state = provider.state; + const verifier = provider.codeVerifier; + + console.log(provider) + + cookies.set("state", state, { + httpOnly: true, + sameSite: "lax", + secure: true, + path: "/", + maxAge: 60 * 60, + }); + + cookies.set("verifier", verifier, { + httpOnly: true, + sameSite: "lax", + secure: true, + path: "/", + maxAge: 60 * 60, + }); + + cookies.set("provider", providerSelected, { + httpOnly: true, + sameSite: "lax", + secure: true, + path: "/", + maxAge: 60 * 60, + }); + + cookies.set("path", "/", { + httpOnly: true, + sameSite: "lax", + secure: true, + path: "/", + maxAge: 60, + }); + + redirect(302, authProviderRedirect); + }, +}; diff --git a/src/routes/index/[tickerID]/+page.svelte b/src/routes/index/[tickerID]/+page.svelte new file mode 100644 index 00000000..6e3b4f9b --- /dev/null +++ b/src/routes/index/[tickerID]/+page.svelte @@ -0,0 +1,1156 @@ + + += 0 ? "▲" : "▼"} ${displayLegend?.change}%`} + description={`Get a real-time ${data?.companyName} (${$indexTicker}) stock chart, price quote with breaking news, financials, statistics, charts and more.`} +/> + +
    +
    +
    + +
    +
    + + + +
    + {#if dataMapping[displayData]?.length === 0} +
    +
    +
    +
      + {#each intervals as interval} +
    • + +
    • + {/each} +
    +
    +
    +
    +
    +
    + No {displayData} chart data available +
    +
    +
    +
    + {:else} +
    +
    +
    +
      + {#each intervals as interval} +
    • + +
    • + {/each} +
    +
    +
    + = 0 + ? "before:content-['+'] text-[#00FC50]" + : "text-[#FF2F1F]"} + > + {displayLegend?.graphChange}% + + +
    +
    + + {#if output !== null && dataMapping[displayData]?.length !== 0} + (chart = api)} + > + {#if displayData === "1D"} + ({ + time, + value: close, + }))} + lineWidth={1.5} + priceScaleId="right" + lineColor={colorChange} + topColor={topColorChange} + bottomColor={bottomColorChange} + priceLineVisible={false} + > + + + {:else if displayData === "1W"} + ({ + time, + value: close, + }))} + lineWidth={1.5} + priceScaleId="right" + lineColor={colorChange} + topColor={topColorChange} + bottomColor={bottomColorChange} + priceLineVisible={false} + > + + + {:else if displayData === "1M"} + ({ + time: time, + value: close, + }))} + lineWidth={1.5} + priceScaleId="right" + lineColor={colorChange} + topColor={topColorChange} + bottomColor={bottomColorChange} + priceLineVisible={false} + > + + + {:else if displayData === "6M"} + ({ + time, + value: close, + }))} + lineWidth={1.5} + priceScaleId="right" + lineColor={colorChange} + topColor={topColorChange} + bottomColor={bottomColorChange} + priceLineVisible={false} + > + + + {:else if displayData === "1Y"} + ({ + time, + value: close, + }))} + lineWidth={1.5} + priceScaleId="right" + lineColor={colorChange} + topColor={topColorChange} + bottomColor={bottomColorChange} + priceLineVisible={false} + > + + + {:else if displayData === "MAX"} + ({ + time, + value: close, + }))} + lineWidth={1.5} + priceScaleId="right" + lineColor={colorChange} + topColor={topColorChange} + bottomColor={bottomColorChange} + priceLineVisible={false} + > + + + {/if} + + {:else} +
    +
    + +
    +
    + {/if} +
    + {/if} + +
    + + + + + + + + + + + + + + + + + + + +
    Bid{$wsBidPrice !== 0 && $wsBidPrice !== null + ? $wsBidPrice + : (data?.getStockQuote?.bid ?? "-")}
    Open{data?.getStockQuote?.open?.toFixed(2)}
    Volume{abbreviateNumber(data?.getStockQuote?.volume)}
    Day's Range{data?.getStockQuote?.dayLow?.toFixed(2)} - {data?.getStockQuote?.dayHigh?.toFixed( + 2, + )}
    Market Capn/a
    Price Avg 50{data?.getStockQuote?.priceAvg50?.toFixed(2)}
    + + + + + + + + + + + + + + + + + + + +
    Ask{$wsAskPrice !== 0 && $wsAskPrice !== null + ? $wsAskPrice + : (data?.getStockQuote?.ask ?? "-")}
    Avg. Volume{abbreviateNumber(data?.getStockQuote?.avgVolume)}
    Previous Close{data?.getStockQuote?.previousClose?.toFixed(2)}
    52-Week Range{data?.getStockQuote?.yearLow?.toFixed(2)} - {data?.getStockQuote?.yearHigh?.toFixed( + 2, + )}
    Holdings + {stockDeck?.holdingsCount !== null + ? abbreviateNumber(stockDeck?.holdingsCount) + : "n/a"}
    Price Avg 200{data?.getStockQuote?.priceAvg200?.toFixed(2)}
    +
    +
    + + + +
    +
    + +
    + +
    +
    + +
    + +
    + +
    +
    +
    +
    +
    +
    +
    +
    + + + + diff --git a/src/routes/index/[tickerID]/history/+page.server.ts b/src/routes/index/[tickerID]/history/+page.server.ts new file mode 100644 index 00000000..b6f18331 --- /dev/null +++ b/src/routes/index/[tickerID]/history/+page.server.ts @@ -0,0 +1,25 @@ +export const load = async ({ locals, params }) => { + const { apiKey, apiURL } = locals; + const getData = 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 { + getData: await getData(), + }; +}; diff --git a/src/routes/index/[tickerID]/history/+page.svelte b/src/routes/index/[tickerID]/history/+page.svelte new file mode 100644 index 00000000..dda62a7a --- /dev/null +++ b/src/routes/index/[tickerID]/history/+page.svelte @@ -0,0 +1,594 @@ + + + + + + + {$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ""} + {$displayCompanyName} ({$etfTicker}) Historical Stock Price Data · Stocknear + + + + + + + + + + + + + + + + +
    +
    +
    +
    +
    +
    +
    +

    + {$etfTicker} Stock Price History +

    +
    +
    + + + + + + + Select time frame + + + + (timePeriod = "Daily")} + class="cursor-pointer hover:bg-primary" + > + Daily + + (timePeriod = "Weekly")} + class="cursor-pointer hover:bg-primary" + > + Weekly + + {#if data?.user?.tier !== "Pro"} + {#each ["Monthly", "Quarterly", "Annual"] as entry} + goto("/pricing")} + class="cursor-pointer hover:bg-primary" + > + {entry} + + + {/each} + {:else} + (timePeriod = "Monthly")} + class="cursor-pointer hover:bg-primary" + > + Monthly + + (timePeriod = "Quarterly")} + class="cursor-pointer hover:bg-primary" + > + Quarterly + + (timePeriod = "Annual")} + class="cursor-pointer hover:bg-primary" + > + Annual + + {/if} + + + +
    + + +
    +
    + {#if isLoaded} + {#if rawData?.length !== 0} +
    +
    + + + + + + + {#each stockList as item, index} + + + + + + + + + + + {/each} + +
    + {#if timePeriod === "Weekly"} + Week of {new Date(item?.time).toLocaleString( + "en-US", + { + month: "short", + day: "numeric", + year: "numeric", + daySuffix: "2-digit", + }, + )} + {:else} + {new Date(item?.time).toLocaleString("en-US", { + month: "short", + day: "numeric", + year: "numeric", + daySuffix: "2-digit", + })} + {/if} + + {item?.open?.toFixed(2)} + + {item?.high?.toFixed(2)} + + {item?.low?.toFixed(2)} + + {item?.close?.toFixed(2)} + + {item?.change !== null ? item?.change : "n/a"} + + {item?.changesPercentage !== null + ? item?.changesPercentage + "%" + : "n/a"} + + {item?.volume?.toLocaleString("en-US")} +
    +
    +
    + {:else} + + {/if} + {:else} +
    +
    + +
    +
    + {/if} +
    +
    + +
    +
    +
    +
    diff --git a/src/routes/index/[tickerID]/holdings/+page.server.ts b/src/routes/index/[tickerID]/holdings/+page.server.ts new file mode 100644 index 00000000..3d824bc8 --- /dev/null +++ b/src/routes/index/[tickerID]/holdings/+page.server.ts @@ -0,0 +1,27 @@ +export const load = async ({ locals, params }) => { + const { apiKey, apiURL } = locals; + const getETFHoldings = async () => { + const postData = { + ticker: params.tickerID, + }; + + // make the POST request to the endpoint + const response = await fetch(apiURL + "/etf-holdings", { + 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 { + getETFHoldings: await getETFHoldings(), + }; +}; diff --git a/src/routes/index/[tickerID]/holdings/+page.svelte b/src/routes/index/[tickerID]/holdings/+page.svelte new file mode 100644 index 00000000..bc4a5206 --- /dev/null +++ b/src/routes/index/[tickerID]/holdings/+page.svelte @@ -0,0 +1,133 @@ + + + + + + + {$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ""} + {$displayCompanyName} ({$etfTicker}) Holdings List · Stocknear + + + + + + + + + + + + + + + + +
    +
    +
    +
    +
    +

    + {$etfTicker} Holdings List +

    + {#if data?.getETFHoldings?.lastUpdate} +
    + As of {formattedDate} +
    + {/if} +
    +
    + +
    + + {#if rawData?.length !== 0} + + {/if} + + + + diff --git a/src/routes/index/[tickerID]/options/+layout.svelte b/src/routes/index/[tickerID]/options/+layout.svelte new file mode 100644 index 00000000..621d407a --- /dev/null +++ b/src/routes/index/[tickerID]/options/+layout.svelte @@ -0,0 +1,182 @@ + + +
    +
    +
    +
    +
    + +
    + +
    +
    + + +
    +
    +
    +
    diff --git a/src/routes/index/[tickerID]/options/+page.server.ts b/src/routes/index/[tickerID]/options/+page.server.ts new file mode 100644 index 00000000..59788669 --- /dev/null +++ b/src/routes/index/[tickerID]/options/+page.server.ts @@ -0,0 +1,209 @@ +import { error, fail, redirect } from "@sveltejs/kit"; +import { validateData } from "$lib/utils"; +import { loginUserSchema, registerUserSchema } from "$lib/schemas"; + + +export const load = async ({ locals, params }) => { + const { apiKey, apiURL } = locals; + + const getDailyStats = async () => { + const postData = { + ticker: params.tickerID, + }; + + const response = await fetch(apiURL + "/options-stats-ticker", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-API-KEY": apiKey, + }, + body: JSON.stringify(postData), + }); + + const output = await response.json(); + + return output; + }; + + + + const getOptionsHistoricalData = async () => { + const postData = { + ticker: params.tickerID, + }; + + // make the POST request to the endpoint + const response = await fetch(apiURL + "/options-historical-data-ticker", { + 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 { + getDailyStats: await getDailyStats(), + getOptionsHistoricalData: await getOptionsHistoricalData(), + }; +}; + + + +export const actions = { + login: async ({ url, request, locals }) => { + + const path = url?.href?.replace("/oauth2","") + + const { formData, errors } = await validateData( + await request.formData(), + loginUserSchema, + ); + + if (errors) { + return fail(400, { + data: formData, + errors: errors.fieldErrors, + }); + } + + try { + await locals.pb + .collection("users") + .authWithPassword(formData.email, formData.password); + + /* + if (!locals.pb?.authStore?.model?.verified) { + locals.pb.authStore.clear(); + return { + notVerified: true, + }; + } + */ + } catch (err) { + console.log("Error: ", err); + error(err.status, err.message); + } + + redirect(302, path); + }, + + register: async ({ url, locals, request }) => { + const path = url?.href?.replace("/oauth2","") + + const { formData, errors } = await validateData( + await request.formData(), + registerUserSchema, + ); + + if (errors) { + return fail(400, { + data: formData, + errors: errors.fieldErrors, + }); + } + + try { + let newUser = await locals.pb.collection("users").create(formData); + /* +await locals.pb?.collection('users').update( + newUser?.id, { + 'freeTrial' : true, + 'tier': 'Pro', //Give new users a free trial for the Pro Subscription + }); +*/ + await locals.pb.collection("users")?.requestVerification(formData.email); + } catch (err) { + console.log("Error: ", err); + error(err.status, err.message); + } + + try { + await locals.pb + .collection("users") + .authWithPassword(formData.email, formData.password); + } catch (err) { + console.log("Error: ", err); + error(err.status, err.message); + } + + redirect(303, path); + }, + + oauth2: async ({ url, locals, request, cookies }) => { + + const path = url?.href?.replace("/oauth2","") + const authMethods = (await locals?.pb + ?.collection("users") + ?.listAuthMethods())?.oauth2; + + + const data = await request?.formData(); + const providerSelected = data?.get("provider"); + + if (!authMethods) { + return { + authProviderRedirect: "", + authProviderState: "", + }; + } + const redirectURL = `${url.origin}/oauth`; + + const targetItem = authMethods?.providers?.findIndex( + (item) => item?.name === providerSelected, + ); + //console.log("==================") + //console.log(authMethods.authProviders) + //console.log('target item is: ', targetItem) + + const provider = authMethods.providers[targetItem]; + const authProviderRedirect = `${provider.authUrl}${redirectURL}`; + const state = provider.state; + const verifier = provider.codeVerifier; + + + + cookies.set("state", state, { + httpOnly: true, + sameSite: "lax", + secure: true, + path: "/", + maxAge: 60 * 60, + }); + + cookies.set("verifier", verifier, { + httpOnly: true, + sameSite: "lax", + secure: true, + path: "/", + maxAge: 60 * 60, + }); + + cookies.set("provider", providerSelected, { + httpOnly: true, + sameSite: "lax", + secure: true, + path: "/", + maxAge: 60 * 60, + }); + + cookies.set("path", path, { + httpOnly: true, + sameSite: "lax", + secure: true, + path: "/", + maxAge: 60, + }); + + redirect(302, authProviderRedirect); + }, + +}; diff --git a/src/routes/index/[tickerID]/options/+page.svelte b/src/routes/index/[tickerID]/options/+page.svelte new file mode 100644 index 00000000..9769618c --- /dev/null +++ b/src/routes/index/[tickerID]/options/+page.svelte @@ -0,0 +1,665 @@ + + + + +
    +
    +
    +
    + {#if Object?.keys(dailyStats)?.length === 0 && rawData?.length === 0} + + {/if} + + {#if Object?.keys(dailyStats)?.length > 0} +
    + +
    + {/if} + + {#if rawData?.length > 0} +
    + + + +
    + +
    + {#if filteredList?.length !== 0} + + {:else} + +
    + + No Options activity found +
    +
    + {/if} +
    + + {#if optionList?.length !== 0} +

    + Historical {$etfTicker} Data +

    + +
    +
    + + + + + + + + + + + + + + + + + + + + {#each data?.user?.tier === "Pro" ? optionList : optionList?.slice(0, 3) as item, index} + + + + + + + + + + + + + + + + + + + + + + + + + + + {/each} + +
    Date% ChangeP/CVolumeC VolumeP VolumeTotal OIOI Change% OI ChangeC PremP Prem
    + {formatDate(item?.date)} + + {#if item?.changesPercentage >= 0 && item?.changesPercentage !== null} + +{item?.changesPercentage >= 1000 + ? abbreviateNumberWithColor( + item?.changesPercentage, + ) + : item?.changesPercentage?.toFixed(2)}% + {:else if item?.changesPercentage < 0 && item?.changesPercentage !== null} + {item?.changesPercentage <= -1000 + ? abbreviateNumberWithColor( + item?.changesPercentage, + ) + : item?.changesPercentage?.toFixed(2)}% + + {:else} + n/a + {/if} + + {item?.putCallRatio} + + {item?.volume?.toLocaleString("en-US")} + + {item?.call_volume?.toLocaleString("en-US")} + + {item?.put_volume?.toLocaleString("en-US")} + + {@html abbreviateNumberWithColor( + item?.total_open_interest, + false, + true, + )} + + {#if item?.changeOI >= 0} + +{item?.changeOI?.toLocaleString("en-US")} + {:else if item?.changeOI < 0} + {item?.changeOI?.toLocaleString("en-US")} + + {:else} + n/a + {/if} + + {#if item?.changesPercentageOI >= 0} + +{item?.changesPercentageOI >= 1000 + ? abbreviateNumberWithColor( + item?.changesPercentageOI, + ) + : item?.changesPercentageOI?.toFixed(2)}% + {:else if item?.changesPercentageOI < 0} + {item?.changesPercentageOI <= -1000 + ? abbreviateNumberWithColor( + item?.changesPercentageOI, + ) + : item?.changesPercentageOI?.toFixed(2)}% + + {:else} + n/a + {/if} + + {@html abbreviateNumberWithColor( + item?.call_premium, + false, + true, + )} + + {@html abbreviateNumberWithColor( + item?.put_premium, + false, + true, + )} +
    +
    + + + {/if} + {/if} +
    +
    + +
    + + + + diff --git a/src/routes/index/[tickerID]/options/dex/+layout.svelte b/src/routes/index/[tickerID]/options/dex/+layout.svelte new file mode 100644 index 00000000..e7c733c0 --- /dev/null +++ b/src/routes/index/[tickerID]/options/dex/+layout.svelte @@ -0,0 +1,94 @@ + + +
    + +
    diff --git a/src/routes/index/[tickerID]/options/dex/+page.server.ts b/src/routes/index/[tickerID]/options/dex/+page.server.ts new file mode 100644 index 00000000..ea04ded8 --- /dev/null +++ b/src/routes/index/[tickerID]/options/dex/+page.server.ts @@ -0,0 +1,51 @@ + + +export const load = async ({ locals, params }) => { + const { apiKey, apiURL, user } = locals; + + const getData = async () => { + const postData = { + params: params.tickerID, + category: "overview", + type: "", + }; + + const response = await fetch(apiURL + "/options-gex-dex", { + 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: "one-year" }; + 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 { + getData: await getData(), + getHistoricalPrice: await getHistoricalPrice(), + }; +}; + + diff --git a/src/routes/index/[tickerID]/options/dex/+page.svelte b/src/routes/index/[tickerID]/options/dex/+page.svelte new file mode 100644 index 00000000..a65c590b --- /dev/null +++ b/src/routes/index/[tickerID]/options/dex/+page.svelte @@ -0,0 +1,33 @@ + + + + +
    +
    +
    + {#if data?.getData?.length > 0} + + {:else} +
    +
    + +
    +
    + {/if} +
    +
    +
    diff --git a/src/routes/index/[tickerID]/options/dex/expiry/+page.server.ts b/src/routes/index/[tickerID]/options/dex/expiry/+page.server.ts new file mode 100644 index 00000000..9f08fc41 --- /dev/null +++ b/src/routes/index/[tickerID]/options/dex/expiry/+page.server.ts @@ -0,0 +1,36 @@ + + +export const load = async ({ locals, params }) => { + const { apiKey, apiURL, user } = locals; + + const getData = async () => { + const postData = { + params: params.tickerID, + category: "expiry", + type: "dex", + }; + + const response = await fetch(apiURL + "/options-gex-dex", { + 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 { + getData: await getData(), + }; +}; + + diff --git a/src/routes/index/[tickerID]/options/dex/expiry/+page.svelte b/src/routes/index/[tickerID]/options/dex/expiry/+page.svelte new file mode 100644 index 00000000..ec6cac90 --- /dev/null +++ b/src/routes/index/[tickerID]/options/dex/expiry/+page.svelte @@ -0,0 +1,35 @@ + + + + +
    +
    +
    + {#if rawData?.length > 0} + + {:else} +
    +
    + +
    +
    + {/if} +
    +
    +
    diff --git a/src/routes/index/[tickerID]/options/dex/strike/+page.server.ts b/src/routes/index/[tickerID]/options/dex/strike/+page.server.ts new file mode 100644 index 00000000..a0a52571 --- /dev/null +++ b/src/routes/index/[tickerID]/options/dex/strike/+page.server.ts @@ -0,0 +1,36 @@ + + +export const load = async ({ locals, params }) => { + const { apiKey, apiURL, user } = locals; + + const getData = async () => { + const postData = { + params: params.tickerID, + category: "strike", + type: "dex", + }; + + const response = await fetch(apiURL + "/options-gex-dex", { + 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 { + getData: await getData(), + }; +}; + + diff --git a/src/routes/index/[tickerID]/options/dex/strike/+page.svelte b/src/routes/index/[tickerID]/options/dex/strike/+page.svelte new file mode 100644 index 00000000..ce86f21e --- /dev/null +++ b/src/routes/index/[tickerID]/options/dex/strike/+page.svelte @@ -0,0 +1,35 @@ + + + + +
    +
    +
    + {#if data?.getData?.length > 0} + + {:else} +
    +
    + +
    +
    + {/if} +
    +
    +
    diff --git a/src/routes/index/[tickerID]/options/gex/+layout.svelte b/src/routes/index/[tickerID]/options/gex/+layout.svelte new file mode 100644 index 00000000..ca64a74b --- /dev/null +++ b/src/routes/index/[tickerID]/options/gex/+layout.svelte @@ -0,0 +1,94 @@ + + +
    + +
    diff --git a/src/routes/index/[tickerID]/options/gex/+page.server.ts b/src/routes/index/[tickerID]/options/gex/+page.server.ts new file mode 100644 index 00000000..7c36b588 --- /dev/null +++ b/src/routes/index/[tickerID]/options/gex/+page.server.ts @@ -0,0 +1,51 @@ + + +export const load = async ({ locals, params }) => { + const { apiKey, apiURL, user } = locals; + + const getData = async () => { + const postData = { + params: params.tickerID, + category: "overview", + type: "", + }; + + const response = await fetch(apiURL + "/options-gex-dex", { + 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: "one-year" }; + 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 { + getData: await getData(), + getHistoricalPrice: await getHistoricalPrice(), + }; +}; + + diff --git a/src/routes/index/[tickerID]/options/gex/+page.svelte b/src/routes/index/[tickerID]/options/gex/+page.svelte new file mode 100644 index 00000000..7914189f --- /dev/null +++ b/src/routes/index/[tickerID]/options/gex/+page.svelte @@ -0,0 +1,32 @@ + + + +
    +
    +
    + {#if data?.getData?.length > 0} + + {:else} +
    +
    + +
    +
    + {/if} +
    +
    +
    diff --git a/src/routes/index/[tickerID]/options/gex/expiry/+page.server.ts b/src/routes/index/[tickerID]/options/gex/expiry/+page.server.ts new file mode 100644 index 00000000..ffcde4bb --- /dev/null +++ b/src/routes/index/[tickerID]/options/gex/expiry/+page.server.ts @@ -0,0 +1,36 @@ + + +export const load = async ({ locals, params }) => { + const { apiKey, apiURL, user } = locals; + + const getData = async () => { + const postData = { + params: params.tickerID, + category: "expiry", + type: "gex", + }; + + const response = await fetch(apiURL + "/options-gex-dex", { + 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 { + getData: await getData(), + }; +}; + + diff --git a/src/routes/index/[tickerID]/options/gex/expiry/+page.svelte b/src/routes/index/[tickerID]/options/gex/expiry/+page.svelte new file mode 100644 index 00000000..3aecea83 --- /dev/null +++ b/src/routes/index/[tickerID]/options/gex/expiry/+page.svelte @@ -0,0 +1,35 @@ + + + + +
    +
    +
    + {#if rawData?.length > 0} + + {:else} +
    +
    + +
    +
    + {/if} +
    +
    +
    diff --git a/src/routes/index/[tickerID]/options/gex/strike/+page.server.ts b/src/routes/index/[tickerID]/options/gex/strike/+page.server.ts new file mode 100644 index 00000000..ccb15ad8 --- /dev/null +++ b/src/routes/index/[tickerID]/options/gex/strike/+page.server.ts @@ -0,0 +1,36 @@ + + +export const load = async ({ locals, params }) => { + const { apiKey, apiURL, user } = locals; + + const getData = async () => { + const postData = { + params: params.tickerID, + category: "strike", + type: "gex", + }; + + const response = await fetch(apiURL + "/options-gex-dex", { + 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 { + getData: await getData(), + }; +}; + + diff --git a/src/routes/index/[tickerID]/options/gex/strike/+page.svelte b/src/routes/index/[tickerID]/options/gex/strike/+page.svelte new file mode 100644 index 00000000..79b65e7e --- /dev/null +++ b/src/routes/index/[tickerID]/options/gex/strike/+page.svelte @@ -0,0 +1,35 @@ + + + + +
    +
    +
    + {#if data?.getData?.length > 0} + + {:else} +
    +
    + +
    +
    + {/if} +
    +
    +
    diff --git a/src/routes/index/[tickerID]/options/hottest-contracts/+page.server.ts b/src/routes/index/[tickerID]/options/hottest-contracts/+page.server.ts new file mode 100644 index 00000000..779ad063 --- /dev/null +++ b/src/routes/index/[tickerID]/options/hottest-contracts/+page.server.ts @@ -0,0 +1,52 @@ + + +export const load = async ({ locals, params }) => { + const { apiKey, apiURL, user } = locals; + + const getData = async () => { + const postData = { + ticker: params.tickerID, + }; + + const response = await fetch(apiURL + "/hottest-contracts", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-API-KEY": apiKey, + }, + body: JSON.stringify(postData), + }); + + let output = await response.json(); + output.volume = user?.tier !== "Pro" ? output?.volume?.slice(0, 3) : output?.volume; + output.openInterest = user?.tier !== "Pro" ? output?.openInterest?.slice(0, 3) : output?.openInterest; + + return output; + }; + + + const getHistoricalPrice = async () => { + const postData = { ticker: params.tickerID, timePeriod: "six-months" }; + 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 { + getData: await getData(), + getHistoricalPrice: await getHistoricalPrice(), + }; +}; + + diff --git a/src/routes/index/[tickerID]/options/hottest-contracts/+page.svelte b/src/routes/index/[tickerID]/options/hottest-contracts/+page.svelte new file mode 100644 index 00000000..66ea1f37 --- /dev/null +++ b/src/routes/index/[tickerID]/options/hottest-contracts/+page.svelte @@ -0,0 +1,1084 @@ + + + + +
    +
    +
    + {#if rawDataVolume?.length > 0} +
    +

    + Hottest Contracts (Highest Volume) +

    +
    + + + + + + {#each volumeList as item, index} + + + + + + + + + + + + + {/each} + +
    + + {item?.option_type === "C" ? "Call" : "Put"} + + + + {item?.dte} + + {item?.otm}% + + {item?.last} + + {item?.low}-{item?.high} + + {item?.volume?.toLocaleString("en-US")} + + {item?.open_interest?.toLocaleString("en-US")} + + {#if item?.changeOI >= 0} + +{item?.changeOI?.toLocaleString("en-US")} + {:else} + {item?.changeOI?.toLocaleString("en-US")} + {/if} + + {item?.iv}% + + {@html abbreviateNumberWithColor( + item?.total_premium, + false, + true, + )} +
    +
    + +

    + Highest OI Contracts +

    +
    + + + + + + {#each openInterestList as item, index} + + + + + + + + + + + + + {/each} + +
    + + {item?.option_type === "C" ? "Call" : "Put"} + + + + {item?.dte} + + {item?.otm}% + + {item?.last} + + {item?.low}-{item?.high} + + {item?.volume?.toLocaleString("en-US")} + + {item?.open_interest?.toLocaleString("en-US")} + + {#if item?.changeOI >= 0} + +{item?.changeOI?.toLocaleString("en-US")} + {:else} + {item?.changeOI?.toLocaleString("en-US")} + {/if} + + {item?.iv}% + + {@html abbreviateNumberWithColor( + item?.total_premium, + false, + true, + )} +
    +
    + + +
    + {:else} +
    +

    + Hottest Contracts +

    +
    + +
    +
    + {/if} +
    +
    +
    + + + + + + + diff --git a/src/routes/index/[tickerID]/options/oi/+layout.svelte b/src/routes/index/[tickerID]/options/oi/+layout.svelte new file mode 100644 index 00000000..c74d732a --- /dev/null +++ b/src/routes/index/[tickerID]/options/oi/+layout.svelte @@ -0,0 +1,83 @@ + + +
    + +
    diff --git a/src/routes/index/[tickerID]/options/oi/+page.server.ts b/src/routes/index/[tickerID]/options/oi/+page.server.ts new file mode 100644 index 00000000..da373752 --- /dev/null +++ b/src/routes/index/[tickerID]/options/oi/+page.server.ts @@ -0,0 +1,34 @@ + + +export const load = async ({ locals, params }) => { + const { apiKey, apiURL, user } = locals; + + const getData = async () => { + const postData = { + params: params.tickerID, + category: "strike" + }; + + const response = await fetch(apiURL + "/options-oi", { + 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 { + getData: await getData(), + }; +}; + + diff --git a/src/routes/index/[tickerID]/options/oi/+page.svelte b/src/routes/index/[tickerID]/options/oi/+page.svelte new file mode 100644 index 00000000..266e052b --- /dev/null +++ b/src/routes/index/[tickerID]/options/oi/+page.svelte @@ -0,0 +1,35 @@ + + + + +
    +
    +
    + {#if data?.getData?.length > 0} + + {:else} +
    +
    + +
    +
    + {/if} +
    +
    +
    diff --git a/src/routes/index/[tickerID]/options/oi/expiry/+page.server.ts b/src/routes/index/[tickerID]/options/oi/expiry/+page.server.ts new file mode 100644 index 00000000..f43ab639 --- /dev/null +++ b/src/routes/index/[tickerID]/options/oi/expiry/+page.server.ts @@ -0,0 +1,34 @@ + + +export const load = async ({ locals, params }) => { + const { apiKey, apiURL } = locals; + + const getData = async () => { + const postData = { + params: params.tickerID, + category: "expiry" + }; + + const response = await fetch(apiURL + "/options-oi", { + 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 { + getData: await getData(), + }; +}; + + diff --git a/src/routes/index/[tickerID]/options/oi/expiry/+page.svelte b/src/routes/index/[tickerID]/options/oi/expiry/+page.svelte new file mode 100644 index 00000000..84fee2ba --- /dev/null +++ b/src/routes/index/[tickerID]/options/oi/expiry/+page.svelte @@ -0,0 +1,35 @@ + + + + +
    +
    +
    + {#if rawData?.length > 0} + + {:else} +
    +
    + +
    +
    + {/if} +
    +
    +
    diff --git a/src/routes/index/[tickerID]/options/unusual-activity/+page.server.ts b/src/routes/index/[tickerID]/options/unusual-activity/+page.server.ts new file mode 100644 index 00000000..1552c12d --- /dev/null +++ b/src/routes/index/[tickerID]/options/unusual-activity/+page.server.ts @@ -0,0 +1,49 @@ + + +export const load = async ({ locals, params }) => { + const { apiKey, apiURL, user } = locals; + + const getData = async () => { + const postData = { + ticker: params.tickerID, + }; + + const response = await fetch(apiURL + "/unusual-activity", { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-API-KEY": apiKey, + }, + body: JSON.stringify(postData), + }); + + let 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 { + getData: await getData(), + getHistoricalPrice: await getHistoricalPrice(), + }; +}; + + diff --git a/src/routes/index/[tickerID]/options/unusual-activity/+page.svelte b/src/routes/index/[tickerID]/options/unusual-activity/+page.svelte new file mode 100644 index 00000000..8f0ffbd8 --- /dev/null +++ b/src/routes/index/[tickerID]/options/unusual-activity/+page.svelte @@ -0,0 +1,37 @@ + + + + +
    +
    +
    + {#if data?.getData?.length > 0} + + {:else} +
    +
    + +
    +
    + {/if} +
    +
    +
    diff --git a/src/routes/index/[tickerID]/options/volatility/+layout.svelte b/src/routes/index/[tickerID]/options/volatility/+layout.svelte new file mode 100644 index 00000000..22ee866b --- /dev/null +++ b/src/routes/index/[tickerID]/options/volatility/+layout.svelte @@ -0,0 +1,15 @@ +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    diff --git a/src/routes/index/[tickerID]/options/volatility/+page.server.ts b/src/routes/index/[tickerID]/options/volatility/+page.server.ts new file mode 100644 index 00000000..2deabb8c --- /dev/null +++ b/src/routes/index/[tickerID]/options/volatility/+page.server.ts @@ -0,0 +1,31 @@ + + +export const load = async ({ locals, params }) => { + const { apiKey, apiURL, user } = locals; + + const getData = async () => { + const postData = { + ticker: params.tickerID, + }; + + const response = await fetch(apiURL + "/implied-volatility", { + 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 { + getData: await getData(), + }; +}; + + diff --git a/src/routes/index/[tickerID]/options/volatility/+page.svelte b/src/routes/index/[tickerID]/options/volatility/+page.svelte new file mode 100644 index 00000000..239f8db6 --- /dev/null +++ b/src/routes/index/[tickerID]/options/volatility/+page.svelte @@ -0,0 +1,35 @@ + + + + +
    +
    +
    + {#if data?.getData?.length > 0} + + {:else} +
    +
    + +
    +
    + {/if} +
    +
    +
    diff --git a/src/routes/sitemap.xml/+server.ts b/src/routes/sitemap.xml/+server.ts index d12939d7..46c0f71a 100644 --- a/src/routes/sitemap.xml/+server.ts +++ b/src/routes/sitemap.xml/+server.ts @@ -158,7 +158,7 @@ const sitemap = ( ? "/stocks/" : ticker.type === "ETF" ? "/etf/" - : "/crypto/"; + : "/index/"; return ` ${website}${path}${ticker.id} diff --git a/src/routes/sitemap/+page.svelte b/src/routes/sitemap/+page.svelte index 1e473975..420377fd 100644 --- a/src/routes/sitemap/+page.svelte +++ b/src/routes/sitemap/+page.svelte @@ -1,6 +1,5 @@ - - - {$numberOfUnreadNotification > 0 ? `(${$numberOfUnreadNotification})` : ""} Sitemap - - Stocknear - - - - - - - - - - - - + -
    - +
    +
    + - - -
    +
    +