Refactor tricky bet calculations to one function
This commit is contained in:
parent
1b9a38ff66
commit
5dcd43f5b2
|
@ -1,3 +1,4 @@
|
||||||
|
import _ from 'lodash'
|
||||||
import { Bet } from './bet'
|
import { Bet } from './bet'
|
||||||
import {
|
import {
|
||||||
calculateCpmmSale,
|
calculateCpmmSale,
|
||||||
|
@ -110,3 +111,62 @@ export function resolvedPayout(contract: Contract, bet: Bet) {
|
||||||
? calculateFixedPayout(contract, bet, outcome)
|
? calculateFixedPayout(contract, bet, outcome)
|
||||||
: calculateDpmPayout(contract, bet, outcome)
|
: calculateDpmPayout(contract, bet, outcome)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getContractBetMetrics(contract: Contract, yourBets: Bet[]) {
|
||||||
|
const { resolution } = contract
|
||||||
|
|
||||||
|
let invested = 0
|
||||||
|
let salesInvested = 0
|
||||||
|
let payout = 0
|
||||||
|
let loan = 0
|
||||||
|
let sellProfit = 0
|
||||||
|
let redeemed = 0
|
||||||
|
|
||||||
|
for (const bet of yourBets) {
|
||||||
|
const { isSold, sale, amount, loanAmount, isRedemption } = bet
|
||||||
|
if (isSold) {
|
||||||
|
sellProfit -= amount
|
||||||
|
salesInvested += amount
|
||||||
|
} else if (sale) {
|
||||||
|
sellProfit += sale.amount
|
||||||
|
} else {
|
||||||
|
if (isRedemption) {
|
||||||
|
redeemed += -1 * amount
|
||||||
|
} else if (amount > 0) {
|
||||||
|
invested += amount
|
||||||
|
}
|
||||||
|
|
||||||
|
loan += loanAmount ?? 0
|
||||||
|
payout += resolution
|
||||||
|
? calculatePayout(contract, bet, resolution)
|
||||||
|
: calculatePayout(contract, bet, 'MKT')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const investedIncludingSales = invested + salesInvested
|
||||||
|
|
||||||
|
const totalValue = payout + sellProfit + redeemed
|
||||||
|
const profit = totalValue - invested
|
||||||
|
const profitPercent = (profit / investedIncludingSales) * 100
|
||||||
|
const netInvestment = payout - loan
|
||||||
|
|
||||||
|
return {
|
||||||
|
invested,
|
||||||
|
payout,
|
||||||
|
totalValue,
|
||||||
|
profit,
|
||||||
|
profitPercent,
|
||||||
|
netInvestment,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getContractBetNullMetrics() {
|
||||||
|
return {
|
||||||
|
invested: 0,
|
||||||
|
payout: 0,
|
||||||
|
totalValue: 0,
|
||||||
|
profit: 0,
|
||||||
|
profitPercent: 0,
|
||||||
|
netInvestment: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -34,7 +34,9 @@ import {
|
||||||
getOutcomeProbability,
|
getOutcomeProbability,
|
||||||
getProbability,
|
getProbability,
|
||||||
getProbabilityAfterSale,
|
getProbabilityAfterSale,
|
||||||
|
getContractBetMetrics,
|
||||||
resolvedPayout,
|
resolvedPayout,
|
||||||
|
getContractBetNullMetrics,
|
||||||
} from '../../common/calculate'
|
} from '../../common/calculate'
|
||||||
|
|
||||||
type BetSort = 'newest' | 'profit' | 'resolutionTime' | 'value' | 'closeTime'
|
type BetSort = 'newest' | 'profit' | 'resolutionTime' | 'value' | 'closeTime'
|
||||||
|
@ -76,34 +78,10 @@ export function BetsList(props: { user: User }) {
|
||||||
const contractBets = _.groupBy(bets, 'contractId')
|
const contractBets = _.groupBy(bets, 'contractId')
|
||||||
const contractsById = _.fromPairs(contracts.map((c) => [c.id, c]))
|
const contractsById = _.fromPairs(contracts.map((c) => [c.id, c]))
|
||||||
|
|
||||||
const contractsCurrentValue = _.mapValues(
|
const contractsMetrics = _.mapValues(contractBets, (bets, contractId) => {
|
||||||
contractBets,
|
|
||||||
(bets, contractId) => {
|
|
||||||
return _.sumBy(bets, (bet) => {
|
|
||||||
const contract = contractsById[contractId]
|
const contract = contractsById[contractId]
|
||||||
if (!contract) return 0
|
if (!contract) return getContractBetNullMetrics()
|
||||||
if (bet.isSold || bet.sale) return 0
|
return getContractBetMetrics(contract, bets)
|
||||||
|
|
||||||
return contract.resolution
|
|
||||||
? calculatePayout(contract, bet, contract.resolution)
|
|
||||||
: calculatePayout(contract, bet, 'MKT')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
const contractsInvestment = _.mapValues(contractBets, (bets) => {
|
|
||||||
return _.sumBy(bets, (bet) => {
|
|
||||||
if (bet.isSold || bet.sale || bet.isRedemption) return 0
|
|
||||||
return bet.amount
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const contractsSaleOrRedemption = _.mapValues(contractBets, (bets) => {
|
|
||||||
return _.sumBy(bets, (bet) => {
|
|
||||||
if (bet.isSold) return -1 * bet.amount
|
|
||||||
if (bet.sale) return bet.sale.amount
|
|
||||||
if (bet.isRedemption) return -1 * bet.amount
|
|
||||||
return 0
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const FILTERS: Record<BetFilter, (c: Contract) => boolean> = {
|
const FILTERS: Record<BetFilter, (c: Contract) => boolean> = {
|
||||||
|
@ -115,11 +93,8 @@ export function BetsList(props: { user: User }) {
|
||||||
// Pepe notes: most users want "settled", to see when their bets or sold; or "realized profit"
|
// Pepe notes: most users want "settled", to see when their bets or sold; or "realized profit"
|
||||||
}
|
}
|
||||||
const SORTS: Record<BetSort, (c: Contract) => number> = {
|
const SORTS: Record<BetSort, (c: Contract) => number> = {
|
||||||
profit: (c) =>
|
profit: (c) => contractsMetrics[c.id].profit,
|
||||||
contractsCurrentValue[c.id] -
|
value: (c) => contractsMetrics[c.id].totalValue,
|
||||||
contractsInvestment[c.id] +
|
|
||||||
contractsSaleOrRedemption[c.id],
|
|
||||||
value: (c) => contractsCurrentValue[c.id] + contractsSaleOrRedemption[c.id],
|
|
||||||
newest: (c) =>
|
newest: (c) =>
|
||||||
Math.max(...contractBets[c.id].map((bet) => bet.createdTime)),
|
Math.max(...contractBets[c.id].map((bet) => bet.createdTime)),
|
||||||
resolutionTime: (c) => -(c.resolutionTime ?? c.closeTime ?? Infinity),
|
resolutionTime: (c) => -(c.resolutionTime ?? c.closeTime ?? Infinity),
|
||||||
|
@ -131,32 +106,28 @@ export function BetsList(props: { user: User }) {
|
||||||
|
|
||||||
const [settled, unsettled] = _.partition(
|
const [settled, unsettled] = _.partition(
|
||||||
contracts,
|
contracts,
|
||||||
(c) => c.isResolved || contractsInvestment[c.id] === 0
|
(c) => c.isResolved || contractsMetrics[c.id].invested === 0
|
||||||
)
|
)
|
||||||
|
|
||||||
const currentInvestment = _.sumBy(unsettled, (c) => contractsInvestment[c.id])
|
const currentInvested = _.sumBy(
|
||||||
|
unsettled,
|
||||||
|
(c) => contractsMetrics[c.id].invested
|
||||||
|
)
|
||||||
const currentBetsValue = _.sumBy(
|
const currentBetsValue = _.sumBy(
|
||||||
unsettled,
|
unsettled,
|
||||||
(c) => contractsCurrentValue[c.id]
|
(c) => contractsMetrics[c.id].payout
|
||||||
)
|
)
|
||||||
const currentBetsValueMinusLoans = _.sumBy(
|
const currentNetInvestment = _.sumBy(
|
||||||
unsettled,
|
unsettled,
|
||||||
(c) =>
|
(c) => contractsMetrics[c.id].netInvestment
|
||||||
contractsCurrentValue[c.id] -
|
|
||||||
// Subtract loans you haven't paid.
|
|
||||||
_.sumBy(contractBets[c.id], (bet) => {
|
|
||||||
if (bet.isSold || bet.sale) return 0
|
|
||||||
return bet.loanAmount ?? 0
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const totalPortfolio = currentBetsValueMinusLoans + user.balance
|
const totalPortfolio = currentNetInvestment + user.balance
|
||||||
|
|
||||||
const totalPnl = totalPortfolio - user.totalDeposits
|
const totalPnl = totalPortfolio - user.totalDeposits
|
||||||
const totalProfitPercent = (totalPnl / user.totalDeposits) * 100
|
const totalProfitPercent = (totalPnl / user.totalDeposits) * 100
|
||||||
const investedProfitPercent =
|
const investedProfitPercent =
|
||||||
((currentBetsValue - currentInvestment) / currentInvestment) * 100
|
((currentBetsValue - currentInvested) / currentInvested) * 100
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Col className="mt-6 gap-4 sm:gap-6">
|
<Col className="mt-6 gap-4 sm:gap-6">
|
||||||
|
@ -165,7 +136,7 @@ export function BetsList(props: { user: User }) {
|
||||||
<Col>
|
<Col>
|
||||||
<div className="text-sm text-gray-500">Investment value</div>
|
<div className="text-sm text-gray-500">Investment value</div>
|
||||||
<div className="text-lg">
|
<div className="text-lg">
|
||||||
{formatMoney(currentBetsValueMinusLoans)}{' '}
|
{formatMoney(currentNetInvestment)}{' '}
|
||||||
<ProfitBadge profitPercent={investedProfitPercent} />
|
<ProfitBadge profitPercent={investedProfitPercent} />
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
|
@ -244,6 +215,11 @@ function MyContractBets(props: {
|
||||||
const isBinary = outcomeType === 'BINARY'
|
const isBinary = outcomeType === 'BINARY'
|
||||||
const probPercent = getBinaryProbPercent(contract)
|
const probPercent = getBinaryProbPercent(contract)
|
||||||
|
|
||||||
|
const { totalValue, profit, profitPercent } = getContractBetMetrics(
|
||||||
|
contract,
|
||||||
|
bets
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
|
@ -292,12 +268,16 @@ function MyContractBets(props: {
|
||||||
</Row>
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<MyBetsSummary
|
<Row className="mr-5 justify-end sm:mr-8">
|
||||||
className="mr-5 justify-end sm:mr-8"
|
<Col>
|
||||||
contract={contract}
|
<div className="whitespace-nowrap text-right text-lg">
|
||||||
bets={bets}
|
{formatMoney(metric === 'profit' ? profit : totalValue)}
|
||||||
onlyMetric={metric}
|
</div>
|
||||||
/>
|
<div className="text-right">
|
||||||
|
<ProfitBadge profitPercent={profitPercent} />
|
||||||
|
</div>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -323,69 +303,28 @@ function MyContractBets(props: {
|
||||||
export function MyBetsSummary(props: {
|
export function MyBetsSummary(props: {
|
||||||
contract: Contract
|
contract: Contract
|
||||||
bets: Bet[]
|
bets: Bet[]
|
||||||
onlyMetric?: 'value' | 'profit'
|
|
||||||
className?: string
|
className?: string
|
||||||
}) {
|
}) {
|
||||||
const { bets, contract, onlyMetric, className } = props
|
const { bets, contract, className } = props
|
||||||
const { resolution, outcomeType } = contract
|
const { resolution, outcomeType } = contract
|
||||||
const isBinary = outcomeType === 'BINARY'
|
const isBinary = outcomeType === 'BINARY'
|
||||||
|
|
||||||
const excludeSales = bets.filter((b) => !b.isSold && !b.sale)
|
const excludeSales = bets.filter((b) => !b.isSold && !b.sale)
|
||||||
const excludeRedemptions = bets.filter((b) => !b.isRedemption)
|
|
||||||
const excludeSalesAndRedemptions = excludeSales.filter((b) => !b.isRedemption)
|
|
||||||
const betsTotal = _.sumBy(excludeSalesAndRedemptions, (bet) => bet.amount)
|
|
||||||
const invested = _.sumBy(excludeSalesAndRedemptions, (bet) =>
|
|
||||||
bet.amount > 0 ? bet.amount : 0
|
|
||||||
)
|
|
||||||
const investedIncludingSales = _.sumBy(excludeRedemptions, (bet) =>
|
|
||||||
bet.amount > 0 ? bet.amount : 0
|
|
||||||
)
|
|
||||||
const redemptionValue = _.sumBy(bets, (bet) =>
|
|
||||||
bet.isRedemption ? -bet.amount : 0
|
|
||||||
)
|
|
||||||
|
|
||||||
const betsPayout = resolution
|
|
||||||
? _.sumBy(excludeSales, (bet) => resolvedPayout(contract, bet))
|
|
||||||
: 0
|
|
||||||
|
|
||||||
const yesWinnings = _.sumBy(excludeSales, (bet) =>
|
const yesWinnings = _.sumBy(excludeSales, (bet) =>
|
||||||
calculatePayout(contract, bet, 'YES')
|
calculatePayout(contract, bet, 'YES')
|
||||||
)
|
)
|
||||||
const noWinnings = _.sumBy(excludeSales, (bet) =>
|
const noWinnings = _.sumBy(excludeSales, (bet) =>
|
||||||
calculatePayout(contract, bet, 'NO')
|
calculatePayout(contract, bet, 'NO')
|
||||||
)
|
)
|
||||||
const marketWinnings = _.sumBy(excludeSales, (bet) =>
|
const { invested, profitPercent, payout } = getContractBetMetrics(
|
||||||
calculatePayout(contract, bet, 'MKT')
|
contract,
|
||||||
)
|
bets
|
||||||
const salesWinnings = _.sumBy(bets, (bet) =>
|
|
||||||
bet.isSold ? -bet.amount : bet.sale ? bet.sale.amount : 0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const currentValue = resolution ? betsPayout : marketWinnings
|
console.log(getContractBetMetrics(contract, bets))
|
||||||
const totalValue = currentValue + redemptionValue + salesWinnings
|
|
||||||
const pnl = totalValue - betsTotal
|
|
||||||
const profit = (pnl / investedIncludingSales) * 100
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row
|
<Row className={clsx('flex-wrap gap-4 sm:flex-nowrap sm:gap-6', className)}>
|
||||||
className={clsx(
|
|
||||||
'gap-4 sm:gap-6',
|
|
||||||
!onlyMetric && 'flex-wrap sm:flex-nowrap',
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{onlyMetric ? (
|
|
||||||
<Row className="gap-4 sm:gap-6">
|
|
||||||
<Col>
|
|
||||||
<div className="whitespace-nowrap text-right text-lg">
|
|
||||||
{formatMoney(onlyMetric === 'profit' ? pnl : totalValue)}
|
|
||||||
</div>
|
|
||||||
<div className="text-right">
|
|
||||||
<ProfitBadge profitPercent={profit} />
|
|
||||||
</div>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
) : (
|
|
||||||
<Row className="gap-4 sm:gap-6">
|
<Row className="gap-4 sm:gap-6">
|
||||||
<Col>
|
<Col>
|
||||||
<div className="whitespace-nowrap text-sm text-gray-500">
|
<div className="whitespace-nowrap text-sm text-gray-500">
|
||||||
|
@ -397,19 +336,12 @@ export function MyBetsSummary(props: {
|
||||||
<Col>
|
<Col>
|
||||||
<div className="text-sm text-gray-500">Payout</div>
|
<div className="text-sm text-gray-500">Payout</div>
|
||||||
<div className="whitespace-nowrap">
|
<div className="whitespace-nowrap">
|
||||||
{formatMoney(betsPayout)} <ProfitBadge profitPercent={profit} />
|
{formatMoney(payout)}{' '}
|
||||||
|
<ProfitBadge profitPercent={profitPercent} />
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{/* <Col>
|
|
||||||
<div className="whitespace-nowrap text-sm text-gray-500">
|
|
||||||
Expectation
|
|
||||||
</div>
|
|
||||||
<div className="whitespace-nowrap">
|
|
||||||
{formatMoney(expectation)}
|
|
||||||
</div>
|
|
||||||
</Col> */}
|
|
||||||
{isBinary && (
|
{isBinary && (
|
||||||
<>
|
<>
|
||||||
<Col>
|
<Col>
|
||||||
|
@ -443,14 +375,11 @@ export function MyBetsSummary(props: {
|
||||||
<>Current payout</>
|
<>Current payout</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="whitespace-nowrap">
|
<div className="whitespace-nowrap">{formatMoney(payout)}</div>
|
||||||
{formatMoney(marketWinnings)}
|
|
||||||
</div>
|
|
||||||
</Col>
|
</Col>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
|
||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user