liquidity provision tracking
This commit is contained in:
parent
9c5478d3d5
commit
845aefa6a8
|
@ -1,23 +1,13 @@
|
|||
import { Bet } from './bet'
|
||||
import { getDpmProbability } from './calculate-dpm'
|
||||
import { getCpmmProbability } from './calculate-cpmm'
|
||||
import { getCpmmLiquidity, getCpmmProbability } from './calculate-cpmm'
|
||||
import { Binary, CPMM, DPM, FreeResponse, FullContract } from './contract'
|
||||
import { User } from './user'
|
||||
import { LiquidityProvision } from './liquidity-provision'
|
||||
|
||||
export const PHANTOM_ANTE = 0.001
|
||||
export const MINIMUM_ANTE = 10
|
||||
|
||||
export const calcStartCpmmPool = (initialProbInt: number, ante: number) => {
|
||||
const p = initialProbInt / 100.0
|
||||
|
||||
const [poolYes, poolNo] =
|
||||
p >= 0.5 ? [ante * (1 / p - 1), ante] : [ante, ante * (1 / (1 - p) - 1)]
|
||||
|
||||
const k = poolYes * poolNo
|
||||
|
||||
return { poolYes, poolNo, k }
|
||||
}
|
||||
|
||||
export function getCpmmAnteBet(
|
||||
creator: User,
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
|
@ -45,20 +35,27 @@ export function getCpmmAnteBet(
|
|||
return bet
|
||||
}
|
||||
|
||||
export const calcStartPool = (initialProbInt: number, ante = 0) => {
|
||||
const p = initialProbInt / 100.0
|
||||
const totalAnte = PHANTOM_ANTE + ante
|
||||
export function getCpmmInitialLiquidity(
|
||||
creator: User,
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
anteId: string,
|
||||
amount: number
|
||||
) {
|
||||
const { createdTime, pool } = contract
|
||||
const liquidity = getCpmmLiquidity(pool)
|
||||
|
||||
const sharesYes = Math.sqrt(p * totalAnte ** 2)
|
||||
const sharesNo = Math.sqrt(totalAnte ** 2 - sharesYes ** 2)
|
||||
const lp: LiquidityProvision = {
|
||||
id: anteId,
|
||||
userId: creator.id,
|
||||
contractId: contract.id,
|
||||
createdTime,
|
||||
isAnte: true,
|
||||
|
||||
const poolYes = p * ante
|
||||
const poolNo = (1 - p) * ante
|
||||
amount: amount,
|
||||
liquidity,
|
||||
}
|
||||
|
||||
const phantomYes = Math.sqrt(p) * PHANTOM_ANTE
|
||||
const phantomNo = Math.sqrt(1 - p) * PHANTOM_ANTE
|
||||
|
||||
return { sharesYes, sharesNo, poolYes, poolNo, phantomYes, phantomNo }
|
||||
return lp
|
||||
}
|
||||
|
||||
export function getAnteBets(
|
||||
|
|
|
@ -19,6 +19,7 @@ export type Bet = {
|
|||
|
||||
isSold?: boolean // true if this BUY bet has been sold
|
||||
isAnte?: boolean
|
||||
isLiquidityProvision?: boolean
|
||||
|
||||
createdTime: number
|
||||
}
|
||||
|
|
|
@ -24,11 +24,11 @@ export function calculateCpmmShares(
|
|||
pool: {
|
||||
[outcome: string]: number
|
||||
},
|
||||
k: number,
|
||||
bet: number,
|
||||
betChoice: string
|
||||
) {
|
||||
const { YES: y, NO: n } = pool
|
||||
const k = y * n
|
||||
const numerator = bet ** 2 + bet * (y + n) - k + y * n
|
||||
const denominator = betChoice === 'YES' ? bet + n : bet + y
|
||||
const shares = numerator / denominator
|
||||
|
@ -40,9 +40,9 @@ export function calculateCpmmPurchase(
|
|||
bet: number,
|
||||
outcome: string
|
||||
) {
|
||||
const { pool, k } = contract
|
||||
const { pool } = contract
|
||||
|
||||
const shares = calculateCpmmShares(pool, k, bet, outcome)
|
||||
const shares = calculateCpmmShares(pool, bet, outcome)
|
||||
const { YES: y, NO: n } = pool
|
||||
|
||||
const [newY, newN] =
|
||||
|
@ -60,11 +60,11 @@ export function calculateCpmmShareValue(
|
|||
shares: number,
|
||||
outcome: string
|
||||
) {
|
||||
const { pool, k } = contract
|
||||
const { pool } = contract
|
||||
const { YES: y, NO: n } = pool
|
||||
|
||||
const poolChange = outcome === 'YES' ? shares + y - n : shares + n - y
|
||||
|
||||
const k = y * n
|
||||
const shareValue = 0.5 * (shares + y + n - Math.sqrt(4 * k + poolChange ** 2))
|
||||
return shareValue
|
||||
}
|
||||
|
@ -101,3 +101,61 @@ export function getCpmmProbabilityAfterSale(
|
|||
const { newPool } = calculateCpmmSale(contract, bet)
|
||||
return getCpmmProbability(newPool)
|
||||
}
|
||||
|
||||
export const calcCpmmInitialPool = (initialProbInt: number, ante: number) => {
|
||||
const p = initialProbInt / 100.0
|
||||
|
||||
const [poolYes, poolNo] =
|
||||
p >= 0.5 ? [ante * (1 / p - 1), ante] : [ante, ante * (1 / (1 - p) - 1)]
|
||||
|
||||
return { poolYes, poolNo }
|
||||
}
|
||||
|
||||
export function getCpmmLiquidity(pool: { [outcome: string]: number }) {
|
||||
// For binary contracts only.
|
||||
const { YES, NO } = pool
|
||||
return Math.sqrt(YES * NO)
|
||||
}
|
||||
|
||||
export function addCpmmLiquidity(
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
amount: number
|
||||
) {
|
||||
const { YES, NO } = contract.pool
|
||||
const p = getCpmmProbability({ YES, NO })
|
||||
|
||||
const [newYes, newNo] =
|
||||
p >= 0.5
|
||||
? [amount * (1 / p - 1), amount]
|
||||
: [amount, amount * (1 / (1 - p) - 1)]
|
||||
|
||||
const betAmount = Math.abs(newYes - newNo)
|
||||
const betOutcome = p >= 0.5 ? 'YES' : 'NO'
|
||||
|
||||
const poolLiquidity = getCpmmLiquidity({ YES, NO })
|
||||
const newPool = { YES: YES + newYes, NO: NO + newNo }
|
||||
const resultingLiquidity = getCpmmLiquidity(newPool)
|
||||
const liquidity = resultingLiquidity - poolLiquidity
|
||||
|
||||
return { newPool, liquidity, betAmount, betOutcome }
|
||||
}
|
||||
|
||||
export function removeCpmmLiquidity(
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
liquidity: number
|
||||
) {
|
||||
const { YES, NO } = contract.pool
|
||||
const poolLiquidity = getCpmmLiquidity({ YES, NO })
|
||||
const p = getCpmmProbability({ YES, NO })
|
||||
|
||||
const f = liquidity / poolLiquidity
|
||||
const [payoutYes, payoutNo] = [f * YES, f * NO]
|
||||
|
||||
const betAmount = Math.abs(payoutYes - payoutNo)
|
||||
const betOutcome = p >= 0.5 ? 'NO' : 'YES' // opposite side as adding liquidity
|
||||
const payout = Math.min(payoutYes, payoutNo)
|
||||
|
||||
const newPool = { YES: YES - payoutYes, NO: NO - payoutNo }
|
||||
|
||||
return { newPool, payout, betAmount, betOutcome }
|
||||
}
|
||||
|
|
|
@ -271,3 +271,23 @@ export const deductDpmFees = (betAmount: number, winnings: number) => {
|
|||
? betAmount + (1 - FEES) * (winnings - betAmount)
|
||||
: winnings
|
||||
}
|
||||
|
||||
export const calcDpmInitialPool = (
|
||||
initialProbInt: number,
|
||||
ante: number,
|
||||
phantomAnte: number
|
||||
) => {
|
||||
const p = initialProbInt / 100.0
|
||||
const totalAnte = phantomAnte + ante
|
||||
|
||||
const sharesYes = Math.sqrt(p * totalAnte ** 2)
|
||||
const sharesNo = Math.sqrt(totalAnte ** 2 - sharesYes ** 2)
|
||||
|
||||
const poolYes = p * ante
|
||||
const poolNo = (1 - p) * ante
|
||||
|
||||
const phantomYes = Math.sqrt(p) * phantomAnte
|
||||
const phantomNo = Math.sqrt(1 - p) * phantomAnte
|
||||
|
||||
return { sharesYes, sharesNo, poolYes, poolNo, phantomYes, phantomNo }
|
||||
}
|
||||
|
|
|
@ -28,11 +28,12 @@ export function getProbability(contract: FullContract<DPM | CPMM, Binary>) {
|
|||
: getDpmProbability(contract.totalShares)
|
||||
}
|
||||
|
||||
// TODO: Deprecate this function
|
||||
export function getInitialProbability(
|
||||
contract: FullContract<DPM | CPMM, Binary>
|
||||
) {
|
||||
return contract.mechanism === 'cpmm-1'
|
||||
? getCpmmProbability(contract.liquidity[contract.creatorId])
|
||||
? getCpmmProbability(contract.pool)
|
||||
: getDpmProbability(contract.phantomShares ?? contract.totalShares)
|
||||
}
|
||||
|
||||
|
@ -62,7 +63,7 @@ export function calculateShares(
|
|||
betChoice: string
|
||||
) {
|
||||
return contract.mechanism === 'cpmm-1'
|
||||
? calculateCpmmShares(contract.pool, contract.k, bet, betChoice)
|
||||
? calculateCpmmShares(contract.pool, bet, betChoice)
|
||||
: calculateDpmShares(contract.totalShares, bet, betChoice)
|
||||
}
|
||||
|
||||
|
|
|
@ -46,10 +46,7 @@ export type DPM = {
|
|||
|
||||
export type CPMM = {
|
||||
mechanism: 'cpmm-1'
|
||||
|
||||
pool: { [outcome: string]: number }
|
||||
k: number // liquidity constant
|
||||
liquidity: { [userId: string]: { [outcome: string]: number } } // track liquidity providers
|
||||
}
|
||||
|
||||
export type FixedPayouts = CPMM
|
||||
|
|
11
common/liquidity-provision.ts
Normal file
11
common/liquidity-provision.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
export type LiquidityProvision = {
|
||||
id: string
|
||||
userId: string
|
||||
contractId: string
|
||||
|
||||
createdTime: number
|
||||
isAnte?: boolean
|
||||
|
||||
amount: number // M$ quantity
|
||||
liquidity: number // sqrt(k)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { calcStartPool, calcStartCpmmPool } from './antes'
|
||||
import { PHANTOM_ANTE } from './antes'
|
||||
import {
|
||||
Binary,
|
||||
Contract,
|
||||
|
@ -10,6 +10,8 @@ import {
|
|||
import { User } from './user'
|
||||
import { parseTags } from './util/parse'
|
||||
import { removeUndefinedProps } from './util/object'
|
||||
import { calcCpmmInitialPool } from './calculate-cpmm'
|
||||
import { calcDpmInitialPool } from './calculate-dpm'
|
||||
|
||||
export function getNewContract(
|
||||
id: string,
|
||||
|
@ -62,7 +64,7 @@ export function getNewContract(
|
|||
|
||||
const getBinaryDpmProps = (initialProb: number, ante: number) => {
|
||||
const { sharesYes, sharesNo, poolYes, poolNo, phantomYes, phantomNo } =
|
||||
calcStartPool(initialProb, ante)
|
||||
calcDpmInitialPool(initialProb, ante, PHANTOM_ANTE)
|
||||
|
||||
const system: DPM & Binary = {
|
||||
mechanism: 'dpm-2',
|
||||
|
@ -81,15 +83,13 @@ const getBinaryCpmmProps = (
|
|||
ante: number,
|
||||
userId: string
|
||||
) => {
|
||||
const { poolYes, poolNo, k } = calcStartCpmmPool(initialProb, ante)
|
||||
const { poolYes, poolNo } = calcCpmmInitialPool(initialProb, ante)
|
||||
const pool = { YES: poolYes, NO: poolNo }
|
||||
|
||||
const system: CPMM & Binary = {
|
||||
mechanism: 'cpmm-1',
|
||||
outcomeType: 'BINARY',
|
||||
pool: pool,
|
||||
k,
|
||||
liquidity: { [userId]: pool },
|
||||
}
|
||||
|
||||
return system
|
||||
|
|
|
@ -5,18 +5,32 @@ import { getProbability } from './calculate'
|
|||
import { deductFixedFees } from './calculate-fixed-payouts'
|
||||
import { Binary, CPMM, FixedPayouts, FullContract } from './contract'
|
||||
import { CREATOR_FEE } from './fees'
|
||||
import { LiquidityProvision } from './liquidity-provision'
|
||||
|
||||
export const getFixedCancelPayouts = (bets: Bet[]) => {
|
||||
return bets.map((bet) => ({
|
||||
userId: bet.userId,
|
||||
payout: bet.amount,
|
||||
export const getFixedCancelPayouts = (
|
||||
contract: FullContract<FixedPayouts, Binary>,
|
||||
bets: Bet[],
|
||||
liquidities: LiquidityProvision[]
|
||||
) => {
|
||||
const liquidityPayouts = liquidities.map((lp) => ({
|
||||
userId: lp.userId,
|
||||
payout: lp.amount,
|
||||
}))
|
||||
|
||||
return bets
|
||||
.filter((b) => !b.isAnte && !b.isLiquidityProvision)
|
||||
.map((bet) => ({
|
||||
userId: bet.userId,
|
||||
payout: bet.amount,
|
||||
}))
|
||||
.concat(liquidityPayouts)
|
||||
}
|
||||
|
||||
export const getStandardFixedPayouts = (
|
||||
outcome: string,
|
||||
contract: FullContract<FixedPayouts, Binary>,
|
||||
bets: Bet[]
|
||||
bets: Bet[],
|
||||
liquidities: LiquidityProvision[]
|
||||
) => {
|
||||
const winningBets = bets.filter((bet) => bet.outcome === outcome)
|
||||
|
||||
|
@ -44,20 +58,29 @@ export const getStandardFixedPayouts = (
|
|||
return payouts
|
||||
.map(({ userId, payout }) => ({ userId, payout }))
|
||||
.concat([{ userId: contract.creatorId, payout: creatorPayout }]) // add creator fee
|
||||
.concat(getLiquidityPoolPayouts(contract, outcome))
|
||||
.concat(getLiquidityPoolPayouts(contract, outcome, liquidities))
|
||||
}
|
||||
|
||||
export const getLiquidityPoolPayouts = (
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
outcome: string
|
||||
outcome: string,
|
||||
liquidities: LiquidityProvision[]
|
||||
) => {
|
||||
const { creatorId, pool } = contract
|
||||
return [{ userId: creatorId, payout: pool[outcome] }]
|
||||
const providedLiquidity = _.sumBy(liquidities, (lp) => lp.liquidity)
|
||||
|
||||
const { pool } = contract
|
||||
const finalPool = pool[outcome]
|
||||
|
||||
return liquidities.map((lp) => ({
|
||||
userId: lp.userId,
|
||||
payout: (lp.liquidity / providedLiquidity) * finalPool,
|
||||
}))
|
||||
}
|
||||
|
||||
export const getMktFixedPayouts = (
|
||||
contract: FullContract<FixedPayouts, Binary>,
|
||||
bets: Bet[],
|
||||
liquidities: LiquidityProvision[],
|
||||
resolutionProbability?: number
|
||||
) => {
|
||||
const p =
|
||||
|
@ -90,14 +113,21 @@ export const getMktFixedPayouts = (
|
|||
return payouts
|
||||
.map(({ userId, payout }) => ({ userId, payout }))
|
||||
.concat([{ userId: contract.creatorId, payout: creatorPayout }]) // add creator fee
|
||||
.concat(getLiquidityPoolProbPayouts(contract, p))
|
||||
.concat(getLiquidityPoolProbPayouts(contract, p, liquidities))
|
||||
}
|
||||
|
||||
export const getLiquidityPoolProbPayouts = (
|
||||
contract: FullContract<CPMM, Binary>,
|
||||
p: number
|
||||
p: number,
|
||||
liquidities: LiquidityProvision[]
|
||||
) => {
|
||||
const { creatorId, pool } = contract
|
||||
const payout = p * pool.YES + (1 - p) * pool.NO
|
||||
return [{ userId: creatorId, payout }]
|
||||
const providedLiquidity = _.sumBy(liquidities, (lp) => lp.liquidity)
|
||||
|
||||
const { pool } = contract
|
||||
const finalPool = p * pool.YES + (1 - p) * pool.NO
|
||||
|
||||
return liquidities.map((lp) => ({
|
||||
userId: lp.userId,
|
||||
payout: (lp.liquidity / providedLiquidity) * finalPool,
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import * as _ from 'lodash'
|
|||
|
||||
import { Bet } from './bet'
|
||||
import { Contract, DPM, FreeResponse, FullContract, Multi } from './contract'
|
||||
import { LiquidityProvision } from './liquidity-provision'
|
||||
import {
|
||||
getDpmCancelPayouts,
|
||||
getDpmMktPayouts,
|
||||
|
@ -22,17 +23,23 @@ export const getPayouts = (
|
|||
},
|
||||
contract: Contract,
|
||||
bets: Bet[],
|
||||
liquidities: LiquidityProvision[],
|
||||
resolutionProbability?: number
|
||||
) => {
|
||||
if (contract.mechanism === 'cpmm-1' && contract.outcomeType === 'BINARY') {
|
||||
switch (outcome) {
|
||||
case 'YES':
|
||||
case 'NO':
|
||||
return getStandardFixedPayouts(outcome, contract, bets)
|
||||
return getStandardFixedPayouts(outcome, contract, bets, liquidities)
|
||||
case 'MKT':
|
||||
return getMktFixedPayouts(contract, bets, resolutionProbability)
|
||||
return getMktFixedPayouts(
|
||||
contract,
|
||||
bets,
|
||||
liquidities,
|
||||
resolutionProbability
|
||||
)
|
||||
case 'CANCEL':
|
||||
return getFixedCancelPayouts(bets)
|
||||
return getFixedCancelPayouts(contract, bets, liquidities)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as _ from 'lodash'
|
||||
|
||||
import { Bet } from './bet'
|
||||
import { Binary, Contract, FullContract } from './contract'
|
||||
import { getPayouts } from './payouts'
|
||||
|
@ -37,6 +38,7 @@ export function scoreUsersByContract(
|
|||
resolution ?? 'MKT',
|
||||
contract,
|
||||
openBets,
|
||||
[],
|
||||
resolutionProbability
|
||||
)
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import { getNewContract } from '../../common/new-contract'
|
|||
import {
|
||||
getAnteBets,
|
||||
getCpmmAnteBet,
|
||||
getCpmmInitialLiquidity,
|
||||
getFreeAnswerAnte,
|
||||
MINIMUM_ANTE,
|
||||
} from '../../common/antes'
|
||||
|
@ -127,7 +128,7 @@ export const createContract = functions
|
|||
|
||||
const outcome = y > n ? 'NO' : 'YES' // more in YES pool if prob leans NO
|
||||
|
||||
const bet = await getCpmmAnteBet(
|
||||
const bet = getCpmmAnteBet(
|
||||
creator,
|
||||
contract as FullContract<CPMM, Binary>,
|
||||
betDoc.id,
|
||||
|
@ -136,6 +137,19 @@ export const createContract = functions
|
|||
)
|
||||
|
||||
await betDoc.set(bet)
|
||||
|
||||
const liquidityDoc = firestore
|
||||
.collection(`contracts/${contract.id}/liquidity`)
|
||||
.doc()
|
||||
|
||||
const lp = getCpmmInitialLiquidity(
|
||||
creator,
|
||||
contract as FullContract<CPMM, Binary>,
|
||||
liquidityDoc.id,
|
||||
ante
|
||||
)
|
||||
|
||||
await liquidityDoc.set(lp)
|
||||
}
|
||||
} else if (outcomeType === 'FREE_RESPONSE') {
|
||||
const noneAnswerDoc = firestore
|
||||
|
|
|
@ -9,6 +9,7 @@ import { getUser, payUser } from './utils'
|
|||
import { sendMarketResolutionEmail } from './emails'
|
||||
import { getLoanPayouts, getPayouts } from '../../common/payouts'
|
||||
import { removeUndefinedProps } from '../../common/util/object'
|
||||
import { LiquidityProvision } from '../../common/liquidity-provision'
|
||||
|
||||
export const resolveMarket = functions
|
||||
.runWith({ minInstances: 1 })
|
||||
|
@ -94,10 +95,19 @@ export const resolveMarket = functions
|
|||
const bets = betsSnap.docs.map((doc) => doc.data() as Bet)
|
||||
const openBets = bets.filter((b) => !b.isSold && !b.sale)
|
||||
|
||||
const liquiditiesSnap = await firestore
|
||||
.collection(`contracts/${contractId}/liquidity`)
|
||||
.get()
|
||||
|
||||
const liquidities = liquiditiesSnap.docs.map(
|
||||
(doc) => doc.data() as LiquidityProvision
|
||||
)
|
||||
|
||||
const payouts = getPayouts(
|
||||
resolutions ?? outcome,
|
||||
contract,
|
||||
openBets,
|
||||
liquidities,
|
||||
resolutionProbability
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user