Compute sale value of shares with binary search to keep k constant.

This commit is contained in:
James Grugett 2022-03-28 21:39:40 -05:00
parent a0ed63070f
commit 43b404e7ed
3 changed files with 58 additions and 14 deletions

View File

@ -114,19 +114,47 @@ export function calculateCpmmPurchase(
return { shares, newPool, newP, fees } return { shares, newPool, newP, fees }
} }
export function calculateCpmmShareValue( function computeK(y: number, n: number, p: number) {
return y ** p * n ** (1 - p)
}
function sellSharesK(
y: number,
n: number,
p: number,
s: number,
outcome: 'YES' | 'NO',
b: number
) {
return outcome === 'YES'
? computeK(y - b + s, n - b, p)
: computeK(y - b, n - b + s, p)
}
function calculateCpmmShareValue(
contract: FullContract<CPMM, Binary>, contract: FullContract<CPMM, Binary>,
shares: number, shares: number,
outcome: string outcome: 'YES' | 'NO'
) { ) {
const { pool } = contract const { pool, p } = contract
const { YES: y, NO: n } = pool
// TODO: calculate using new function const k = computeK(pool.YES, pool.NO, p)
const poolChange = outcome === 'YES' ? shares + y - n : shares + n - y
const k = y * n // Find bet amount that preserves k after selling shares.
const shareValue = 0.5 * (shares + y + n - Math.sqrt(4 * k + poolChange ** 2)) let lowAmount = 0
return shareValue let highAmount = shares
let mid = 0
let kGuess = 0
while (Math.abs(k - kGuess) > 0.00000000001) {
mid = lowAmount + (highAmount - lowAmount) / 2
kGuess = sellSharesK(pool.YES, pool.NO, p, shares, outcome, mid)
if (kGuess < k) {
highAmount = mid
} else {
lowAmount = mid
}
}
return mid
} }
export function calculateCpmmSale( export function calculateCpmmSale(
@ -135,7 +163,11 @@ export function calculateCpmmSale(
) { ) {
const { shares, outcome } = bet const { shares, outcome } = bet
const rawSaleValue = calculateCpmmShareValue(contract, shares, outcome) const rawSaleValue = calculateCpmmShareValue(
contract,
Math.abs(shares),
outcome as 'YES' | 'NO'
)
const { fees, remainingBet: saleValue } = getCpmmLiquidityFee( const { fees, remainingBet: saleValue } = getCpmmLiquidityFee(
contract, contract,
@ -153,9 +185,11 @@ export function calculateCpmmSale(
? [y + shares - saleValue + fee, n - saleValue + fee] ? [y + shares - saleValue + fee, n - saleValue + fee]
: [y - saleValue + fee, n + shares - saleValue + fee] : [y - saleValue + fee, n + shares - saleValue + fee]
const newPool = { YES: newY, NO: newN } const postBetPool = { YES: newY, NO: newN }
return { saleValue, newPool, fees } const { newPool, newP } = addCpmmLiquidity(postBetPool, contract.p, fee)
return { saleValue, newPool, newP, fees }
} }
export function getCpmmProbabilityAfterSale( export function getCpmmProbabilityAfterSale(

View File

@ -92,7 +92,7 @@ export const getCpmmSellBetInfo = (
) => { ) => {
const { pool, p } = contract const { pool, p } = contract
const { saleValue, newPool, fees } = calculateCpmmSale(contract, { const { saleValue, newPool, newP, fees } = calculateCpmmSale(contract, {
shares, shares,
outcome, outcome,
}) })
@ -128,6 +128,7 @@ export const getCpmmSellBetInfo = (
return { return {
newBet, newBet,
newPool, newPool,
newP,
newBalance, newBalance,
fees, fees,
} }

View File

@ -48,7 +48,7 @@ export const sellShares = functions.runWith({ minInstances: 1 }).https.onCall(
.collection(`contracts/${contractId}/bets`) .collection(`contracts/${contractId}/bets`)
.doc() .doc()
const { newBet, newPool, newBalance, fees } = getCpmmSellBetInfo( const { newBet, newPool, newP, newBalance, fees } = getCpmmSellBetInfo(
user, user,
shares, shares,
outcome, outcome,
@ -56,15 +56,24 @@ export const sellShares = functions.runWith({ minInstances: 1 }).https.onCall(
newBetDoc.id newBetDoc.id
) )
if (!isFinite(newP)) {
return {
status: 'error',
message: 'Trade rejected due to overflow error.',
}
}
if (!isFinite(newBalance)) { if (!isFinite(newBalance)) {
throw new Error('Invalid user balance for ' + user.username) throw new Error('Invalid user balance for ' + user.username)
} }
transaction.update(userDoc, { balance: newBalance }) transaction.update(userDoc, { balance: newBalance })
transaction.create(newBetDoc, newBet) transaction.create(newBetDoc, newBet)
transaction.update( transaction.update(
contractDoc, contractDoc,
removeUndefinedProps({ removeUndefinedProps({
pool: newPool, pool: newPool,
p: newP,
collectedFees: addObjects(fees ?? {}, collectedFees ?? {}), collectedFees: addObjects(fees ?? {}, collectedFees ?? {}),
volume: volume + Math.abs(newBet.amount), volume: volume + Math.abs(newBet.amount),
}) })