create numeric contracts
This commit is contained in:
parent
1e3a7747ee
commit
51866c8612
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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[],
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user