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 }
}
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>,
shares: number,
outcome: string
outcome: 'YES' | 'NO'
) {
const { pool } = contract
const { YES: y, NO: n } = pool
const { pool, p } = contract
// TODO: calculate using new function
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
const k = computeK(pool.YES, pool.NO, p)
// Find bet amount that preserves k after selling shares.
let lowAmount = 0
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(
@ -135,7 +163,11 @@ export function calculateCpmmSale(
) {
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(
contract,
@ -153,9 +185,11 @@ export function calculateCpmmSale(
? [y + shares - saleValue + fee, n - 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(

View File

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

View File

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