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