separate logic to payouts-dpm, payouts-fixed
This commit is contained in:
parent
a315f6d841
commit
9c5478d3d5
146
common/payouts-dpm.ts
Normal file
146
common/payouts-dpm.ts
Normal file
|
@ -0,0 +1,146 @@
|
|||
import * as _ from 'lodash'
|
||||
|
||||
import { Bet } from './bet'
|
||||
import { deductDpmFees, getDpmProbability } from './calculate-dpm'
|
||||
import { DPM, FreeResponse, FullContract, Multi } from './contract'
|
||||
import { CREATOR_FEE, FEES } from './fees'
|
||||
|
||||
export const getDpmCancelPayouts = (
|
||||
contract: FullContract<DPM, any>,
|
||||
bets: Bet[]
|
||||
) => {
|
||||
const { pool } = contract
|
||||
const poolTotal = _.sum(Object.values(pool))
|
||||
console.log('resolved N/A, pool M$', poolTotal)
|
||||
|
||||
const betSum = _.sumBy(bets, (b) => b.amount)
|
||||
|
||||
return bets.map((bet) => ({
|
||||
userId: bet.userId,
|
||||
payout: (bet.amount / betSum) * poolTotal,
|
||||
}))
|
||||
}
|
||||
|
||||
export const getDpmStandardPayouts = (
|
||||
outcome: string,
|
||||
contract: FullContract<DPM, any>,
|
||||
bets: Bet[]
|
||||
) => {
|
||||
const winningBets = bets.filter((bet) => bet.outcome === outcome)
|
||||
|
||||
const poolTotal = _.sum(Object.values(contract.pool))
|
||||
const totalShares = _.sumBy(winningBets, (b) => b.shares)
|
||||
|
||||
const payouts = winningBets.map(({ userId, amount, shares }) => {
|
||||
const winnings = (shares / totalShares) * poolTotal
|
||||
const profit = winnings - amount
|
||||
|
||||
// profit can be negative if using phantom shares
|
||||
const payout = amount + (1 - FEES) * Math.max(0, profit)
|
||||
return { userId, profit, payout }
|
||||
})
|
||||
|
||||
const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit))
|
||||
const creatorPayout = CREATOR_FEE * profits
|
||||
|
||||
console.log(
|
||||
'resolved',
|
||||
outcome,
|
||||
'pool',
|
||||
poolTotal,
|
||||
'profits',
|
||||
profits,
|
||||
'creator fee',
|
||||
creatorPayout
|
||||
)
|
||||
|
||||
return payouts
|
||||
.map(({ userId, payout }) => ({ userId, payout }))
|
||||
.concat([{ userId: contract.creatorId, payout: creatorPayout }]) // add creator fee
|
||||
}
|
||||
|
||||
export const getDpmMktPayouts = (
|
||||
contract: FullContract<DPM, any>,
|
||||
bets: Bet[],
|
||||
resolutionProbability?: number
|
||||
) => {
|
||||
const p =
|
||||
resolutionProbability === undefined
|
||||
? getDpmProbability(contract.totalShares)
|
||||
: resolutionProbability
|
||||
|
||||
const weightedShareTotal = _.sumBy(bets, (b) =>
|
||||
b.outcome === 'YES' ? p * b.shares : (1 - p) * b.shares
|
||||
)
|
||||
|
||||
const pool = contract.pool.YES + contract.pool.NO
|
||||
|
||||
const payouts = bets.map(({ userId, outcome, amount, shares }) => {
|
||||
const betP = outcome === 'YES' ? p : 1 - p
|
||||
const winnings = ((betP * shares) / weightedShareTotal) * pool
|
||||
const profit = winnings - amount
|
||||
const payout = deductDpmFees(amount, winnings)
|
||||
return { userId, profit, payout }
|
||||
})
|
||||
|
||||
const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit))
|
||||
const creatorPayout = CREATOR_FEE * profits
|
||||
|
||||
console.log(
|
||||
'resolved MKT',
|
||||
p,
|
||||
'pool',
|
||||
pool,
|
||||
'profits',
|
||||
profits,
|
||||
'creator fee',
|
||||
creatorPayout
|
||||
)
|
||||
|
||||
return payouts
|
||||
.map(({ userId, payout }) => ({ userId, payout }))
|
||||
.concat([{ userId: contract.creatorId, payout: creatorPayout }]) // add creator fee
|
||||
}
|
||||
|
||||
export const getPayoutsMultiOutcome = (
|
||||
resolutions: { [outcome: string]: number },
|
||||
contract: FullContract<DPM, Multi | FreeResponse>,
|
||||
bets: Bet[]
|
||||
) => {
|
||||
const poolTotal = _.sum(Object.values(contract.pool))
|
||||
const winningBets = bets.filter((bet) => resolutions[bet.outcome])
|
||||
|
||||
const betsByOutcome = _.groupBy(winningBets, (bet) => bet.outcome)
|
||||
const sharesByOutcome = _.mapValues(betsByOutcome, (bets) =>
|
||||
_.sumBy(bets, (bet) => bet.shares)
|
||||
)
|
||||
|
||||
const probTotal = _.sum(Object.values(resolutions))
|
||||
|
||||
const payouts = winningBets.map(({ userId, outcome, amount, shares }) => {
|
||||
const prob = resolutions[outcome] / probTotal
|
||||
const winnings = (shares / sharesByOutcome[outcome]) * prob * poolTotal
|
||||
const profit = winnings - amount
|
||||
|
||||
const payout = amount + (1 - FEES) * Math.max(0, profit)
|
||||
return { userId, profit, payout }
|
||||
})
|
||||
|
||||
const profits = _.sumBy(payouts, (po) => po.profit)
|
||||
const creatorPayout = CREATOR_FEE * profits
|
||||
|
||||
console.log(
|
||||
'resolved',
|
||||
resolutions,
|
||||
'pool',
|
||||
poolTotal,
|
||||
'profits',
|
||||
profits,
|
||||
'creator fee',
|
||||
creatorPayout
|
||||
)
|
||||
|
||||
return payouts
|
||||
.map(({ userId, payout }) => ({ userId, payout }))
|
||||
.concat([{ userId: contract.creatorId, payout: creatorPayout }]) // add creator fee
|
||||
}
|
103
common/payouts-fixed.ts
Normal file
103
common/payouts-fixed.ts
Normal file
|
@ -0,0 +1,103 @@
|
|||
import * as _ from 'lodash'
|
||||
|
||||
import { Bet } from './bet'
|
||||
import { getProbability } from './calculate'
|
||||
import { deductFixedFees } from './calculate-fixed-payouts'
|
||||
import { Binary, CPMM, FixedPayouts, FullContract } from './contract'
|
||||
import { CREATOR_FEE } from './fees'
|
||||
|
||||
export const getFixedCancelPayouts = (bets: Bet[]) => {
|
||||
return bets.map((bet) => ({
|
||||
userId: bet.userId,
|
||||
payout: bet.amount,
|
||||
}))
|
||||
}
|
||||
|
||||
export const getStandardFixedPayouts = (
|
||||
outcome: string,
|
||||
contract: FullContract<FixedPayouts, Binary>,
|
||||
bets: Bet[]
|
||||
) => {
|
||||
const winningBets = bets.filter((bet) => bet.outcome === outcome)
|
||||
|
||||
const payouts = winningBets.map(({ userId, amount, shares }) => {
|
||||
const winnings = shares
|
||||
const profit = winnings - amount
|
||||
const payout = deductFixedFees(amount, winnings)
|
||||
return { userId, profit, payout }
|
||||
})
|
||||
|
||||
const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit))
|
||||
const creatorPayout = CREATOR_FEE * profits
|
||||
|
||||
console.log(
|
||||
'resolved',
|
||||
outcome,
|
||||
'pool',
|
||||
contract.pool,
|
||||
'profits',
|
||||
profits,
|
||||
'creator fee',
|
||||
creatorPayout
|
||||
)
|
||||
|
||||
return payouts
|
||||
.map(({ userId, payout }) => ({ userId, payout }))
|
||||
.concat([{ userId: contract.creatorId, payout: creatorPayout }]) // add creator fee
|
||||
.concat(getLiquidityPoolPayouts(contract, outcome))
|
||||
}
|
||||
|
||||
export const getLiquidityPoolPayouts = (
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
outcome: string
|
||||
) => {
|
||||
const { creatorId, pool } = contract
|
||||
return [{ userId: creatorId, payout: pool[outcome] }]
|
||||
}
|
||||
|
||||
export const getMktFixedPayouts = (
|
||||
contract: FullContract<FixedPayouts, Binary>,
|
||||
bets: Bet[],
|
||||
resolutionProbability?: number
|
||||
) => {
|
||||
const p =
|
||||
resolutionProbability === undefined
|
||||
? getProbability(contract)
|
||||
: resolutionProbability
|
||||
|
||||
const payouts = bets.map(({ userId, outcome, amount, shares }) => {
|
||||
const betP = outcome === 'YES' ? p : 1 - p
|
||||
const winnings = betP * shares
|
||||
const profit = winnings - amount
|
||||
const payout = deductFixedFees(amount, winnings)
|
||||
return { userId, profit, payout }
|
||||
})
|
||||
|
||||
const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit))
|
||||
const creatorPayout = CREATOR_FEE * profits
|
||||
|
||||
console.log(
|
||||
'resolved MKT',
|
||||
p,
|
||||
'pool',
|
||||
contract.pool,
|
||||
'profits',
|
||||
profits,
|
||||
'creator fee',
|
||||
creatorPayout
|
||||
)
|
||||
|
||||
return payouts
|
||||
.map(({ userId, payout }) => ({ userId, payout }))
|
||||
.concat([{ userId: contract.creatorId, payout: creatorPayout }]) // add creator fee
|
||||
.concat(getLiquidityPoolProbPayouts(contract, p))
|
||||
}
|
||||
|
||||
export const getLiquidityPoolProbPayouts = (
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
p: number
|
||||
) => {
|
||||
const { creatorId, pool } = contract
|
||||
const payout = p * pool.YES + (1 - p) * pool.NO
|
||||
return [{ userId: creatorId, payout }]
|
||||
}
|
|
@ -1,225 +1,36 @@
|
|||
import * as _ from 'lodash'
|
||||
|
||||
import { Bet } from './bet'
|
||||
import { deductDpmFees, getDpmProbability } from './calculate-dpm'
|
||||
import { Contract, DPM, FreeResponse, FullContract, Multi } from './contract'
|
||||
import {
|
||||
Binary,
|
||||
Contract,
|
||||
CPMM,
|
||||
DPM,
|
||||
FreeResponse,
|
||||
FullContract,
|
||||
Multi,
|
||||
} from './contract'
|
||||
import { CREATOR_FEE, FEES } from './fees'
|
||||
|
||||
export const getDpmCancelPayouts = (
|
||||
contract: FullContract<DPM, any>,
|
||||
bets: Bet[]
|
||||
) => {
|
||||
const { pool } = contract
|
||||
const poolTotal = _.sum(Object.values(pool))
|
||||
console.log('resolved N/A, pool M$', poolTotal)
|
||||
|
||||
const betSum = _.sumBy(bets, (b) => b.amount)
|
||||
|
||||
return bets.map((bet) => ({
|
||||
userId: bet.userId,
|
||||
payout: (bet.amount / betSum) * poolTotal,
|
||||
}))
|
||||
}
|
||||
|
||||
export const getFixedCancelPayouts = (bets: Bet[]) => {
|
||||
return bets.map((bet) => ({
|
||||
userId: bet.userId,
|
||||
payout: bet.amount,
|
||||
}))
|
||||
}
|
||||
|
||||
export const getStandardFixedPayouts = (
|
||||
outcome: string,
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
bets: Bet[]
|
||||
) => {
|
||||
const winningBets = bets.filter((bet) => bet.outcome === outcome)
|
||||
|
||||
const payouts = winningBets.map(({ userId, amount, shares }) => {
|
||||
const winnings = shares
|
||||
const profit = winnings - amount
|
||||
|
||||
const payout = amount + (1 - FEES) * profit
|
||||
return { userId, profit, payout }
|
||||
})
|
||||
|
||||
const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit))
|
||||
const creatorPayout = CREATOR_FEE * profits
|
||||
|
||||
console.log(
|
||||
'resolved',
|
||||
outcome,
|
||||
'pool',
|
||||
contract.pool,
|
||||
'profits',
|
||||
profits,
|
||||
'creator fee',
|
||||
creatorPayout
|
||||
)
|
||||
|
||||
return payouts
|
||||
.map(({ userId, payout }) => ({ userId, payout }))
|
||||
.concat([{ userId: contract.creatorId, payout: creatorPayout }]) // add creator fee
|
||||
.concat(getLiquidityPoolPayouts(contract, outcome))
|
||||
}
|
||||
|
||||
export const getLiquidityPoolPayouts = (
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
outcome: string
|
||||
) => {
|
||||
const { creatorId, pool } = contract
|
||||
return [{ userId: creatorId, payout: pool[outcome] }]
|
||||
}
|
||||
|
||||
export const getStandardPayouts = (
|
||||
outcome: string,
|
||||
contract: FullContract<DPM, any>,
|
||||
bets: Bet[]
|
||||
) => {
|
||||
const winningBets = bets.filter((bet) => bet.outcome === outcome)
|
||||
|
||||
const poolTotal = _.sum(Object.values(contract.pool))
|
||||
const totalShares = _.sumBy(winningBets, (b) => b.shares)
|
||||
|
||||
const payouts = winningBets.map(({ userId, amount, shares }) => {
|
||||
const winnings = (shares / totalShares) * poolTotal
|
||||
const profit = winnings - amount
|
||||
|
||||
// profit can be negative if using phantom shares
|
||||
const payout = amount + (1 - FEES) * Math.max(0, profit)
|
||||
return { userId, profit, payout }
|
||||
})
|
||||
|
||||
const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit))
|
||||
const creatorPayout = CREATOR_FEE * profits
|
||||
|
||||
console.log(
|
||||
'resolved',
|
||||
outcome,
|
||||
'pool',
|
||||
poolTotal,
|
||||
'profits',
|
||||
profits,
|
||||
'creator fee',
|
||||
creatorPayout
|
||||
)
|
||||
|
||||
return payouts
|
||||
.map(({ userId, payout }) => ({ userId, payout }))
|
||||
.concat([{ userId: contract.creatorId, payout: creatorPayout }]) // add creator fee
|
||||
}
|
||||
|
||||
export const getMktPayouts = (
|
||||
contract: FullContract<DPM, any>,
|
||||
bets: Bet[],
|
||||
resolutionProbability?: number
|
||||
) => {
|
||||
const p =
|
||||
resolutionProbability === undefined
|
||||
? getDpmProbability(contract.totalShares)
|
||||
: resolutionProbability
|
||||
|
||||
const weightedShareTotal = _.sumBy(bets, (b) =>
|
||||
b.outcome === 'YES' ? p * b.shares : (1 - p) * b.shares
|
||||
)
|
||||
|
||||
const pool = contract.pool.YES + contract.pool.NO
|
||||
|
||||
const payouts = bets.map(({ userId, outcome, amount, shares }) => {
|
||||
const betP = outcome === 'YES' ? p : 1 - p
|
||||
const winnings = ((betP * shares) / weightedShareTotal) * pool
|
||||
const profit = winnings - amount
|
||||
const payout = deductDpmFees(amount, winnings)
|
||||
return { userId, profit, payout }
|
||||
})
|
||||
|
||||
const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit))
|
||||
const creatorPayout = CREATOR_FEE * profits
|
||||
|
||||
console.log(
|
||||
'resolved MKT',
|
||||
p,
|
||||
'pool',
|
||||
pool,
|
||||
'profits',
|
||||
profits,
|
||||
'creator fee',
|
||||
creatorPayout
|
||||
)
|
||||
|
||||
return payouts
|
||||
.map(({ userId, payout }) => ({ userId, payout }))
|
||||
.concat([{ userId: contract.creatorId, payout: creatorPayout }]) // add creator fee
|
||||
}
|
||||
|
||||
export const getMktFixedPayouts = (
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
bets: Bet[],
|
||||
resolutionProbability?: number
|
||||
) => {
|
||||
const p =
|
||||
resolutionProbability === undefined
|
||||
? getDpmProbability(contract.pool)
|
||||
: resolutionProbability
|
||||
|
||||
const payouts = bets.map(({ userId, outcome, amount, shares }) => {
|
||||
const betP = outcome === 'YES' ? p : 1 - p
|
||||
const winnings = betP * shares
|
||||
const profit = winnings - amount
|
||||
const payout = deductDpmFees(amount, winnings)
|
||||
return { userId, profit, payout }
|
||||
})
|
||||
|
||||
const profits = _.sumBy(payouts, (po) => Math.max(0, po.profit))
|
||||
const creatorPayout = CREATOR_FEE * profits
|
||||
|
||||
console.log(
|
||||
'resolved MKT',
|
||||
p,
|
||||
'pool',
|
||||
contract.pool,
|
||||
'profits',
|
||||
profits,
|
||||
'creator fee',
|
||||
creatorPayout
|
||||
)
|
||||
|
||||
return payouts
|
||||
.map(({ userId, payout }) => ({ userId, payout }))
|
||||
.concat([{ userId: contract.creatorId, payout: creatorPayout }]) // add creator fee
|
||||
.concat(getLiquidityPoolProbPayouts(contract, p))
|
||||
}
|
||||
|
||||
export const getLiquidityPoolProbPayouts = (
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
p: number
|
||||
) => {
|
||||
const { creatorId, pool } = contract
|
||||
const payout = p * pool.YES + (1 - p) * pool.NO
|
||||
return [{ userId: creatorId, payout }]
|
||||
}
|
||||
getDpmCancelPayouts,
|
||||
getDpmMktPayouts,
|
||||
getDpmStandardPayouts,
|
||||
getPayoutsMultiOutcome,
|
||||
} from './payouts-dpm'
|
||||
import {
|
||||
getFixedCancelPayouts,
|
||||
getMktFixedPayouts,
|
||||
getStandardFixedPayouts,
|
||||
} from './payouts-fixed'
|
||||
|
||||
export const getPayouts = (
|
||||
outcome: string,
|
||||
outcome:
|
||||
| string
|
||||
| {
|
||||
[outcome: string]: number
|
||||
},
|
||||
contract: Contract,
|
||||
bets: Bet[],
|
||||
resolutionProbability?: number
|
||||
) => {
|
||||
if (contract.mechanism === 'cpmm-1') {
|
||||
if (contract.mechanism === 'cpmm-1' && contract.outcomeType === 'BINARY') {
|
||||
switch (outcome) {
|
||||
case 'YES':
|
||||
case 'NO':
|
||||
return getStandardFixedPayouts(outcome, contract as any, bets)
|
||||
return getStandardFixedPayouts(outcome, contract, bets)
|
||||
case 'MKT':
|
||||
return getMktFixedPayouts(contract as any, bets, resolutionProbability)
|
||||
return getMktFixedPayouts(contract, bets, resolutionProbability)
|
||||
case 'CANCEL':
|
||||
return getFixedCancelPayouts(bets)
|
||||
}
|
||||
|
@ -228,58 +39,21 @@ export const getPayouts = (
|
|||
switch (outcome) {
|
||||
case 'YES':
|
||||
case 'NO':
|
||||
return getStandardPayouts(outcome, contract, bets)
|
||||
return getDpmStandardPayouts(outcome, contract, bets)
|
||||
case 'MKT':
|
||||
return getMktPayouts(contract, bets, resolutionProbability)
|
||||
return getDpmMktPayouts(contract, bets, resolutionProbability)
|
||||
case 'CANCEL':
|
||||
return getDpmCancelPayouts(contract, bets)
|
||||
default:
|
||||
// Multi outcome.
|
||||
return getStandardPayouts(outcome, contract, bets)
|
||||
}
|
||||
}
|
||||
|
||||
export const getPayoutsMultiOutcome = (
|
||||
resolutions: { [outcome: string]: number },
|
||||
contract: FullContract<DPM, Multi | FreeResponse>,
|
||||
bets: Bet[]
|
||||
) => {
|
||||
const poolTotal = _.sum(Object.values(contract.pool))
|
||||
const winningBets = bets.filter((bet) => resolutions[bet.outcome])
|
||||
|
||||
const betsByOutcome = _.groupBy(winningBets, (bet) => bet.outcome)
|
||||
const sharesByOutcome = _.mapValues(betsByOutcome, (bets) =>
|
||||
_.sumBy(bets, (bet) => bet.shares)
|
||||
return getPayoutsMultiOutcome(
|
||||
outcome as {
|
||||
[outcome: string]: number
|
||||
},
|
||||
contract as FullContract<DPM, Multi | FreeResponse>,
|
||||
bets
|
||||
)
|
||||
|
||||
const probTotal = _.sum(Object.values(resolutions))
|
||||
|
||||
const payouts = winningBets.map(({ userId, outcome, amount, shares }) => {
|
||||
const prob = resolutions[outcome] / probTotal
|
||||
const winnings = (shares / sharesByOutcome[outcome]) * prob * poolTotal
|
||||
const profit = winnings - amount
|
||||
|
||||
const payout = amount + (1 - FEES) * Math.max(0, profit)
|
||||
return { userId, profit, payout }
|
||||
})
|
||||
|
||||
const profits = _.sumBy(payouts, (po) => po.profit)
|
||||
const creatorPayout = CREATOR_FEE * profits
|
||||
|
||||
console.log(
|
||||
'resolved',
|
||||
resolutions,
|
||||
'pool',
|
||||
poolTotal,
|
||||
'profits',
|
||||
profits,
|
||||
'creator fee',
|
||||
creatorPayout
|
||||
)
|
||||
|
||||
return payouts
|
||||
.map(({ userId, payout }) => ({ userId, payout }))
|
||||
.concat([{ userId: contract.creatorId, payout: creatorPayout }]) // add creator fee
|
||||
}
|
||||
}
|
||||
|
||||
export const getLoanPayouts = (bets: Bet[]) => {
|
||||
|
|
|
@ -7,11 +7,7 @@ import { User } from '../../common/user'
|
|||
import { Bet } from '../../common/bet'
|
||||
import { getUser, payUser } from './utils'
|
||||
import { sendMarketResolutionEmail } from './emails'
|
||||
import {
|
||||
getLoanPayouts,
|
||||
getPayouts,
|
||||
getPayoutsMultiOutcome,
|
||||
} from '../../common/payouts'
|
||||
import { getLoanPayouts, getPayouts } from '../../common/payouts'
|
||||
import { removeUndefinedProps } from '../../common/util/object'
|
||||
|
||||
export const resolveMarket = functions
|
||||
|
@ -98,10 +94,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 =
|
||||
outcomeType === 'FREE_RESPONSE' && resolutions
|
||||
? getPayoutsMultiOutcome(resolutions, contract as any, openBets)
|
||||
: getPayouts(outcome, contract, openBets, resolutionProbability)
|
||||
const payouts = getPayouts(
|
||||
resolutions ?? outcome,
|
||||
contract,
|
||||
openBets,
|
||||
resolutionProbability
|
||||
)
|
||||
|
||||
const loanPayouts = getLoanPayouts(openBets)
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user