diff --git a/web/components/bet-panel.tsx b/web/components/bet-panel.tsx index 4f934290..d17f02da 100644 --- a/web/components/bet-panel.tsx +++ b/web/components/bet-panel.tsx @@ -14,7 +14,7 @@ import { getProbability, calculateShares, getProbabilityAfterBet, -} from '../lib/calculation/contract' +} from '../lib/calculate' import { firebaseLogin } from '../lib/firebase/users' export function BetPanel(props: { contract: Contract; className?: string }) { diff --git a/web/components/bets-list.tsx b/web/components/bets-list.tsx index 3a977363..dab70bb0 100644 --- a/web/components/bets-list.tsx +++ b/web/components/bets-list.tsx @@ -13,11 +13,13 @@ import { Row } from './layout/row' import { UserLink } from './user-page' import { calculatePayout, + calculateSaleAmount, currentValue, resolvedPayout, -} from '../lib/calculation/contract' +} from '../lib/calculate' import clsx from 'clsx' import { cloudFunction } from '../lib/firebase/api-call' +import { ConfirmationButton } from './confirmation-button' export function BetsList(props: { user: User }) { const { user } = props @@ -229,6 +231,11 @@ export function ContractBetsTable(props: { }) { const { contract, bets, className } = props + const [sales, buys] = _.partition(bets, (bet) => bet.sale) + const salesDict = _.fromPairs( + sales.map((sale) => [sale.sale?.betId ?? '', sale]) + ) + const { isResolved } = contract return ( @@ -238,16 +245,21 @@ export function ContractBetsTable(props: { Date Outcome - Bet + Amount Probability {!isResolved && Est. max payout} {isResolved ? <>Payout : <>Current value} - {!isResolved && } + - {bets.map((bet) => ( - + {buys.map((bet) => ( + ))} @@ -255,8 +267,8 @@ export function ContractBetsTable(props: { ) } -function BetRow(props: { bet: Bet; contract: Contract }) { - const { bet, contract } = props +function BetRow(props: { bet: Bet; contract: Contract; sale?: Bet }) { + const { bet, sale, contract } = props const { amount, outcome, @@ -264,14 +276,13 @@ function BetRow(props: { bet: Bet; contract: Contract }) { probBefore, probAfter, shares, - sale, isSold, } = bet const { isResolved } = contract return ( - {dayjs(createdTime).format('MMM D, H:mma')} + {dayjs(createdTime).format('MMM D, h:mma')} @@ -281,25 +292,39 @@ function BetRow(props: { bet: Bet; contract: Contract }) { {!isResolved && {formatMoney(shares)}} - {formatMoney( - isResolved - ? resolvedPayout(contract, bet) - : currentValue(contract, bet) - )} + {bet.isSold + ? 'N/A' + : formatMoney( + isResolved + ? resolvedPayout(contract, bet) + : bet.sale + ? bet.sale.amount ?? 0 + : currentValue(contract, bet) + )} - {!isResolved && !sale && !isSold && ( - - - + {sale ? ( + SOLD for {formatMoney(Math.abs(sale.amount))} + ) : ( + !isResolved && + !isSold && ( + + { + await sellBet({ contractId: contract.id, betId: bet.id }) + }} + > +
Sell
+
+ Do you want to sell your {formatMoney(bet.amount)} bet for{' '} + {formatMoney(calculateSaleAmount(contract, bet))}? +
+
+ + ) )} ) diff --git a/web/lib/calculation/contract.ts b/web/lib/calculate.ts similarity index 53% rename from web/lib/calculation/contract.ts rename to web/lib/calculate.ts index 869c1c8f..cb4b6163 100644 --- a/web/lib/calculation/contract.ts +++ b/web/lib/calculate.ts @@ -1,5 +1,5 @@ -import { Bet } from '../firebase/bets' -import { Contract } from '../firebase/contracts' +import { Bet } from './firebase/bets' +import { Contract } from './firebase/contracts' const fees = 0.02 @@ -44,16 +44,24 @@ export function calculatePayout( if (outcome === 'CANCEL') return amount if (betOutcome !== outcome) return 0 - let { totalShares } = contract + const { totalShares } = contract - // Fake data if not set. - // if (!totalShares) totalShares = { YES: 100, NO: 100 } + if (totalShares[outcome] === 0) return 0 const startPool = contract.startPool.YES + contract.startPool.NO const pool = contract.pool.YES + contract.pool.NO - startPool + console.log( + 'calcPayout', + 'shares', + shares, + 'totalShares', + totalShares, + 'pool' + ) return (1 - fees) * (shares / totalShares[outcome]) * pool } + export function resolvedPayout(contract: Contract, bet: Bet) { if (contract.resolution) return calculatePayout(contract, bet, contract.resolution) @@ -65,5 +73,51 @@ export function currentValue(contract: Contract, bet: Bet) { const yesPayout = calculatePayout(contract, bet, 'YES') const noPayout = calculatePayout(contract, bet, 'NO') + console.log('calculate value', { + prob, + yesPayout, + noPayout, + amount: bet.amount, + }) + return prob * yesPayout + (1 - prob) * noPayout } +export function calculateSaleAmount(contract: Contract, bet: Bet) { + const { shares, outcome } = bet + + const { YES: yesPool, NO: noPool } = contract.pool + const { YES: yesStart, NO: noStart } = contract.startPool + const { YES: yesShares, NO: noShares } = contract.totalShares + + const [y, n, s] = [yesPool, noPool, shares] + + const shareValue = + outcome === 'YES' + ? // https://www.wolframalpha.com/input/?i=b+%2B+%28b+n%5E2%29%2F%28y+%28-b+%2B+y%29%29+%3D+c+solve+b + (n ** 2 + + s * y + + y ** 2 - + Math.sqrt( + n ** 4 + (s - y) ** 2 * y ** 2 + 2 * n ** 2 * y * (s + y) + )) / + (2 * y) + : (y ** 2 + + s * n + + n ** 2 - + Math.sqrt( + y ** 4 + (s - n) ** 2 * n ** 2 + 2 * y ** 2 * n * (s + n) + )) / + (2 * n) + + const startPool = yesStart + noStart + const pool = yesPool + noPool - startPool + + const f = outcome === 'YES' ? pool / yesShares : pool / noShares + + const myPool = outcome === 'YES' ? yesPool - yesStart : noPool - noStart + + const adjShareValue = Math.min(Math.min(1, f) * shareValue, myPool) + + const saleAmount = (1 - fees) * adjShareValue + return saleAmount +}