Skip to content

Commit

Permalink
Feature/stock peer comparison (#14)
Browse files Browse the repository at this point in the history
* feat: implement page

* implement peer comparison page

* feat: implement stock peer page

* feat: implement peer page

* feat: implement peer page

* feat: implement peer page
  • Loading branch information
zmcx16 authored May 13, 2024
1 parent 7c88cb4 commit 5a0eb5f
Show file tree
Hide file tree
Showing 9 changed files with 632 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/common/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const pageRouterTable = [
{ text: 'Short Stocks Summary', path: '/short-stocks-summary/' },
{ text: 'Dividend Champions', path: '/dividend-champions/' },
{ text: 'ESG Stocks Summary', path: '/esg-stocks-summary/' },
{ text: 'Stock Peer Comparsion', path: '/stock-peer-comparison/'},
{ text: "Stock Benford's Law", path: '/stock-benford-law/' },
{ text: 'Stock Price Simulation', path: '/stock-price-simulation/' },
{ text: 'Market Correlation Matrix', path: '/market-correlation-matrix/' },
Expand Down
2 changes: 1 addition & 1 deletion src/common/dataGridUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export function PriceField(field, headerName, width, hide, description = null, s
width: width,
renderCell: (params) => (
<Link href={ source=="yahoo" ? YahooFinanceUrl + 'quote/' + params.row["symbol"] : FinvizUrl + 'quote.ashx?t=' + params.row["symbol"] } target="_blank" rel="noreferrer noopener">
<span>{params.value === '-' || params.value === -Number.MAX_VALUE || params.value === Number.MAX_VALUE || params.value === null || params.value === undefined || params.value === "Infinity" || params.value === 'NaN' ? '-' : params.value}</span>
<span>{params.value === '-' || params.value === -Number.MAX_VALUE || params.value === Number.MAX_VALUE || params.value === null || params.value === undefined || params.value === "Infinity" || params.value === 'NaN' ? '-' : params.value.toFixed(2)}</span>
</Link>
),
hide: hide
Expand Down
114 changes: 114 additions & 0 deletions src/common/peer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
export const COOKIE_KEY_FACTOR_PANNEL = 'factor_pannel'

export const PeerTemplate = {
tactics: [
{
type: "PEP",
displayName: "P/E Peer",
color: {
main: { backgroundColor: '#f44336', color: '#fff' }, // Shade: 500
avatar: { backgroundColor: '#d32f2f', color: '#fff' }, // Shade: 700
},
enable: true,
flag: -1,
},
{
type: "FwdPEP",
displayName: "Fwd P/E Peer",
color: {
main: { backgroundColor: '#2196f3', color: '#fff' },
avatar: { backgroundColor: '#1976d2', color: '#fff' },
},
enable: true,
flag: -1,
},
{
type: "PEGP",
displayName: "PEG Peer",
color: {
main: { backgroundColor: '#4caf50', color: '#fff' },
avatar: { backgroundColor: '#388e3c', color: '#fff' },
},
enable: true,
flag: -1,
},
{
type: "PSP",
displayName: "P/S Peer",
color: {
main: { backgroundColor: '#ff9800', color: '#fff' },
avatar: { backgroundColor: '#f57c00', color: '#fff' },
},
enable: true,
flag: -1,
},
{
type: "PBP",
displayName: "P/B Peer",
color: {
main: { backgroundColor: '#9c27b0', color: '#fff' },
avatar: { backgroundColor: '#7b1fa2', color: '#fff' },
},
enable: true,
flag: -1,
},
{
type: "PCP",
displayName: "P/C Peer",
color: {
main: { backgroundColor: '#ffeb3b', color: 'rgba(0, 0, 0, 0.87)' },
avatar: { backgroundColor: '#fbc02d', color: 'rgba(0, 0, 0, 0.87)' },
},
enable: true,
flag: -1,
},
{
type: "PFCFP",
displayName: "P/FCF Peer",
color: {
main: { backgroundColor: '#00bcd4', color: '#fff' },
avatar: { backgroundColor: '#0097a7', color: '#fff' },
},
enable: true,
flag: -1,
},
{
type: "DividendP",
displayName: "Dividend Peer",
color: {
main: { backgroundColor: '#795548', color: '#fff' },
avatar: { backgroundColor: '#5d4037', color: '#fff' },
},
enable: true,
flag: 1,
},
{
type: "FloatShortP",
displayName: "Float Short Peer",
color: {
main: { backgroundColor: '#607d8b', color: '#fff' },
avatar: { backgroundColor: '#455a64', color: '#fff' },
},
enable: true,
flag: -1,
},
{
type: "RecomP",
displayName: "Recom Peer",
color: {
main: { backgroundColor: '#ff5722', color: '#fff' },
avatar: { backgroundColor: '#e64a19', color: '#fff' },
},
enable: true,
flag: -1,
}
]
}

export const GetPeerTemplateDict = () => {
let ret = {}
PeerTemplate.tactics.forEach((element)=>{
ret[element.type] = element
})
return ret
}
2 changes: 1 addition & 1 deletion src/common/reactMaterialTableUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function PriceField(field, headerName, width, description = null) {
Cell: ({ cell, row }) => (
cell.getValue() === -Number.MAX_VALUE || cell.getValue() === Number.MAX_VALUE || cell.getValue() === "-" || cell.getValue() === null || cell.getValue() === undefined || cell.getValue() === "Infinity" || cell.getValue() === 'NaN' ? <span>-</span> :
<Link href={ YahooFinanceUrl + 'quote/' + row.original.symbol} target="_blank" rel="noreferrer noopener">
<span>{cell.getValue()}</span>
<span>{cell.getValue().toFixed(2)}</span>
</Link>
),
}
Expand Down
104 changes: 104 additions & 0 deletions src/components/stock-peer-comparison/factorPannel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React, { useState, useEffect } from 'react'
import Chip from '@mui/material/Chip'
import { createTheme } from '@mui/material/styles'

import { PeerTemplate, COOKIE_KEY_FACTOR_PANNEL } from '../../common/peer'
import Cookies from 'universal-cookie'

import factorPannelStyle from './factorPannel.module.scss'

export const GetTacticFromCookie = () => {
const cookies = new Cookies()
let cookie_t = cookies.get(COOKIE_KEY_FACTOR_PANNEL)
let ret = PeerTemplate.tactics.reduce((accumulator, currentValue)=>{
if (!cookie_t) {
accumulator[currentValue.type] = currentValue.enable
} else {
accumulator[currentValue.type] = cookie_t.tactics[currentValue.type]
}
return accumulator
}, {})
return ret
}

const FactorPannel = ({ factorPannelRef }) => {
const cookies = new Cookies()

// factorPannelRef API
factorPannelRef.current.getValue = () => {
return tactic
}

factorPannelRef.current.setValue = (Setting) => {
let tactic_t = { ...tactic }
Object.entries(Setting.tactics).forEach(([key, value]) => {
tactic_t[key] = value
})
setTactic(tactic_t)
}

factorPannelRef.current.getEnableTacticStrings = () => {
let enableList=[]
Object.entries(tactic).forEach(([key, value]) => {
if (value){
enableList.push(key)
}
})
return enableList.join(",")
}

// tactic
const [tactic, setTactic] = useState(GetTacticFromCookie())

// tactic color
let tacticsColor = {
palette: {
default:
{
main: { backgroundColor: '#e0e0e0', color: 'rgba(0, 0, 0, 0.87)' },
avatar: { backgroundColor: '#bdbdbd', color: '#616161' },
}
}
}

PeerTemplate.tactics.forEach((element)=>{
tacticsColor.palette[element.type] = element.color
})

const tacticsTheme = createTheme(tacticsColor)

useEffect(() => {
// componentDidMount is here!
// componentDidUpdate is here!
return () => {
// componentWillUnmount is here!
}
}, [])

return (
<div className={factorPannelStyle.container}>
<div>
{PeerTemplate.tactics.map((value) => {
return (
<Chip key={value.type}
label={value.displayName}
style={tactic[value.type] ? { ...tacticsTheme.palette[value.type].main, ...{ margin: '3px 5px' } } : { ...tacticsTheme.palette.default.main, ...{ margin: '3px 5px' }}}
onClick={() => {
let tactic_t = { ...tactic }
tactic_t[value.type] = !tactic_t[value.type]
setTactic(tactic_t)
cookies.set(COOKIE_KEY_FACTOR_PANNEL, { tactics: tactic_t }, {
path: '/',
expires: new Date(Date.now() + 10 * 365 * 24 * 60 * 60 * 1000),
maxAge: 10 * 365 * 24 * 60 * 60,
})
}} />
)
})}
</div>
</div>
)
}


export default FactorPannel
3 changes: 3 additions & 0 deletions src/components/stock-peer-comparison/factorPannel.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.container {
margin: 10px 0;
}
Loading

0 comments on commit 5a0eb5f

Please sign in to comment.