diff --git a/common/calculate-multi.ts b/common/calculate-multi.ts deleted file mode 100644 index 5ff23cdb..00000000 --- a/common/calculate-multi.ts +++ /dev/null @@ -1,27 +0,0 @@ -import * as _ from 'lodash' - -export function getMultiProbability( - totalShares: { - [answerId: string]: number - }, - answerId: string -) { - const squareSum = _.sumBy(Object.values(totalShares), (shares) => shares ** 2) - const shares = totalShares[answerId] ?? 0 - return shares ** 2 / squareSum -} - -export function calculateMultiShares( - totalShares: { - [answerId: string]: number - }, - bet: number, - betChoice: string -) { - const squareSum = _.sumBy(Object.values(totalShares), (shares) => shares ** 2) - const shares = totalShares[betChoice] ?? 0 - - const c = 2 * bet * Math.sqrt(squareSum) - - return Math.sqrt(bet ** 2 + shares ** 2 + c) - shares -} diff --git a/common/calculate.ts b/common/calculate.ts index fbbfd66f..eeec5251 100644 --- a/common/calculate.ts +++ b/common/calculate.ts @@ -1,39 +1,51 @@ +import _ from 'lodash' import { Bet } from './bet' import { Contract } from './contract' import { FEES } from './fees' export function getProbability(totalShares: { YES: number; NO: number }) { - const { YES: y, NO: n } = totalShares - return y ** 2 / (y ** 2 + n ** 2) + return getOutcomeProbability(totalShares, 'YES') +} + +export function getOutcomeProbability( + totalShares: { + [outcome: string]: number + }, + outcome: string +) { + const squareSum = _.sumBy(Object.values(totalShares), (shares) => shares ** 2) + const shares = totalShares[outcome] ?? 0 + return shares ** 2 / squareSum } export function getProbabilityAfterBet( - totalShares: { YES: number; NO: number }, - outcome: 'YES' | 'NO', + totalShares: { + [outcome: string]: number + }, + outcome: string, bet: number ) { const shares = calculateShares(totalShares, bet, outcome) - const [YES, NO] = - outcome === 'YES' - ? [totalShares.YES + shares, totalShares.NO] - : [totalShares.YES, totalShares.NO + shares] + const prevShares = totalShares[outcome] ?? 0 + const newTotalShares = { ...totalShares, outcome: prevShares + shares } - return getProbability({ YES, NO }) + return getOutcomeProbability(newTotalShares, outcome) } export function calculateShares( - totalShares: { YES: number; NO: number }, + totalShares: { + [outcome: string]: number + }, bet: number, - betChoice: 'YES' | 'NO' + betChoice: string ) { - const [yesShares, noShares] = [totalShares.YES, totalShares.NO] + const squareSum = _.sumBy(Object.values(totalShares), (shares) => shares ** 2) + const shares = totalShares[betChoice] ?? 0 - const c = 2 * bet * Math.sqrt(yesShares ** 2 + noShares ** 2) + const c = 2 * bet * Math.sqrt(squareSum) - return betChoice === 'YES' - ? Math.sqrt(bet ** 2 + yesShares ** 2 + c) - yesShares - : Math.sqrt(bet ** 2 + noShares ** 2 + c) - noShares + return Math.sqrt(bet ** 2 + shares ** 2 + c) - shares } export function calculateEstimatedWinnings( @@ -128,15 +140,15 @@ export function calculateCancelPayout(contract: Contract, bet: Bet) { export function calculateStandardPayout( contract: Contract, bet: Bet, - outcome: 'YES' | 'NO' + outcome: string ) { const { amount, outcome: betOutcome, shares } = bet if (betOutcome !== outcome) return 0 const { totalShares, totalBets, phantomShares } = contract - if (totalShares[outcome] === 0) return 0 + if (!totalShares[outcome]) return 0 - const truePool = contract.pool.YES + contract.pool.NO + const truePool = _.sum(Object.values(totalShares)) if (totalBets[outcome] >= truePool) return (amount / totalBets[outcome]) * truePool diff --git a/common/new-bet.ts b/common/new-bet.ts index 5b23c84e..67f1e90a 100644 --- a/common/new-bet.ts +++ b/common/new-bet.ts @@ -1,6 +1,9 @@ import { Bet } from './bet' -import { calculateShares, getProbability } from './calculate' -import { calculateMultiShares, getMultiProbability } from './calculate-multi' +import { + calculateShares, + getProbability, + getOutcomeProbability, +} from './calculate' import { Contract } from './contract' import { User } from './user' @@ -66,7 +69,7 @@ export const getNewMultiBetInfo = ( const prevOutcomePool = pool[outcome] ?? 0 const newPool = { ...pool, outcome: prevOutcomePool + amount } - const shares = calculateMultiShares(contract.totalShares, amount, outcome) + const shares = calculateShares(contract.totalShares, amount, outcome) const prevShares = totalShares[outcome] ?? 0 const newTotalShares = { ...totalShares, outcome: prevShares + shares } @@ -74,8 +77,8 @@ export const getNewMultiBetInfo = ( const prevTotalBets = totalBets[outcome] ?? 0 const newTotalBets = { ...totalBets, outcome: prevTotalBets + amount } - const probBefore = getMultiProbability(totalShares, outcome) - const probAfter = getMultiProbability(newTotalShares, outcome) + const probBefore = getOutcomeProbability(totalShares, outcome) + const probAfter = getOutcomeProbability(newTotalShares, outcome) const newBet: Bet<'MULTI'> = { id: newBetId, diff --git a/web/components/answers-panel.tsx b/web/components/answers-panel.tsx index 3745bced..771be5be 100644 --- a/web/components/answers-panel.tsx +++ b/web/components/answers-panel.tsx @@ -1,18 +1,32 @@ import clsx from 'clsx' -import { useState } from 'react' +import { useEffect, useRef, useState } from 'react' import Textarea from 'react-expanding-textarea' import { Answer } from '../../common/answer' import { Contract } from '../../common/contract' import { AmountInput } from './amount-input' import { Col } from './layout/col' -import { createAnswer } from '../lib/firebase/api-call' +import { createAnswer, placeBet } from '../lib/firebase/api-call' import { Row } from './layout/row' import { Avatar } from './avatar' import { SiteLink } from './site-link' import { DateTimeTooltip } from './datetime-tooltip' import dayjs from 'dayjs' import { BuyButton } from './yes-no-selector' +import { Spacer } from './layout/spacer' +import { + formatMoney, + formatPercent, + formatWithCommas, +} from '../../common/util/format' +import { InfoTooltip } from './info-tooltip' +import { useUser } from '../hooks/use-user' +import { + getProbabilityAfterBet, + getOutcomeProbability, + calculateShares, +} from '../../common/calculate' +import { firebaseLogin } from '../lib/firebase/users' export function AnswersPanel(props: { contract: Contract<'MULTI'> @@ -36,33 +50,188 @@ function AnswerItem(props: { answer: Answer; contract: Contract<'MULTI'> }) { const createdDate = dayjs(createdTime).format('MMM D') + const [isBetting, setIsBetting] = useState(false) + return ( -