diff --git a/common/calculate.ts b/common/calculate.ts index 52a51b83..82944e0b 100644 --- a/common/calculate.ts +++ b/common/calculate.ts @@ -28,7 +28,7 @@ export function getProbabilityAfterBet( const shares = calculateShares(totalShares, bet, outcome) const prevShares = totalShares[outcome] ?? 0 - const newTotalShares = { ...totalShares, outcome: prevShares + shares } + const newTotalShares = { ...totalShares, [outcome]: prevShares + shares } return getOutcomeProbability(newTotalShares, outcome) } @@ -48,54 +48,49 @@ export function calculateShares( return Math.sqrt(bet ** 2 + shares ** 2 + c) - shares } -export function calculateEstimatedWinnings( - totalShares: { YES: number; NO: number }, - shares: number, - betChoice: 'YES' | 'NO' -) { - const ind = betChoice === 'YES' ? 1 : 0 - - const yesShares = totalShares.YES + ind * shares - const noShares = totalShares.NO + (1 - ind) * shares - - const estPool = Math.sqrt(yesShares ** 2 + noShares ** 2) - const total = ind * yesShares + (1 - ind) * noShares - - return ((1 - FEES) * (shares * estPool)) / total -} - export function calculateRawShareValue( - totalShares: { YES: number; NO: number }, + totalShares: { + [outcome: string]: number + }, shares: number, - betChoice: 'YES' | 'NO' + betChoice: string ) { - const [yesShares, noShares] = [totalShares.YES, totalShares.NO] - const currentValue = Math.sqrt(yesShares ** 2 + noShares ** 2) + const currentValue = Math.sqrt( + _.sumBy(Object.values(totalShares), (shares) => shares ** 2) + ) - const postSaleValue = - betChoice === 'YES' - ? Math.sqrt(Math.max(0, yesShares - shares) ** 2 + noShares ** 2) - : Math.sqrt(yesShares ** 2 + Math.max(0, noShares - shares) ** 2) + const postSaleValue = Math.sqrt( + _.sumBy(Object.keys(totalShares), (outcome) => + outcome === betChoice + ? Math.max(0, totalShares[outcome] - shares) ** 2 + : totalShares[outcome] ** 2 + ) + ) return currentValue - postSaleValue } -export function calculateMoneyRatio( - contract: Contract, - bet: Bet, +export function calculateMoneyRatio( + contract: Contract, + bet: Bet, shareValue: number ) { - const { totalShares, pool } = contract + const { totalShares, totalBets, pool } = contract + const { outcome, amount } = bet - const p = getProbability(totalShares) + const p = getOutcomeProbability(totalShares, outcome) const actual = pool.YES + pool.NO - shareValue - const betAmount = - bet.outcome === 'YES' ? p * bet.amount : (1 - p) * bet.amount + const betAmount = p * amount const expected = - p * contract.totalBets.YES + (1 - p) * contract.totalBets.NO - betAmount + _.sumBy( + Object.keys(totalBets), + (outcome) => + getOutcomeProbability(totalShares, outcome) * + (totalBets as { [outcome: string]: number })[outcome] + ) - betAmount if (actual <= 0 || expected <= 0) return 0 @@ -103,14 +98,13 @@ export function calculateMoneyRatio( } export function calculateShareValue(contract: Contract, bet: Bet) { - const shareValue = calculateRawShareValue( - contract.totalShares, - bet.shares, - bet.outcome - ) + const { pool, totalShares } = contract + const { shares, outcome } = bet + + const shareValue = calculateRawShareValue(totalShares, shares, outcome) const f = calculateMoneyRatio(contract, bet, shareValue) - const myPool = contract.pool[bet.outcome] + const myPool = pool[outcome] const adjShareValue = Math.min(Math.min(1, f) * shareValue, myPool) return adjShareValue } @@ -133,10 +127,11 @@ export function calculatePayout( } export function calculateCancelPayout(contract: Contract, bet: Bet) { - const totalBets = contract.totalBets.YES + contract.totalBets.NO - const pool = contract.pool.YES + contract.pool.NO + const { totalBets, pool } = contract + const betTotal = _.sum(Object.values(totalBets)) + const poolTotal = _.sum(Object.values(pool)) - return (bet.amount / totalBets) * pool + return (bet.amount / betTotal) * poolTotal } export function calculateStandardPayout( @@ -152,7 +147,8 @@ export function calculateStandardPayout( const pool = _.sum(Object.values(totalShares)) - const total = totalShares[outcome] - phantomShares[outcome] + const total = + totalShares[outcome] - (phantomShares ? phantomShares[outcome] : 0) const winnings = (shares / total) * pool // profit can be negative if using phantom shares @@ -161,45 +157,55 @@ export function calculateStandardPayout( export function calculatePayoutAfterCorrectBet(contract: Contract, bet: Bet) { const { totalShares, pool, totalBets } = contract - - const ind = bet.outcome === 'YES' ? 1 : 0 - const { shares, amount } = bet + const { shares, amount, outcome } = bet const newContract = { ...contract, totalShares: { - YES: totalShares.YES + ind * shares, - NO: totalShares.NO + (1 - ind) * shares, + ...totalShares, + [outcome]: totalShares[outcome] + shares, }, pool: { - YES: pool.YES + ind * amount, - NO: pool.NO + (1 - ind) * amount, + ...pool, + [outcome]: pool[outcome] + amount, }, totalBets: { - YES: totalBets.YES + ind * amount, - NO: totalBets.NO + (1 - ind) * amount, + ...totalBets, + [outcome]: totalBets[outcome] + amount, }, } - return calculateStandardPayout(newContract, bet, bet.outcome) + return calculateStandardPayout(newContract, bet, outcome) } function calculateMktPayout(contract: Contract, bet: Bet) { - const p = - contract.resolutionProbability !== undefined - ? contract.resolutionProbability - : getProbability(contract.totalShares) + const { resolutionProbability, totalShares, pool, phantomShares } = contract - const pool = contract.pool.YES + contract.pool.NO + const totalPool = _.sum(Object.values(pool)) + const squareSum = _.sumBy(Object.values(totalShares), (shares) => shares ** 2) - const weightedShareTotal = - p * (contract.totalShares.YES - contract.phantomShares.YES) + - (1 - p) * (contract.totalShares.NO - contract.phantomShares.NO) + let weightedShareTotal = _.sumBy(Object.keys(totalShares), (outcome) => { + const shareTotals = totalShares as { [outcome: string]: number } + + // Avoid O(n^2) by reusing squareSum for prob. + const prob = shareTotals[outcome] ** 2 / squareSum + const shares = + shareTotals[outcome] - + (phantomShares ? phantomShares[outcome as 'YES' | 'NO'] : 0) + return prob * shares + }) + + // Compute binary case if resolutionProbability provided. + if (resolutionProbability !== undefined) { + weightedShareTotal = + resolutionProbability * (totalShares.YES - phantomShares.YES) + + (1 - resolutionProbability) * (totalShares.NO - phantomShares.NO) + } const { outcome, amount, shares } = bet - const betP = outcome === 'YES' ? p : 1 - p - const winnings = ((betP * shares) / weightedShareTotal) * pool + const betP = getOutcomeProbability(totalShares, outcome) + const winnings = ((betP * shares) / weightedShareTotal) * totalPool return deductFees(amount, winnings) } @@ -210,15 +216,6 @@ export function resolvedPayout(contract: Contract, bet: Bet) { throw new Error('Contract was not resolved') } -// deprecated use MKT payout -export function currentValue(contract: Contract, bet: Bet) { - const prob = getProbability(contract.pool) - const yesPayout = calculatePayout(contract, bet, 'YES') - const noPayout = calculatePayout(contract, bet, 'NO') - - return prob * yesPayout + (1 - prob) * noPayout -} - export const deductFees = (betAmount: number, winnings: number) => { return winnings > betAmount ? betAmount + (1 - FEES) * (winnings - betAmount) diff --git a/common/new-bet.ts b/common/new-bet.ts index 67f1e90a..78862b9d 100644 --- a/common/new-bet.ts +++ b/common/new-bet.ts @@ -67,15 +67,15 @@ export const getNewMultiBetInfo = ( const { pool, totalShares, totalBets } = contract const prevOutcomePool = pool[outcome] ?? 0 - const newPool = { ...pool, outcome: prevOutcomePool + amount } + const newPool = { ...pool, [outcome]: prevOutcomePool + amount } const shares = calculateShares(contract.totalShares, amount, outcome) const prevShares = totalShares[outcome] ?? 0 - const newTotalShares = { ...totalShares, outcome: prevShares + shares } + const newTotalShares = { ...totalShares, [outcome]: prevShares + shares } const prevTotalBets = totalBets[outcome] ?? 0 - const newTotalBets = { ...totalBets, outcome: prevTotalBets + amount } + const newTotalBets = { ...totalBets, [outcome]: prevTotalBets + amount } const probBefore = getOutcomeProbability(totalShares, outcome) const probAfter = getOutcomeProbability(newTotalShares, outcome)