create numeric contracts

This commit is contained in:
mantikoros 2022-05-06 14:42:59 -04:00
parent 1e3a7747ee
commit 51866c8612
6 changed files with 111 additions and 8 deletions

View File

@ -9,7 +9,7 @@ export type Answer = {
userId: string userId: string
username: string username: string
name: string name: string
avatarUrl: string avatarUrl?: string
text: string text: string
} }

View File

@ -1,9 +1,17 @@
import { Bet } from './bet' import { Bet } from './bet'
import { getDpmProbability } from './calculate-dpm' import { getDpmProbability } from './calculate-dpm'
import { Binary, CPMM, DPM, FreeResponse, FullContract } from './contract' import {
Binary,
CPMM,
DPM,
FreeResponse,
FullContract,
Numeric,
} from './contract'
import { User } from './user' import { User } from './user'
import { LiquidityProvision } from './liquidity-provision' import { LiquidityProvision } from './liquidity-provision'
import { noFees } from './fees' import { noFees } from './fees'
import * as _ from 'lodash'
export const FIXED_ANTE = 100 export const FIXED_ANTE = 100
@ -106,3 +114,31 @@ export function getFreeAnswerAnte(
return anteBet return anteBet
} }
export function getNumericAntes(
creator: User,
contract: FullContract<DPM, Numeric>,
ante: number
) {
const { bucketCount, createdTime } = contract
const betAnte = ante / bucketCount
const betShares = Math.sqrt(ante ** 2 / bucketCount)
return _.range(0, bucketCount).map((i) => {
const anteBet: Omit<Bet, 'id'> = {
userId: creator.id,
contractId: contract.id,
amount: betAnte,
shares: betShares,
outcome: i.toString(),
probBefore: 0,
probAfter: 1 / bucketCount,
createdTime,
isAnte: true,
fees: noFees,
}
return anteBet
})
}

View File

@ -1,4 +1,4 @@
import _ from 'lodash' import * as _ from 'lodash'
import { Answer } from './answer' import { Answer } from './answer'
import { Fees } from './fees' import { Fees } from './fees'
@ -12,7 +12,7 @@ export type FullContract<
creatorId: string creatorId: string
creatorName: string creatorName: string
creatorUsername: string creatorUsername: string
creatorAvatarUrl: string creatorAvatarUrl?: string
question: string question: string
description: string // More info about what the contract is about description: string // More info about what the contract is about

View File

@ -1,3 +1,5 @@
import * as _ from 'lodash'
import { PHANTOM_ANTE } from './antes' import { PHANTOM_ANTE } from './antes'
import { import {
Binary, Binary,
@ -5,6 +7,7 @@ import {
CPMM, CPMM,
DPM, DPM,
FreeResponse, FreeResponse,
Numeric,
outcomeType, outcomeType,
} from './contract' } from './contract'
import { User } from './user' import { User } from './user'
@ -22,7 +25,12 @@ export function getNewContract(
initialProb: number, initialProb: number,
ante: number, ante: number,
closeTime: number, closeTime: number,
extraTags: string[] extraTags: string[],
// used for numeric markets
bucketCount: number,
min: number,
max: number
) { ) {
const tags = parseTags( const tags = parseTags(
`${question} ${description} ${extraTags.map((tag) => `#${tag}`).join(' ')}` `${question} ${description} ${extraTags.map((tag) => `#${tag}`).join(' ')}`
@ -32,6 +40,8 @@ export function getNewContract(
const propsByOutcomeType = const propsByOutcomeType =
outcomeType === 'BINARY' outcomeType === 'BINARY'
? getBinaryCpmmProps(initialProb, ante) // getBinaryDpmProps(initialProb, ante) ? getBinaryCpmmProps(initialProb, ante) // getBinaryDpmProps(initialProb, ante)
: outcomeType === 'NUMERIC'
? getNumericProps(ante, bucketCount, min, max)
: getFreeAnswerProps(ante) : getFreeAnswerProps(ante)
const volume = outcomeType === 'BINARY' ? 0 : ante const volume = outcomeType === 'BINARY' ? 0 : ante
@ -115,6 +125,37 @@ const getFreeAnswerProps = (ante: number) => {
return system return system
} }
const getNumericProps = (
ante: number,
bucketCount: number,
min: number,
max: number
) => {
const buckets = _.range(0, bucketCount).map((i) => i.toString())
const betAnte = ante / bucketCount
const pool = Object.fromEntries(buckets.map((answer) => [answer, betAnte]))
const totalBets = pool
const betShares = Math.sqrt(ante ** 2 / bucketCount)
const totalShares = Object.fromEntries(
buckets.map((answer) => [answer, betShares])
)
const system: DPM & Numeric = {
mechanism: 'dpm-2',
outcomeType: 'NUMERIC',
pool,
totalBets,
totalShares,
bucketCount,
min,
max,
}
return system
}
const getMultiProps = ( const getMultiProps = (
outcomes: string[], outcomes: string[],
initialProbs: number[], initialProbs: number[],

View File

@ -4,7 +4,7 @@ export type User = {
name: string name: string
username: string username: string
avatarUrl: string avatarUrl?: string
// For their user page // For their user page
bio?: string bio?: string

View File

@ -12,6 +12,7 @@ import {
MAX_DESCRIPTION_LENGTH, MAX_DESCRIPTION_LENGTH,
MAX_QUESTION_LENGTH, MAX_QUESTION_LENGTH,
MAX_TAG_LENGTH, MAX_TAG_LENGTH,
Numeric,
outcomeType, outcomeType,
} from '../../common/contract' } from '../../common/contract'
import { slugify } from '../../common/util/slugify' import { slugify } from '../../common/util/slugify'
@ -22,6 +23,7 @@ import {
getAnteBets, getAnteBets,
getCpmmInitialLiquidity, getCpmmInitialLiquidity,
getFreeAnswerAnte, getFreeAnswerAnte,
getNumericAntes,
HOUSE_LIQUIDITY_PROVIDER_ID, HOUSE_LIQUIDITY_PROVIDER_ID,
MINIMUM_ANTE, MINIMUM_ANTE,
} from '../../common/antes' } from '../../common/antes'
@ -63,7 +65,9 @@ export const createContract = functions
tags = tags?.map((tag) => tag.toString().slice(0, MAX_TAG_LENGTH)) tags = tags?.map((tag) => tag.toString().slice(0, MAX_TAG_LENGTH))
let outcomeType = data.outcomeType ?? 'BINARY' let outcomeType = data.outcomeType ?? 'BINARY'
if (!['BINARY', 'MULTI', 'FREE_RESPONSE'].includes(outcomeType)) if (
!['BINARY', 'MULTI', 'FREE_RESPONSE', 'NUMERIC'].includes(outcomeType)
)
return { status: 'error', message: 'Invalid outcomeType' } return { status: 'error', message: 'Invalid outcomeType' }
if ( if (
@ -115,7 +119,10 @@ export const createContract = functions
initialProb, initialProb,
ante, ante,
closeTime, closeTime,
tags ?? [] tags ?? [],
1000,
1,
1000
) )
if (!isFree && ante) await chargeUser(creator.id, ante) if (!isFree && ante) await chargeUser(creator.id, ante)
@ -174,6 +181,25 @@ export const createContract = functions
anteBetDoc.id anteBetDoc.id
) )
await anteBetDoc.set(anteBet) await anteBetDoc.set(anteBet)
} else if (outcomeType === 'NUMERIC') {
const antes = getNumericAntes(
creator,
contract as FullContract<DPM, Numeric>,
ante
)
await firestore.runTransaction(async (transaction) => {
for (let anteBet of antes) {
const anteBetDoc = firestore
.collection(`contracts/${contract.id}/bets`)
.doc()
await transaction.set(anteBetDoc, {
id: anteBetDoc.id,
...anteBet,
})
}
})
} }
} }