Merge branch 'main' into swap3
This commit is contained in:
commit
2cf672fac1
4
.github/workflows/check.yml
vendored
4
.github/workflows/check.yml
vendored
|
@ -48,8 +48,8 @@ jobs:
|
|||
- name: Run Typescript checker on web client
|
||||
if: ${{ success() || failure() }}
|
||||
working-directory: web
|
||||
run: tsc --pretty --project tsconfig.json --noEmit
|
||||
run: tsc -b -v --pretty
|
||||
- name: Run Typescript checker on cloud functions
|
||||
if: ${{ success() || failure() }}
|
||||
working-directory: functions
|
||||
run: tsc --pretty --project tsconfig.json --noEmit
|
||||
run: tsc -b -v --pretty
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -3,3 +3,5 @@
|
|||
.vercel
|
||||
node_modules
|
||||
yarn-error.log
|
||||
|
||||
firebase-debug.log
|
14
.vscode/extensions.json
vendored
Normal file
14
.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
||||
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
||||
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": [
|
||||
"esbenp.prettier-vscode",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"toba.vsfire",
|
||||
"bradlc.vscode-tailwindcss"
|
||||
],
|
||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||
"unwantedRecommendations": []
|
||||
}
|
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"javascript.preferences.importModuleSpecifier": "shortest",
|
||||
"typescript.preferences.importModuleSpecifier": "shortest",
|
||||
"files.eol": "\r\n",
|
||||
"files.eol": "\n",
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
"**/package-lock.json": true,
|
||||
|
|
|
@ -17,6 +17,14 @@ module.exports = {
|
|||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'warn',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
caughtErrorsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
5
common/.gitignore
vendored
5
common/.gitignore
vendored
|
@ -1,6 +1,5 @@
|
|||
# Compiled JavaScript files
|
||||
lib/**/*.js
|
||||
lib/**/*.js.map
|
||||
lib/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
@ -10,4 +9,4 @@ node_modules/
|
|||
|
||||
package-lock.json
|
||||
ui-debug.log
|
||||
firebase-debug.log
|
||||
firebase-debug.log
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { sum, groupBy, mapValues, sumBy } from 'lodash'
|
||||
import { sum, groupBy, mapValues, sumBy, partition } from 'lodash'
|
||||
|
||||
import { CPMMContract } from './contract'
|
||||
import { CREATOR_FEE, Fees, LIQUIDITY_FEE, noFees, PLATFORM_FEE } from './fees'
|
||||
|
@ -260,27 +260,30 @@ export function addCpmmLiquidity(
|
|||
return { newPool, liquidity, newP }
|
||||
}
|
||||
|
||||
const calculateLiquidityDelta = (p: number) => (l: LiquidityProvision) => {
|
||||
const oldLiquidity = getCpmmLiquidity(l.pool, p)
|
||||
|
||||
const newPool = addObjects(l.pool, { YES: l.amount, NO: l.amount })
|
||||
const newLiquidity = getCpmmLiquidity(newPool, p)
|
||||
|
||||
const liquidity = newLiquidity - oldLiquidity
|
||||
return liquidity
|
||||
}
|
||||
|
||||
export function getCpmmLiquidityPoolWeights(
|
||||
contract: CPMMContract,
|
||||
liquidities: LiquidityProvision[]
|
||||
) {
|
||||
const { p } = contract
|
||||
const [antes, nonAntes] = partition(liquidities, (l) => !!l.isAnte)
|
||||
|
||||
const liquidityShares = liquidities.map((l) => {
|
||||
const oldLiquidity = getCpmmLiquidity(l.pool, p)
|
||||
const calcLiqudity = calculateLiquidityDelta(contract.p)
|
||||
const liquidityShares = nonAntes.map(calcLiqudity)
|
||||
|
||||
const newPool = addObjects(l.pool, { YES: l.amount, NO: l.amount })
|
||||
const newLiquidity = getCpmmLiquidity(newPool, p)
|
||||
|
||||
const liquidity = newLiquidity - oldLiquidity
|
||||
return liquidity
|
||||
})
|
||||
|
||||
const shareSum = sum(liquidityShares)
|
||||
const shareSum = sum(liquidityShares) + sum(antes.map(calcLiqudity))
|
||||
|
||||
const weights = liquidityShares.map((s, i) => ({
|
||||
weight: s / shareSum,
|
||||
providerId: liquidities[i].userId,
|
||||
providerId: nonAntes[i].userId,
|
||||
}))
|
||||
|
||||
const userWeights = groupBy(weights, (w) => w.providerId)
|
||||
|
@ -290,22 +293,13 @@ export function getCpmmLiquidityPoolWeights(
|
|||
return totalUserWeights
|
||||
}
|
||||
|
||||
// export function removeCpmmLiquidity(
|
||||
// contract: CPMMContract,
|
||||
// liquidity: number
|
||||
// ) {
|
||||
// const { YES, NO } = contract.pool
|
||||
// const poolLiquidity = getCpmmLiquidity({ YES, NO })
|
||||
// const p = getCpmmProbability({ YES, NO }, contract.p)
|
||||
export function getUserLiquidityShares(
|
||||
userId: string,
|
||||
contract: CPMMContract,
|
||||
liquidities: LiquidityProvision[]
|
||||
) {
|
||||
const weights = getCpmmLiquidityPoolWeights(contract, liquidities)
|
||||
const userWeight = weights[userId] ?? 0
|
||||
|
||||
// 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 }
|
||||
// }
|
||||
return mapValues(contract.pool, (shares) => userWeight * shares)
|
||||
}
|
||||
|
|
|
@ -346,10 +346,6 @@ function calculateMktDpmPayout(contract: DPMContract, bet: Bet) {
|
|||
probs = mapValues(totalShares, (shares) => shares ** 2 / squareSum)
|
||||
}
|
||||
|
||||
const weightedShareTotal = sumBy(Object.keys(totalShares), (outcome) => {
|
||||
return probs[outcome] * totalShares[outcome]
|
||||
})
|
||||
|
||||
const { outcome, amount, shares } = bet
|
||||
|
||||
const poolFrac =
|
||||
|
@ -359,11 +355,11 @@ function calculateMktDpmPayout(contract: DPMContract, bet: Bet) {
|
|||
(outcome) => {
|
||||
return (
|
||||
(probs[outcome] * (bet as NumericBet).allOutcomeShares[outcome]) /
|
||||
weightedShareTotal
|
||||
totalShares[outcome]
|
||||
)
|
||||
}
|
||||
)
|
||||
: (probs[outcome] * shares) / weightedShareTotal
|
||||
: (probs[outcome] * shares) / totalShares[outcome]
|
||||
|
||||
const totalPool = sum(Object.values(pool))
|
||||
const winnings = poolFrac * totalPool
|
||||
|
|
|
@ -142,6 +142,10 @@ export function getContractBetMetrics(contract: Contract, yourBets: Bet[]) {
|
|||
const profit = payout + saleValue + redeemed - totalInvested
|
||||
const profitPercent = (profit / totalInvested) * 100
|
||||
|
||||
const hasShares = Object.values(totalShares).some(
|
||||
(shares) => shares > 0
|
||||
)
|
||||
|
||||
return {
|
||||
invested: Math.max(0, currentInvested),
|
||||
payout,
|
||||
|
@ -149,6 +153,7 @@ export function getContractBetMetrics(contract: Contract, yourBets: Bet[]) {
|
|||
profit,
|
||||
profitPercent,
|
||||
totalShares,
|
||||
hasShares,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,6 +165,7 @@ export function getContractBetNullMetrics() {
|
|||
profit: 0,
|
||||
profitPercent: 0,
|
||||
totalShares: {} as { [outcome: string]: number },
|
||||
hasShares: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { difference } from 'lodash'
|
||||
|
||||
export const CATEGORIES = {
|
||||
politics: 'Politics',
|
||||
technology: 'Technology',
|
||||
|
@ -12,10 +14,19 @@ export const CATEGORIES = {
|
|||
crypto: 'Crypto',
|
||||
gaming: 'Gaming',
|
||||
fun: 'Fun',
|
||||
} as { [category: string]: string }
|
||||
}
|
||||
|
||||
export type category = keyof typeof CATEGORIES
|
||||
|
||||
export const TO_CATEGORY = Object.fromEntries(
|
||||
Object.entries(CATEGORIES).map(([k, v]) => [v, k])
|
||||
)
|
||||
|
||||
export const CATEGORY_LIST = Object.keys(CATEGORIES)
|
||||
|
||||
export const EXCLUDED_CATEGORIES: category[] = ['fun', 'manifold', 'personal']
|
||||
|
||||
export const DEFAULT_CATEGORIES = difference(
|
||||
CATEGORY_LIST,
|
||||
EXCLUDED_CATEGORIES
|
||||
)
|
||||
|
|
|
@ -58,6 +58,19 @@ export const charities: Charity[] = [
|
|||
- Promoting long-term thinking`,
|
||||
tags: ['Featured'] as CharityTag[],
|
||||
},
|
||||
{
|
||||
name: 'New Science',
|
||||
website: 'https://newscience.org/',
|
||||
photo: 'https://i.imgur.com/C7PoR4q.png',
|
||||
preview:
|
||||
'Facilitating scientific breakthroughs by empowering the next generation of scientists and building the 21st century institutions of basic science.',
|
||||
description: `As its first major project, in the summer of 2022, New Science will run an in-person research fellowship in Boston for young life scientists, during which they will independently explore an ambitious high-risk scientific idea they couldn’t work on otherwise and start building the foundations for a bigger research project, while having much more freedom than they could expect in their normal research environment but also much more support from us. This is inspired by Cold Spring Harbor Laboratory, which started as a place where leading molecular biologists came for the summer to hang out and work on random projects together, and which eventually housed 8 Nobel Prize winners.
|
||||
|
||||
As its second major project, in the fall of 2022, New Science will run an in-person 12-month-long fellowship for young scientists starting to directly attack the biggest structural issues of the established institutions of science. We will double down on things that worked well during the summer fellowship, while extending the fellowship to one year, thus allowing researchers to make much more progress and will strive to provide them as much scientific leverage as possible.
|
||||
|
||||
In several years, New Science will start funding entire labs outside of academia and then will be creating an entire network of scientific organizations, while supporting the broader scientific ecosystem that will constitute the 21st century institutions of basic science.`,
|
||||
tags: ['Featured'] as CharityTag[],
|
||||
},
|
||||
{
|
||||
name: 'Global Health and Development Fund',
|
||||
website: 'https://funds.effectivealtruism.org/funds/global-development',
|
||||
|
@ -472,9 +485,9 @@ Future plans: We expect to focus on similar theoretical problems in alignment un
|
|||
name: 'The Trevor Project',
|
||||
website: 'https://www.thetrevorproject.org/',
|
||||
photo: 'https://i.imgur.com/QN4mVNn.jpeg',
|
||||
preview: 'The Trevor Project is the world’s largest suicide prevention and crisis intervention organization for LGBTQ (lesbian, gay, bisexual, transgender, queer, and questioning) young people.',
|
||||
description:
|
||||
`Two decades ago, we responded to a health crisis. Now we’re building a safer, more-inclusive world. LGBTQ young people are four times more likely to attempt suicide, and suicide remains the second leading cause of death among all young people in the U.S.
|
||||
preview:
|
||||
'The Trevor Project is the world’s largest suicide prevention and crisis intervention organization for LGBTQ (lesbian, gay, bisexual, transgender, queer, and questioning) young people.',
|
||||
description: `Two decades ago, we responded to a health crisis. Now we’re building a safer, more-inclusive world. LGBTQ young people are four times more likely to attempt suicide, and suicide remains the second leading cause of death among all young people in the U.S.
|
||||
|
||||
Our Mission
|
||||
To end suicide among lesbian, gay, bisexual, transgender, queer & questioning young people.
|
||||
|
@ -485,6 +498,24 @@ Future plans: We expect to focus on similar theoretical problems in alignment un
|
|||
Our Goal
|
||||
To serve 1.8 million crisis contacts annually, by the end of our 25th year, while continuing to innovate on our core services.`,
|
||||
},
|
||||
{
|
||||
name: 'ACLU',
|
||||
website: 'https://www.aclu.org/',
|
||||
photo: 'https://i.imgur.com/nbSYuDC.png',
|
||||
preview:
|
||||
'The ACLU works in the courts, legislatures, and communities to defend and preserve the individual rights and liberties guaranteed to all people in this country by the Constitution and laws of the United States.',
|
||||
description: `
|
||||
THREE THINGS TO KNOW ABOUT THE ACLU
|
||||
• We protect American values. In many ways, the ACLU is the nation's most conservative organization. Our job is to conserve America's original civic values - the Constitution and the Bill of Rights - and defend the rights of every man, woman and child in this country.
|
||||
• We're not anti-anything. The only things we fight are attempts to take away or limit your civil liberties, like your right to practice any religion you want (or none at all); or to decide in private whether or not to have a child; or to speak out - for or against - anything at all; or to be treated with equality and fairness, no matter who you are.
|
||||
• We're there for you. Rich or poor, straight or gay, black or white or brown, urban or rural, pious or atheist, American-born or foreign-born, able-bodied or living with a disability. Every person in this country should have the same basic rights. And since our founding in 1920, we've been working hard to make sure no one takes them away.
|
||||
|
||||
The American Civil Liberties Union is our nation's guardian of liberty, working daily in courts, legislatures and communities to defend and preserve the individual rights and liberties that the Constitution and laws of the United States guarantee everyone in this country.
|
||||
|
||||
"So long as we have enough people in this country willing to fight for their rights, we'll be called a democracy," ACLU Founder Roger Baldwin said.
|
||||
|
||||
The U.S. Constitution and the Bill of Rights trumpet our aspirations for the kind of society that we want to be. But for much of our history, our nation failed to fulfill the promise of liberty for whole groups of people.`,
|
||||
},
|
||||
].map((charity) => {
|
||||
const slug = charity.name.toLowerCase().replace(/\s/g, '-')
|
||||
return {
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
// They're uniquely identified by the pair contractId/betId.
|
||||
export type Comment = {
|
||||
id: string
|
||||
contractId: string
|
||||
contractId?: string
|
||||
groupId?: string
|
||||
betId?: string
|
||||
answerOutcome?: string
|
||||
replyToCommentId?: string
|
||||
|
|
|
@ -33,6 +33,7 @@ export type Contract<T extends AnyContractType = AnyContractType> = {
|
|||
isResolved: boolean
|
||||
resolutionTime?: number // When the contract creator resolved the market
|
||||
resolution?: string
|
||||
resolutionProbability?: number,
|
||||
|
||||
closeEmailsSent?: number
|
||||
|
||||
|
@ -94,6 +95,9 @@ export type outcomeType = AnyOutcomeType['outcomeType']
|
|||
export type resolution = 'YES' | 'NO' | 'MKT' | 'CANCEL'
|
||||
export const RESOLUTIONS = ['YES', 'NO', 'MKT', 'CANCEL'] as const
|
||||
export const OUTCOME_TYPES = ['BINARY', 'FREE_RESPONSE', 'NUMERIC'] as const
|
||||
|
||||
export const MAX_QUESTION_LENGTH = 480
|
||||
export const MAX_DESCRIPTION_LENGTH = 10000
|
||||
export const MAX_TAG_LENGTH = 60
|
||||
|
||||
export const CPMM_MIN_POOL_QTY = 0.01
|
||||
|
|
|
@ -12,8 +12,7 @@ export const DEV_CONFIG: EnvConfig = {
|
|||
appId: '1:134303100058:web:27f9ea8b83347251f80323',
|
||||
measurementId: 'G-YJC9E37P37',
|
||||
},
|
||||
functionEndpoints: {
|
||||
placebet: 'https://placebet-w3txbmd3ba-uc.a.run.app',
|
||||
createmarket: 'https://createmarket-w3txbmd3ba-uc.a.run.app',
|
||||
},
|
||||
cloudRunId: 'w3txbmd3ba',
|
||||
cloudRunRegion: 'uc',
|
||||
amplitudeApiKey: 'fd8cbfd964b9a205b8678a39faae71b3',
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
export type V2CloudFunction = 'placebet' | 'createmarket'
|
||||
|
||||
export type EnvConfig = {
|
||||
domain: string
|
||||
firebaseConfig: FirebaseConfig
|
||||
functionEndpoints: Record<V2CloudFunction, string>
|
||||
amplitudeApiKey?: string
|
||||
|
||||
// IDs for v2 cloud functions -- find these by deploying a cloud function and
|
||||
// examining the URL, https://[name]-[cloudRunId]-[cloudRunRegion].a.run.app
|
||||
cloudRunId: string
|
||||
cloudRunRegion: string
|
||||
|
||||
// Access controls
|
||||
adminEmails: string[]
|
||||
|
@ -30,6 +33,8 @@ type FirebaseConfig = {
|
|||
|
||||
export const PROD_CONFIG: EnvConfig = {
|
||||
domain: 'manifold.markets',
|
||||
amplitudeApiKey: '2d6509fd4185ebb8be29709842752a15',
|
||||
|
||||
firebaseConfig: {
|
||||
apiKey: 'AIzaSyDp3J57vLeAZCzxLD-vcPaGIkAmBoGOSYw',
|
||||
authDomain: 'mantic-markets.firebaseapp.com',
|
||||
|
@ -40,10 +45,8 @@ export const PROD_CONFIG: EnvConfig = {
|
|||
appId: '1:128925704902:web:f61f86944d8ffa2a642dc7',
|
||||
measurementId: 'G-SSFK1Q138D',
|
||||
},
|
||||
functionEndpoints: {
|
||||
placebet: 'https://placebet-nggbo3neva-uc.a.run.app',
|
||||
createmarket: 'https://createmarket-nggbo3neva-uc.a.run.app',
|
||||
},
|
||||
cloudRunId: 'nggbo3neva',
|
||||
cloudRunRegion: 'uc',
|
||||
adminEmails: [
|
||||
'akrolsmir@gmail.com', // Austin
|
||||
'jahooma@gmail.com', // James
|
||||
|
|
|
@ -12,11 +12,8 @@ export const THEOREMONE_CONFIG: EnvConfig = {
|
|||
appId: '1:698012149198:web:b342af75662831aa84b79f',
|
||||
measurementId: 'G-Y3EZ1WNT6E',
|
||||
},
|
||||
// TODO: fill in real endpoints for T1
|
||||
functionEndpoints: {
|
||||
placebet: 'https://placebet-nggbo3neva-uc.a.run.app',
|
||||
createmarket: 'https://createmarket-nggbo3neva-uc.a.run.app',
|
||||
},
|
||||
cloudRunId: 'nggbo3neva', // TODO: fill in real ID for T1
|
||||
cloudRunRegion: 'uc',
|
||||
adminEmails: [...PROD_CONFIG.adminEmails, 'david.glidden@theoremone.co'],
|
||||
whitelistEmail: '@theoremone.co',
|
||||
moneyMoniker: 'T$',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export const PLATFORM_FEE = 0.01
|
||||
export const CREATOR_FEE = 0.06
|
||||
export const LIQUIDITY_FEE = 0.06
|
||||
export const PLATFORM_FEE = 0
|
||||
export const CREATOR_FEE = 0.1
|
||||
export const LIQUIDITY_FEE = 0
|
||||
|
||||
export const DPM_PLATFORM_FEE = 0.01
|
||||
export const DPM_CREATOR_FEE = 0.04
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
export type Fold = {
|
||||
id: string
|
||||
slug: string
|
||||
name: string
|
||||
about: string
|
||||
curatorId: string // User id
|
||||
createdTime: number
|
||||
|
||||
tags: string[]
|
||||
lowercaseTags: string[]
|
||||
|
||||
contractIds: string[]
|
||||
excludedContractIds: string[]
|
||||
|
||||
// Invariant: exactly one of the following is defined.
|
||||
// Default: creatorIds: undefined, excludedCreatorIds: []
|
||||
creatorIds?: string[]
|
||||
excludedCreatorIds?: string[]
|
||||
|
||||
followCount: number
|
||||
|
||||
disallowMarketCreation?: boolean
|
||||
}
|
4
common/follow.ts
Normal file
4
common/follow.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export type Follow = {
|
||||
userId: string
|
||||
timestamp: number
|
||||
}
|
15
common/group.ts
Normal file
15
common/group.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
export type Group = {
|
||||
id: string
|
||||
slug: string
|
||||
name: string
|
||||
about: string
|
||||
creatorId: string // User id
|
||||
createdTime: number
|
||||
mostRecentActivityTime: number
|
||||
memberIds: string[] // User ids
|
||||
anyoneCanJoin: boolean
|
||||
contractIds: string[]
|
||||
}
|
||||
export const MAX_GROUP_NAME_LENGTH = 75
|
||||
export const MAX_ABOUT_LENGTH = 140
|
||||
export const MAX_ID_LENGTH = 60
|
35
common/manalink.ts
Normal file
35
common/manalink.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
export type Manalink = {
|
||||
// The link to send: https://manifold.markets/send/{slug}
|
||||
// Also functions as the unique id for the link.
|
||||
slug: string
|
||||
|
||||
// Note: we assume both fromId and toId are of SourceType 'USER'
|
||||
fromId: string
|
||||
|
||||
// Displayed to people claiming the link
|
||||
message: string
|
||||
|
||||
// How much to send with the link
|
||||
amount: number
|
||||
token: 'M$' // TODO: could send eg YES shares too??
|
||||
|
||||
createdTime: number
|
||||
// If null, the link is valid forever
|
||||
expiresTime: number | null
|
||||
// If null, the link can be used infinitely
|
||||
maxUses: number | null
|
||||
|
||||
// Used for simpler caching
|
||||
claimedUserIds: string[]
|
||||
// Successful redemptions of the link
|
||||
claims: Claim[]
|
||||
}
|
||||
|
||||
export type Claim = {
|
||||
toId: string
|
||||
|
||||
// The ID of the successful txn that tracks the money moved
|
||||
txnId: string
|
||||
|
||||
claimedTime: number
|
||||
}
|
|
@ -14,6 +14,14 @@ export type Notification = {
|
|||
sourceUserName?: string
|
||||
sourceUserUsername?: string
|
||||
sourceUserAvatarUrl?: string
|
||||
sourceText?: string
|
||||
|
||||
sourceContractTitle?: string
|
||||
sourceContractCreatorUsername?: string
|
||||
sourceContractSlug?: string
|
||||
|
||||
sourceSlug?: string
|
||||
sourceTitle?: string
|
||||
}
|
||||
export type notification_source_types =
|
||||
| 'contract'
|
||||
|
@ -24,6 +32,7 @@ export type notification_source_types =
|
|||
| 'follow'
|
||||
| 'tip'
|
||||
| 'admin_message'
|
||||
| 'group'
|
||||
|
||||
export type notification_source_update_types =
|
||||
| 'created'
|
||||
|
@ -42,3 +51,5 @@ export type notification_reason_types =
|
|||
| 'reply_to_users_answer'
|
||||
| 'reply_to_users_comment'
|
||||
| 'on_new_follow'
|
||||
| 'you_follow_user'
|
||||
| 'added_you_to_group'
|
||||
|
|
27
common/quadratic-funding.ts
Normal file
27
common/quadratic-funding.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { groupBy, mapValues, sum, sumBy } from 'lodash'
|
||||
import { Txn } from './txn'
|
||||
|
||||
// Returns a map of charity ids to the amount of M$ matched
|
||||
export function quadraticMatches(
|
||||
allCharityTxns: Txn[],
|
||||
matchingPool: number
|
||||
): Record<string, number> {
|
||||
// For each charity, group the donations by each individual donor
|
||||
const donationsByCharity = groupBy(allCharityTxns, 'toId')
|
||||
const donationsByDonors = mapValues(donationsByCharity, (txns) =>
|
||||
groupBy(txns, 'fromId')
|
||||
)
|
||||
|
||||
// Weight for each charity = [sum of sqrt(individual donor)] ^ 2
|
||||
const weights = mapValues(donationsByDonors, (byDonor) => {
|
||||
const sumByDonor = Object.values(byDonor).map((txns) =>
|
||||
sumBy(txns, 'amount')
|
||||
)
|
||||
const sumOfRoots = sumBy(sumByDonor, Math.sqrt)
|
||||
return sumOfRoots ** 2
|
||||
})
|
||||
|
||||
// Then distribute the matching pool based on the individual weights
|
||||
const totalWeight = sum(Object.values(weights))
|
||||
return mapValues(weights, (weight) => matchingPool * (weight / totalWeight))
|
||||
}
|
|
@ -7,7 +7,12 @@ import { getPayouts } from './payouts'
|
|||
export function scoreCreators(contracts: Contract[]) {
|
||||
const creatorScore = mapValues(
|
||||
groupBy(contracts, ({ creatorId }) => creatorId),
|
||||
(contracts) => sumBy(contracts, ({ pool }) => pool.YES + pool.NO)
|
||||
(contracts) =>
|
||||
sumBy(
|
||||
contracts.map((contract) => {
|
||||
return contract.volume
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
return creatorScore
|
||||
|
|
|
@ -1,22 +1,18 @@
|
|||
import { Bet } from './bet'
|
||||
import {
|
||||
getDpmProbability,
|
||||
calculateDpmShareValue,
|
||||
deductDpmFees,
|
||||
getDpmOutcomeProbability,
|
||||
} from './calculate-dpm'
|
||||
import { calculateCpmmSale, getCpmmProbability } from './calculate-cpmm'
|
||||
import { CPMMContract, DPMContract } from './contract'
|
||||
import { DPM_CREATOR_FEE, DPM_PLATFORM_FEE, Fees } from './fees'
|
||||
import { User } from './user'
|
||||
|
||||
export const getSellBetInfo = (
|
||||
user: User,
|
||||
bet: Bet,
|
||||
contract: DPMContract,
|
||||
newBetId: string
|
||||
) => {
|
||||
export type CandidateBet<T extends Bet> = Omit<T, 'id' | 'userId'>
|
||||
|
||||
export const getSellBetInfo = (bet: Bet, contract: DPMContract) => {
|
||||
const { pool, totalShares, totalBets } = contract
|
||||
const { id: betId, amount, shares, outcome, loanAmount } = bet
|
||||
const { id: betId, amount, shares, outcome } = bet
|
||||
|
||||
const adjShareValue = calculateDpmShareValue(contract, bet)
|
||||
|
||||
|
@ -29,8 +25,8 @@ export const getSellBetInfo = (
|
|||
|
||||
const newTotalBets = { ...totalBets, [outcome]: totalBets[outcome] - amount }
|
||||
|
||||
const probBefore = getDpmProbability(totalShares)
|
||||
const probAfter = getDpmProbability(newTotalShares)
|
||||
const probBefore = getDpmOutcomeProbability(totalShares, outcome)
|
||||
const probAfter = getDpmOutcomeProbability(newTotalShares, outcome)
|
||||
|
||||
const profit = adjShareValue - amount
|
||||
|
||||
|
@ -54,9 +50,7 @@ export const getSellBetInfo = (
|
|||
creatorFee
|
||||
)
|
||||
|
||||
const newBet: Bet = {
|
||||
id: newBetId,
|
||||
userId: user.id,
|
||||
const newBet: CandidateBet<Bet> = {
|
||||
contractId: contract.id,
|
||||
amount: -adjShareValue,
|
||||
shares: -shares,
|
||||
|
@ -71,25 +65,20 @@ export const getSellBetInfo = (
|
|||
fees,
|
||||
}
|
||||
|
||||
const newBalance = user.balance + saleAmount - (loanAmount ?? 0)
|
||||
|
||||
return {
|
||||
newBet,
|
||||
newPool,
|
||||
newTotalShares,
|
||||
newTotalBets,
|
||||
newBalance,
|
||||
fees,
|
||||
}
|
||||
}
|
||||
|
||||
export const getCpmmSellBetInfo = (
|
||||
user: User,
|
||||
shares: number,
|
||||
outcome: 'YES' | 'NO',
|
||||
contract: CPMMContract,
|
||||
prevLoanAmount: number,
|
||||
newBetId: string
|
||||
prevLoanAmount: number
|
||||
) => {
|
||||
const { pool, p } = contract
|
||||
|
||||
|
@ -100,8 +89,6 @@ export const getCpmmSellBetInfo = (
|
|||
)
|
||||
|
||||
const loanPaid = Math.min(prevLoanAmount, saleValue)
|
||||
const netAmount = saleValue - loanPaid
|
||||
|
||||
const probBefore = getCpmmProbability(pool, p)
|
||||
const probAfter = getCpmmProbability(newPool, p)
|
||||
|
||||
|
@ -115,9 +102,7 @@ export const getCpmmSellBetInfo = (
|
|||
fees.creatorFee
|
||||
)
|
||||
|
||||
const newBet: Bet = {
|
||||
id: newBetId,
|
||||
userId: user.id,
|
||||
const newBet: CandidateBet<Bet> = {
|
||||
contractId: contract.id,
|
||||
amount: -saleValue,
|
||||
shares: -shares,
|
||||
|
@ -129,13 +114,10 @@ export const getCpmmSellBetInfo = (
|
|||
fees,
|
||||
}
|
||||
|
||||
const newBalance = user.balance + netAmount
|
||||
|
||||
return {
|
||||
newBet,
|
||||
newPool,
|
||||
newP,
|
||||
newBalance,
|
||||
fees,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "../",
|
||||
"composite": true,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"noImplicitReturns": true,
|
||||
"outDir": "lib",
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
// A txn (pronounced "texan") respresents a payment between two ids on Manifold
|
||||
// Shortened from "transaction" to distinguish from Firebase transactions (and save chars)
|
||||
export type Txn = {
|
||||
type AnyTxnType = Donation | Tip | Manalink
|
||||
type SourceType = 'USER' | 'CONTRACT' | 'CHARITY' | 'BANK'
|
||||
|
||||
export type Txn<T extends AnyTxnType = AnyTxnType> = {
|
||||
id: string
|
||||
createdTime: number
|
||||
|
||||
|
@ -13,9 +16,36 @@ export type Txn = {
|
|||
amount: number
|
||||
token: 'M$' // | 'USD' | MarketOutcome
|
||||
|
||||
category: 'CHARITY' // | 'BET' | 'TIP'
|
||||
category: 'CHARITY' | 'MANALINK' | 'TIP' // | 'BET'
|
||||
// Any extra data
|
||||
data?: { [key: string]: any }
|
||||
|
||||
// Human-readable description
|
||||
description?: string
|
||||
} & T
|
||||
|
||||
type Donation = {
|
||||
fromType: 'USER'
|
||||
toType: 'CHARITY'
|
||||
category: 'CHARITY'
|
||||
}
|
||||
|
||||
export type SourceType = 'USER' | 'CONTRACT' | 'CHARITY' | 'BANK'
|
||||
type Tip = {
|
||||
fromType: 'USER'
|
||||
toType: 'USER'
|
||||
category: 'TIP'
|
||||
data: {
|
||||
contractId: string
|
||||
commentId: string
|
||||
}
|
||||
}
|
||||
|
||||
type Manalink = {
|
||||
fromType: 'USER'
|
||||
toType: 'USER'
|
||||
category: 'MANALINK'
|
||||
}
|
||||
|
||||
export type DonationTxn = Txn & Donation
|
||||
export type TipTxn = Txn & Tip
|
||||
export type ManalinkTxn = Txn & Manalink
|
||||
|
|
|
@ -15,8 +15,22 @@ export type User = {
|
|||
|
||||
balance: number
|
||||
totalDeposits: number
|
||||
totalPnLCached: number
|
||||
creatorVolumeCached: number
|
||||
|
||||
profitCached: {
|
||||
daily: number
|
||||
weekly: number
|
||||
monthly: number
|
||||
allTime: number
|
||||
}
|
||||
|
||||
creatorVolumeCached: {
|
||||
daily: number
|
||||
weekly: number
|
||||
monthly: number
|
||||
allTime: number
|
||||
}
|
||||
|
||||
followerCountCached: number
|
||||
|
||||
followedCategories?: string[]
|
||||
}
|
||||
|
@ -40,3 +54,11 @@ export type PrivateUser = {
|
|||
}
|
||||
|
||||
export type notification_subscribe_types = 'all' | 'less' | 'none'
|
||||
|
||||
export type PortfolioMetrics = {
|
||||
investmentValue: number
|
||||
balance: number
|
||||
totalDeposits: number
|
||||
timestamp: number
|
||||
userId: string
|
||||
}
|
||||
|
|
|
@ -7,6 +7,6 @@ export const cleanUsername = (name: string, maxLength = 25) => {
|
|||
.substring(0, maxLength)
|
||||
}
|
||||
|
||||
export const cleanDisplayName = (displayName: string, maxLength = 25) => {
|
||||
export const cleanDisplayName = (displayName: string, maxLength = 30) => {
|
||||
return displayName.replace(/\s+/g, ' ').substring(0, maxLength).trim()
|
||||
}
|
||||
|
|
|
@ -12,6 +12,10 @@ export function formatMoney(amount: number) {
|
|||
return ENV_CONFIG.moneyMoniker + formatter.format(newAmount).replace('$', '')
|
||||
}
|
||||
|
||||
export function formatMoneyWithDecimals(amount: number) {
|
||||
return ENV_CONFIG.moneyMoniker + amount.toFixed(2)
|
||||
}
|
||||
|
||||
export function formatWithCommas(amount: number) {
|
||||
return formatter.format(Math.floor(amount)).replace('$', '')
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { sortBy, sum } from 'lodash'
|
||||
|
||||
export const logInterpolation = (min: number, max: number, value: number) => {
|
||||
if (value <= min) return 0
|
||||
if (value >= max) return 1
|
||||
|
@ -16,4 +18,19 @@ export function normpdf(x: number, mean = 0, variance = 1) {
|
|||
)
|
||||
}
|
||||
|
||||
const TAU = Math.PI * 2
|
||||
export const TAU = Math.PI * 2
|
||||
|
||||
export function median(xs: number[]) {
|
||||
if (xs.length === 0) return NaN
|
||||
|
||||
const sorted = sortBy(xs, (x) => x)
|
||||
const mid = Math.floor(sorted.length / 2)
|
||||
if (sorted.length % 2 === 0) {
|
||||
return (sorted[mid - 1] + sorted[mid]) / 2
|
||||
}
|
||||
return sorted[mid]
|
||||
}
|
||||
|
||||
export function average(xs: number[]) {
|
||||
return sum(xs) / xs.length
|
||||
}
|
||||
|
|
|
@ -23,3 +23,18 @@ export const addObjects = <T extends { [key: string]: number }>(
|
|||
|
||||
return newObj as T
|
||||
}
|
||||
|
||||
export const subtractObjects = <T extends { [key: string]: number }>(
|
||||
obj1: T,
|
||||
obj2: T
|
||||
) => {
|
||||
const keys = union(Object.keys(obj1), Object.keys(obj2))
|
||||
const newObj = {} as any
|
||||
|
||||
for (const key of keys) {
|
||||
newObj[key] = (obj1[key] ?? 0) - (obj2[key] ?? 0)
|
||||
}
|
||||
|
||||
return newObj as T
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
# docs
|
||||
|
||||
Manifold Markets Docs
|
||||
|
||||
## Getting started
|
||||
|
||||
0. Make sure you have [Yarn 1.x][yarn]
|
||||
1. `$ cd docs`
|
||||
2. `$ yarn`
|
||||
3. `$ yarn start`
|
||||
4. The docs site will be available on http://localhost:3000
|
||||
|
|
61
docs/docs/$how-to.md
Normal file
61
docs/docs/$how-to.md
Normal file
|
@ -0,0 +1,61 @@
|
|||
# How to Manifold
|
||||
|
||||
Manifold Markets is a novel site where users can bet against each other to predict the outcomes of all types of questions. Engage in intense discussion, or joke with friends, whilst putting play-money where your mouth is.
|
||||
|
||||
## Mana
|
||||
|
||||
Mana (M$) is our virtual play currency that cannot be converted to real money.
|
||||
|
||||
- **Its Value**
|
||||
|
||||
You can redeem your Mana and we will [donate to a charity](https://manifold.markets/charity) on your behalf. Redeeming and purchasing Mana occurs at a rate of M$100 to $1. You will be able to redeem it for merch and other cool items soon too!
|
||||
|
||||
- **It sets us apart**
|
||||
|
||||
Using play-money sets us apart from other similar sites as we don’t want our users to solely focus on monetary gains. Instead we prioritize providing value in the form of an enjoyable experience and facilitating a more informed world through the power of prediction markets.
|
||||
|
||||
## How probabilities work
|
||||
|
||||
The probability of a market represents what the collective bets of users predict the chances of an outcome occurring is. How this is calculated depends on the type of market - see below!
|
||||
|
||||
## Types of markets
|
||||
|
||||
There are currently 3 types of markets: Yes/No (binary), Free response, and Numerical.
|
||||
|
||||
- **Yes/No (Binary)**
|
||||
|
||||
The creator asks a question where traders can bet yes or no.
|
||||
|
||||
Check out [Maniswap](https://www.notion.so/Maniswap-ce406e1e897d417cbd491071ea8a0c39) for more info on its automated market maker.
|
||||
|
||||
- **Free Response**
|
||||
|
||||
The creator asks an open ended question. Both the creator and users can propose answers which can be bet on. Don’t be intimidated to add new answers! The payout system and initial liquidity rewards users who bet on new answers early. The algorithm used to determine the probability and payout is complicated but if you want to learn more check out [DPM](https://www.notion.so/DPM-b9b48a09ea1f45b88d991231171730c5).
|
||||
|
||||
- **Numerical**
|
||||
|
||||
Retracted whilst we make improvements. You still may see some old ones floating around though. Questions which can be answered by a number within a given range. Betting on a value will cause you to buy shares from ‘buckets’ surrounding the number you choose.
|
||||
|
||||
## Compete and build your portfolio
|
||||
|
||||
Generate profits to prove your expertise and shine above your friends.
|
||||
|
||||
To the moon 🚀
|
||||
|
||||
- **Find inaccurate probabilities**
|
||||
|
||||
Use your superior knowledge on topics to identify markets which have inaccurate probabilities. This gives you favorable odds, so bet accordingly to shift the probability to what you think it should be.
|
||||
|
||||
- **React to news**
|
||||
|
||||
Markets are dynamic and ongoing events can drastically affect what the probability should look like. Be the keenest to react and there is a lot of Mana to be made.
|
||||
|
||||
- **Buy low, sell high**
|
||||
|
||||
Similar to a stock market, probabilities can be overvalued and undervalued. If you bet (buy shares) at one end of the spectrum and subsequently other users buy even more shares of that same type, the value of your own shares will increase. Sometimes it will be most profitable to wait for the market to resolve but often it can be wise to sell your shares and take the immediate profits. This can also be a great way to free up Mana if you are lacking funds.
|
||||
|
||||
- **Create innovative answers**
|
||||
|
||||
Certain free response markets provide room for creativity! The answers themselves can often affect the outcome based on how compelling they are.
|
||||
|
||||
More questions? Check out **[this community-driven FAQ](https://docs.manifold.markets/faq)**!
|
|
@ -5,7 +5,7 @@ slug: /
|
|||
|
||||
# About Manifold Markets
|
||||
|
||||
Manifold Markets lets anyone create a prediction market on any topic. Win virtual money betting on what you know, from **[chess tournaments](https://manifold.markets/SG/will-magnus-carlsen-lose-any-regula)** to **[lunar collisions](https://manifold.markets/Duncan/will-the-wayward-falcon-9-booster-h)** to **[newsletter subscriber rates](https://manifold.markets/Nu%C3%B1oSempere/how-many-additional-subscribers-wil)** - or learn about the future by creating your own market!
|
||||
Manifold Markets lets anyone create a prediction market on any topic. Win virtual play money betting on what you know, from **[chess tournaments](https://manifold.markets/SG/will-magnus-carlsen-lose-any-regula)** to **[lunar collisions](https://manifold.markets/Duncan/will-the-wayward-falcon-9-booster-h)** to **[newsletter subscriber rates](https://manifold.markets/Nu%C3%B1oSempere/how-many-additional-subscribers-wil)** - or learn about the future by creating your own market!
|
||||
|
||||
### **What are prediction markets?**
|
||||
|
||||
|
@ -17,27 +17,13 @@ If I think the Democrats are very likely to win, and you disagree, I might offer
|
|||
|
||||
Now, you or I could be mistaken and overshooting the true probability one way or another. If so, there's an incentive for someone else to bet and correct it! Over time, the implied probability will converge to the **[market's best estimate](https://en.wikipedia.org/wiki/Efficient-market_hypothesis)**. Since these probabilities are public, anyone can use them to make better decisions!
|
||||
|
||||
### **How does Manifold Markets work?**
|
||||
|
||||
1. **Anyone can create a market for any yes-or-no question.**
|
||||
|
||||
You can ask questions about the future like "Will Taiwan remove its 14-day COVID quarantine by Jun 01, 2022?" If the market thinks this is very likely, you can plan more activities for your trip.
|
||||
|
||||
You can also ask subjective, personal questions like "Will I enjoy my 2022 Taiwan trip?". Then share the market with your family and friends and get their takes!
|
||||
|
||||
2. **Anyone can bet on a market using Manifold Dollars (M$), our platform currency.**
|
||||
|
||||
You get M$ 1,000 just for signing up, so you can start betting immediately! When a market creator decides an outcome in your favor, you'll win Manifold Dollars from people who bet against you.
|
||||
|
||||
More questions? Check out **[this community-driven FAQ](https://outsidetheasylum.blog/manifold-markets-faq/)**!
|
||||
|
||||
### **Can prediction markets work without real money?**
|
||||
|
||||
Yes! There is substantial evidence that play-money prediction markets provide real predictive power. Examples include **[sports betting](http://www.electronicmarkets.org/fileadmin/user_upload/doc/Issues/Volume_16/Issue_01/V16I1_Statistical_Tests_of_Real-Money_versus_Play-Money_Prediction_Markets.pdf)** and internal prediction markets at firms like **[Google](https://www.networkworld.com/article/2284098/google-bets-on-value-of-prediction-markets.html)**.
|
||||
|
||||
Our overall design also ensures that good forecasting will come out on top in the long term. In the competitive environment of the marketplace, bettors that are correct more often will gain influence, leading to better-calibrated forecasts over time.
|
||||
|
||||
Since our launch, we've seen hundreds of users trade each day, on over a thousand different markets! You can track the popularity of our platform at **[http://manifold.markets/analytics](http://manifold.markets/analytics)**.
|
||||
Since our launch, we've seen hundreds of users trade each day, on over a thousand different markets! You can track the popularity of our platform at **[https://manifold.markets/stats](https://manifold.markets/stats)**.
|
||||
|
||||
### **Why is this important?**
|
||||
|
||||
|
@ -67,7 +53,7 @@ Manifold Markets is currently a team of three:
|
|||
- Stephen Grugett
|
||||
- Austin Chen
|
||||
|
||||
We've previously launched consumer-facing startups (**[Throne](https://throne.live/)**, **[One Word](http://oneword.games/platform)**), and worked at top tech and trading firms (Google, Susquehanna).
|
||||
We've previously launched consumer-facing startups (**[Throne](https://throne.live/)**, **[One Word](https://oneword.games/platform)**), and worked at top tech and trading firms (Google, Susquehanna).
|
||||
|
||||
## **Talk to us!**
|
||||
|
||||
|
|
581
docs/docs/api.md
581
docs/docs/api.md
|
@ -4,42 +4,85 @@
|
|||
|
||||
Our API is still in alpha — things may change or break at any time!
|
||||
|
||||
:::
|
||||
|
||||
Manifold currently supports a basic, read-only API for getting information about our markets.
|
||||
|
||||
If you have questions, come chat with us on [Discord](https://discord.com/invite/eHQBNBqXuh). We’d love to hear about what you build!
|
||||
|
||||
## List out all markets
|
||||
:::
|
||||
|
||||
### `/v0/markets`
|
||||
## General notes
|
||||
|
||||
Some APIs are not associated with any particular user. Other APIs require authentication.
|
||||
|
||||
APIs that require authentication accept an `Authorization` header in one of two formats:
|
||||
|
||||
- `Authorization: Key {key}`. A Manifold API key associated with a user
|
||||
account. Each account may have zero or one API keys. To generate an API key
|
||||
for your account, visit your user profile, click "edit", and click the
|
||||
"refresh" button next to the API key field at the bottom. You can click it
|
||||
again any time to invalidate your existing key and generate a new one.
|
||||
|
||||
- `Authorization: Bearer {jwt}`. A signed JWT from Firebase asserting your
|
||||
identity. This is what our web client uses. It will probably be annoying for
|
||||
you to generate and we will not document it further here.
|
||||
|
||||
API requests that accept parameters should either have the parameters in the
|
||||
query string if they are GET requests, or have a body with a JSON object with
|
||||
one property per parameter if they are POST requests.
|
||||
|
||||
API responses should always either have a body with a JSON result object (if
|
||||
the response was a 200) or with a JSON object representing an error (if the
|
||||
response was a 4xx or 5xx.)
|
||||
|
||||
## Endpoints
|
||||
|
||||
### `GET /v0/markets`
|
||||
|
||||
Lists all markets, ordered by creation date descending.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `limit`: Optional. How many markets to return. The maximum and the default is 1000.
|
||||
- `before`: Optional. The ID of the market before which the list will start. For
|
||||
example, if you ask for the most recent 10 markets, and then perform a second
|
||||
query for 10 more markets with `before=[the id of the 10th market]`, you will
|
||||
get markets 11 through 20.
|
||||
|
||||
Requires no authorization.
|
||||
|
||||
- Example request
|
||||
```
|
||||
http://manifold.markets/api/v0/markets
|
||||
https://manifold.markets/api/v0/markets?limit=1
|
||||
```
|
||||
- Example response
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id":"FKtYX3t8ZfIp5gytJWAI",
|
||||
"creatorUsername":"JamesGrugett",
|
||||
"creatorName":"James Grugett",
|
||||
"createdTime":1645139406452,
|
||||
"closeTime":1647406740000,
|
||||
"question":"What will be the best assessment of the Free response feature on March 15th?",
|
||||
"description":"Hey guys, let's try this out!\nWe will see how people use the new Free response market type over the next month. Then I will pick the answer that I think best describes the consensus view of this feature on March 15th. Cheers.",
|
||||
"id":"EvIhzcJXwhL0HavaszD7",
|
||||
"creatorUsername":"Austin",
|
||||
"creatorName":"Austin",
|
||||
"createdTime":1653850472294,
|
||||
"creatorAvatarUrl":"https://lh3.googleusercontent.com/a-/AOh14GiZyl1lBehuBMGyJYJhZd-N-mstaUtgE4xdI22lLw=s96-c",
|
||||
"closeTime":1653893940000,
|
||||
"question":"Will I write a new blog post today?",
|
||||
"description":"I'm supposed to, or else Beeminder charges me $90.\nTentative topic ideas:\n- \"Manifold funding, a history\"\n- \"Markets and bounties allow trades through time\"\n- \"equity vs money vs time\"\n\nClose date updated to 2022-05-29 11:59 pm",
|
||||
"tags":[
|
||||
"ManifoldMarkets"
|
||||
],
|
||||
"url":"https://manifold.markets/JamesGrugett/what-will-be-the-best-assessment-of",
|
||||
"pool":null,
|
||||
"probability":0,
|
||||
"volume7Days":100,
|
||||
"volume24Hours":100,
|
||||
"isResolved":false,
|
||||
...
|
||||
}
|
||||
"personal",
|
||||
"commitments"
|
||||
],
|
||||
"url":"https://manifold.markets/Austin/will-i-write-a-new-blog-post-today",
|
||||
"pool":146.73022894879944,
|
||||
"probability":0.8958175225896258,
|
||||
"p":0.08281474972181882,
|
||||
"totalLiquidity":102.65696071594805,
|
||||
"outcomeType":"BINARY",
|
||||
"mechanism":"cpmm-1",
|
||||
"volume":241,
|
||||
"volume7Days":0,
|
||||
"volume24Hours":0,
|
||||
"isResolved":true,
|
||||
"resolution":"YES",
|
||||
"resolutionTime":1653924077078
|
||||
},
|
||||
...
|
||||
```
|
||||
- Response type: Array of `LiteMarket`
|
||||
|
||||
|
@ -52,29 +95,47 @@ If you have questions, come chat with us on [Discord](https://discord.com/invite
|
|||
// Attributes about the creator
|
||||
creatorUsername: string
|
||||
creatorName: string
|
||||
createdTime: number
|
||||
createdTime: number // milliseconds since epoch
|
||||
creatorAvatarUrl?: string
|
||||
|
||||
// Market attributes. All times are in milliseconds since epoch
|
||||
closeTime?: number // Min of creator's chosen date, and resolutionTime
|
||||
question: string
|
||||
description: string
|
||||
|
||||
// A list of tags on each market. Any user can add tags to any market.
|
||||
// This list also includes the predefined categories shown as filters on the home page.
|
||||
tags: string[]
|
||||
|
||||
// Note: This url always points to https://manifold.markets, regardless of what instance the api is running on.
|
||||
// This url includes the creator's username, but this doesn't need to be correct when constructing valid URLs.
|
||||
// i.e. https://manifold.markets/Austin/test-market is the same as https://manifold.markets/foo/test-market
|
||||
url: string
|
||||
|
||||
pool: number
|
||||
outcomeType: string // BINARY, FREE_RESPONSE, or NUMERIC
|
||||
mechanism: string // dpm-2 or cpmm-1
|
||||
|
||||
probability: number
|
||||
pool: { outcome: number } // For CPMM markets, the number of shares in the liquidity pool. For DPM markets, the amount of mana invested in each answer.
|
||||
p?: number // CPMM markets only, probability constant in y^p * n^(1-p) = k
|
||||
totalLiquidity?: number // CPMM markets only, the amount of mana deposited into the liquidity pool
|
||||
|
||||
volume: number
|
||||
volume7Days: number
|
||||
volume24Hours: number
|
||||
|
||||
isResolved: boolean
|
||||
resolutionTime?: number
|
||||
resolution?: string
|
||||
resolutionProbability?: number // Used for BINARY markets resolved to MKT
|
||||
}
|
||||
```
|
||||
|
||||
## Get information about one market
|
||||
### `GET /v0/market/[marketId]`
|
||||
|
||||
### `/v0/market/[marketId]`
|
||||
Gets information about a single market by ID. Includes comments, bets, and answers.
|
||||
|
||||
Requires no authorization.
|
||||
|
||||
- Example request
|
||||
|
||||
|
@ -86,227 +147,204 @@ If you have questions, come chat with us on [Discord](https://discord.com/invite
|
|||
|
||||
```json
|
||||
{
|
||||
"id": "3zspH9sSzMlbFQLn9GKR",
|
||||
"creatorUsername": "Austin",
|
||||
"creatorName": "Austin Chen",
|
||||
"createdTime": 1644103005345,
|
||||
"closeTime": 1667894340000,
|
||||
"question": "Will Carrick Flynn win the general election for Oregon's 6th District?",
|
||||
"description": "The Effective Altruism movement usually stays out of politics, but here is a recent, highly-upvoted endorsement of donating to Carrick Flynn as a high-impact area: https://forum.effectivealtruism.org/posts/Qi9nnrmjwNbBqWbNT/the-best-usd5-800-i-ve-ever-donated-to-pandemic-prevention\nFurther reading: https://ballotpedia.org/Oregon%27s_6th_Congressional_District_election,_2022\n\n#EffectiveAltruism #Politics",
|
||||
"tags": ["EffectiveAltruism", "Politics"],
|
||||
"url": "https://manifold.markets/Austin/will-carrick-flynn-win-the-general",
|
||||
"pool": 400.0916328426886,
|
||||
"probability": 0.34455568984059187,
|
||||
"volume7Days": 326.9083671573114,
|
||||
"id": "lEoqtnDgJzft6apSKzYK",
|
||||
"creatorUsername": "Angela",
|
||||
"creatorName": "Angela",
|
||||
"createdTime": 1655258914863,
|
||||
"creatorAvatarUrl": "https://firebasestorage.googleapis.com/v0/b/mantic-markets.appspot.com/o/user-images%2FAngela%2F50463444807_edfd4598d6_o.jpeg?alt=media&token=ef44e13b-2e6c-4498-b9c4-8e38bdaf1476",
|
||||
"closeTime": 1655265001448,
|
||||
"question": "What is good?",
|
||||
"description": "Resolves proportionally to the answer(s) which I find most compelling. (Obviously I’ll refrain from giving my own answers)\n\n(Please have at it with philosophy, ethics, etc etc)\n\n\nContract resolved automatically.",
|
||||
"tags": [],
|
||||
"url": "https://manifold.markets/Angela/what-is-good",
|
||||
"pool": null,
|
||||
"outcomeType": "FREE_RESPONSE",
|
||||
"mechanism": "dpm-2",
|
||||
"volume": 112,
|
||||
"volume7Days": 212,
|
||||
"volume24Hours": 0,
|
||||
"isResolved": false,
|
||||
"bets": [
|
||||
"isResolved": true,
|
||||
"resolution": "MKT",
|
||||
"resolutionTime": 1655265001448,
|
||||
"answers": [
|
||||
{
|
||||
"createdTime": 1644103005345,
|
||||
"isAnte": true,
|
||||
"shares": 83.66600265340756,
|
||||
"userId": "igi2zGXsfxYPgB0DJTXVJVmwCOr2",
|
||||
"amount": 70,
|
||||
"probAfter": 0.3,
|
||||
"probBefore": 0.3,
|
||||
"id": "E1MjiVYBM0GkqRXhv5cR",
|
||||
"outcome": "NO",
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR"
|
||||
"createdTime": 1655258941573,
|
||||
"avatarUrl": "https://firebasestorage.googleapis.com/v0/b/mantic-markets.appspot.com/o/user-images%2FAngela%2F50463444807_edfd4598d6_o.jpeg?alt=media&token=ef44e13b-2e6c-4498-b9c4-8e38bdaf1476",
|
||||
"id": "1",
|
||||
"username": "Angela",
|
||||
"number": 1,
|
||||
"name": "Angela",
|
||||
"contractId": "lEoqtnDgJzft6apSKzYK",
|
||||
"text": "ANTE",
|
||||
"userId": "qe2QqIlOkeWsbljfeF3MsxpSJ9i2",
|
||||
"probability": 0.66749733001068
|
||||
},
|
||||
{
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"probAfter": 0.3,
|
||||
"shares": 54.77225575051661,
|
||||
"userId": "igi2zGXsfxYPgB0DJTXVJVmwCOr2",
|
||||
"isAnte": true,
|
||||
"createdTime": 1644103005345,
|
||||
"id": "jn3iIGwD5f0vxOHxo62o",
|
||||
"amount": 30,
|
||||
"probBefore": 0.3,
|
||||
"outcome": "YES"
|
||||
"name": "Isaac King",
|
||||
"username": "IsaacKing",
|
||||
"text": "This answer",
|
||||
"userId": "y1hb6k7txdZPV5mgyxPFApZ7nQl2",
|
||||
"id": "2",
|
||||
"number": 2,
|
||||
"avatarUrl": "https://lh3.googleusercontent.com/a-/AOh14GhNVriOvxK2VUAmE-jvYZwC-XIymatzVirT0Bqb2g=s96-c",
|
||||
"contractId": "lEoqtnDgJzft6apSKzYK",
|
||||
"createdTime": 1655261198074,
|
||||
"probability": 0.008922214311142757
|
||||
},
|
||||
{
|
||||
"shares": 11.832723364874056,
|
||||
"probAfter": 0.272108843537415,
|
||||
"userId": "PkBnU8cAZiOLa0fjxiUzMKsFMYZ2",
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"outcome": "NO",
|
||||
"amount": 10,
|
||||
"id": "f6sHBab6lbGw9PsnVXdc",
|
||||
"probBefore": 0.3,
|
||||
"createdTime": 1644203305863
|
||||
"createdTime": 1655263226587,
|
||||
"userId": "jbgplxty4kUKIa1MmgZk22byJq03",
|
||||
"id": "3",
|
||||
"avatarUrl": "https://firebasestorage.googleapis.com/v0/b/mantic-markets.appspot.com/o/user-images%2FMartin%2Fgiphy.gif?alt=media&token=422ef610-553f-47e3-bf6f-c0c5cc16c70a",
|
||||
"text": "Toyota Camry",
|
||||
"contractId": "lEoqtnDgJzft6apSKzYK",
|
||||
"name": "Undox",
|
||||
"username": "Undox",
|
||||
"number": 3,
|
||||
"probability": 0.008966714133143469
|
||||
},
|
||||
{
|
||||
"userId": "BTksWMdCeHfDitWVaAZdjLSdu3o1",
|
||||
"amount": 10,
|
||||
"id": "Vfui2KOQwy7gkRPP7xc6",
|
||||
"shares": 18.12694184700382,
|
||||
"outcome": "YES",
|
||||
"createdTime": 1644212358699,
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"probBefore": 0.272108843537415,
|
||||
"probAfter": 0.3367768595041322
|
||||
},
|
||||
{
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"probAfter": 0.3659259259259259,
|
||||
"userId": "BTksWMdCeHfDitWVaAZdjLSdu3o1",
|
||||
"probBefore": 0.3367768595041322,
|
||||
"amount": 5,
|
||||
"outcome": "YES",
|
||||
"createdTime": 1644433184238,
|
||||
"id": "eGI1VwAWF822LkcmOUot",
|
||||
"shares": 8.435122540124937
|
||||
},
|
||||
{
|
||||
"userId": "NHA7Gv9nNpb7b60GpLD3oFkBvPa2",
|
||||
"shares": 59.79133423528123,
|
||||
"amount": 50,
|
||||
"probAfter": 0.24495867768595042,
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"createdTime": 1644693685223,
|
||||
"probBefore": 0.3659259259259259,
|
||||
"id": "fbU0DbmDWMnubggpQotw",
|
||||
"outcome": "NO"
|
||||
},
|
||||
{
|
||||
"amount": 25,
|
||||
"userId": "iXw2OSyhs0c4QW2fAfK3yqmaYDv1",
|
||||
"probAfter": 0.20583333333333328,
|
||||
"outcome": "NO",
|
||||
"shares": 28.3920247989266,
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"createdTime": 1644695698202,
|
||||
"id": "k9hyljJD3MMXK2OYxTsR",
|
||||
"probBefore": 0.24495867768595042
|
||||
},
|
||||
{
|
||||
"createdTime": 1644716782308,
|
||||
"shares": 11.17480183821209,
|
||||
"probBefore": 0.20583333333333328,
|
||||
"userId": "clvYFhVDzccYu20OUc5NBKJyDxj2",
|
||||
"probAfter": 0.1927679500520291,
|
||||
"id": "yYkZ4JpLgZHrRQUugpCD",
|
||||
"outcome": "NO",
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"amount": 10
|
||||
},
|
||||
{
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"outcome": "YES",
|
||||
"amount": 30,
|
||||
"id": "IU2Hb1DesgKIN140BkhE",
|
||||
"shares": 58.893424111838016,
|
||||
"createdTime": 1644736846538,
|
||||
"probBefore": 0.1927679500520291,
|
||||
"userId": "BTksWMdCeHfDitWVaAZdjLSdu3o1",
|
||||
"probAfter": 0.3289359861591695
|
||||
},
|
||||
{
|
||||
"isSold": true,
|
||||
"userId": "5zeWhzi9nlNNf5C9TVjshAN7QOd2",
|
||||
"createdTime": 1644751343436,
|
||||
"outcome": "NO",
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||
"amount": 25,
|
||||
"probBefore": 0.3289359861591695,
|
||||
"id": "fkCxVH7THaDbEhyJjXVk",
|
||||
"probAfter": 0.2854194032651529,
|
||||
"shares": 30.022082866721178
|
||||
},
|
||||
{
|
||||
"probAfter": 0.2838618650900295,
|
||||
"id": "Ao05LRRMXVWw8d7LtwhL",
|
||||
"outcome": "NO",
|
||||
"probBefore": 0.2854194032651529,
|
||||
"shares": 1.1823269994736165,
|
||||
"userId": "pUF3dMs9oLNpgU2LYtFmodaoDow1",
|
||||
"amount": 1,
|
||||
"contractId": "3zspH9sSzMlbFQLn9GKR",
|
||||