From 918f2578cec229f49898268e357490a4b1e30b12 Mon Sep 17 00:00:00 2001 From: MuslemRahimi Date: Sun, 22 Sep 2024 19:21:05 +0200 Subject: [PATCH] update cramer stock picks --- src/routes/cramer-tracker/+page.server.ts | 8 +- src/routes/cramer-tracker/+page.svelte | 342 ++++++++++++++++++++-- 2 files changed, 316 insertions(+), 34 deletions(-) diff --git a/src/routes/cramer-tracker/+page.server.ts b/src/routes/cramer-tracker/+page.server.ts index 6de70b33..60b2472c 100644 --- a/src/routes/cramer-tracker/+page.server.ts +++ b/src/routes/cramer-tracker/+page.server.ts @@ -1,4 +1,4 @@ -export const load = async ({ locals, setHeaders }) => { +export const load = async ({ locals }) => { const getCramerTracker = async () => { const { apiKey, apiURL, user } = locals; @@ -10,11 +10,7 @@ export const load = async ({ locals, setHeaders }) => { }, }); - let output = await response.json(); - - output = user?.tier !== "Pro" ? output?.slice(0, 5) : output; - - setHeaders({ "cache-control": "public, max-age=3000" }); + const output = await response.json(); return output; }; diff --git a/src/routes/cramer-tracker/+page.svelte b/src/routes/cramer-tracker/+page.svelte index f208ce68..60636839 100644 --- a/src/routes/cramer-tracker/+page.svelte +++ b/src/routes/cramer-tracker/+page.svelte @@ -3,19 +3,31 @@ import { numberOfUnreadNotification, screenWidth } from '$lib/store'; import { onMount } from 'svelte'; import ArrowLogo from "lucide-svelte/icons/move-up-right"; - import UpgradeToPro from '$lib/components/UpgradeToPro.svelte'; + import * as Card from "$lib/components/shadcn/card/index.ts"; + + import { Chart } from 'svelte-echarts' +import Lazy from '$lib/components/Lazy.svelte'; + +import { init, use } from 'echarts/core' +import { GaugeChart, LineChart,} from 'echarts/charts' +import { GridComponent, TooltipComponent } from 'echarts/components' +import { CanvasRenderer } from 'echarts/renderers' +use([GaugeChart, LineChart, GridComponent, TooltipComponent, CanvasRenderer]) - export let data; - let cloudFrontUrl = import.meta.env.VITE_IMAGE_URL; - - let isLoaded = false; - let rawData = [] - let displayList = []; + export let data; + let optionGraphWinrate; + let optionGraphReturn; + let cloudFrontUrl = import.meta.env.VITE_IMAGE_URL; + + let isLoaded = false; + let rawData = data?.getCramerTracker ?? []; + let displayList = rawData?.slice(0,50) ?? [] + let cumulativeList = [] + let winRate; - async function handleScroll() { const scrollThreshold = document.body.offsetHeight * 0.8; // 80% of the website height const isBottom = window.innerHeight + window.scrollY >= scrollThreshold; @@ -26,13 +38,204 @@ async function handleScroll() { } } +function computeWinRate(data) { + // Filter sentiments that should be considered bullish/buy or bearish/sell + const bullishSentiments = ['bullish', 'buy']; + const bearishSentiments = ['bearish', 'sell']; + + // Reduce through the array to calculate the total trades and wins + const { wins, totalTrades } = data.reduce((acc, item) => { + const sentiment = item.sentiment.toLowerCase(); // Normalize to lower case for easier comparison + const isBullish = bullishSentiments.some(keyword => sentiment.includes(keyword)); + const isBearish = bearishSentiments.some(keyword => sentiment.includes(keyword)); + // Count the total trades + acc.totalTrades++; + + // Evaluate the wins based on sentiment and returnSince + if ((isBullish && item.returnSince > 0) || (isBearish && item.returnSince < 0)) { + acc.wins++; + } + + return acc; + }, { wins: 0, totalTrades: 0 }); + + // Calculate and return the win rate percentage + return (wins / totalTrades) ; +} + + +function getPlotOptions() { + let dates = []; + const returnMap = {}; + const reverseData = data?.getCramerTracker?.reverse(); + // Iterate over the data and sum the returnSince values for each unique date + reverseData?.forEach(item => { + const { date, returnSince } = item; + + if (returnMap[date]) { + returnMap[date] += returnSince; // Add to the existing return + } else { + returnMap[date] = returnSince; // Initialize the return for this date + dates.push(date); // Save the unique date in the dates array + } + }); + + // Convert the returnMap to an array of objects (cumulativeList) + cumulativeList = Object?.keys(returnMap)?.map(date => ( + returnMap[date]?.toFixed(1) + )); + + //console.log('Cumulative List:', cumulativeList); + //console.log('Unique Dates:', dates); + + const optionCumulativeReturn = { + silent: true, + animation: false, + tooltip: { + trigger: 'axis', + hideDelay: 100, // Set the delay in milliseconds + }, + grid: { + left: '3%', + right: '4%', + bottom: '0%', + top: $screenWidth < 640 ? '20%' : '10%', + containLabel: true + }, + xAxis: [ + { + type: 'category', + boundaryGap: false, + splitLine: { + show: false, // Disable x-axis grid lines + }, + data: dates, + axisLabel: { + show: false // Hide x-axis labels + } + } + ], + yAxis: [ + { + type: 'value', + splitLine: { + show: false, // Disable x-axis grid lines + }, + axisLabel: { + show: false // Hide y-axis labels + } + }, + ], + series: [ + { + name: 'Cumulative Returns [%]', + type: 'line', + smooth: true, + lineStyle: { + width: 0 + }, + showSymbol: false, + areaStyle: { + opacity: 1, + color: '#3B82F6' + }, + emphasis: { + focus: 'series' + }, + data: cumulativeList + }, + ] +}; + + const optionWinrate = { + silent: true, + animation: false, + series: [ + { + type: 'gauge', + startAngle: 180, + endAngle: 0, + center: ['50%', '75%'], + radius: '90%', + min: 0, + max: 1, + splitNumber: 8, + axisLine: { + lineStyle: { + width: 6, + color: [ + [0.3, '#F71F4F'], + [0.7, '#FFA838'], + [1, '#20AE54'] + ] + } + }, + pointer: { + icon: 'path://M12.8,0.7l12,40.1H0.7L12.8,0.7z', + length: '12%', + width: 10, + offsetCenter: [0, '-60%'], + itemStyle: { + color: 'auto' + } + }, + axisTick: { + length: 4, + lineStyle: { + color: 'auto', + width: 1 + } + }, + splitLine: { + length: 20, + lineStyle: { + color: 'auto', + width: 2 + } + }, + axisLabel: { + formatter: function (value) { + return ''; + } + }, + title: { + offsetCenter: [0, '5%'], + fontSize: 14, + color: '#fff', + }, + detail: { + fontSize: 22, + offsetCenter: [0, '-20%'], + valueAnimation: true, + formatter: function (value) { + return (value * 100)?.toFixed(0)+ '%'; + }, + color: 'inherit' + }, + data: [ + { + value: winRate, + name: 'Winrate' + } + ] + } + ] +}; + + + return {optionCumulativeReturn, optionWinrate}; + } + + + onMount(() => { + winRate = computeWinRate(rawData); + + const {optionCumulativeReturn, optionWinrate} = getPlotOptions() + optionGraphReturn = optionCumulativeReturn; + optionGraphWinrate = optionWinrate; - onMount(() => { - rawData = data?.getCramerTracker ?? []; - displayList = rawData?.slice(0,50) ?? [] isLoaded = true; - window.addEventListener('scroll', handleScroll); return () => { window.removeEventListener('scroll', handleScroll); @@ -135,7 +338,44 @@ async function handleScroll() { {#if isLoaded} - + +
+ + + Cumulative Return + + Following Jim Cramer's stock picks since {rawData?.slice(0)?.at(0)?.date}, would have yielded a {cumulativeList?.slice(-1) > 0 ? '+' : ''}{cumulativeList?.slice(-1)}% cumulative return. + + + + +
+ +
+
+
+
+ + + Winrate + + Cramer was accurate in {(winRate*100)?.toFixed(0)}% of his last {rawData?.length} forecasts. + Time to consider the "Inverse Cramer" strategy? + + + + +
+ +
+
+
+
+ + + +
+
@@ -145,10 +385,7 @@ async function handleScroll() { - Symbol - - - Name + Company Name Date @@ -167,17 +404,50 @@ async function handleScroll() { {#each displayList as item, index} - + - - - {item?.ticker} - + + {#if index >= 5 && data?.user?.tier !== 'Pro'} + + + XXXX + + +
+ XXXXXXXXXXXXXXXX +
+ +
+ + + + + + + Upgrade + +
+
+ {:else} +
+ + {item?.ticker} + + + {item?.name?.length > charNumber + ? item?.name?.slice(0, charNumber) + "..." + : item?.name} + +
+ {/if} - - {item?.name?.length > charNumber ? item?.name?.slice(0,charNumber) + "..." : item?.name} - {new Date(item?.date)?.toLocaleString('en-US', { month: 'short', day: 'numeric', year: 'numeric', daySuffix: '2-digit' })} @@ -189,7 +459,7 @@ async function handleScroll() { - {item?.returnSince}% + {item?.returnSince > 0 ? '+' : ''}{item?.returnSince}% @@ -201,7 +471,6 @@ async function handleScroll() {
- @@ -276,4 +545,21 @@ async function handleScroll() { - \ No newline at end of file + \ No newline at end of file