From 97c8b86259bf512e74d74d0cb03593f5f3c87d64 Mon Sep 17 00:00:00 2001 From: mantikoros Date: Sun, 30 Jan 2022 00:22:01 -0600 Subject: [PATCH] resolve to PROB --- common/calculate.ts | 5 +++- common/contract.ts | 1 + common/payouts.ts | 17 +++++++++---- functions/src/emails.ts | 16 +++++++++---- functions/src/resolve-market.ts | 37 +++++++++++++++++++++++++---- web/components/resolution-panel.tsx | 20 +++++++++++++++- web/lib/firebase/contracts.ts | 3 ++- web/lib/firebase/scoring.ts | 9 +++++-- 8 files changed, 89 insertions(+), 19 deletions(-) diff --git a/common/calculate.ts b/common/calculate.ts index b5c6818b..2e69690e 100644 --- a/common/calculate.ts +++ b/common/calculate.ts @@ -174,7 +174,10 @@ export function calculatePayoutAfterCorrectBet(contract: Contract, bet: Bet) { } function calculateMktPayout(contract: Contract, bet: Bet) { - const p = getProbability(contract.totalShares) + const p = + contract.resolutionProbability !== undefined + ? contract.resolutionProbability + : getProbability(contract.totalShares) const weightedTotal = p * contract.totalBets.YES + (1 - p) * contract.totalBets.NO diff --git a/common/contract.ts b/common/contract.ts index d220da38..ed642cfa 100644 --- a/common/contract.ts +++ b/common/contract.ts @@ -27,6 +27,7 @@ export type Contract = { isResolved: boolean resolutionTime?: number // When the contract creator resolved the market resolution?: outcome // Chosen by creator; must be one of outcomes + resolutionProbability?: number volume24Hours: number volume7Days: number diff --git a/common/payouts.ts b/common/payouts.ts index 851861df..c4010ba2 100644 --- a/common/payouts.ts +++ b/common/payouts.ts @@ -59,8 +59,16 @@ export const getStandardPayouts = ( ]) // add creator fee } -export const getMktPayouts = (contract: Contract, bets: Bet[]) => { - const p = getProbability(contract.totalShares) +export const getMktPayouts = ( + contract: Contract, + bets: Bet[], + resolutionProbability?: number +) => { + const p = + resolutionProbability === undefined + ? getProbability(contract.totalShares) + : resolutionProbability + const poolTotal = contract.pool.YES + contract.pool.NO console.log('Resolved MKT at p=', p, 'pool: $M', poolTotal) @@ -116,14 +124,15 @@ export const getMktPayouts = (contract: Contract, bets: Bet[]) => { export const getPayouts = ( outcome: outcome, contract: Contract, - bets: Bet[] + bets: Bet[], + resolutionProbability?: number ) => { switch (outcome) { case 'YES': case 'NO': return getStandardPayouts(outcome, contract, bets) case 'MKT': - return getMktPayouts(contract, bets) + return getMktPayouts(contract, bets, resolutionProbability) case 'CANCEL': return getCancelPayouts(contract, bets) } diff --git a/functions/src/emails.ts b/functions/src/emails.ts index 971c51e7..63d0c9d2 100644 --- a/functions/src/emails.ts +++ b/functions/src/emails.ts @@ -20,7 +20,8 @@ export const sendMarketResolutionEmail = async ( payout: number, creator: User, contract: Contract, - resolution: 'YES' | 'NO' | 'CANCEL' | 'MKT' + resolution: 'YES' | 'NO' | 'CANCEL' | 'MKT', + resolutionProbability?: number ) => { const privateUser = await getPrivateUser(userId) if ( @@ -33,8 +34,15 @@ export const sendMarketResolutionEmail = async ( const user = await getUser(userId) if (!user) return - const prob = formatPercent(getProbability(contract.totalShares)) - const outcome = toDisplayResolution[resolution].replace('PROB', prob) + const prob = resolutionProbability ?? getProbability(contract.totalShares) + + const toDisplayResolution = { + YES: 'YES', + NO: 'NO', + CANCEL: 'N/A', + MKT: formatPercent(prob), + } + const outcome = toDisplayResolution[resolution] const subject = `Resolved ${outcome}: ${contract.question}` @@ -59,5 +67,3 @@ export const sendMarketResolutionEmail = async ( templateData ) } - -const toDisplayResolution = { YES: 'YES', NO: 'NO', CANCEL: 'N/A', MKT: 'PROB' } diff --git a/functions/src/resolve-market.ts b/functions/src/resolve-market.ts index df10feed..6816a8d7 100644 --- a/functions/src/resolve-market.ts +++ b/functions/src/resolve-market.ts @@ -16,17 +16,24 @@ export const resolveMarket = functions data: { outcome: 'YES' | 'NO' | 'CANCEL' | 'MKT' contractId: string + probabilityInt?: number }, context ) => { const userId = context?.auth?.uid if (!userId) return { status: 'error', message: 'Not authorized' } - const { outcome, contractId } = data + const { outcome, contractId, probabilityInt } = data if (!['YES', 'NO', 'MKT', 'CANCEL'].includes(outcome)) return { status: 'error', message: 'Invalid outcome' } + if ( + probabilityInt !== undefined && + (probabilityInt < 1 || probabilityInt > 99 || !isFinite(probabilityInt)) + ) + return { status: 'error', message: 'Invalid probability' } + const contractDoc = firestore.doc(`contracts/${contractId}`) const contractSnap = await contractDoc.get() if (!contractSnap.exists) @@ -42,10 +49,16 @@ export const resolveMarket = functions const creator = await getUser(contract.creatorId) if (!creator) return { status: 'error', message: 'Creator not found' } + const resolutionProbability = + probabilityInt !== undefined ? probabilityInt / 100 : undefined + await contractDoc.update({ isResolved: true, resolution: outcome, resolutionTime: Date.now(), + ...(resolutionProbability === undefined + ? {} + : { resolutionProbability }), }) console.log('contract ', contractId, 'resolved to:', outcome) @@ -57,7 +70,12 @@ export const resolveMarket = functions const bets = betsSnap.docs.map((doc) => doc.data() as Bet) const openBets = bets.filter((b) => !b.isSold && !b.sale) - const payouts = getPayouts(outcome, contract, openBets) + const payouts = getPayouts( + outcome, + contract, + openBets, + resolutionProbability + ) console.log('payouts:', payouts) @@ -79,7 +97,8 @@ export const resolveMarket = functions userPayouts, creator, contract, - outcome + outcome, + resolutionProbability ) return result @@ -91,7 +110,8 @@ const sendResolutionEmails = async ( userPayouts: { [userId: string]: number }, creator: User, contract: Contract, - outcome: 'YES' | 'NO' | 'CANCEL' | 'MKT' + outcome: 'YES' | 'NO' | 'CANCEL' | 'MKT', + resolutionProbability?: number ) => { const nonWinners = _.difference( _.uniq(openBets.map(({ userId }) => userId)), @@ -103,7 +123,14 @@ const sendResolutionEmails = async ( ] await Promise.all( emailPayouts.map(([userId, payout]) => - sendMarketResolutionEmail(userId, payout, creator, contract, outcome) + sendMarketResolutionEmail( + userId, + payout, + creator, + contract, + outcome, + resolutionProbability + ) ) ) } diff --git a/web/components/resolution-panel.tsx b/web/components/resolution-panel.tsx index 25ca650e..1a409f7e 100644 --- a/web/components/resolution-panel.tsx +++ b/web/components/resolution-panel.tsx @@ -9,6 +9,8 @@ import { YesNoCancelSelector } from './yes-no-selector' import { Spacer } from './layout/spacer' import { ConfirmationButton as ConfirmationButton } from './confirmation-button' import { resolveMarket } from '../lib/firebase/api-call' +import { ProbabilitySelector } from './probability-selector' +import { getProbability } from '../../common/calculate' export function ResolutionPanel(props: { creator: User @@ -26,6 +28,8 @@ export function ResolutionPanel(props: { 'YES' | 'NO' | 'MKT' | 'CANCEL' | undefined >() + const [prob, setProb] = useState(getProbability(contract.totalShares) * 100) + const [isSubmitting, setIsSubmitting] = useState(false) const [error, setError] = useState(undefined) @@ -35,6 +39,7 @@ export function ResolutionPanel(props: { const result = await resolveMarket({ outcome, contractId: contract.id, + probabilityInt: prob, }).then((r) => r.data as any) console.log('resolved', outcome, 'result:', result) @@ -113,7 +118,20 @@ export function ResolutionPanel(props: { }} onSubmit={resolve} > -

Are you sure you want to resolve this market?

+ {outcome === 'MKT' ? ( + <> +

+ What probability would you like to resolve the market to? +

+ + + + ) : ( +

Are you sure you want to resolve this market?

+ )} ) diff --git a/web/lib/firebase/contracts.ts b/web/lib/firebase/contracts.ts index 1f02a27f..a2302c75 100644 --- a/web/lib/firebase/contracts.ts +++ b/web/lib/firebase/contracts.ts @@ -35,10 +35,11 @@ export function contractMetrics(contract: Contract) { createdTime, resolutionTime, isResolved, + resolutionProbability, } = contract const truePool = pool.YES + pool.NO - const prob = getProbability(totalShares) + const prob = resolutionProbability ?? getProbability(totalShares) const probPercent = Math.round(prob * 100) + '%' const startProb = getProbability(phantomShares) diff --git a/web/lib/firebase/scoring.ts b/web/lib/firebase/scoring.ts index 020a42ae..fa09d1c3 100644 --- a/web/lib/firebase/scoring.ts +++ b/web/lib/firebase/scoring.ts @@ -27,13 +27,18 @@ export function scoreTraders(contracts: Contract[], bets: Bet[][]) { } function scoreUsersByContract(contract: Contract, bets: Bet[]) { - const { resolution } = contract + const { resolution, resolutionProbability } = contract const [closedBets, openBets] = _.partition( bets, (bet) => bet.isSold || bet.sale ) - const resolvePayouts = getPayouts(resolution ?? 'MKT', contract, openBets) + const resolvePayouts = getPayouts( + resolution ?? 'MKT', + contract, + openBets, + resolutionProbability + ) const salePayouts = closedBets.map((bet) => { const { userId, sale } = bet