This commit is contained in:
mantikoros 2022-09-26 12:51:48 -04:00
parent 1e8f03d614
commit 71253f3670
5 changed files with 154 additions and 133 deletions

View File

@ -0,0 +1,120 @@
import { sumBy } from 'lodash'
import clsx from 'clsx'
import { Bet } from 'web/lib/firebase/bets'
import {
formatMoney,
formatWithCommas,
} from 'common/util/format'
import { Col } from './layout/col'
import {
Contract,
} from 'web/lib/firebase/contracts'
import { Row } from './layout/row'
import { YesLabel, NoLabel } from './outcome-label'
import {
calculatePayout,
getContractBetMetrics,
getProbability,
} from 'common/calculate'
import { floatingEqual } from 'common/util/math'
import { InfoTooltip } from './info-tooltip'
import { ProfitBadge } from './profit-badge'
export function BetsSummary(props: {
contract: Contract
bets: Bet[]
isYourBets: boolean
className?: string
}) {
const { contract, className } = props
const { resolution, outcomeType } = contract
const isBinary = outcomeType === 'BINARY'
const bets = props.bets.filter((b) => !b.isAnte)
const { profitPercent, payout, profit, totalShares } = getContractBetMetrics(
contract,
bets
)
const excludeSales = bets.filter((b) => !b.isSold && !b.sale)
const yesWinnings = sumBy(excludeSales, (bet) =>
calculatePayout(contract, bet, 'YES')
)
const noWinnings = sumBy(excludeSales, (bet) =>
calculatePayout(contract, bet, 'NO')
)
const prob = isBinary ? getProbability(contract) : 0
const expectation = prob * yesWinnings + (1 - prob) * noWinnings
if (
isBinary &&
floatingEqual(totalShares.YES ?? 0, 0) &&
floatingEqual(totalShares.NO ?? 0, 0)
)
return <></>
return (
<Col className={clsx(className, 'gap-4')}>
<Row className="flex-wrap gap-4 sm:flex-nowrap sm:gap-6">
{resolution ? (
<Col>
<div className="text-sm text-gray-500">Payout</div>
<div className="whitespace-nowrap">
{formatMoney(payout)}{' '}
<ProfitBadge profitPercent={profitPercent} />
</div>
</Col>
) : isBinary ? (
<Col>
<div className="whitespace-nowrap text-sm text-gray-500">
Position{' '}
<InfoTooltip text="Number of shares you own on net. 1 YES share = M$1 if the market resolves YES." />
</div>
<div className="whitespace-nowrap">
{yesWinnings > 0 ? (
<>
<YesLabel /> {formatWithCommas(yesWinnings)}
</>
) : (
<>
<NoLabel /> {formatWithCommas(noWinnings)}
</>
)}
</div>
</Col>
) : (
<Col>
<div className="whitespace-nowrap text-sm text-gray-500">
Expected value {''}
<InfoTooltip text="The estimated payout of your position using the current market probability." />
</div>
<div className="whitespace-nowrap">{formatMoney(payout)}</div>
</Col>
)}
{isBinary && (
<Col>
<div className="whitespace-nowrap text-sm text-gray-500">
Expected value{' '}
<InfoTooltip text="The estimated payout of your position using the current market probability." />
</div>
<div className="whitespace-nowrap">{formatMoney(expectation)}</div>
</Col>
)}
<Col>
<div className="whitespace-nowrap text-sm text-gray-500">
Profit{' '}
<InfoTooltip text="Includes both realized & unrealized gains/losses from trades." />
</div>
<div className="whitespace-nowrap">
{formatMoney(profit)} <ProfitBadge profitPercent={profitPercent} />
</div>
</Col>
</Row>
<Row className="flex-wrap-none gap-4"></Row>
</Col>
)
}

View File

@ -22,7 +22,7 @@ import {
import { Row } from './layout/row'
import { sellBet } from 'web/lib/firebase/api'
import { ConfirmationButton } from './confirmation-button'
import { OutcomeLabel, YesLabel, NoLabel } from './outcome-label'
import { OutcomeLabel } from './outcome-label'
import { LoadingIndicator } from './loading-indicator'
import { SiteLink } from './site-link'
import {
@ -33,7 +33,6 @@ import {
getContractBetMetrics,
resolvedPayout,
getContractBetNullMetrics,
getProbability,
} from 'common/calculate'
import { NumericContract } from 'common/contract'
import { formatNumericProbability } from 'common/pseudo-numeric'
@ -41,12 +40,12 @@ import { useUser } from 'web/hooks/use-user'
import { useUserBets } from 'web/hooks/use-user-bets'
import { useUnfilledBets } from 'web/hooks/use-bets'
import { LimitBet } from 'common/bet'
import { floatingEqual } from 'common/util/math'
import { Pagination } from './pagination'
import { LimitOrderTable } from './limit-bets'
import { UserLink } from 'web/components/user-link'
import { useUserBetContracts } from 'web/hooks/use-contracts'
import { InfoTooltip } from './info-tooltip'
import { BetsSummary } from './bet-summary'
import { ProfitBadge } from './profit-badge'
type BetSort = 'newest' | 'profit' | 'closeTime' | 'value'
type BetFilter = 'open' | 'limit_bet' | 'sold' | 'closed' | 'resolved' | 'all'
@ -374,104 +373,6 @@ function ContractBets(props: {
)
}
export function BetsSummary(props: {
contract: Contract
bets: Bet[]
isYourBets: boolean
className?: string
}) {
const { contract, className } = props
const { resolution, outcomeType } = contract
const isBinary = outcomeType === 'BINARY'
const bets = props.bets.filter((b) => !b.isAnte)
const { profitPercent, payout, profit, totalShares } = getContractBetMetrics(
contract,
bets
)
const excludeSales = bets.filter((b) => !b.isSold && !b.sale)
const yesWinnings = sumBy(excludeSales, (bet) =>
calculatePayout(contract, bet, 'YES')
)
const noWinnings = sumBy(excludeSales, (bet) =>
calculatePayout(contract, bet, 'NO')
)
const prob = isBinary ? getProbability(contract) : 0
const expectation = prob * yesWinnings + (1 - prob) * noWinnings
if (
isBinary &&
floatingEqual(totalShares.YES ?? 0, 0) &&
floatingEqual(totalShares.NO ?? 0, 0)
)
return <></>
return (
<Col className={clsx(className, 'gap-4')}>
<Row className="flex-wrap gap-4 sm:flex-nowrap sm:gap-6">
{resolution ? (
<Col>
<div className="text-sm text-gray-500">Payout</div>
<div className="whitespace-nowrap">
{formatMoney(payout)}{' '}
<ProfitBadge profitPercent={profitPercent} />
</div>
</Col>
) : isBinary ? (
<Col>
<div className="whitespace-nowrap text-sm text-gray-500">
Position{' '}
<InfoTooltip text="Number of shares you own on net. 1 YES share = M$1 if the market resolves YES." />
</div>
<div className="whitespace-nowrap">
{yesWinnings > 0 ? (
<>
<YesLabel /> {formatWithCommas(yesWinnings)}
</>
) : (
<>
<NoLabel /> {formatWithCommas(noWinnings)}
</>
)}
</div>
</Col>
) : (
<Col>
<div className="whitespace-nowrap text-sm text-gray-500">
Expected value {''}
<InfoTooltip text="The estimated payout of your position using the current market probability." />
</div>
<div className="whitespace-nowrap">{formatMoney(payout)}</div>
</Col>
)}
{isBinary && (
<Col>
<div className="whitespace-nowrap text-sm text-gray-500">
Expected value{' '}
<InfoTooltip text="The estimated payout of your position using the current market probability." />
</div>
<div className="whitespace-nowrap">{formatMoney(expectation)}</div>
</Col>
)}
<Col>
<div className="whitespace-nowrap text-sm text-gray-500">
Profit{' '}
<InfoTooltip text="Includes both realized & unrealized gains/losses from trades." />
</div>
<div className="whitespace-nowrap">
{formatMoney(profit)} <ProfitBadge profitPercent={profitPercent} />
</div>
</Col>
</Row>
<Row className="flex-wrap-none gap-4"></Row>
</Col>
)
}
export function ContractBetsTable(props: {
contract: Contract
bets: Bet[]
@ -733,30 +634,3 @@ function SellButton(props: {
</ConfirmationButton>
)
}
export function ProfitBadge(props: {
profitPercent: number
round?: boolean
className?: string
}) {
const { profitPercent, round, className } = props
if (!profitPercent) return null
const colors =
profitPercent > 0
? 'bg-green-100 text-green-800'
: 'bg-red-100 text-red-800'
return (
<span
className={clsx(
'ml-1 inline-flex items-center rounded-full px-3 py-0.5 text-sm font-medium',
colors,
className
)}
>
{(profitPercent > 0 ? '+' : '') +
profitPercent.toFixed(round ? 0 : 1) +
'%'}
</span>
)
}

View File

@ -0,0 +1,28 @@
import clsx from 'clsx'
export function ProfitBadge(props: {
profitPercent: number
round?: boolean
className?: string
}) {
const { profitPercent, round, className } = props
if (!profitPercent) return null
const colors =
profitPercent > 0
? 'bg-green-100 text-green-800'
: 'bg-red-100 text-red-800'
return (
<span
className={clsx(
'ml-1 inline-flex items-center rounded-full px-3 py-0.5 text-sm font-medium',
colors,
className
)}
>
{(profitPercent > 0 ? '+' : '') +
profitPercent.toFixed(round ? 0 : 1) +
'%'}
</span>
)
}

View File

@ -1,5 +1,6 @@
import React, { memo, useEffect, useMemo, useState } from 'react'
import { ArrowLeftIcon } from '@heroicons/react/outline'
import dayjs from 'dayjs'
import { useContractWithPreload } from 'web/hooks/use-contract'
import { ContractOverview } from 'web/components/contract/contract-overview'
@ -44,9 +45,7 @@ import { useAdmin } from 'web/hooks/use-admin'
import { BetSignUpPrompt } from 'web/components/sign-up-prompt'
import { PlayMoneyDisclaimer } from 'web/components/play-money-disclaimer'
import BetButton from 'web/components/bet-button'
import dayjs from 'dayjs'
import { BetsSummary } from 'web/components/bets-list'
import { BetsSummary } from 'web/components/bet-summary'
export const getStaticProps = fromPropz(getStaticPropz)
export async function getStaticPropz(props: {

View File

@ -33,7 +33,6 @@ 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 { ProfitBadge } from 'web/components/bets-list'
import { calculatePortfolioProfit } from 'common/calculate-metrics'
import { hasCompletedStreakToday } from 'web/components/profile/betting-streak-modal'
import { ContractsGrid } from 'web/components/contract/contracts-grid'
@ -45,6 +44,7 @@ import { usePrefetch } from 'web/hooks/use-prefetch'
import { Title } from 'web/components/title'
import { CPMMBinaryContract } from 'common/contract'
import { useContractsByDailyScoreGroups } from 'web/hooks/use-contracts'
import { ProfitBadge } from 'web/components/profit-badge'
export default function Home() {
const user = useUser()