clean code
This commit is contained in:
parent
f8161d5cd8
commit
fbab3bb01e
327
package-lock.json
generated
327
package-lock.json
generated
@ -38,15 +38,11 @@
|
|||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
"date-fns-tz": "^3.1.3",
|
"date-fns-tz": "^3.1.3",
|
||||||
"date-picker-svelte": "^2.12.0",
|
"date-picker-svelte": "^2.12.0",
|
||||||
"echarts": "^5.5.1",
|
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"flowbite-svelte": "^0.46.15",
|
|
||||||
"got": "^14.4.2",
|
"got": "^14.4.2",
|
||||||
"html2canvas": "^1.4.1",
|
|
||||||
"html2canvas-pro": "^1.5.8",
|
"html2canvas-pro": "^1.5.8",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"katex": "^0.16.11",
|
"katex": "^0.16.11",
|
||||||
"lightweight-charts": "^4.1.3",
|
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"lucide-svelte": "^0.438.0",
|
"lucide-svelte": "^0.438.0",
|
||||||
"luxon": "^3.5.0",
|
"luxon": "^3.5.0",
|
||||||
@ -58,8 +54,6 @@
|
|||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"prettier-plugin-svelte": "^3.2.7",
|
"prettier-plugin-svelte": "^3.2.7",
|
||||||
"quill": "^2.0.2",
|
|
||||||
"quill-delta-to-html": "^0.12.1",
|
|
||||||
"rollup-plugin-visualizer": "^5.12.0",
|
"rollup-plugin-visualizer": "^5.12.0",
|
||||||
"rollup-plugin-web-worker-loader": "^1.6.1",
|
"rollup-plugin-web-worker-loader": "^1.6.1",
|
||||||
"sass": "^1.75.0",
|
"sass": "^1.75.0",
|
||||||
@ -67,10 +61,8 @@
|
|||||||
"string-similarity": "^4.0.4",
|
"string-similarity": "^4.0.4",
|
||||||
"svelte": "^4.2.15",
|
"svelte": "^4.2.15",
|
||||||
"svelte-check": "^3.6.9",
|
"svelte-check": "^3.6.9",
|
||||||
"svelte-echarts": "^1.0.0-rc3",
|
|
||||||
"svelte-inview": "^4.0.2",
|
"svelte-inview": "^4.0.2",
|
||||||
"svelte-lazy": "^1.2.11",
|
"svelte-lazy": "^1.2.11",
|
||||||
"svelte-lightweight-charts": "^2.2.0",
|
|
||||||
"svelte-loading-spinners": "^0.3.6",
|
"svelte-loading-spinners": "^0.3.6",
|
||||||
"svelte-preprocess": "^5.1.4",
|
"svelte-preprocess": "^5.1.4",
|
||||||
"svelte-sonner": "^0.3.27",
|
"svelte-sonner": "^0.3.27",
|
||||||
@ -1280,16 +1272,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@popperjs/core": {
|
|
||||||
"version": "2.11.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
|
||||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
|
||||||
"dev": true,
|
|
||||||
"funding": {
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/popperjs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/plugin-commonjs": {
|
"node_modules/@rollup/plugin-commonjs": {
|
||||||
"version": "28.0.1",
|
"version": "28.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.1.tgz",
|
||||||
@ -2822,12 +2804,6 @@
|
|||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/@yr/monotone-cubic-spline": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@yr/monotone-cubic-spline/-/monotone-cubic-spline-1.0.3.tgz",
|
|
||||||
"integrity": "sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/abs-svg-path": {
|
"node_modules/abs-svg-path": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz",
|
||||||
@ -2989,21 +2965,6 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/apexcharts": {
|
|
||||||
"version": "3.53.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.53.0.tgz",
|
|
||||||
"integrity": "sha512-QESZHZY3w9LPQ64PGh1gEdfjYjJ5Jp+Dfy0D/CLjsLOPTpXzdxwlNMqRj+vPbTcP0nAHgjWv1maDqcEq6u5olw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@yr/monotone-cubic-spline": "^1.0.3",
|
|
||||||
"svg.draggable.js": "^2.2.2",
|
|
||||||
"svg.easing.js": "^2.0.0",
|
|
||||||
"svg.filter.js": "^2.0.2",
|
|
||||||
"svg.pathmorphing.js": "^0.1.3",
|
|
||||||
"svg.resize.js": "^1.4.3",
|
|
||||||
"svg.select.js": "^3.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/aria-query": {
|
"node_modules/aria-query": {
|
||||||
"version": "5.3.0",
|
"version": "5.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
|
||||||
@ -4331,22 +4292,6 @@
|
|||||||
"safe-buffer": "^5.0.1"
|
"safe-buffer": "^5.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/echarts": {
|
|
||||||
"version": "5.5.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.1.tgz",
|
|
||||||
"integrity": "sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": "2.3.0",
|
|
||||||
"zrender": "5.6.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/echarts/node_modules/tslib": {
|
|
||||||
"version": "2.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
|
|
||||||
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.79",
|
"version": "1.5.79",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.79.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.79.tgz",
|
||||||
@ -4688,12 +4633,6 @@
|
|||||||
"es5-ext": "~0.10.14"
|
"es5-ext": "~0.10.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/eventemitter3": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/events": {
|
"node_modules/events": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||||
@ -4772,12 +4711,6 @@
|
|||||||
"node": ">=0.4.0"
|
"node": ">=0.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fancy-canvas": {
|
|
||||||
"version": "2.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/fancy-canvas/-/fancy-canvas-2.1.0.tgz",
|
|
||||||
"integrity": "sha512-nifxXJ95JNLFR2NgRV4/MxVP45G9909wJTEKz5fg/TZS20JJZA6hfgRVh/bC9bwl2zBtBNcYPjiBE4njQHVBwQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/fast-deep-equal": {
|
"node_modules/fast-deep-equal": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
@ -4785,12 +4718,6 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/fast-diff": {
|
|
||||||
"version": "1.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
|
|
||||||
"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/fast-glob": {
|
"node_modules/fast-glob": {
|
||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
|
||||||
@ -4876,46 +4803,6 @@
|
|||||||
"dtype": "^2.0.0"
|
"dtype": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/flowbite": {
|
|
||||||
"version": "2.5.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/flowbite/-/flowbite-2.5.1.tgz",
|
|
||||||
"integrity": "sha512-7jP1jy9c3QP7y+KU9lc8ueMkTyUdMDvRP+lteSWgY5TigSZjf9K1kqZxmqjhbx2gBnFQxMl1GAjVThCa8cEpKA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@popperjs/core": "^2.9.3",
|
|
||||||
"flowbite-datepicker": "^1.3.0",
|
|
||||||
"mini-svg-data-uri": "^1.4.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/flowbite-datepicker": {
|
|
||||||
"version": "1.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/flowbite-datepicker/-/flowbite-datepicker-1.3.0.tgz",
|
|
||||||
"integrity": "sha512-CLVqzuoE2vkUvWYK/lJ6GzT0be5dlTbH3uuhVwyB67+PjqJWABm2wv68xhBf5BqjpBxvTSQ3mrmLHpPJ2tvrSQ==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
||||||
"flowbite": "^2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/flowbite-svelte": {
|
|
||||||
"version": "0.46.15",
|
|
||||||
"resolved": "https://registry.npmjs.org/flowbite-svelte/-/flowbite-svelte-0.46.15.tgz",
|
|
||||||
"integrity": "sha512-xWhyLDez/gafTAmQayPMPKmWj1BAoq80SoA48yHZ12Wk3Vu3hFrELLUZf0UksxnjQL9hMOKRUlYl3/IH6pHwnQ==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@floating-ui/dom": "^1.6.7",
|
|
||||||
"apexcharts": "^3.49.2",
|
|
||||||
"flowbite": "^2.4.1",
|
|
||||||
"tailwind-merge": "^2.3.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0",
|
|
||||||
"pnpm": ">=8.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"svelte": "^3.55.1 || ^4.0.0 || ^5.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/focus-trap": {
|
"node_modules/focus-trap": {
|
||||||
"version": "7.5.4",
|
"version": "7.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz",
|
||||||
@ -5578,20 +5465,6 @@
|
|||||||
"integrity": "sha512-08iL2VyCRbkQKBySkSh6m8zMUa3sADAxGVWs3Z1aPcUkTJeK0ETG4Fc27tEmQBGUAXZjIsXOZqBvacuVNSC/fQ==",
|
"integrity": "sha512-08iL2VyCRbkQKBySkSh6m8zMUa3sADAxGVWs3Z1aPcUkTJeK0ETG4Fc27tEmQBGUAXZjIsXOZqBvacuVNSC/fQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/html2canvas": {
|
|
||||||
"version": "1.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
|
|
||||||
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"css-line-break": "^2.1.0",
|
|
||||||
"text-segmentation": "^1.0.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/html2canvas-pro": {
|
"node_modules/html2canvas-pro": {
|
||||||
"version": "1.5.8",
|
"version": "1.5.8",
|
||||||
"resolved": "https://registry.npmjs.org/html2canvas-pro/-/html2canvas-pro-1.5.8.tgz",
|
"resolved": "https://registry.npmjs.org/html2canvas-pro/-/html2canvas-pro-1.5.8.tgz",
|
||||||
@ -6455,15 +6328,6 @@
|
|||||||
"url": "https://opencollective.com/parcel"
|
"url": "https://opencollective.com/parcel"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lightweight-charts": {
|
|
||||||
"version": "4.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/lightweight-charts/-/lightweight-charts-4.1.3.tgz",
|
|
||||||
"integrity": "sha512-SJacmEyx3LmT2Qsc7Kq7cEX7nEHtQv0MOlujhRlcDxhW62pG6nkBlcM52/jNqkq8B28KQeVmgOQ7zrdJ4BCPDw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"fancy-canvas": "2.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/lilconfig": {
|
"node_modules/lilconfig": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
||||||
@ -6515,12 +6379,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||||
},
|
},
|
||||||
"node_modules/lodash.clonedeep": {
|
|
||||||
"version": "4.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
|
||||||
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/lodash.includes": {
|
"node_modules/lodash.includes": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||||
@ -6533,12 +6391,6 @@
|
|||||||
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
|
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/lodash.isequal": {
|
|
||||||
"version": "4.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
|
||||||
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/lodash.isinteger": {
|
"node_modules/lodash.isinteger": {
|
||||||
"version": "4.0.4",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
||||||
@ -6875,15 +6727,6 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mini-svg-data-uri": {
|
|
||||||
"version": "1.4.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz",
|
|
||||||
"integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==",
|
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
|
||||||
"mini-svg-data-uri": "cli.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/minimalistic-assert": {
|
"node_modules/minimalistic-assert": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||||
@ -7250,12 +7093,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/parchment": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/parenthesis": {
|
"node_modules/parenthesis": {
|
||||||
"version": "3.1.8",
|
"version": "3.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/parenthesis/-/parenthesis-3.1.8.tgz",
|
"resolved": "https://registry.npmjs.org/parenthesis/-/parenthesis-3.1.8.tgz",
|
||||||
@ -7830,44 +7667,6 @@
|
|||||||
"integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==",
|
"integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/quill": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/quill/-/quill-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-QfazNrhMakEdRG57IoYFwffUIr04LWJxbS/ZkidRFXYCQt63c1gK6Z7IHUXMx/Vh25WgPBU42oBaNzQ0K1R/xw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"eventemitter3": "^5.0.1",
|
|
||||||
"lodash-es": "^4.17.21",
|
|
||||||
"parchment": "^3.0.0",
|
|
||||||
"quill-delta": "^5.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"npm": ">=8.2.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/quill-delta": {
|
|
||||||
"version": "5.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz",
|
|
||||||
"integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"fast-diff": "^1.3.0",
|
|
||||||
"lodash.clonedeep": "^4.5.0",
|
|
||||||
"lodash.isequal": "^4.5.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 12.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/quill-delta-to-html": {
|
|
||||||
"version": "0.12.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/quill-delta-to-html/-/quill-delta-to-html-0.12.1.tgz",
|
|
||||||
"integrity": "sha512-QhpeMk9+5ge3HYbL5A0Ewz3pXCsbemqGvIF/kw5D6D4V68AtcUp7yt9xNUkzOk/0IQz43hKy3IkzBzRhLIE+oA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"lodash.isequal": "^4.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/raf": {
|
"node_modules/raf": {
|
||||||
"version": "3.4.1",
|
"version": "3.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
|
||||||
@ -8693,16 +8492,6 @@
|
|||||||
"svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0"
|
"svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svelte-echarts": {
|
|
||||||
"version": "1.0.0-rc3",
|
|
||||||
"resolved": "https://registry.npmjs.org/svelte-echarts/-/svelte-echarts-1.0.0-rc3.tgz",
|
|
||||||
"integrity": "sha512-jM9EU4m7yFJaISCXhnGgtYirMD1VP2DzoI3Lhbl2jsvGzZFIAg8wsn/5n8GccZxm7wHgzcEL48EVJVlrKPU7gQ==",
|
|
||||||
"dev": true,
|
|
||||||
"peerDependencies": {
|
|
||||||
"echarts": "^5.0.0",
|
|
||||||
"svelte": "^4.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/svelte-hmr": {
|
"node_modules/svelte-hmr": {
|
||||||
"version": "0.16.0",
|
"version": "0.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz",
|
"resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz",
|
||||||
@ -8734,16 +8523,6 @@
|
|||||||
"svelte": "^3.0.0 || ^4.0.0 || ^5.0.0"
|
"svelte": "^3.0.0 || ^4.0.0 || ^5.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svelte-lightweight-charts": {
|
|
||||||
"version": "2.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/svelte-lightweight-charts/-/svelte-lightweight-charts-2.2.0.tgz",
|
|
||||||
"integrity": "sha512-LXdha4vfLMuOPc0Yyetu+DLSDJkPryGkufUQgpCkfguCscSQUcrLiI9MdqKwQk6Fkm6AZbg8SQ5qDIYxcyC+Dg==",
|
|
||||||
"dev": true,
|
|
||||||
"peerDependencies": {
|
|
||||||
"lightweight-charts": ">=4.0.0",
|
|
||||||
"svelte": ">=3.44.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/svelte-loading-spinners": {
|
"node_modules/svelte-loading-spinners": {
|
||||||
"version": "0.3.6",
|
"version": "0.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/svelte-loading-spinners/-/svelte-loading-spinners-0.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/svelte-loading-spinners/-/svelte-loading-spinners-0.3.6.tgz",
|
||||||
@ -8891,97 +8670,6 @@
|
|||||||
"svg-path-bounds": "^1.0.1"
|
"svg-path-bounds": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/svg.draggable.js": {
|
|
||||||
"version": "2.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz",
|
|
||||||
"integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"svg.js": "^2.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/svg.easing.js": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"svg.js": ">=2.3.x"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/svg.filter.js": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"svg.js": "^2.2.5"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/svg.js": {
|
|
||||||
"version": "2.7.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz",
|
|
||||||
"integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/svg.pathmorphing.js": {
|
|
||||||
"version": "0.1.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz",
|
|
||||||
"integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"svg.js": "^2.4.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/svg.resize.js": {
|
|
||||||
"version": "1.4.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz",
|
|
||||||
"integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"svg.js": "^2.6.5",
|
|
||||||
"svg.select.js": "^2.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/svg.resize.js/node_modules/svg.select.js": {
|
|
||||||
"version": "2.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz",
|
|
||||||
"integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"svg.js": "^2.2.5"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/svg.select.js": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"svg.js": "^2.6.5"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/tabbable": {
|
"node_modules/tabbable": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz",
|
||||||
@ -10025,21 +9713,6 @@
|
|||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"node_modules/zrender": {
|
|
||||||
"version": "5.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.0.tgz",
|
|
||||||
"integrity": "sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": "2.3.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/zrender/node_modules/tslib": {
|
|
||||||
"version": "2.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz",
|
|
||||||
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
|
|
||||||
"dev": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,15 +38,11 @@
|
|||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
"date-fns-tz": "^3.1.3",
|
"date-fns-tz": "^3.1.3",
|
||||||
"date-picker-svelte": "^2.12.0",
|
"date-picker-svelte": "^2.12.0",
|
||||||
"echarts": "^5.5.1",
|
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"flowbite-svelte": "^0.46.15",
|
|
||||||
"got": "^14.4.2",
|
"got": "^14.4.2",
|
||||||
"html2canvas": "^1.4.1",
|
|
||||||
"html2canvas-pro": "^1.5.8",
|
"html2canvas-pro": "^1.5.8",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"katex": "^0.16.11",
|
"katex": "^0.16.11",
|
||||||
"lightweight-charts": "^4.1.3",
|
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"lucide-svelte": "^0.438.0",
|
"lucide-svelte": "^0.438.0",
|
||||||
"luxon": "^3.5.0",
|
"luxon": "^3.5.0",
|
||||||
@ -58,8 +54,6 @@
|
|||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"prettier-plugin-svelte": "^3.2.7",
|
"prettier-plugin-svelte": "^3.2.7",
|
||||||
"quill": "^2.0.2",
|
|
||||||
"quill-delta-to-html": "^0.12.1",
|
|
||||||
"rollup-plugin-visualizer": "^5.12.0",
|
"rollup-plugin-visualizer": "^5.12.0",
|
||||||
"rollup-plugin-web-worker-loader": "^1.6.1",
|
"rollup-plugin-web-worker-loader": "^1.6.1",
|
||||||
"sass": "^1.75.0",
|
"sass": "^1.75.0",
|
||||||
@ -67,10 +61,8 @@
|
|||||||
"string-similarity": "^4.0.4",
|
"string-similarity": "^4.0.4",
|
||||||
"svelte": "^4.2.15",
|
"svelte": "^4.2.15",
|
||||||
"svelte-check": "^3.6.9",
|
"svelte-check": "^3.6.9",
|
||||||
"svelte-echarts": "^1.0.0-rc3",
|
|
||||||
"svelte-inview": "^4.0.2",
|
"svelte-inview": "^4.0.2",
|
||||||
"svelte-lazy": "^1.2.11",
|
"svelte-lazy": "^1.2.11",
|
||||||
"svelte-lightweight-charts": "^2.2.0",
|
|
||||||
"svelte-loading-spinners": "^0.3.6",
|
"svelte-loading-spinners": "^0.3.6",
|
||||||
"svelte-preprocess": "^5.1.4",
|
"svelte-preprocess": "^5.1.4",
|
||||||
"svelte-sonner": "^0.3.27",
|
"svelte-sonner": "^0.3.27",
|
||||||
|
|||||||
@ -1,411 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Chart } from "svelte-echarts";
|
|
||||||
import InfoModal from "$lib/components/InfoModal.svelte";
|
|
||||||
import {
|
|
||||||
dcfComponent,
|
|
||||||
stockTicker,
|
|
||||||
screenWidth,
|
|
||||||
getCache,
|
|
||||||
setCache,
|
|
||||||
} from "$lib/store";
|
|
||||||
import Lazy from "svelte-lazy";
|
|
||||||
|
|
||||||
//export let quantData;
|
|
||||||
export let data;
|
|
||||||
|
|
||||||
let fairPrice;
|
|
||||||
let isLoaded = false;
|
|
||||||
let lastPrice: Number;
|
|
||||||
|
|
||||||
let optionsBarChart;
|
|
||||||
|
|
||||||
let change: Number;
|
|
||||||
|
|
||||||
const contentModal = `<span class="text-white">
|
|
||||||
Discounted Cash Flow (DCF) is a core method for valuing a company's true worth. It starts by predicting the company's growth and how it affects its future cash flow.
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
Then, it adjusts these future cash flows to their present value using a discount rate. This considers the risk associated with future cash flow predictions. Simply put, higher discount rates signal greater risk.
|
|
||||||
</span>`;
|
|
||||||
|
|
||||||
const plotBarChart = () => {
|
|
||||||
const options = {
|
|
||||||
grid: {
|
|
||||||
left: "0%",
|
|
||||||
right: "0%",
|
|
||||||
top: "0%",
|
|
||||||
bottom: "0%",
|
|
||||||
containLabel: true,
|
|
||||||
},
|
|
||||||
animation: false,
|
|
||||||
silent: true,
|
|
||||||
xAxis: {
|
|
||||||
type: "value",
|
|
||||||
axisLabel: {
|
|
||||||
show: false, // Hide the x-axis labels
|
|
||||||
},
|
|
||||||
axisLine: {
|
|
||||||
show: false, // Hide the y-axis lines
|
|
||||||
},
|
|
||||||
splitLine: {
|
|
||||||
show: false, // Hide the grid lines on the y-axis
|
|
||||||
},
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
type: "category",
|
|
||||||
axisLabel: {
|
|
||||||
show: false, // Hide the x-axis labels
|
|
||||||
},
|
|
||||||
axisLine: {
|
|
||||||
show: true, // Hide the y-axis lines
|
|
||||||
},
|
|
||||||
splitLine: {
|
|
||||||
show: false, // Hide the grid lines on the y-axis
|
|
||||||
},
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: "Current Price",
|
|
||||||
type: "bar",
|
|
||||||
barWidth: $screenWidth < 640 ? "30%" : "30%",
|
|
||||||
smooth: true,
|
|
||||||
stack: change < 0 ? "lastPriceStack" : "",
|
|
||||||
data: [lastPrice],
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
position: "inside",
|
|
||||||
formatter: function (params) {
|
|
||||||
return [
|
|
||||||
"{a|Current Price}",
|
|
||||||
"{b|" + "$" + params.value + "}",
|
|
||||||
].join("\n");
|
|
||||||
},
|
|
||||||
rich: {
|
|
||||||
a: {
|
|
||||||
color: "white",
|
|
||||||
fontSize: $screenWidth < 640 ? 15 : 20,
|
|
||||||
fontWeight: "bold",
|
|
||||||
},
|
|
||||||
b: {
|
|
||||||
color: "white",
|
|
||||||
fontSize: $screenWidth < 640 ? 24 : 30,
|
|
||||||
fontWeight: "bold",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
markLine: {
|
|
||||||
symbol: "none",
|
|
||||||
label: {
|
|
||||||
position: "middle",
|
|
||||||
formatter: "{b}",
|
|
||||||
},
|
|
||||||
lineStyle: {
|
|
||||||
color: "white",
|
|
||||||
fontWeight: "bold", // Make the mark line bold
|
|
||||||
type: "dashed",
|
|
||||||
width: 2, // Increase the dashed line width
|
|
||||||
},
|
|
||||||
data: [{ type: "average", name: "" }],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Fair Price",
|
|
||||||
type: "bar",
|
|
||||||
barWidth: $screenWidth < 640 ? "30%" : "30%",
|
|
||||||
smooth: true,
|
|
||||||
stack: change > 0 ? "fairPriceStack" : "",
|
|
||||||
data: [fairPrice],
|
|
||||||
itemStyle: {
|
|
||||||
color: "#2DC97E",
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
position: lastPrice > fairPrice ? "outside" : "inside",
|
|
||||||
formatter: function (params) {
|
|
||||||
return ["{a|Fair Price}", "{b|" + "$" + params.value + "}"].join(
|
|
||||||
"\n",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
rich: {
|
|
||||||
a: {
|
|
||||||
color: "white",
|
|
||||||
fontSize: $screenWidth < 640 ? 15 : 20,
|
|
||||||
fontWeight: "bold",
|
|
||||||
},
|
|
||||||
b: {
|
|
||||||
color: "white",
|
|
||||||
fontSize: $screenWidth < 640 ? 24 : 30,
|
|
||||||
fontWeight: "bold",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
markLine: {
|
|
||||||
symbol: "none",
|
|
||||||
label: {
|
|
||||||
position: "middle",
|
|
||||||
formatter: "{b}",
|
|
||||||
},
|
|
||||||
lineStyle: {
|
|
||||||
color: "white",
|
|
||||||
fontWeight: "bold", // Make the mark line bold
|
|
||||||
type: "dashed",
|
|
||||||
width: 2, // Increase the dashed line width
|
|
||||||
},
|
|
||||||
data: [{ type: "average", name: "" }],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Add the new bar chart on top
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "Difference",
|
|
||||||
type: "bar",
|
|
||||||
barWidth: "100%", // Set the width to cover the other bars
|
|
||||||
smooth: true,
|
|
||||||
stack: change > 0 ? "fairPriceStack" : "lastPriceStack", // Stack this bar on top of the others
|
|
||||||
data: change > 0 ? [lastPrice - fairPrice] : [fairPrice - lastPrice], // Set the value to 200
|
|
||||||
itemStyle: {
|
|
||||||
color: "#FF2F1F",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
aria: {
|
|
||||||
enabled: true,
|
|
||||||
decal: {
|
|
||||||
show: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return options;
|
|
||||||
};
|
|
||||||
|
|
||||||
async function getFairPrice(ticker) {
|
|
||||||
const cachedData = getCache(ticker, "getFairPrice");
|
|
||||||
if (cachedData) {
|
|
||||||
fairPrice = cachedData;
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
const response = await fetch("/api/ticker-data", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ ticker: ticker, path: "fair-price" }),
|
|
||||||
});
|
|
||||||
fairPrice = await response.json();
|
|
||||||
|
|
||||||
setCache(ticker, fairPrice, "getFairPrice");
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Failed to fetch swap data:", error);
|
|
||||||
fairPrice = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fairPrice !== null && fairPrice >= 0) {
|
|
||||||
$dcfComponent = true;
|
|
||||||
} else {
|
|
||||||
$dcfComponent = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$: {
|
|
||||||
if ($stockTicker && typeof window !== "undefined") {
|
|
||||||
isLoaded = false;
|
|
||||||
getFairPrice($stockTicker).then(() => {
|
|
||||||
if (fairPrice !== null) {
|
|
||||||
lastPrice = data?.getStockQuote?.price?.toFixed(2);
|
|
||||||
change = ((1 - fairPrice / lastPrice) * 100)?.toFixed(2);
|
|
||||||
optionsBarChart = plotBarChart();
|
|
||||||
}
|
|
||||||
|
|
||||||
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="dcfInfo"
|
|
||||||
class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-3xl font-bold"
|
|
||||||
>
|
|
||||||
Discounted Cashflow Model
|
|
||||||
</label>
|
|
||||||
<InfoModal
|
|
||||||
title={"Discounted Cashflow Model"}
|
|
||||||
content={contentModal}
|
|
||||||
id={"dcfInfo"}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if ["Pro", "Plus"]?.includes(data?.user?.tier)}
|
|
||||||
{#if isLoaded}
|
|
||||||
{#if fairPrice !== null}
|
|
||||||
<div
|
|
||||||
class="p-3 sm:p-0 mt-2 pb-8 sm:pb-2 rounded-md bg-default sm:bg-default"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="mt-4 text-white text-[1rem] sm:text-xl pb-4 sm:pb-0 m-auto text-start"
|
|
||||||
>
|
|
||||||
The DCF model signals a
|
|
||||||
|
|
||||||
{#if change < -3}
|
|
||||||
<span class="text-green-700 dark:text-[#00FC50]">
|
|
||||||
<svg
|
|
||||||
class="w-6 h-6 sm:w-7 sm:h-7 inline-block"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
><g
|
|
||||||
fill="none"
|
|
||||||
stroke="#00FC50"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2.5"
|
|
||||||
><path d="m3 17l6-6l4 4l8-8" /><path d="M17 7h4v4" /></g
|
|
||||||
></svg
|
|
||||||
>
|
|
||||||
Buy
|
|
||||||
</span>
|
|
||||||
{:else if change > 3}
|
|
||||||
<span class="text-[#E57C34]">
|
|
||||||
<svg
|
|
||||||
class="w-6 h-6 sm:w-7 sm:h-7 inline-block"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 256 256"
|
|
||||||
><path
|
|
||||||
fill="#ff2f1f"
|
|
||||||
d="M244 136v64a12 12 0 0 1-12 12h-64a12 12 0 0 1 0-24h35l-67-67l-31.51 31.52a12 12 0 0 1-17 0l-72-72a12 12 0 0 1 17-17L96 127l31.51-31.52a12 12 0 0 1 17 0L220 171v-35a12 12 0 0 1 24 0Z"
|
|
||||||
/></svg
|
|
||||||
>
|
|
||||||
Sell
|
|
||||||
</span>
|
|
||||||
{:else}
|
|
||||||
<span class="text-red-700 dark:text-[#FF2F1F]">
|
|
||||||
<svg
|
|
||||||
class="w-6 h-6 sm:w-7 sm:h-7 inline-block"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
><path
|
|
||||||
fill="#e57c34"
|
|
||||||
d="m22 12l-4-4v3H3v2h15v3l4-4Z"
|
|
||||||
/></svg
|
|
||||||
>
|
|
||||||
Hold
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if change > 0}
|
|
||||||
<div class="text-white">
|
|
||||||
The Stock Price is
|
|
||||||
<span class="text-[#FF2F1F] sm:text-lg"
|
|
||||||
>{Math?.abs(change)}% overvalued</span
|
|
||||||
>.
|
|
||||||
</div>
|
|
||||||
{:else if change < 0}
|
|
||||||
<div class="text-white">
|
|
||||||
The Stock Price is
|
|
||||||
<span class="text-green-700 dark:text-[#00FC50]"
|
|
||||||
>{Math?.abs(change)}% undervalued</span
|
|
||||||
>.
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<div class="text-white text-md mt-2">
|
|
||||||
<span class="text-blue-400">${$stockTicker}</span>
|
|
||||||
(${lastPrice}) is trading {change < 0 ? "below" : "above"}
|
|
||||||
our estimate of fair value (${fairPrice}).
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
<div class="text-white text-md mb-10 -mt-3">
|
|
||||||
What is the Fair Price of <span class="font-normal text-blue-400"
|
|
||||||
>${$stockTicker}</span
|
|
||||||
> when looking at its future cash flows? For this estimate we use a
|
|
||||||
Discounted Cash Flow model (DCF).
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Lazy
|
|
||||||
height={300}
|
|
||||||
fadeOption={{ delay: 100, duration: 500 }}
|
|
||||||
keep={true}
|
|
||||||
>
|
|
||||||
<div class="app w-full m-auto mb-5">
|
|
||||||
<Chart options={optionsBarChart} class="chart w-full" />
|
|
||||||
</div>
|
|
||||||
</Lazy>
|
|
||||||
|
|
||||||
{#if Math?.abs(change) > 30}
|
|
||||||
<div
|
|
||||||
class=" mb-5 text-gray-100 text-sm sm:text-[1rem] sm:rounded-md h-auto border border-gray-600 p-4"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
class="w-5 h-5 inline-block mr-0.5 shrink-0"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 256 256"
|
|
||||||
><path
|
|
||||||
fill="#fff"
|
|
||||||
d="M128 24a104 104 0 1 0 104 104A104.11 104.11 0 0 0 128 24m-4 48a12 12 0 1 1-12 12a12 12 0 0 1 12-12m12 112a16 16 0 0 1-16-16v-40a8 8 0 0 1 0-16a16 16 0 0 1 16 16v40a8 8 0 0 1 0 16"
|
|
||||||
/></svg
|
|
||||||
>
|
|
||||||
Caution: The DCF model may not be reliable for
|
|
||||||
<span class="text-blue-400">${$stockTicker}</span> due to significant
|
|
||||||
deviation between intrinsic value and current price.
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</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}
|
|
||||||
{:else}
|
|
||||||
<div
|
|
||||||
class="shadow-lg shadow-bg-[#000] bg-[#111112] sm:bg-opacity-[0.5] text-sm sm:text-[1rem] rounded-md w-full p-4 min-h-24 mt-4 text-white m-auto flex justify-center items-center text-center font-semibold"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
class="mr-1.5 w-5 h-5 inline-block"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
><path
|
|
||||||
fill="#A3A3A3"
|
|
||||||
d="M17 9V7c0-2.8-2.2-5-5-5S7 4.2 7 7v2c-1.7 0-3 1.3-3 3v7c0 1.7 1.3 3 3 3h10c1.7 0 3-1.3 3-3v-7c0-1.7-1.3-3-3-3M9 7c0-1.7 1.3-3 3-3s3 1.3 3 3v2H9z"
|
|
||||||
/></svg
|
|
||||||
>
|
|
||||||
Unlock content with
|
|
||||||
<a
|
|
||||||
class="inline-block ml-2 text-blue-400 sm:hover:text-white"
|
|
||||||
href="/pricing">Pro Subscription</a
|
|
||||||
>
|
|
||||||
</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: 180px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -277,7 +277,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="chart mt-5 sm:mt-0 border border-gray-300 dark:border-gray-800 rounded"
|
class="shadow-sm mt-5 sm:mt-0 border border-gray-300 dark:border-gray-800 rounded"
|
||||||
use:highcharts={config}
|
use:highcharts={config}
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
|
|||||||
@ -1,57 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import * as HoverCard from "$lib/components/shadcn/hover-card/index.js";
|
|
||||||
|
|
||||||
import { ColorType } from "lightweight-charts";
|
|
||||||
import { Chart, BaselineSeries, PriceLine } from "svelte-lightweight-charts";
|
|
||||||
import { screenWidth, getCache, setCache } from "$lib/store";
|
|
||||||
import { abbreviateNumber } from "$lib/utils";
|
|
||||||
import { afterUpdate } from "svelte";
|
|
||||||
export let symbol;
|
export let symbol;
|
||||||
export let assetType = "stock";
|
export let assetType = "stock";
|
||||||
export let link = null;
|
export let link = null;
|
||||||
|
|
||||||
let priceData = [];
|
|
||||||
let changesPercentage = 0;
|
|
||||||
let change = 0;
|
|
||||||
let stockChartData;
|
|
||||||
|
|
||||||
async function getStockData(ticker: string) {
|
|
||||||
const cachedData = getCache(ticker, "hoverStockChart");
|
|
||||||
if (cachedData) {
|
|
||||||
stockChartData = cachedData;
|
|
||||||
} else {
|
|
||||||
const postData = { ticker: ticker };
|
|
||||||
const response = await fetch("/api/hover-stock-chart", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify(postData),
|
|
||||||
});
|
|
||||||
|
|
||||||
stockChartData = (await response?.json()) ?? {};
|
|
||||||
|
|
||||||
setCache(ticker, stockChartData, "hoverStockChart");
|
|
||||||
}
|
|
||||||
|
|
||||||
changesPercentage = stockChartData?.changesPercentage;
|
|
||||||
change = stockChartData?.change;
|
|
||||||
|
|
||||||
priceData = stockChartData?.history;
|
|
||||||
|
|
||||||
priceData = priceData
|
|
||||||
?.map((item) => ({
|
|
||||||
time: Date?.parse(item?.time), // Assuming 'time' is the correct property to parse
|
|
||||||
value: item?.close ?? null,
|
|
||||||
}))
|
|
||||||
?.filter(
|
|
||||||
(item) =>
|
|
||||||
item?.value !== 0 &&
|
|
||||||
item?.value !== null &&
|
|
||||||
item?.value !== undefined,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getHref(symbol: string) {
|
function getHref(symbol: string) {
|
||||||
let path = "";
|
let path = "";
|
||||||
if (symbol?.length !== 0) {
|
if (symbol?.length !== 0) {
|
||||||
@ -67,208 +18,10 @@
|
|||||||
}
|
}
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
$: topLineColor = changesPercentage >= 0 ? "#71CA96" : "#FF7070";
|
|
||||||
|
|
||||||
let width = $screenWidth < 640 ? 80 : 150; //= ($screenWidth <= 1200 && $screenWidth > 900) ? 360 : ($screenWidth <= 900 && $screenWidth > 700) ? 260 : ($screenWidth <= 700 && $screenWidth >=600 ) ? 200 : ($screenWidth < 600 && $screenWidth >=500 ) ? 150 : 80;
|
|
||||||
|
|
||||||
//Initial height of graph
|
|
||||||
let height = $screenWidth < 640 ? 50 : 60;
|
|
||||||
|
|
||||||
let observer;
|
|
||||||
let ref;
|
|
||||||
let chart;
|
|
||||||
|
|
||||||
ref = (element) => {
|
|
||||||
if (observer) {
|
|
||||||
observer?.disconnect();
|
|
||||||
}
|
|
||||||
if (!element) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
observer = new ResizeObserver(([entry]) => {
|
|
||||||
width = entry.contentRect.width;
|
|
||||||
height = entry.contentRect.height;
|
|
||||||
});
|
|
||||||
observer.observe(element);
|
|
||||||
};
|
|
||||||
|
|
||||||
const THEMES = {
|
|
||||||
Dark: {
|
|
||||||
chart: {
|
|
||||||
layout: {
|
|
||||||
background: {
|
|
||||||
type: ColorType.Solid,
|
|
||||||
color: "#11151D",
|
|
||||||
},
|
|
||||||
lineColor: "#2B2B43",
|
|
||||||
textColor: "#D9D9D9",
|
|
||||||
},
|
|
||||||
crosshair: {
|
|
||||||
mode: 2,
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
vertLines: {
|
|
||||||
visible: false,
|
|
||||||
},
|
|
||||||
horzLines: {
|
|
||||||
visible: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
series: {
|
|
||||||
baseValue: { type: "price", price: priceData?.at(0)?.value },
|
|
||||||
priceLineVisible: false,
|
|
||||||
baseLineVisible: true,
|
|
||||||
baseLineColor: "#B2B5BE",
|
|
||||||
baseLineWidth: 1,
|
|
||||||
baseLineStyle: 1,
|
|
||||||
lineWidth: 1.5,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const AVAILABLE_THEMES = Object?.keys(THEMES);
|
|
||||||
|
|
||||||
let selected = AVAILABLE_THEMES[0];
|
|
||||||
$: theme = THEMES[selected];
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
rightPriceScale: {
|
|
||||||
visible: false,
|
|
||||||
},
|
|
||||||
timeScale: {
|
|
||||||
borderColor: "#FFFFFF",
|
|
||||||
textColor: "#FFFFFF",
|
|
||||||
visible: false,
|
|
||||||
fixLeftEdge: true,
|
|
||||||
fixRightEdge: true,
|
|
||||||
},
|
|
||||||
handleScale: {
|
|
||||||
mouseWheel: false,
|
|
||||||
},
|
|
||||||
handleScroll: {
|
|
||||||
mouseWheel: false,
|
|
||||||
horzTouchDrag: false,
|
|
||||||
vertTouchDrag: false,
|
|
||||||
pressedMouseMove: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
afterUpdate(async () => {
|
|
||||||
if (
|
|
||||||
$screenWidth &&
|
|
||||||
stockChartData &&
|
|
||||||
chart &&
|
|
||||||
typeof window !== "undefined"
|
|
||||||
) {
|
|
||||||
chart?.timeScale()?.fitContent();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$: charNumber = 20;
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div on:mouseover={() => getStockData(symbol)} class="inline-block">
|
<a
|
||||||
<HoverCard.Root>
|
|
||||||
<div on:mouseover>
|
|
||||||
<HoverCard.Trigger
|
|
||||||
class="rounded-sm underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-offset-8 focus-visible:outline-black"
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
href={getHref(symbol)}
|
href={getHref(symbol)}
|
||||||
class="sm:hover:text-muted dark:sm:hover:text-white text-blue-700 dark:text-blue-400"
|
class="sm:hover:text-muted dark:sm:hover:text-white text-blue-700 dark:text-blue-400"
|
||||||
>{symbol?.length !== 0 ? symbol : "-"}</a
|
>{symbol?.length !== 0 ? symbol : "-"}</a
|
||||||
>
|
>
|
||||||
</HoverCard.Trigger>
|
|
||||||
</div>
|
|
||||||
<HoverCard.Content class=" w-96 bg-[#11151D] border border-gray-600">
|
|
||||||
<div class="flex justify-between space-x-4 w-full text-white">
|
|
||||||
<div class="space-y-1 w-full">
|
|
||||||
<!--Hover Stock Chart-->
|
|
||||||
<label
|
|
||||||
class=" text-sm flex flex-row items-center justify-start bg-[#11151D]"
|
|
||||||
>
|
|
||||||
<div class="flex flex-col items-start w-full">
|
|
||||||
<div class=" flex flex-col items-start pb-1">
|
|
||||||
<h4 class="text-sm text-blue-400 inline-block">
|
|
||||||
{symbol}
|
|
||||||
</h4>
|
|
||||||
<h5 class="text-sm inline-block">
|
|
||||||
{stockChartData?.name?.length > charNumber
|
|
||||||
? stockChartData?.name?.slice(0, charNumber) + "..."
|
|
||||||
: stockChartData?.name}
|
|
||||||
</h5>
|
|
||||||
</div>
|
|
||||||
<p>
|
|
||||||
Current Price: {stockChartData?.price?.toFixed(2)} (<span
|
|
||||||
class="text-xs {change >= 0
|
|
||||||
? "before:content-['+'] text-green-700 dark:text-[#00FC50]"
|
|
||||||
: 'text-red-700 dark:text-[#FF2F1F]'}"
|
|
||||||
>{change?.toFixed(2)}</span
|
|
||||||
>)
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<svg
|
|
||||||
class="-ml-1 inline-block w-4 h-4 {changesPercentage >= 0
|
|
||||||
? ''
|
|
||||||
: 'rotate-180'}"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill={changesPercentage >= 0 ? "#00FC50" : "#FF2F1F"}
|
|
||||||
d="M12.884 5.116a1.253 1.253 0 0 0-1.768 0l-5 5a1.25 1.25 0 0 0 1.768 1.768l2.866-2.866V18a1.25 1.25 0 1 0 2.5 0V9.018l2.866 2.866a1.25 1.25 0 1 0 1.768-1.768z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
{#if changesPercentage != null}
|
|
||||||
<span
|
|
||||||
class="-ml-1"
|
|
||||||
style="color: {changesPercentage >= 0
|
|
||||||
? '#00FC50'
|
|
||||||
: '#FF2F1F'}"
|
|
||||||
>
|
|
||||||
{changesPercentage >= 1000 || changesPercentage <= -1000
|
|
||||||
? abbreviateNumber(changesPercentage)
|
|
||||||
: changesPercentage?.toFixed(2)}%
|
|
||||||
</span>
|
|
||||||
{:else}
|
|
||||||
-
|
|
||||||
{/if}
|
|
||||||
today
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
{#if priceData?.length > 0}
|
|
||||||
<div class="w-[40%]">
|
|
||||||
<Chart
|
|
||||||
{...options}
|
|
||||||
{...theme.chart}
|
|
||||||
autoSize={true}
|
|
||||||
ref={(ref) => (chart = ref)}
|
|
||||||
>
|
|
||||||
<BaselineSeries
|
|
||||||
data={priceData}
|
|
||||||
{...theme.series}
|
|
||||||
{topLineColor}
|
|
||||||
>
|
|
||||||
<PriceLine
|
|
||||||
price={priceData?.at(0)?.value}
|
|
||||||
lineWidth={1}
|
|
||||||
color="#fff"
|
|
||||||
/>
|
|
||||||
</BaselineSeries>
|
|
||||||
</Chart>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<!--Hover Stock Chart-->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</HoverCard.Content>
|
|
||||||
</HoverCard.Root>
|
|
||||||
</div>
|
|
||||||
|
|||||||
@ -1,195 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { ColorType } from "lightweight-charts";
|
|
||||||
import { Chart, BaselineSeries, PriceLine } from "svelte-lightweight-charts";
|
|
||||||
import { screenWidth, etfTicker } from "$lib/store";
|
|
||||||
import { goto } from "$app/navigation";
|
|
||||||
|
|
||||||
export let title;
|
|
||||||
export let priceData;
|
|
||||||
export let changesPercentage;
|
|
||||||
export let previousClose;
|
|
||||||
|
|
||||||
const topLineColor = "#71CA96";
|
|
||||||
const topFillColor1 = "rgba( 38, 166, 154, 0.2)";
|
|
||||||
const bottomLineColor = "#FF7070";
|
|
||||||
const bottomFillColor1 = "rgba(239, 83, 80, 0.2)";
|
|
||||||
|
|
||||||
let width = $screenWidth < 640 ? 80 : $screenWidth < 1500 ? 150 : 220; //= ($screenWidth <= 1200 && $screenWidth > 900) ? 360 : ($screenWidth <= 900 && $screenWidth > 700) ? 260 : ($screenWidth <= 700 && $screenWidth >=600 ) ? 200 : ($screenWidth < 600 && $screenWidth >=500 ) ? 150 : 80;
|
|
||||||
|
|
||||||
//Initial height of graph
|
|
||||||
let height = $screenWidth < 640 ? 50 : 60;
|
|
||||||
|
|
||||||
let observer;
|
|
||||||
let ref;
|
|
||||||
let chart;
|
|
||||||
|
|
||||||
ref = (element) => {
|
|
||||||
if (observer) {
|
|
||||||
observer?.disconnect();
|
|
||||||
}
|
|
||||||
if (!element) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
observer = new ResizeObserver(([entry]) => {
|
|
||||||
width = entry.contentRect.width;
|
|
||||||
height = entry.contentRect.height;
|
|
||||||
});
|
|
||||||
observer.observe(element);
|
|
||||||
};
|
|
||||||
|
|
||||||
const THEMES = {
|
|
||||||
Dark: {
|
|
||||||
chart: {
|
|
||||||
layout: {
|
|
||||||
background: {
|
|
||||||
type: ColorType.Solid,
|
|
||||||
color: "#09090B",
|
|
||||||
},
|
|
||||||
lineColor: "#2B2B43",
|
|
||||||
textColor: "#D9D9D9",
|
|
||||||
},
|
|
||||||
crosshair: {
|
|
||||||
mode: 2,
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
vertLines: {
|
|
||||||
visible: false,
|
|
||||||
},
|
|
||||||
horzLines: {
|
|
||||||
visible: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
series: {
|
|
||||||
baseValue: { type: "price", price: priceData?.at(0)?.value },
|
|
||||||
priceLineVisible: false,
|
|
||||||
baseLineVisible: true,
|
|
||||||
baseLineColor: "#B2B5BE",
|
|
||||||
baseLineWidth: 1,
|
|
||||||
baseLineStyle: 1,
|
|
||||||
lineWidth: 1.5,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const AVAILABLE_THEMES = Object?.keys(THEMES);
|
|
||||||
|
|
||||||
let selected = AVAILABLE_THEMES[0];
|
|
||||||
$: theme = THEMES[selected];
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
rightPriceScale: {
|
|
||||||
visible: false,
|
|
||||||
},
|
|
||||||
timeScale: {
|
|
||||||
borderColor: "#FFFFFF",
|
|
||||||
textColor: "#FFFFFF",
|
|
||||||
visible: false,
|
|
||||||
fixLeftEdge: true,
|
|
||||||
fixRightEdge: true,
|
|
||||||
},
|
|
||||||
handleScale: {
|
|
||||||
mouseWheel: false,
|
|
||||||
},
|
|
||||||
handleScroll: {
|
|
||||||
mouseWheel: false,
|
|
||||||
horzTouchDrag: false,
|
|
||||||
vertTouchDrag: false,
|
|
||||||
pressedMouseMove: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function etfSelector() {
|
|
||||||
if (title === "S&P500") {
|
|
||||||
etfTicker.update((value) => "SPY");
|
|
||||||
goto("/etf/SPY");
|
|
||||||
} else if (title === "Nasdaq") {
|
|
||||||
etfTicker.update((value) => "QQQ");
|
|
||||||
goto("/etf/QQQ");
|
|
||||||
} else if (title === "Dow") {
|
|
||||||
etfTicker.update((value) => "DIA");
|
|
||||||
goto("/etf/DIA");
|
|
||||||
} else if (title === "Russel") {
|
|
||||||
etfTicker.update((value) => "IWM");
|
|
||||||
goto("/etf/IWM");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$: {
|
|
||||||
if (chart && typeof window !== "undefined") {
|
|
||||||
chart?.timeScale()?.fitContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<label
|
|
||||||
on:click={etfSelector}
|
|
||||||
class="sm:hover:border-[#3C74D4] duration-100 transition ease-in-out cursor-pointer flex flex-row items-center rounded-md shadow-lg border border-gray-600 bg-default"
|
|
||||||
>
|
|
||||||
<div class="flex flex-col items-center lg:mr-5">
|
|
||||||
<span
|
|
||||||
class="text-white font-semibold text-xs w-20 text-start pl-3 uppercase"
|
|
||||||
>{title}</span
|
|
||||||
>
|
|
||||||
<div class="flex flex-row mt-1 items-center">
|
|
||||||
{#if changesPercentage >= 0}
|
|
||||||
<svg
|
|
||||||
class="w-4 h-4 -mr-0.5"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
><g id="evaArrowUpFill0"
|
|
||||||
><g id="evaArrowUpFill1"
|
|
||||||
><path
|
|
||||||
id="evaArrowUpFill2"
|
|
||||||
fill="#00FC50"
|
|
||||||
d="M16.21 16H7.79a1.76 1.76 0 0 1-1.59-1a2.1 2.1 0 0 1 .26-2.21l4.21-5.1a1.76 1.76 0 0 1 2.66 0l4.21 5.1A2.1 2.1 0 0 1 17.8 15a1.76 1.76 0 0 1-1.59 1Z"
|
|
||||||
/></g
|
|
||||||
></g
|
|
||||||
></svg
|
|
||||||
>
|
|
||||||
<span class="text-[#00FC50] text-xs"
|
|
||||||
>+{changesPercentage?.toFixed(2)}%</span
|
|
||||||
>
|
|
||||||
{:else}
|
|
||||||
<svg
|
|
||||||
class="w-4 h-4 -mr-0.5 rotate-180"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
><g id="evaArrowUpFill0"
|
|
||||||
><g id="evaArrowUpFill1"
|
|
||||||
><path
|
|
||||||
id="evaArrowUpFill2"
|
|
||||||
fill="#FF2F1F"
|
|
||||||
d="M16.21 16H7.79a1.76 1.76 0 0 1-1.59-1a2.1 2.1 0 0 1 .26-2.21l4.21-5.1a1.76 1.76 0 0 1 2.66 0l4.21 5.1A2.1 2.1 0 0 1 17.8 15a1.76 1.76 0 0 1-1.59 1Z"
|
|
||||||
/></g
|
|
||||||
></g
|
|
||||||
></svg
|
|
||||||
>
|
|
||||||
<span class="text-[#FF2F1F] text-xs"
|
|
||||||
>{changesPercentage?.toFixed(2)}%
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Chart
|
|
||||||
{...options}
|
|
||||||
{...theme.chart}
|
|
||||||
autoSize={true}
|
|
||||||
ref={(ref) => (chart = ref)}
|
|
||||||
>
|
|
||||||
<BaselineSeries
|
|
||||||
data={priceData}
|
|
||||||
{...theme.series}
|
|
||||||
{topLineColor}
|
|
||||||
{topFillColor1}
|
|
||||||
{bottomLineColor}
|
|
||||||
{bottomFillColor1}
|
|
||||||
>
|
|
||||||
<PriceLine price={priceData?.at(0)?.value} lineWidth={1} color="#fff" />
|
|
||||||
</BaselineSeries>
|
|
||||||
</Chart>
|
|
||||||
</label>
|
|
||||||
@ -1,344 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { displayCompanyName, screenWidth } from "$lib/store";
|
|
||||||
import InfoModal from "$lib/components/InfoModal.svelte";
|
|
||||||
import { Chart } from "svelte-echarts";
|
|
||||||
import { onMount } from "svelte";
|
|
||||||
|
|
||||||
import { init, use } from "echarts/core";
|
|
||||||
import { LineChart } from "echarts/charts";
|
|
||||||
import { GridComponent, TooltipComponent } from "echarts/components";
|
|
||||||
import { CanvasRenderer } from "echarts/renderers";
|
|
||||||
|
|
||||||
use([LineChart, GridComponent, TooltipComponent, CanvasRenderer]);
|
|
||||||
|
|
||||||
export let data;
|
|
||||||
let isLoaded = false;
|
|
||||||
|
|
||||||
let priceAnalysisDict = data?.getPriceAnalysis;
|
|
||||||
let lastPrice = data?.getStockQuote?.price ?? "n/a";
|
|
||||||
|
|
||||||
const modalContent = `
|
|
||||||
Our AI model, employing a Bayesian approach, predicts future prices by breaking down trends, seasonality, and holiday effects. It integrates uncertainty to offer forecasts with intervals.<br><br>
|
|
||||||
<span class="font-semibold underline"><span class="italic">R</span><sup>2</sup> Score</span>: How well the regression model fits the data. A high score (close to 100%) is good, indicating a strong fit, while a low score (close to 0%) is bad, suggesting poor fit.
|
|
||||||
<br><br>
|
|
||||||
<span class="font-semibold underline">Mean Absolute Percentage Error (MAPE)</span>: Measures the average percentage difference between predicted and actual values. A lower MAPE indicates better accuracy, while a higher MAPE suggests less accurate predictions.
|
|
||||||
`;
|
|
||||||
|
|
||||||
const monthNames = [
|
|
||||||
"Jan",
|
|
||||||
"Feb",
|
|
||||||
"Mar",
|
|
||||||
"Apr",
|
|
||||||
"May",
|
|
||||||
"Jun",
|
|
||||||
"Jul",
|
|
||||||
"Aug",
|
|
||||||
"Sep",
|
|
||||||
"Oct",
|
|
||||||
"Nov",
|
|
||||||
"Dec",
|
|
||||||
];
|
|
||||||
|
|
||||||
let r2Score;
|
|
||||||
let mape;
|
|
||||||
let priceSentiment = "n/a";
|
|
||||||
let oneYearPricePrediction = "n/a";
|
|
||||||
let optionsData;
|
|
||||||
|
|
||||||
function getPlotOptions() {
|
|
||||||
const predictionDate = priceAnalysisDict?.predictionDate;
|
|
||||||
const upperBand = priceAnalysisDict?.upperBand;
|
|
||||||
const lowerBand = priceAnalysisDict?.lowerBand?.map((value) =>
|
|
||||||
value < 0 ? 0 : value,
|
|
||||||
);
|
|
||||||
const historicalPrice = priceAnalysisDict?.historicalPrice;
|
|
||||||
|
|
||||||
const option = {
|
|
||||||
silent: true,
|
|
||||||
animation: false,
|
|
||||||
grid: {
|
|
||||||
left: screenWidth < 640 ? "0%" : "2%",
|
|
||||||
right: screenWidth < 640 ? "5%" : "2%",
|
|
||||||
bottom: screenWidth < 640 ? "0%" : "5%",
|
|
||||||
containLabel: true,
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
trigger: "axis",
|
|
||||||
hideDelay: 100,
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
type: "category",
|
|
||||||
boundaryGap: false,
|
|
||||||
data: predictionDate,
|
|
||||||
axisLabel: {
|
|
||||||
color: "#fff",
|
|
||||||
formatter(value) {
|
|
||||||
const dateParts = value?.split("-");
|
|
||||||
const year = dateParts[0]?.substring(2);
|
|
||||||
const monthIndex = parseInt(dateParts[1], 10) - 1;
|
|
||||||
return `${monthNames[monthIndex]} '${year}`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
yAxis: [
|
|
||||||
{
|
|
||||||
type: "value",
|
|
||||||
splitLine: { show: false },
|
|
||||||
axisLabel: { show: false },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: "Stock Price",
|
|
||||||
data: historicalPrice,
|
|
||||||
showSymbol: false,
|
|
||||||
smooth: true,
|
|
||||||
type: "line",
|
|
||||||
itemStyle: { color: "#fff" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Upperband",
|
|
||||||
data: upperBand,
|
|
||||||
showSymbol: false,
|
|
||||||
smooth: true,
|
|
||||||
type: "line",
|
|
||||||
areaStyle: { color: "rgba(60, 116, 212, 0.4)" },
|
|
||||||
lineStyle: { color: "rgb(60, 116, 212)" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Lowerband",
|
|
||||||
data: lowerBand,
|
|
||||||
showSymbol: false,
|
|
||||||
smooth: true,
|
|
||||||
type: "line",
|
|
||||||
areaStyle: { color: "#09090B", opacity: 1 },
|
|
||||||
lineStyle: { color: "rgb(60, 116, 212)" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
return option;
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
oneYearPricePrediction = priceAnalysisDict?.meanResult?.slice(-1)?.at(0);
|
|
||||||
mape = priceAnalysisDict?.mape;
|
|
||||||
r2Score = priceAnalysisDict?.r2Score;
|
|
||||||
priceSentiment = lastPrice < oneYearPricePrediction ? "Bullish" : "Bearish";
|
|
||||||
optionsData = await getPlotOptions();
|
|
||||||
isLoaded = true;
|
|
||||||
});
|
|
||||||
</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="priceAnalysisInfo"
|
|
||||||
class="mr-1 cursor-pointer flex flex-row items-center text-white text-xl sm:text-3xl font-bold"
|
|
||||||
>
|
|
||||||
AI Price Analysis
|
|
||||||
</label>
|
|
||||||
<InfoModal
|
|
||||||
title={"Price Analysis"}
|
|
||||||
content={modalContent}
|
|
||||||
id={"priceAnalysisInfo"}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if isLoaded}
|
|
||||||
{#if Object?.keys(priceAnalysisDict)?.length !== 0}
|
|
||||||
<div class="w-full flex flex-col items-start">
|
|
||||||
<div class="text-white text-[1rem] mt-1 sm:mt-3 mb-1 w-full">
|
|
||||||
Our model predicts future prices by analyzing trends, seasonal
|
|
||||||
variations, and holiday impacts. Here are the stats of the model for {$displayCompanyName}
|
|
||||||
to ensure transparency and reliability.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="w-full mt-5 mb-5 flex justify-start items-center">
|
|
||||||
<div
|
|
||||||
class="w-full grid grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-y-3 gap-x-3"
|
|
||||||
>
|
|
||||||
<!--Start Flow Sentiment-->
|
|
||||||
<div
|
|
||||||
class="flex flex-row items-center flex-wrap w-full px-3 sm:px-5 bg-primary shadow-lg rounded-md h-20"
|
|
||||||
>
|
|
||||||
<div class="flex flex-col items-start">
|
|
||||||
<span class=" text-gray-200 text-sm">Price Sentiment</span>
|
|
||||||
<span
|
|
||||||
class="text-start text-[1rem] sm:text-lg font-semibold {priceSentiment ===
|
|
||||||
'Bullish'
|
|
||||||
? 'text-[#37C97D]'
|
|
||||||
: 'text-red-700 dark:text-[#FF2F1F]'}"
|
|
||||||
>{priceSentiment}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!--End Flow Sentiment-->
|
|
||||||
<!--Start Put/Call-->
|
|
||||||
<div
|
|
||||||
class="flex flex-row items-center flex-wrap w-full px-3 sm:px-5 bg-primary shadow-lg rounded-md h-20"
|
|
||||||
>
|
|
||||||
<div class="flex flex-col items-start">
|
|
||||||
<span class=" text-gray-200 text-sm"
|
|
||||||
><span class="italic">R</span><sup>2</sup> Score</span
|
|
||||||
>
|
|
||||||
<span class="text-start text-sm sm:text-[1rem] text-white">
|
|
||||||
{r2Score >= 65 ? "Good" : r2Score >= 50 ? "Moderate" : "Bad"}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<!-- Circular Progress -->
|
|
||||||
<div class="relative size-14 ml-auto">
|
|
||||||
<svg
|
|
||||||
class="size-full w-14 h-14"
|
|
||||||
viewBox="0 0 36 36"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<!-- Background Circle -->
|
|
||||||
<circle
|
|
||||||
cx="18"
|
|
||||||
cy="18"
|
|
||||||
r="16"
|
|
||||||
fill="none"
|
|
||||||
class="stroke-current text-[#3E3E3E]"
|
|
||||||
stroke-width="3"
|
|
||||||
></circle>
|
|
||||||
<!-- Progress Circle inside a group with rotation -->
|
|
||||||
<g class="origin-center -rotate-90 transform">
|
|
||||||
<circle
|
|
||||||
cx="18"
|
|
||||||
cy="18"
|
|
||||||
r="16"
|
|
||||||
fill="none"
|
|
||||||
class="stroke-current {r2Score >= 65
|
|
||||||
? 'text-green-700 dark:text-[#00FC50]'
|
|
||||||
: r2Score >= 50
|
|
||||||
? 'text-[#F8901E]'
|
|
||||||
: 'text-red-700 dark:text-[#FF2F1F]'}"
|
|
||||||
stroke-width="3"
|
|
||||||
stroke-dasharray="100"
|
|
||||||
stroke-dashoffset={100 - r2Score}
|
|
||||||
></circle>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
<!-- Percentage Text -->
|
|
||||||
<div
|
|
||||||
class="absolute top-1/2 start-1/2 transform -translate-y-1/2 -translate-x-1/2"
|
|
||||||
>
|
|
||||||
<span class="text-center text-white text-sm">{r2Score}%</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- End Circular Progress -->
|
|
||||||
</div>
|
|
||||||
<!--End Put/Call-->
|
|
||||||
|
|
||||||
<!--Start mape-->
|
|
||||||
<div
|
|
||||||
class="flex flex-row items-center flex-wrap w-full px-3 sm:px-5 bg-primary shadow-lg rounded-md h-20"
|
|
||||||
>
|
|
||||||
<div class="flex flex-col items-start">
|
|
||||||
<span class=" text-gray-200 text-sm">MAPE</span>
|
|
||||||
<span class="text-start text-sm sm:text-[1rem] text-white">
|
|
||||||
{mape <= 15 ? "Good" : mape <= 35 ? "Moderate" : "Bad"}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<!-- Circular Progress -->
|
|
||||||
<div class="relative size-14 ml-auto">
|
|
||||||
<svg
|
|
||||||
class="size-full w-14 h-14"
|
|
||||||
viewBox="0 0 36 36"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<!-- Background Circle -->
|
|
||||||
<circle
|
|
||||||
cx="18"
|
|
||||||
cy="18"
|
|
||||||
r="16"
|
|
||||||
fill="none"
|
|
||||||
class="stroke-current text-[#3E3E3E]"
|
|
||||||
stroke-width="3"
|
|
||||||
></circle>
|
|
||||||
<!-- Progress Circle inside a group with rotation -->
|
|
||||||
<g class="origin-center -rotate-90 transform">
|
|
||||||
<circle
|
|
||||||
cx="18"
|
|
||||||
cy="18"
|
|
||||||
r="16"
|
|
||||||
fill="none"
|
|
||||||
class="stroke-current {mape <= 15
|
|
||||||
? 'text-green-700 dark:text-[#00FC50]'
|
|
||||||
: mape <= 35
|
|
||||||
? 'text-[#F8901E]'
|
|
||||||
: 'text-red-700 dark:text-[#FF2F1F]'}"
|
|
||||||
stroke-width="3"
|
|
||||||
stroke-dasharray="100"
|
|
||||||
stroke-dashoffset={100 - mape > 0 ? 100 - mape : 1}
|
|
||||||
></circle>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
<!-- Percentage Text -->
|
|
||||||
<div
|
|
||||||
class="absolute top-1/2 start-1/2 transform -translate-y-1/2 -translate-x-1/2"
|
|
||||||
>
|
|
||||||
<span class="text-center text-white text-sm">{mape}%</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- End Circular Progress -->
|
|
||||||
</div>
|
|
||||||
<!--End mape-->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="app w-full h-[300px]">
|
|
||||||
<Chart {init} options={optionsData} class="chart" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="text-white text-[1rem] mt-4 sm:mt-7 ml-1">
|
|
||||||
Over the next 12 months, the model predicts a
|
|
||||||
<span
|
|
||||||
class="font-semibold {priceSentiment === 'Bullish'
|
|
||||||
? 'text-[#37C97D]'
|
|
||||||
: 'text-red-700 dark:text-[#FF2F1F]'}">{priceSentiment}</span
|
|
||||||
>
|
|
||||||
trend, suggesting that the future price is expected to {priceSentiment ===
|
|
||||||
"Bullish"
|
|
||||||
? "surpass"
|
|
||||||
: "to be less than"} the previous price of
|
|
||||||
<span class="font-semibold">${lastPrice?.toFixed(2) ?? "n/a"}</span>,
|
|
||||||
with a mean value of
|
|
||||||
<span class="font-semibold">${oneYearPricePrediction}</span>.
|
|
||||||
</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>
|
|
||||||
@ -69,9 +69,9 @@
|
|||||||
animation: false,
|
animation: false,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
text: `<div class="text-muted dark:text-gray-200 mt-3 text-center font-normal text-2xl">Price Target: <span class="${changesPercentage >= 0 ? "text-green-700 dark:text-[#00FC50]" : "text-[#FF2F1F]"}">$${priceTarget}</span></div>
|
text: `<div class="text-muted dark:text-gray-200 mt-3 text-center font-normal text-2xl">Price Target: <span class="${changesPercentage >= 0 ? "text-green-700 dark:text-[#00FC50]" : "text-red-700 dark:text-[#FF2F1F]"}">$${priceTarget}</span></div>
|
||||||
<div class="text-muted dark:text-gray-200 mb-2 text-center font-normal text-xl">(${changesPercentage}% ${changesPercentage >= 0 ? "upside" : "downside"})</div>
|
<div class="text-muted dark:text-gray-200 mb-2 text-center font-normal text-xl">(${changesPercentage}% ${changesPercentage >= 0 ? "upside" : "downside"})</div>
|
||||||
<div class="text-muted dark:text-gray-200 text-center font-normal text-xl flex justify-center items-center">Analyst Consensus: <span class="ml-1 ${consensusRating === "Buy" ? "text-green-700 dark:text-[#00FC50]" : consensusRating === "Sell" ? "text-red-500 dark:text-[#FF2F1F]" : consensusRating === "Hold" ? "text-orange-500 dark:text-[#D5AB31]" : "text-muted dark:text-white"}">${consensusRating ?? "n/a"}</span></div>`,
|
<div class="text-muted dark:text-gray-200 text-center font-normal text-xl flex justify-center items-center">Analyst Consensus: <span class="ml-1 ${consensusRating === "Buy" ? "text-green-700 dark:text-[#00FC50]" : consensusRating === "Sell" ? "text-red-700 dark:text-[#FF2F1F]" : consensusRating === "Hold" ? "text-orange-500 dark:text-[#D5AB31]" : "text-muted dark:text-white"}">${consensusRating ?? "n/a"}</span></div>`,
|
||||||
style: {
|
style: {
|
||||||
color: "white",
|
color: "white",
|
||||||
// Using inline CSS for margin-top and margin-bottom
|
// Using inline CSS for margin-top and margin-bottom
|
||||||
@ -299,7 +299,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="mt-3 border border-gray-300 dark:border-gray-700 rounded"
|
class="mt-3 border border-gray-300 dark:border-gray-800 rounded"
|
||||||
use:highcharts={configAnalyst}
|
use:highcharts={configAnalyst}
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
|
|||||||
@ -1,253 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import {
|
|
||||||
varComponent,
|
|
||||||
displayCompanyName,
|
|
||||||
stockTicker,
|
|
||||||
etfTicker,
|
|
||||||
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";
|
|
||||||
|
|
||||||
import { init, use } from "echarts/core";
|
|
||||||
import { LineChart } from "echarts/charts";
|
|
||||||
import { GridComponent, TooltipComponent } from "echarts/components";
|
|
||||||
import { CanvasRenderer } from "echarts/renderers";
|
|
||||||
|
|
||||||
export let data;
|
|
||||||
export let rawData = {};
|
|
||||||
|
|
||||||
use([LineChart, GridComponent, TooltipComponent, CanvasRenderer]);
|
|
||||||
|
|
||||||
let isLoaded = false;
|
|
||||||
let rating: string | undefined;
|
|
||||||
let outlook: string | undefined;
|
|
||||||
let valueAtRisk: number | string | undefined;
|
|
||||||
let optionsData: any;
|
|
||||||
let monthlyVarAvg: string | undefined;
|
|
||||||
|
|
||||||
function getPlotOptions() {
|
|
||||||
const dates: string[] = [];
|
|
||||||
const varList: number[] = [];
|
|
||||||
const priceList = [];
|
|
||||||
|
|
||||||
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);
|
|
||||||
monthlyVarAvg = (sum / varList.length)?.toFixed(2);
|
|
||||||
|
|
||||||
const option = {
|
|
||||||
silent: true,
|
|
||||||
tooltip: {
|
|
||||||
trigger: "axis",
|
|
||||||
hideDelay: 100,
|
|
||||||
},
|
|
||||||
animation: false,
|
|
||||||
grid: {
|
|
||||||
left: "2%",
|
|
||||||
right: "2%",
|
|
||||||
bottom: "2%",
|
|
||||||
top: "5%",
|
|
||||||
containLabel: true,
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
type: "category",
|
|
||||||
boundaryGap: false,
|
|
||||||
data: dates,
|
|
||||||
axisLabel: {
|
|
||||||
color: "#fff",
|
|
||||||
formatter: (value: string) => {
|
|
||||||
const date = new Date(value + "-01");
|
|
||||||
return new Intl.DateTimeFormat("en-US", {
|
|
||||||
year: "numeric",
|
|
||||||
month: "short",
|
|
||||||
}).format(date);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
yAxis: [
|
|
||||||
{
|
|
||||||
type: "value",
|
|
||||||
splitLine: { show: false },
|
|
||||||
axisLabel: { show: false },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "value",
|
|
||||||
splitLine: { show: false },
|
|
||||||
axisLabel: { show: false },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: "VaR",
|
|
||||||
data: varList,
|
|
||||||
type: "line",
|
|
||||||
itemStyle: { color: "#E11D48" },
|
|
||||||
showSymbol: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Stock Price",
|
|
||||||
data: priceList,
|
|
||||||
type: "line",
|
|
||||||
yAxisIndex: 1,
|
|
||||||
itemStyle: { color: "#fff" },
|
|
||||||
showSymbol: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
return option;
|
|
||||||
}
|
|
||||||
|
|
||||||
$: {
|
|
||||||
const ticker = $assetType === "stock" ? $stockTicker : "etf";
|
|
||||||
|
|
||||||
if (ticker) {
|
|
||||||
isLoaded = false;
|
|
||||||
|
|
||||||
rating = rawData.rating;
|
|
||||||
outlook = rawData.outlook;
|
|
||||||
valueAtRisk = rawData.history?.slice(-1)?.at(0)?.var ?? "n/a";
|
|
||||||
optionsData = getPlotOptions();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<section class="overflow-hidden text-white h-full pb-10 sm:pb-0">
|
|
||||||
<main class="overflow-hidden">
|
|
||||||
<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-2xl font-bold"
|
|
||||||
>
|
|
||||||
Value at Risk
|
|
||||||
</label>
|
|
||||||
<InfoModal
|
|
||||||
title={"Value at Risk"}
|
|
||||||
content={`Value at Risk (VaR) quantifies the potential loss of investment or capital within a specified time frame (N days) under typical market conditions, providing an estimate of potential losses with a given probability for ${$displayCompanyName}.`}
|
|
||||||
id={"varInfo"}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#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"
|
|
||||||
>
|
|
||||||
<div class="flex flex-row items-center justify-between">
|
|
||||||
<div class="relative size-[60px] sm:size-[90px] ml-auto">
|
|
||||||
<svg
|
|
||||||
class="size-full w-[60px] h-[60px] sm:w-[90px] sm:h-[90px]"
|
|
||||||
viewBox="0 0 36 36"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<!-- Background Circle -->
|
|
||||||
<circle
|
|
||||||
cx="18"
|
|
||||||
cy="18"
|
|
||||||
r="16"
|
|
||||||
fill="none"
|
|
||||||
class="stroke-current text-[#303030]"
|
|
||||||
stroke-width="4"
|
|
||||||
></circle>
|
|
||||||
<!-- Progress Circle inside a group with rotation -->
|
|
||||||
<g class="origin-center -rotate-90 transform">
|
|
||||||
<circle
|
|
||||||
cx="18"
|
|
||||||
cy="18"
|
|
||||||
r="16"
|
|
||||||
fill="none"
|
|
||||||
class="stroke-current {rating > 5
|
|
||||||
? 'text-green-700 dark:text-[#00FC50]'
|
|
||||||
: rating < 5
|
|
||||||
? 'text-red-700 dark:text-[#FF2F1F]'
|
|
||||||
: 'text-white'} "
|
|
||||||
stroke-width="4"
|
|
||||||
stroke-dasharray="100"
|
|
||||||
stroke-dashoffset={100 - rating * 10}
|
|
||||||
></circle>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
<!-- Percentage Text -->
|
|
||||||
<div
|
|
||||||
class="absolute top-1/2 start-1/2 transform -translate-y-1/2 -translate-x-1/2"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="text-center text-white text-xl sm:text-2xl font-semibold"
|
|
||||||
>
|
|
||||||
{rating}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="flex flex-col items-start ml-4 sm:ml-10 mr-auto sm:-top-3 sm:relative"
|
|
||||||
>
|
|
||||||
<h3
|
|
||||||
class="hidden sm:block text-gray-300 text-[1rem] sm:text-lg font-semibold"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class={outlook === "Minimum Risk"
|
|
||||||
? "text-[#10BC09]"
|
|
||||||
: outlook === "Risky"
|
|
||||||
? "text-red-500"
|
|
||||||
: "text-white"}>{outlook}</span
|
|
||||||
> outlook:
|
|
||||||
</h3>
|
|
||||||
<span class="text-gray-200 text-sm sm:text-lg mt-1">
|
|
||||||
Under typical market conditions, there is a <span
|
|
||||||
class="font-semibold">95%</span
|
|
||||||
>
|
|
||||||
probability that
|
|
||||||
{$assetType === "stock" ? $stockTicker : $etfTicker}
|
|
||||||
will incur a maximum loss of
|
|
||||||
<span class="text-[#FF2F1F] font-semibold">{valueAtRisk}%</span>
|
|
||||||
in the upcoming week.
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2 class="text-white text-xl sm:text-2xl font-semibold mt-5">
|
|
||||||
Historical VaR
|
|
||||||
</h2>
|
|
||||||
<div class="text-white text-[1rem] mt-3">
|
|
||||||
Based on historical price data, the company experienced an average
|
|
||||||
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}
|
|
||||||
<Infobox text="No data available" />
|
|
||||||
{/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>
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
export const load = async ({ locals, setHeaders }) => {
|
|
||||||
const getEconomicIndicator = async () => {
|
|
||||||
const { apiKey, apiURL } = locals;
|
|
||||||
|
|
||||||
const response = await fetch(apiURL + "/economic-indicator", {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
"X-API-KEY": apiKey,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const output = await response.json();
|
|
||||||
|
|
||||||
return output;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Make sure to return a promise
|
|
||||||
return {
|
|
||||||
getEconomicIndicator: await getEconomicIndicator(),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,19 +1,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { displayCompanyName, screenWidth, indexTicker } from "$lib/store";
|
import { displayCompanyName, indexTicker } from "$lib/store";
|
||||||
import DailyStats from "$lib/components/Options/DailyStats.svelte";
|
import DailyStats from "$lib/components/Options/DailyStats.svelte";
|
||||||
import { Chart } from "svelte-echarts";
|
import { abbreviateNumber } from "$lib/utils";
|
||||||
import { abbreviateNumberWithColor, monthNames } from "$lib/utils";
|
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import UpgradeToPro from "$lib/components/UpgradeToPro.svelte";
|
import UpgradeToPro from "$lib/components/UpgradeToPro.svelte";
|
||||||
import { init, use } from "echarts/core";
|
|
||||||
import { BarChart, LineChart } from "echarts/charts";
|
|
||||||
import { GridComponent, TooltipComponent } from "echarts/components";
|
|
||||||
import { CanvasRenderer } from "echarts/renderers";
|
|
||||||
import Infobox from "$lib/components/Infobox.svelte";
|
import Infobox from "$lib/components/Infobox.svelte";
|
||||||
import SEO from "$lib/components/SEO.svelte";
|
import SEO from "$lib/components/SEO.svelte";
|
||||||
|
|
||||||
use([BarChart, LineChart, GridComponent, TooltipComponent, CanvasRenderer]);
|
|
||||||
|
|
||||||
export let data;
|
export let data;
|
||||||
let dailyStats = data?.getDailyStats;
|
let dailyStats = data?.getDailyStats;
|
||||||
|
|
||||||
@ -56,145 +50,6 @@
|
|||||||
displayData = event.target.value;
|
displayData = event.target.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function plotData(callData, putData, priceList) {
|
|
||||||
const options = {
|
|
||||||
animation: false,
|
|
||||||
tooltip: {
|
|
||||||
trigger: "axis",
|
|
||||||
hideDelay: 100,
|
|
||||||
borderColor: "#969696", // Black border color
|
|
||||||
borderWidth: 1, // Border width of 1px
|
|
||||||
backgroundColor: "#313131", // Optional: Set background color for contrast
|
|
||||||
textStyle: {
|
|
||||||
color: "#fff", // Optional: Text color for better visibility
|
|
||||||
},
|
|
||||||
formatter: function (params) {
|
|
||||||
// Get the timestamp from the first parameter
|
|
||||||
const timestamp = params[0].axisValue;
|
|
||||||
|
|
||||||
// Initialize result with timestamp
|
|
||||||
let result = timestamp + "<br/>";
|
|
||||||
|
|
||||||
// Sort params to ensure Vol appears last
|
|
||||||
params.sort((a, b) => {
|
|
||||||
if (a.seriesName === "Vol") return 1;
|
|
||||||
if (b.seriesName === "Vol") return -1;
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add each series data
|
|
||||||
params?.forEach((param) => {
|
|
||||||
const marker =
|
|
||||||
'<span style="display:inline-block;margin-right:4px;' +
|
|
||||||
"border-radius:10px;width:10px;height:10px;background-color:" +
|
|
||||||
param.color +
|
|
||||||
'"></span>';
|
|
||||||
result +=
|
|
||||||
marker +
|
|
||||||
param.seriesName +
|
|
||||||
": " +
|
|
||||||
abbreviateNumberWithColor(param.value, false, true) +
|
|
||||||
"<br/>";
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
axisPointer: {
|
|
||||||
lineStyle: {
|
|
||||||
color: "#fff",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
silent: true,
|
|
||||||
grid: {
|
|
||||||
left: $screenWidth < 640 ? "5%" : "2%",
|
|
||||||
right: $screenWidth < 640 ? "5%" : "2%",
|
|
||||||
bottom: "10%",
|
|
||||||
containLabel: true,
|
|
||||||
},
|
|
||||||
xAxis: [
|
|
||||||
{
|
|
||||||
type: "category",
|
|
||||||
data: dateList,
|
|
||||||
axisLabel: {
|
|
||||||
color: "#fff",
|
|
||||||
|
|
||||||
formatter: function (value) {
|
|
||||||
// Assuming dates are in the format 'yyyy-mm-dd'
|
|
||||||
const dateParts = value.split("-");
|
|
||||||
const monthIndex = parseInt(dateParts[1]) - 1; // Months are zero-indexed in JavaScript Date objects
|
|
||||||
const year = parseInt(dateParts[0]);
|
|
||||||
const day = parseInt(dateParts[2]);
|
|
||||||
return `${day} ${monthNames[monthIndex]} ${year}`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
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
|
|
||||||
},
|
|
||||||
position: "right",
|
|
||||||
axisLabel: {
|
|
||||||
show: false, // Hide y-axis labels
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: "Call",
|
|
||||||
type: "bar",
|
|
||||||
stack: "Put-Call Ratio",
|
|
||||||
emphasis: {
|
|
||||||
focus: "series",
|
|
||||||
},
|
|
||||||
data: callData,
|
|
||||||
itemStyle: {
|
|
||||||
color: "#00FC50",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Put",
|
|
||||||
type: "bar",
|
|
||||||
stack: "Put-Call Ratio",
|
|
||||||
emphasis: {
|
|
||||||
focus: "series",
|
|
||||||
},
|
|
||||||
data: putData,
|
|
||||||
itemStyle: {
|
|
||||||
color: "#EE5365", //'#7A1C16'
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Price", // Name for the line chart
|
|
||||||
type: "line", // Type of the chart (line)
|
|
||||||
yAxisIndex: 1, // Use the second y-axis on the right
|
|
||||||
data: priceList, // iv60Data (assumed to be passed as priceList)
|
|
||||||
itemStyle: {
|
|
||||||
color: "#fff", // Choose a color for the line (gold in this case)
|
|
||||||
},
|
|
||||||
lineStyle: {
|
|
||||||
width: 2, // Set the width of the line
|
|
||||||
},
|
|
||||||
smooth: true, // Optional: make the line smooth
|
|
||||||
showSymbol: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
function filterDate(filteredList, displayTimePeriod) {
|
function filterDate(filteredList, displayTimePeriod) {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
let cutoffDate;
|
let cutoffDate;
|
||||||
@ -244,11 +99,13 @@
|
|||||||
putOpenInterestList = filteredList?.map((item) => item?.put_open_interest);
|
putOpenInterestList = filteredList?.map((item) => item?.put_open_interest);
|
||||||
|
|
||||||
// Determine the type of plot data to generate based on displayData
|
// Determine the type of plot data to generate based on displayData
|
||||||
|
/*
|
||||||
if (displayData === "volume") {
|
if (displayData === "volume") {
|
||||||
options = plotData(callVolumeList, putVolumeList, priceList);
|
options = plotData(callVolumeList, putVolumeList, priceList);
|
||||||
} else if (displayData === "openInterest") {
|
} else if (displayData === "openInterest") {
|
||||||
options = plotData(callOpenInterestList, putOpenInterestList, priceList);
|
options = plotData(callOpenInterestList, putOpenInterestList, priceList);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleScroll() {
|
async function handleScroll() {
|
||||||
@ -290,7 +147,7 @@
|
|||||||
<div
|
<div
|
||||||
class="w-full relative flex justify-center items-center overflow-hidden"
|
class="w-full relative flex justify-center items-center overflow-hidden"
|
||||||
>
|
>
|
||||||
<div class="sm:pl-7 sm:pb-7 sm:pt-7 w-full m-auto mt-2 sm:mt-0">
|
<div class="sm:pl-5 sm:pb-7 sm:pt-7 w-full m-auto mt-2 sm:mt-0">
|
||||||
{#if Object?.keys(dailyStats)?.length === 0 && rawData?.length === 0}
|
{#if Object?.keys(dailyStats)?.length === 0 && rawData?.length === 0}
|
||||||
<Infobox text="No Options data available" />
|
<Infobox text="No Options data available" />
|
||||||
{/if}
|
{/if}
|
||||||
@ -302,56 +159,6 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if rawData?.length > 0}
|
{#if rawData?.length > 0}
|
||||||
<div class="flex flex-row items-center w-full mt-10">
|
|
||||||
<!--
|
|
||||||
<select
|
|
||||||
class="ml-1 w-40 select select-bordered select-sm p-0 pl-5 bg-secondary"
|
|
||||||
on:change={changeTimePeriod}
|
|
||||||
>
|
|
||||||
<option disabled>Choose a time period</option>
|
|
||||||
<option value="oneWeek">1 Week</option>
|
|
||||||
<option value="oneMonth">1 Month</option>
|
|
||||||
<option value="threeMonths">3 Months</option>
|
|
||||||
<option value="sixMonths">6 Months</option>
|
|
||||||
<option value="oneYear" selected>1 Year</option>
|
|
||||||
</select>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<select
|
|
||||||
class=" w-40 select select-bordered select-sm p-0 pl-5 bg-secondary"
|
|
||||||
on:change={changeVolumeOI}
|
|
||||||
>
|
|
||||||
<option disabled>Choose a category</option>
|
|
||||||
<option value="volume" selected>Volume</option>
|
|
||||||
<option value="openInterest">Open Interest</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="app w-full bg-default">
|
|
||||||
{#if filteredList?.length !== 0}
|
|
||||||
<Chart {init} {options} class="chart" />
|
|
||||||
{:else}
|
|
||||||
<span
|
|
||||||
class="text-xl text-white m-auto flex justify-center items-center h-full"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="text-white text-sm sm:text-[1rem] sm:rounded-md h-auto border border-gray-600 p-4"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
class="w-5 h-5 inline-block sm:mr-2 shrink-0"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 256 256"
|
|
||||||
><path
|
|
||||||
fill="#fff"
|
|
||||||
d="M128 24a104 104 0 1 0 104 104A104.11 104.11 0 0 0 128 24m-4 48a12 12 0 1 1-12 12a12 12 0 0 1 12-12m12 112a16 16 0 0 1-16-16v-40a8 8 0 0 1 0-16a16 16 0 0 1 16 16v40a8 8 0 0 1 0 16"
|
|
||||||
/></svg
|
|
||||||
>
|
|
||||||
No Options activity found
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{#if optionList?.length !== 0}
|
{#if optionList?.length !== 0}
|
||||||
<h3 class="text-xl sm:text-2xl text-white font-bold text-start">
|
<h3 class="text-xl sm:text-2xl text-white font-bold text-start">
|
||||||
Historical {$indexTicker} Data
|
Historical {$indexTicker} Data
|
||||||
@ -359,92 +166,66 @@
|
|||||||
|
|
||||||
<div class="flex justify-start items-center m-auto overflow-x-auto">
|
<div class="flex justify-start items-center m-auto overflow-x-auto">
|
||||||
<table
|
<table
|
||||||
class="w-full table table-sm table-compact bg-table border border-gray-800 rounded-none sm:rounded-md m-auto mt-4 overflow-x-auto"
|
class="table table-sm table-compact no-scrollbar rounded-none sm:rounded-md w-full bg-white dark:bg-table border border-gray-300 dark:border-gray-800 m-auto mt-3"
|
||||||
>
|
>
|
||||||
<thead class="bg-default">
|
<thead class="text-muted dark:text-white dark:bg-default">
|
||||||
<tr class="">
|
<tr class="">
|
||||||
<td class="text-white font-semibold text-sm text-start"
|
<td class=" font-semibold text-sm text-start">Date</td>
|
||||||
>Date</td
|
<td class=" font-semibold text-sm text-end">% Change</td>
|
||||||
>
|
<td class=" font-semibold text-sm text-end">P/C</td>
|
||||||
<td class="text-white font-semibold text-sm text-end"
|
<td class=" font-semibold text-sm text-center">Volume</td>
|
||||||
>% Change</td
|
<td class=" font-semibold text-sm text-center">C Volume</td>
|
||||||
>
|
<td class=" font-semibold text-sm text-center">P Volume</td>
|
||||||
<td class="text-white font-semibold text-sm text-end"
|
|
||||||
>P/C</td
|
|
||||||
>
|
|
||||||
<td class="text-white font-semibold text-sm text-center"
|
|
||||||
>Volume</td
|
|
||||||
>
|
|
||||||
<td class="text-white font-semibold text-sm text-center"
|
|
||||||
>C Volume</td
|
|
||||||
>
|
|
||||||
<td class="text-white font-semibold text-sm text-center"
|
|
||||||
>P Volume</td
|
|
||||||
>
|
|
||||||
<!--
|
<!--
|
||||||
<td class="text-white font-semibold text-sm text-end"
|
<td class=" font-semibold text-sm text-end"
|
||||||
>Vol/30D</td
|
>Vol/30D</td
|
||||||
>
|
>
|
||||||
-->
|
-->
|
||||||
<!--
|
<!--
|
||||||
<td class="text-white font-semibold text-sm text-end"
|
<td class=" font-semibold text-sm text-end"
|
||||||
>🐻/🐂 Prem</td
|
>🐻/🐂 Prem</td
|
||||||
>
|
>
|
||||||
-->
|
-->
|
||||||
<td class="text-white font-semibold text-sm text-end"
|
<td class=" font-semibold text-sm text-end">Total OI</td>
|
||||||
>Total OI</td
|
<td class=" font-semibold text-sm text-end">OI Change</td>
|
||||||
>
|
<td class=" font-semibold text-sm text-end">% OI Change</td>
|
||||||
<td class="text-white font-semibold text-sm text-end"
|
<td class=" font-semibold text-sm text-end">C Prem</td>
|
||||||
>OI Change</td
|
<td class=" font-semibold text-sm text-end">P Prem</td>
|
||||||
>
|
|
||||||
<td class="text-white font-semibold text-sm text-end"
|
|
||||||
>% OI Change</td
|
|
||||||
>
|
|
||||||
<td class="text-white font-semibold text-sm text-end"
|
|
||||||
>C Prem</td
|
|
||||||
>
|
|
||||||
<td class="text-white font-semibold text-sm text-end"
|
|
||||||
>P Prem</td
|
|
||||||
>
|
|
||||||
<!--
|
<!--
|
||||||
<td class="text-white font-semibold text-sm text-end"
|
<td class=" font-semibold text-sm text-end"
|
||||||
>Net Prem</td
|
>Net Prem</td
|
||||||
>
|
>
|
||||||
<td class="text-white font-semibold text-sm text-end"
|
<td class=" font-semibold text-sm text-end"
|
||||||
>Total Prem</td
|
>Total Prem</td
|
||||||
>
|
>
|
||||||
-->
|
-->
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#each ["Pro"]?.includes(data?.user?.tier) ? optionList : optionList?.slice(0, 3) as item, index}
|
{#each data?.user?.tier === "Pro" ? optionList : optionList?.slice(0, 3) as item, index}
|
||||||
<tr
|
<tr
|
||||||
class="dark:sm:hover:bg-[#245073]/10 odd:bg-[#F6F7F8] dark:odd:bg-oddborder-b border-gray-800 {index +
|
class="dark:sm:hover:bg-[#245073]/10 odd:bg-[#F6F7F8] dark:odd:bg-odd {index +
|
||||||
1 ===
|
1 ===
|
||||||
optionList?.slice(0, 3)?.length &&
|
optionList?.slice(0, 3)?.length &&
|
||||||
!['Pro']?.includes(data?.user?.tier)
|
!['Pro']?.includes(data?.user?.tier)
|
||||||
? 'opacity-[0.1]'
|
? 'opacity-[0.1]'
|
||||||
: ''}"
|
: ''}"
|
||||||
>
|
>
|
||||||
<td class="text-white text-sm sm:text-[1rem] text-start">
|
<td class=" text-sm sm:text-[1rem] text-start">
|
||||||
{formatDate(item?.date)}
|
{formatDate(item?.date)}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="text-white text-sm sm:text-[1rem] text-end">
|
<td class=" text-sm sm:text-[1rem] text-end">
|
||||||
{#if item?.changesPercentage >= 0 && item?.changesPercentage !== null}
|
{#if item?.changesPercentage >= 0 && item?.changesPercentage !== null}
|
||||||
<span class="text-green-700 dark:text-[#00FC50]"
|
<span class="text-green-700 dark:text-[#00FC50]"
|
||||||
>+{item?.changesPercentage >= 1000
|
>+{item?.changesPercentage >= 1000
|
||||||
? abbreviateNumberWithColor(
|
? abbreviateNumber(item?.changesPercentage)
|
||||||
item?.changesPercentage,
|
|
||||||
)
|
|
||||||
: item?.changesPercentage?.toFixed(2)}%</span
|
: item?.changesPercentage?.toFixed(2)}%</span
|
||||||
>
|
>
|
||||||
{:else if item?.changesPercentage < 0 && item?.changesPercentage !== null}
|
{:else if item?.changesPercentage < 0 && item?.changesPercentage !== null}
|
||||||
<span class="text-red-700 dark:text-[#FF2F1F]"
|
<span class="text-red-700 dark:text-[#FF2F1F]"
|
||||||
>{item?.changesPercentage <= -1000
|
>{item?.changesPercentage <= -1000
|
||||||
? abbreviateNumberWithColor(
|
? abbreviateNumber(item?.changesPercentage)
|
||||||
item?.changesPercentage,
|
|
||||||
)
|
|
||||||
: item?.changesPercentage?.toFixed(2)}%
|
: item?.changesPercentage?.toFixed(2)}%
|
||||||
</span>
|
</span>
|
||||||
{:else}
|
{:else}
|
||||||
@ -452,11 +233,11 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="text-sm sm:text-[1rem] text-end text-white">
|
<td class="text-sm sm:text-[1rem] text-end">
|
||||||
{item?.putCallRatio}
|
{item?.putCallRatio}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="text-sm sm:text-[1rem] text-end text-white">
|
<td class="text-sm sm:text-[1rem] text-end">
|
||||||
{item?.volume?.toLocaleString("en-US")}
|
{item?.volume?.toLocaleString("en-US")}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
@ -472,7 +253,7 @@
|
|||||||
{item?.put_volume?.toLocaleString("en-US")}
|
{item?.put_volume?.toLocaleString("en-US")}
|
||||||
</td>
|
</td>
|
||||||
<!--
|
<!--
|
||||||
<td class="text-sm sm:text-[1rem] text-white text-end">
|
<td class="text-sm sm:text-[1rem] text-end">
|
||||||
{item?.avgVolumeRatio?.toFixed(2)}
|
{item?.avgVolumeRatio?.toFixed(2)}
|
||||||
</td>
|
</td>
|
||||||
-->
|
-->
|
||||||
@ -520,24 +301,24 @@
|
|||||||
>
|
>
|
||||||
<div class="flex justify-between space-x-4">
|
<div class="flex justify-between space-x-4">
|
||||||
<div
|
<div
|
||||||
class="space-y-1 flex flex-col items-start text-white"
|
class="space-y-1 flex flex-col items-start "
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
Bearish: {@html abbreviateNumberWithColor(
|
Bearish: {@html abbreviateNumber(
|
||||||
item?.premium_ratio[0],
|
item?.premium_ratio[0],
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Neutral: {@html abbreviateNumberWithColor(
|
Neutral: {@html abbreviateNumber(
|
||||||
item?.premium_ratio[1],
|
item?.premium_ratio[1],
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Bullish: {@html abbreviateNumberWithColor(
|
Bullish: {@html abbreviateNumber(
|
||||||
item?.premium_ratio[2],
|
item?.premium_ratio[2],
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
@ -550,15 +331,15 @@
|
|||||||
</td>
|
</td>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<td class="text-sm sm:text-[1rem] text-end text-white">
|
<td class="text-sm sm:text-[1rem] text-end">
|
||||||
{@html abbreviateNumberWithColor(
|
{@html abbreviateNumber(
|
||||||
item?.total_open_interest,
|
item?.total_open_interest,
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="text-white text-sm sm:text-[1rem] text-end">
|
<td class=" text-sm sm:text-[1rem] text-end">
|
||||||
{#if item?.changeOI >= 0}
|
{#if item?.changeOI >= 0}
|
||||||
<span class="text-green-700 dark:text-[#00FC50]"
|
<span class="text-green-700 dark:text-[#00FC50]"
|
||||||
>+{item?.changeOI?.toLocaleString("en-US")}</span
|
>+{item?.changeOI?.toLocaleString("en-US")}</span
|
||||||
@ -568,58 +349,50 @@
|
|||||||
>{item?.changeOI?.toLocaleString("en-US")}
|
>{item?.changeOI?.toLocaleString("en-US")}
|
||||||
</span>
|
</span>
|
||||||
{:else}
|
{:else}
|
||||||
<span class="text-white"> n/a </span>
|
<span class=""> n/a </span>
|
||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="text-white text-sm sm:text-[1rem] text-end">
|
<td class=" text-sm sm:text-[1rem] text-end">
|
||||||
{#if item?.changesPercentageOI >= 0}
|
{#if item?.changesPercentageOI >= 0}
|
||||||
<span class="text-green-700 dark:text-[#00FC50]"
|
<span class="text-green-700 dark:text-[#00FC50]"
|
||||||
>+{item?.changesPercentageOI >= 1000
|
>+{item?.changesPercentageOI >= 1000
|
||||||
? abbreviateNumberWithColor(
|
? abbreviateNumber(item?.changesPercentageOI)
|
||||||
item?.changesPercentageOI,
|
|
||||||
)
|
|
||||||
: item?.changesPercentageOI?.toFixed(2)}%</span
|
: item?.changesPercentageOI?.toFixed(2)}%</span
|
||||||
>
|
>
|
||||||
{:else if item?.changesPercentageOI < 0}
|
{:else if item?.changesPercentageOI < 0}
|
||||||
<span class="text-red-700 dark:text-[#FF2F1F]"
|
<span class="text-red-700 dark:text-[#FF2F1F]"
|
||||||
>{item?.changesPercentageOI <= -1000
|
>{item?.changesPercentageOI <= -1000
|
||||||
? abbreviateNumberWithColor(
|
? abbreviateNumber(item?.changesPercentageOI)
|
||||||
item?.changesPercentageOI,
|
|
||||||
)
|
|
||||||
: item?.changesPercentageOI?.toFixed(2)}%
|
: item?.changesPercentageOI?.toFixed(2)}%
|
||||||
</span>
|
</span>
|
||||||
{:else}
|
{:else}
|
||||||
<span class="text-white"> n/a </span>
|
<span class=""> n/a </span>
|
||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="text-sm sm:text-[1rem] text-end text-white">
|
<td class="text-sm sm:text-[1rem] text-end">
|
||||||
{@html abbreviateNumberWithColor(
|
{@html abbreviateNumber(
|
||||||
item?.call_premium,
|
item?.call_premium,
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="text-sm sm:text-[1rem] text-end text-white">
|
<td class="text-sm sm:text-[1rem] text-end">
|
||||||
{@html abbreviateNumberWithColor(
|
{@html abbreviateNumber(item?.put_premium, false, true)}
|
||||||
item?.put_premium,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
)}
|
|
||||||
</td>
|
</td>
|
||||||
<!--
|
<!--
|
||||||
<td class="text-sm sm:text-[1rem] text-end text-white">
|
<td class="text-sm sm:text-[1rem] text-end ">
|
||||||
{@html abbreviateNumberWithColor(
|
{@html abbreviateNumber(
|
||||||
item?.net_premium,
|
item?.net_premium,
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class="text-sm sm:text-[1rem] text-end text-white">
|
<td class="text-sm sm:text-[1rem] text-end ">
|
||||||
{@html abbreviateNumberWithColor(
|
{@html abbreviateNumber(
|
||||||
item?.total_premium,
|
item?.total_premium,
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
|||||||
@ -1166,7 +1166,7 @@
|
|||||||
<div class="flex flex-row items-center justify-between">
|
<div class="flex flex-row items-center justify-between">
|
||||||
<h3 class="text-2xl md:text-3xl font-bold">Lifetime</h3>
|
<h3 class="text-2xl md:text-3xl font-bold">Lifetime</h3>
|
||||||
<div>
|
<div>
|
||||||
<span class="text-3xl md:text-4xl font-bold">$599</span>
|
<span class="text-3xl md:text-4xl font-bold">$799</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class=" md:text-lg mt-4 lg:mt-2">
|
<p class=" md:text-lg mt-4 lg:mt-2">
|
||||||
@ -1196,24 +1196,30 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative flex flex-col items-center">
|
|
||||||
<div
|
|
||||||
class="h-4 w-4 rounded-full bg-blue-500 relative z-10"
|
|
||||||
></div>
|
|
||||||
<div class="mt-2 text-center">
|
|
||||||
<div class="text-lg sm:text-xl font-semibold">
|
|
||||||
<span class="text-zinc-900 dark:text-white">$599</span>
|
|
||||||
</div>
|
|
||||||
<div class="text-xs sm:text-sm">First 100 users</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="relative flex flex-col items-center">
|
<div class="relative flex flex-col items-center">
|
||||||
<div
|
<div
|
||||||
class="h-4 w-4 rounded-full bg-zinc-500 dark:bg-gray-600 relative z-10"
|
class="h-4 w-4 rounded-full bg-zinc-500 dark:bg-gray-600 relative z-10"
|
||||||
></div>
|
></div>
|
||||||
<div class="mt-2 text-center">
|
<div class="mt-2 text-center">
|
||||||
<div class="text-lg sm:text-xl font-semibold">
|
<div class="text-lg sm:text-xl font-semibold">
|
||||||
<span class="text-zinc-500 dark:text-zinc-400">$799</span>
|
<span class="text-gray-800 dark:text-gray-300 line-through"
|
||||||
|
>$599</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-xs sm:text-sm text-gray-800 dark:text-gray-300"
|
||||||
|
>
|
||||||
|
First 100 users
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="relative flex flex-col items-center">
|
||||||
|
<div
|
||||||
|
class="h-4 w-4 rounded-full bg-blue-500 relative z-10"
|
||||||
|
></div>
|
||||||
|
<div class="mt-2 text-center">
|
||||||
|
<div class="text-lg sm:text-xl font-semibold">
|
||||||
|
<span class="">$799</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="text-xs sm:text-sm text-gray-800 dark:text-gray-300"
|
class="text-xs sm:text-sm text-gray-800 dark:text-gray-300"
|
||||||
|
|||||||
@ -788,11 +788,16 @@
|
|||||||
class="flex flex-col border-b border-gray-300 dark:border-gray-800 py-1 sm:table-row sm:py-0"
|
class="flex flex-col border-b border-gray-300 dark:border-gray-800 py-1 sm:table-row sm:py-0"
|
||||||
><td
|
><td
|
||||||
class="whitespace-nowrap px-0.5 py-[1px] xs:px-1 text-sm sm:text-[1rem]"
|
class="whitespace-nowrap px-0.5 py-[1px] xs:px-1 text-sm sm:text-[1rem]"
|
||||||
>Revenue (ttm)</td
|
><a
|
||||||
|
href={`/stocks/${$stockTicker}/statistics/revenue`}
|
||||||
|
class="sm:hover:text-muted dark:sm:hover:text-blue-400 underline underline-offset-4"
|
||||||
|
>Revenue (ttm)</a
|
||||||
|
></td
|
||||||
>
|
>
|
||||||
<td
|
<td
|
||||||
class="whitespace-nowrap px-0.5 py-[1px] text-left text-sm text-[1rem] font-semibold dark:font-normal xs:px-1 sm:text-right"
|
class="whitespace-nowrap px-0.5 py-[1px] text-left text-sm text-[1rem] font-semibold dark:font-normal xs:px-1 sm:text-right"
|
||||||
>{@html stockDeck?.revenueTTM !== null &&
|
>
|
||||||
|
{@html stockDeck?.revenueTTM !== null &&
|
||||||
stockDeck?.revenueTTM !== 0
|
stockDeck?.revenueTTM !== 0
|
||||||
? abbreviateNumber(stockDeck?.revenueTTM, false, true)
|
? abbreviateNumber(stockDeck?.revenueTTM, false, true)
|
||||||
: "n/a"}</td
|
: "n/a"}</td
|
||||||
|
|||||||
@ -237,7 +237,7 @@
|
|||||||
<span>Revenue (ttm)</span>
|
<span>Revenue (ttm)</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-baseline">
|
<div class="flex items-baseline">
|
||||||
<span class="text-xl font-bold">
|
<span class="text-xl font-semibold">
|
||||||
{abbreviateNumber(rawData?.revenue, true)}</span
|
{abbreviateNumber(rawData?.revenue, true)}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@ -250,7 +250,7 @@
|
|||||||
<span>Revenue Growth</span>
|
<span>Revenue Growth</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-baseline">
|
<div class="flex items-baseline">
|
||||||
<span class="text-xl font-bold"
|
<span class="text-xl font-semibold"
|
||||||
>{rawData?.growthRevenue}%</span
|
>{rawData?.growthRevenue}%</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@ -263,7 +263,7 @@
|
|||||||
<span>Price / Sales Ratio</span>
|
<span>Price / Sales Ratio</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-baseline">
|
<div class="flex items-baseline">
|
||||||
<span class="text-xl font-bold"
|
<span class="text-xl font-semibold"
|
||||||
>{rawData?.priceToSalesRatio}</span
|
>{rawData?.priceToSalesRatio}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@ -276,7 +276,7 @@
|
|||||||
<span>Revenue / Employee </span>
|
<span>Revenue / Employee </span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-baseline">
|
<div class="flex items-baseline">
|
||||||
<span class="text-xl font-bold"
|
<span class="text-xl font-semibold"
|
||||||
>{abbreviateNumber(
|
>{abbreviateNumber(
|
||||||
rawData?.revenuePerEmployee,
|
rawData?.revenuePerEmployee,
|
||||||
true,
|
true,
|
||||||
@ -292,7 +292,7 @@
|
|||||||
<span>Employees </span>
|
<span>Employees </span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-baseline">
|
<div class="flex items-baseline">
|
||||||
<span class="text-xl font-bold"
|
<span class="text-xl font-semibold"
|
||||||
>{rawData?.employees?.toLocaleString("en-US")}</span
|
>{rawData?.employees?.toLocaleString("en-US")}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@ -304,7 +304,7 @@
|
|||||||
<span>Market Cap </span>
|
<span>Market Cap </span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-baseline">
|
<div class="flex items-baseline">
|
||||||
<span class="text-xl font-bold"
|
<span class="text-xl font-semibold"
|
||||||
>{abbreviateNumber(data?.getStockQuote?.marketCap)}</span
|
>{abbreviateNumber(data?.getStockQuote?.marketCap)}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1377,9 +1377,9 @@
|
|||||||
on:input={handleInput}
|
on:input={handleInput}
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
autofocus=""
|
autofocus=""
|
||||||
class="text-sm w-full bg-white dark:bg-default border-0 focus:border-gray-200 focus:ring-0 placeholder:text-gray-300 pr-8"
|
class="text-sm w-full bg-white dark:bg-default border-0 focus:border-gray-200 focus:ring-0 focus:outline-none placeholder:text-gray-400 dark:placeholder:text-gray-600 pr-8"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder=""
|
placeholder="Search indicators..."
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Clear Button - Shown only when searchQuery has input -->
|
<!-- Clear Button - Shown only when searchQuery has input -->
|
||||||
|
|||||||
@ -5,7 +5,6 @@ module.exports = {
|
|||||||
safelist: ["dark"],
|
safelist: ["dark"],
|
||||||
content: [
|
content: [
|
||||||
"./src/**/*.{html,js,svelte,ts}",
|
"./src/**/*.{html,js,svelte,ts}",
|
||||||
"./node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}",
|
|
||||||
],
|
],
|
||||||
theme: {
|
theme: {
|
||||||
container: {
|
container: {
|
||||||
@ -194,5 +193,4 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [require("flowbite/plugin")],
|
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user