This commit is contained in:
commit
07c4c0b064
|
@ -266,6 +266,8 @@ export const calculateMetricsByContract = (
|
|||
})
|
||||
}
|
||||
|
||||
export type ContractMetrics = ReturnType<typeof calculateMetricsByContract>[number]
|
||||
|
||||
const calculatePeriodProfit = (
|
||||
contract: CPMMBinaryContract,
|
||||
bets: Bet[],
|
||||
|
|
|
@ -178,6 +178,8 @@ function getDpmInvested(yourBets: Bet[]) {
|
|||
})
|
||||
}
|
||||
|
||||
export type ContractBetMetrics = ReturnType<typeof getContractBetMetrics>
|
||||
|
||||
export function getContractBetMetrics(contract: Contract, yourBets: Bet[]) {
|
||||
const { resolution } = contract
|
||||
const isCpmm = contract.mechanism === 'cpmm-1'
|
||||
|
|
|
@ -44,6 +44,10 @@ service cloud.firestore {
|
|||
allow read;
|
||||
}
|
||||
|
||||
match /{somePath=**}/contract-metrics/{contractId} {
|
||||
allow read;
|
||||
}
|
||||
|
||||
match /{somePath=**}/challenges/{challengeId}{
|
||||
allow read;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import clsx from 'clsx'
|
||||
import Link from 'next/link'
|
||||
import { Row } from '../layout/row'
|
||||
import { formatLargeNumber, formatPercent } from 'common/util/format'
|
||||
import {
|
||||
formatLargeNumber,
|
||||
formatMoney,
|
||||
formatPercent,
|
||||
} from 'common/util/format'
|
||||
import { contractPath, getBinaryProbPercent } from 'web/lib/firebase/contracts'
|
||||
import { Col } from '../layout/col'
|
||||
import {
|
||||
|
@ -17,6 +21,7 @@ import {
|
|||
import {
|
||||
AnswerLabel,
|
||||
BinaryContractOutcomeLabel,
|
||||
BinaryOutcomeLabel,
|
||||
CancelLabel,
|
||||
FreeResponseOutcomeLabel,
|
||||
} from '../outcome-label'
|
||||
|
@ -29,7 +34,7 @@ import { AvatarDetails, MiscDetails, ShowTime } from './contract-details'
|
|||
import { getExpectedValue, getValueFromBucket } from 'common/calculate-dpm'
|
||||
import { getColor, ProbBar, QuickBet } from './quick-bet'
|
||||
import { useContractWithPreload } from 'web/hooks/use-contract'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
import { useUser, useUserContractMetrics } from 'web/hooks/use-user'
|
||||
import { track } from '@amplitude/analytics-browser'
|
||||
import { trackCallback } from 'web/lib/service/analytics'
|
||||
import { getMappedValue } from 'common/pseudo-numeric'
|
||||
|
@ -37,6 +42,7 @@ import { Tooltip } from '../tooltip'
|
|||
import { SiteLink } from '../site-link'
|
||||
import { ProbChange } from './prob-change-table'
|
||||
import { Card } from '../card'
|
||||
import { ProfitBadgeMana } from '../profit-badge'
|
||||
|
||||
export function ContractCard(props: {
|
||||
contract: Contract
|
||||
|
@ -390,11 +396,18 @@ export function PseudoNumericResolutionOrExpectation(props: {
|
|||
export function ContractCardProbChange(props: {
|
||||
contract: CPMMContract
|
||||
noLinkAvatar?: boolean
|
||||
showPosition?: boolean
|
||||
className?: string
|
||||
}) {
|
||||
const { noLinkAvatar, className } = props
|
||||
const { noLinkAvatar, showPosition, className } = props
|
||||
const contract = useContractWithPreload(props.contract) as CPMMBinaryContract
|
||||
|
||||
const user = useUser()
|
||||
const metrics = useUserContractMetrics(user?.id, contract.id)
|
||||
const dayMetrics = metrics && metrics.from && metrics.from.day
|
||||
const outcome =
|
||||
metrics && metrics.hasShares && metrics.totalShares.YES ? 'YES' : 'NO'
|
||||
|
||||
return (
|
||||
<Card className={clsx(className, 'mb-4')}>
|
||||
<AvatarDetails
|
||||
|
@ -411,6 +424,28 @@ export function ContractCardProbChange(props: {
|
|||
</SiteLink>
|
||||
<ProbChange className="py-2 pr-4" contract={contract} />
|
||||
</Row>
|
||||
{showPosition && metrics && (
|
||||
<Row
|
||||
className={clsx(
|
||||
'items-center justify-between gap-4 pl-6 pr-4 pb-2 text-sm'
|
||||
)}
|
||||
>
|
||||
<Row className="gap-1">
|
||||
<div className="text-gray-500">Position</div>
|
||||
{formatMoney(metrics.payout)}
|
||||
<BinaryOutcomeLabel outcome={outcome} />
|
||||
</Row>
|
||||
|
||||
{dayMetrics && (
|
||||
<>
|
||||
<Row className="items-center">
|
||||
<div className="mr-1 text-gray-500">Daily profit</div>
|
||||
<ProfitBadgeMana amount={dayMetrics.profit} />
|
||||
</Row>
|
||||
</>
|
||||
)}
|
||||
</Row>
|
||||
)}
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -81,6 +81,7 @@ export function ContractsGrid(props: {
|
|||
<ContractCardProbChange
|
||||
key={contract.id}
|
||||
contract={contract as CPMMBinaryContract}
|
||||
showPosition
|
||||
/>
|
||||
) : (
|
||||
<ContractCard
|
||||
|
|
|
@ -1,11 +1,70 @@
|
|||
import clsx from 'clsx'
|
||||
import { CPMMContract } from 'common/contract'
|
||||
import { formatPercent } from 'common/util/format'
|
||||
import { sortBy } from 'lodash'
|
||||
import { filterDefined } from 'common/util/array'
|
||||
import { ContractMetrics } from 'common/calculate-metrics'
|
||||
import { CPMMBinaryContract, CPMMContract } from 'common/contract'
|
||||
import { formatPercent } from 'common/util/format'
|
||||
import { Col } from '../layout/col'
|
||||
import { LoadingIndicator } from '../loading-indicator'
|
||||
import { ContractCardProbChange } from './contract-card'
|
||||
|
||||
export function ProfitChangeTable(props: {
|
||||
contracts: CPMMBinaryContract[]
|
||||
metrics: ContractMetrics[]
|
||||
}) {
|
||||
const { contracts, metrics } = props
|
||||
|
||||
const contractProfit = metrics.map(
|
||||
(m) => [m.contractId, m.from?.day.profit ?? 0] as const
|
||||
)
|
||||
|
||||
const positiveProfit = sortBy(
|
||||
contractProfit.filter(([, profit]) => profit > 0),
|
||||
([, profit]) => profit
|
||||
).reverse()
|
||||
const positive = filterDefined(
|
||||
positiveProfit.map(([contractId]) =>
|
||||
contracts.find((c) => c.id === contractId)
|
||||
)
|
||||
)
|
||||
|
||||
const negativeProfit = sortBy(
|
||||
contractProfit.filter(([, profit]) => profit < 0),
|
||||
([, profit]) => profit
|
||||
)
|
||||
const negative = filterDefined(
|
||||
negativeProfit.map(([contractId]) =>
|
||||
contracts.find((c) => c.id === contractId)
|
||||
)
|
||||
)
|
||||
|
||||
if (positive.length === 0 && negative.length === 0)
|
||||
return <div className="px-4 text-gray-500">None</div>
|
||||
|
||||
return (
|
||||
<Col className="mb-4 w-full gap-4 rounded-lg md:flex-row">
|
||||
<Col className="flex-1">
|
||||
{positive.map((contract) => (
|
||||
<ContractCardProbChange
|
||||
key={contract.id}
|
||||
contract={contract}
|
||||
showPosition
|
||||
/>
|
||||
))}
|
||||
</Col>
|
||||
<Col className="flex-1">
|
||||
{negative.map((contract) => (
|
||||
<ContractCardProbChange
|
||||
key={contract.id}
|
||||
contract={contract}
|
||||
showPosition
|
||||
/>
|
||||
))}
|
||||
</Col>
|
||||
</Col>
|
||||
)
|
||||
}
|
||||
|
||||
export function ProbChangeTable(props: {
|
||||
changes: CPMMContract[] | undefined
|
||||
full?: boolean
|
||||
|
@ -39,12 +98,20 @@ export function ProbChangeTable(props: {
|
|||
<Col className="mb-4 w-full gap-4 rounded-lg md:flex-row">
|
||||
<Col className="flex-1">
|
||||
{filteredPositiveChanges.map((contract) => (
|
||||
<ContractCardProbChange key={contract.id} contract={contract} />
|
||||
<ContractCardProbChange
|
||||
key={contract.id}
|
||||
contract={contract}
|
||||
showPosition
|
||||
/>
|
||||
))}
|
||||
</Col>
|
||||
<Col className="flex-1">
|
||||
{filteredNegativeChanges.map((contract) => (
|
||||
<ContractCardProbChange key={contract.id} contract={contract} />
|
||||
<ContractCardProbChange
|
||||
key={contract.id}
|
||||
contract={contract}
|
||||
showPosition
|
||||
/>
|
||||
))}
|
||||
</Col>
|
||||
</Col>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import clsx from 'clsx'
|
||||
import { ENV_CONFIG } from 'common/envs/constants'
|
||||
|
||||
export function ProfitBadge(props: {
|
||||
profitPercent: number
|
||||
|
@ -26,3 +27,24 @@ export function ProfitBadge(props: {
|
|||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export function ProfitBadgeMana(props: { amount: number; className?: string }) {
|
||||
const { amount, className } = props
|
||||
const colors =
|
||||
amount > 0 ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
|
||||
|
||||
const formatted =
|
||||
ENV_CONFIG.moneyMoniker + (amount > 0 ? '+' : '') + amount.toFixed(0)
|
||||
|
||||
return (
|
||||
<span
|
||||
className={clsx(
|
||||
'ml-1 inline-flex items-center rounded-full px-3 py-0.5 text-sm font-medium',
|
||||
colors,
|
||||
className
|
||||
)}
|
||||
>
|
||||
{formatted}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
import { useContext } from 'react'
|
||||
import { useFirestoreDocumentData } from '@react-query-firebase/firestore'
|
||||
import { useQueryClient } from 'react-query'
|
||||
import {
|
||||
useFirestoreDocumentData,
|
||||
useFirestoreQueryData,
|
||||
} from '@react-query-firebase/firestore'
|
||||
import { useQuery, useQueryClient } from 'react-query'
|
||||
|
||||
import { doc, DocumentData } from 'firebase/firestore'
|
||||
import { getUser, User, users } from 'web/lib/firebase/users'
|
||||
import { AuthContext } from 'web/components/auth-context'
|
||||
import { ContractMetrics } from 'common/calculate-metrics'
|
||||
import { getUserContractMetricsQuery } from 'web/lib/firebase/contract-metrics'
|
||||
import { getContractFromId } from 'web/lib/firebase/contracts'
|
||||
import { buildArray, filterDefined } from 'common/util/array'
|
||||
import { CPMMBinaryContract } from 'common/contract'
|
||||
|
||||
export const useUser = () => {
|
||||
const authUser = useContext(AuthContext)
|
||||
|
@ -38,3 +46,45 @@ export const usePrefetchUsers = (userIds: string[]) => {
|
|||
queryClient.prefetchQuery(['users', userId], () => getUser(userId))
|
||||
)
|
||||
}
|
||||
|
||||
export const useUserContractMetricsByProfit = (
|
||||
userId: string,
|
||||
count: number
|
||||
) => {
|
||||
const positiveResult = useFirestoreQueryData<ContractMetrics>(
|
||||
['contract-metrics-descending', userId, count],
|
||||
getUserContractMetricsQuery(userId, count, 'desc')
|
||||
)
|
||||
const negativeResult = useFirestoreQueryData<ContractMetrics>(
|
||||
['contract-metrics-ascending', userId, count],
|
||||
getUserContractMetricsQuery(userId, count, 'asc')
|
||||
)
|
||||
|
||||
const metrics = buildArray(positiveResult.data, negativeResult.data)
|
||||
const contractIds = metrics.map((m) => m.contractId)
|
||||
|
||||
const contractResult = useQuery(['contracts', contractIds], () =>
|
||||
Promise.all(contractIds.map(getContractFromId))
|
||||
)
|
||||
const contracts = contractResult.data
|
||||
|
||||
if (!positiveResult.data || !negativeResult.data || !contracts)
|
||||
return undefined
|
||||
|
||||
const filteredContracts = filterDefined(contracts) as CPMMBinaryContract[]
|
||||
const filteredMetrics = metrics.filter(
|
||||
(m) => m.from && Math.abs(m.from.day.profit) >= 0.5
|
||||
)
|
||||
return { contracts: filteredContracts, metrics: filteredMetrics }
|
||||
}
|
||||
|
||||
export const useUserContractMetrics = (userId = '_', contractId: string) => {
|
||||
const result = useFirestoreDocumentData<DocumentData, ContractMetrics>(
|
||||
['user-contract-metrics', userId, contractId],
|
||||
doc(users, userId, 'contract-metrics', contractId)
|
||||
)
|
||||
|
||||
if (userId === '_') return undefined
|
||||
|
||||
return result.data
|
||||
}
|
||||
|
|
23
web/lib/firebase/contract-metrics.ts
Normal file
23
web/lib/firebase/contract-metrics.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { ContractMetrics } from 'common/calculate-metrics'
|
||||
import {
|
||||
query,
|
||||
limit,
|
||||
Query,
|
||||
collection,
|
||||
orderBy,
|
||||
where,
|
||||
} from 'firebase/firestore'
|
||||
import { db } from './init'
|
||||
|
||||
export function getUserContractMetricsQuery(
|
||||
userId: string,
|
||||
count: number,
|
||||
sort: 'asc' | 'desc'
|
||||
) {
|
||||
return query(
|
||||
collection(db, 'users', userId, 'contract-metrics'),
|
||||
where('from.day.profit', sort === 'desc' ? '>' : '<', 0),
|
||||
orderBy('from.day.profit', sort),
|
||||
limit(count)
|
||||
) as Query<ContractMetrics>
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
import { ProbChangeTable } from 'web/components/contract/prob-change-table'
|
||||
import { ProfitChangeTable } from 'web/components/contract/prob-change-table'
|
||||
import { Col } from 'web/components/layout/col'
|
||||
import { Row } from 'web/components/layout/row'
|
||||
import { LoadingIndicator } from 'web/components/loading-indicator'
|
||||
import { Page } from 'web/components/page'
|
||||
import { Title } from 'web/components/title'
|
||||
import { useProbChanges } from 'web/hooks/use-prob-changes'
|
||||
import { useTracking } from 'web/hooks/use-tracking'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
import { useUser, useUserContractMetricsByProfit } from 'web/hooks/use-user'
|
||||
import { DailyProfit } from './home'
|
||||
|
||||
export default function DailyMovers() {
|
||||
const user = useUser()
|
||||
|
@ -13,7 +15,10 @@ export default function DailyMovers() {
|
|||
return (
|
||||
<Page>
|
||||
<Col className="pm:mx-10 gap-4 sm:px-4 sm:pb-4">
|
||||
<Title className="mx-4 !mb-0 sm:mx-0" text="Daily movers" />
|
||||
<Row className="mt-4 items-start justify-between sm:mt-0">
|
||||
<Title className="mx-4 !mb-0 !mt-0 sm:mx-0" text="Daily movers" />
|
||||
<DailyProfit user={user} />
|
||||
</Row>
|
||||
{user && <ProbChangesWrapper userId={user.id} />}
|
||||
</Col>
|
||||
</Page>
|
||||
|
@ -23,9 +28,9 @@ export default function DailyMovers() {
|
|||
function ProbChangesWrapper(props: { userId: string }) {
|
||||
const { userId } = props
|
||||
|
||||
const changes = useProbChanges({ bettorId: userId })?.filter(
|
||||
(c) => Math.abs(c.probChanges.day) >= 0.01
|
||||
)
|
||||
const data = useUserContractMetricsByProfit(userId, 50)
|
||||
|
||||
return <ProbChangeTable changes={changes} full />
|
||||
if (!data) return <LoadingIndicator />
|
||||
|
||||
return <ProfitChangeTable contracts={data.contracts} metrics={data.metrics} />
|
||||
}
|
||||
|
|
|
@ -19,19 +19,22 @@ import { useSaveReferral } from 'web/hooks/use-save-referral'
|
|||
import { Sort } from 'web/components/contract-search'
|
||||
import { Group } from 'common/group'
|
||||
import { SiteLink } from 'web/components/site-link'
|
||||
import { usePrivateUser, useUser } from 'web/hooks/use-user'
|
||||
import {
|
||||
usePrivateUser,
|
||||
useUser,
|
||||
useUserContractMetricsByProfit,
|
||||
} from 'web/hooks/use-user'
|
||||
import {
|
||||
useMemberGroupsSubscription,
|
||||
useTrendingGroups,
|
||||
} from 'web/hooks/use-group'
|
||||
import { Button } from 'web/components/button'
|
||||
import { Row } from 'web/components/layout/row'
|
||||
import { ProbChangeTable } from 'web/components/contract/prob-change-table'
|
||||
import { ProfitChangeTable } from 'web/components/contract/prob-change-table'
|
||||
import { groupPath, joinGroup, leaveGroup } from 'web/lib/firebase/groups'
|
||||
import { usePortfolioHistory } from 'web/hooks/use-portfolio-history'
|
||||
import { formatMoney } from 'common/util/format'
|
||||
import { useProbChanges } from 'web/hooks/use-prob-changes'
|
||||
import { calculatePortfolioProfit } from 'common/calculate-metrics'
|
||||
import { ContractMetrics } from 'common/calculate-metrics'
|
||||
import { hasCompletedStreakToday } from 'web/components/profile/betting-streak-modal'
|
||||
import { ContractsGrid } from 'web/components/contract/contracts-grid'
|
||||
import { PillButton } from 'web/components/buttons/pill-button'
|
||||
|
@ -74,7 +77,11 @@ export default function Home() {
|
|||
}
|
||||
}, [user, sections])
|
||||
|
||||
const dailyMovers = useProbChanges({ bettorId: user?.id })
|
||||
const contractMetricsByProfit = useUserContractMetricsByProfit(
|
||||
user?.id ?? '_',
|
||||
3
|
||||
)
|
||||
|
||||
const trendingContracts = useTrendingContracts(6)
|
||||
const newContracts = useNewContracts(6)
|
||||
const dailyTrendingContracts = useContractsByDailyScoreNotBetOn(user?.id, 6)
|
||||
|
@ -87,7 +94,7 @@ export default function Home() {
|
|||
|
||||
const isLoading =
|
||||
!user ||
|
||||
!dailyMovers ||
|
||||
!contractMetricsByProfit ||
|
||||
!trendingContracts ||
|
||||
!newContracts ||
|
||||
!dailyTrendingContracts
|
||||
|
@ -118,7 +125,7 @@ export default function Home() {
|
|||
score: trendingContracts,
|
||||
newest: newContracts,
|
||||
'daily-trending': dailyTrendingContracts,
|
||||
'daily-movers': dailyMovers,
|
||||
'daily-movers': contractMetricsByProfit,
|
||||
})}
|
||||
|
||||
{groups && groupContracts && trendingGroups.length > 0 ? (
|
||||
|
@ -184,7 +191,10 @@ export const getHomeItems = (sections: string[]) => {
|
|||
function renderSections(
|
||||
sections: { id: string; label: string }[],
|
||||
sectionContracts: {
|
||||
'daily-movers': CPMMBinaryContract[]
|
||||
'daily-movers': {
|
||||
contracts: CPMMBinaryContract[]
|
||||
metrics: ContractMetrics[]
|
||||
}
|
||||
'daily-trending': CPMMBinaryContract[]
|
||||
newest: CPMMBinaryContract[]
|
||||
score: CPMMBinaryContract[]
|
||||
|
@ -193,13 +203,16 @@ function renderSections(
|
|||
return (
|
||||
<>
|
||||
{sections.map((s) => {
|
||||
const { id, label } = s
|
||||
const contracts =
|
||||
sectionContracts[s.id as keyof typeof sectionContracts]
|
||||
|
||||
if (id === 'daily-movers') {
|
||||
return <DailyMoversSection key={id} contracts={contracts} />
|
||||
const { id, label } = s as {
|
||||
id: keyof typeof sectionContracts
|
||||
label: string
|
||||
}
|
||||
if (id === 'daily-movers') {
|
||||
return <DailyMoversSection key={id} {...sectionContracts[id]} />
|
||||
}
|
||||
|
||||
const contracts = sectionContracts[id]
|
||||
|
||||
if (id === 'daily-trending') {
|
||||
return (
|
||||
<SearchSection
|
||||
|
@ -347,58 +360,39 @@ function GroupSection(props: {
|
|||
)
|
||||
}
|
||||
|
||||
function DailyMoversSection(props: { contracts: CPMMBinaryContract[] }) {
|
||||
const { contracts } = props
|
||||
function DailyMoversSection(props: {
|
||||
contracts: CPMMBinaryContract[]
|
||||
metrics: ContractMetrics[]
|
||||
}) {
|
||||
const { contracts, metrics } = props
|
||||
|
||||
const changes = contracts.filter((c) => Math.abs(c.probChanges.day) >= 0.01)
|
||||
|
||||
if (changes.length === 0) {
|
||||
if (contracts.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Col className="gap-2">
|
||||
<SectionHeader label="Daily movers" href="/daily-movers" />
|
||||
<ProbChangeTable changes={changes} />
|
||||
<ProfitChangeTable contracts={contracts} metrics={metrics} />
|
||||
</Col>
|
||||
)
|
||||
}
|
||||
|
||||
function DailyStats(props: {
|
||||
user: User | null | undefined
|
||||
className?: string
|
||||
}) {
|
||||
const { user, className } = props
|
||||
|
||||
const metrics = usePortfolioHistory(user?.id ?? '', 'daily') ?? []
|
||||
const [first, last] = [metrics[0], metrics[metrics.length - 1]]
|
||||
function DailyStats(props: { user: User | null | undefined }) {
|
||||
const { user } = props
|
||||
|
||||
const privateUser = usePrivateUser()
|
||||
const streaks = privateUser?.notificationPreferences?.betting_streaks ?? []
|
||||
const streaksHidden = streaks.length === 0
|
||||
|
||||
let profit = 0
|
||||
let profitPercent = 0
|
||||
if (first && last) {
|
||||
profit = calculatePortfolioProfit(last) - calculatePortfolioProfit(first)
|
||||
profitPercent = profit / first.investmentValue
|
||||
}
|
||||
|
||||
return (
|
||||
<Row className={'flex-shrink-0 gap-4'}>
|
||||
<Col>
|
||||
<div className="text-gray-500">Daily profit</div>
|
||||
<Row className={clsx(className, 'items-center text-lg')}>
|
||||
<span>{formatMoney(profit)}</span>{' '}
|
||||
<ProfitBadge profitPercent={profitPercent * 100} />
|
||||
</Row>
|
||||
</Col>
|
||||
<DailyProfit user={user} />
|
||||
{!streaksHidden && (
|
||||
<Col>
|
||||
<div className="text-gray-500">Streak</div>
|
||||
<Row
|
||||
className={clsx(
|
||||
className,
|
||||
'items-center text-lg',
|
||||
user && !hasCompletedStreakToday(user) && 'grayscale'
|
||||
)}
|
||||
|
@ -411,6 +405,39 @@ function DailyStats(props: {
|
|||
)
|
||||
}
|
||||
|
||||
export function DailyProfit(props: { user: User | null | undefined }) {
|
||||
const { user } = props
|
||||
|
||||
const contractMetricsByProfit = useUserContractMetricsByProfit(
|
||||
user?.id ?? '_',
|
||||
100
|
||||
)
|
||||
const profit = sum(
|
||||
contractMetricsByProfit?.metrics.map((m) =>
|
||||
m.from ? m.from.day.profit : 0
|
||||
) ?? []
|
||||
)
|
||||
|
||||
const metrics = usePortfolioHistory(user?.id ?? '', 'daily') ?? []
|
||||
const [first, last] = [metrics[0], metrics[metrics.length - 1]]
|
||||
|
||||
let profitPercent = 0
|
||||
if (first && last) {
|
||||
// profit = calculatePortfolioProfit(last) - calculatePortfolioProfit(first)
|
||||
profitPercent = profit / first.investmentValue
|
||||
}
|
||||
|
||||
return (
|
||||
<SiteLink className="flex flex-col" href="/daily-movers">
|
||||
<div className="text-gray-500">Daily profit</div>
|
||||
<Row className="items-center text-lg">
|
||||
<span>{formatMoney(profit)}</span>{' '}
|
||||
<ProfitBadge profitPercent={profitPercent * 100} />
|
||||
</Row>
|
||||
</SiteLink>
|
||||
)
|
||||
}
|
||||
|
||||
export function TrendingGroupsSection(props: {
|
||||
user: User
|
||||
myGroups: Group[]
|
||||
|
|
Loading…
Reference in New Issue
Block a user