Skip to content

Commit

Permalink
Merge pull request #95 from casesandberg/feature/counts-on-market-and…
Browse files Browse the repository at this point in the history
…-options

Counts on market and options
  • Loading branch information
casesandberg authored Sep 4, 2024
2 parents de46956 + 9eaa15d commit 7ea621f
Show file tree
Hide file tree
Showing 32 changed files with 432 additions and 217 deletions.
2 changes: 1 addition & 1 deletion packages/api-helpers/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export async function getMyBalance() {

export async function getMarkets({ tag }: { tag?: string } = {}) {
return apiHandler<{
markets: Array<ExtendedMarket & { commentCount: number; liquidityCount: number; uniqueTraderCount: number }>
markets: Array<ExtendedMarket>
}>(`${process.env.NEXT_PUBLIC_API_URL}/v1/markets${tag ? `?tag=${tag}` : ''}`, {
next: { tags: ['markets'] },
})
Expand Down
13 changes: 13 additions & 0 deletions packages/comments/lib/createComment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ export async function createComment({
})
}

if (entityType === 'MARKET') {
await db.market.update({
where: {
id: entityId,
},
data: {
commentCount: {
increment: 1,
},
},
})
}

// TODO switch this to watchers of the market.
const recipientIds = await getUniqueLiquidityProviderIds(market.id, [authorId, comment.parent?.authorId])

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- AlterTable
ALTER TABLE "Market" ADD COLUMN "commentCount" DECIMAL(65,30),
ADD COLUMN "liquidityCount" DECIMAL(65,30),
ADD COLUMN "uniquePromotersCount" DECIMAL(65,30),
ADD COLUMN "uniqueTradersCount" DECIMAL(65,30);

-- AlterTable
ALTER TABLE "MarketOption" ADD COLUMN "probability" DECIMAL(65,30);
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
Warnings:
- You are about to alter the column `commentCount` on the `Market` table. The data in that column could be lost. The data in that column will be cast from `Decimal(65,30)` to `Integer`.
- You are about to alter the column `liquidityCount` on the `Market` table. The data in that column could be lost. The data in that column will be cast from `Decimal(65,30)` to `Integer`.
- You are about to alter the column `uniquePromotersCount` on the `Market` table. The data in that column could be lost. The data in that column will be cast from `Decimal(65,30)` to `Integer`.
- You are about to alter the column `uniqueTradersCount` on the `Market` table. The data in that column could be lost. The data in that column will be cast from `Decimal(65,30)` to `Integer`.
- You are about to alter the column `probability` on the `MarketOption` table. The data in that column could be lost. The data in that column will be cast from `Decimal(65,30)` to `Integer`.
*/
-- AlterTable
ALTER TABLE "Market" ALTER COLUMN "commentCount" SET DATA TYPE INTEGER,
ALTER COLUMN "liquidityCount" SET DATA TYPE INTEGER,
ALTER COLUMN "uniquePromotersCount" SET DATA TYPE INTEGER,
ALTER COLUMN "uniqueTradersCount" SET DATA TYPE INTEGER;

-- AlterTable
ALTER TABLE "MarketOption" ALTER COLUMN "probability" SET DATA TYPE INTEGER;
9 changes: 7 additions & 2 deletions packages/database/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ export function mockMarket(overrides?: Partial<Market>): Market {
createdBy: faker.string.uuid(),
createdAt: faker.date.past(),
updatedAt: faker.date.recent(),
liquidityCount: parseInt(faker.string.numeric({ length: { min: 4, max: 5 } })),
commentCount: parseInt(faker.string.numeric({ length: { min: 0, max: 1 } })),
uniqueTradersCount: parseInt(faker.string.numeric({ length: { min: 0, max: 1 } })),
uniquePromotersCount: parseInt(faker.string.numeric({ length: { min: 0, max: 1 } })),
...overrides,
}
}
Expand All @@ -80,8 +84,8 @@ export function mockExtendedMarket(overrides?: Partial<ExtendedMarket>): Extende
createdBy: user.id,
})

const yesOption = { ...mockMarketOption({ id: '1', name: 'Yes', marketId: market.id }), probability: 0.65 }
const noOption = { ...mockMarketOption({ id: '2', name: 'No', marketId: market.id }), probability: 0.35 }
const yesOption = mockMarketOption({ id: '1', name: 'Yes', marketId: market.id, probability: 0.65 })
const noOption = mockMarketOption({ id: '2', name: 'No', marketId: market.id, probability: 0.35 })

return {
...market,
Expand Down Expand Up @@ -169,6 +173,7 @@ export function mockMarketOption(overrides?: Partial<MarketOption>): MarketOptio
marketId: faker.string.uuid(),
createdAt: faker.date.past(),
updatedAt: faker.date.past(),
probability: 0.5,
...overrides,
}
}
Expand Down
12 changes: 10 additions & 2 deletions packages/database/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,14 @@ model Market {
marketResolution MarketResolution?
balances Balance[]
positions MarketOptionPosition[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Cached values
commentCount Int?
uniqueTradersCount Int?
uniquePromotersCount Int?
liquidityCount Int?
// Unnecessary reverse relations
notifications Notification[] @relation()
Expand Down Expand Up @@ -135,6 +140,9 @@ model MarketOption {
positions MarketOptionPosition[]
transactions Transaction[]
// Cached values
probability Int?
// Unnecessary reverse relations
notifications Notification[] @relation()
}
Expand Down
186 changes: 186 additions & 0 deletions packages/database/scripts/backfill-counts-on-markets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
import { getMarketBalances } from '@play-money/finance/lib/getBalances'
import { updateMarketOptionProbabilities } from '@play-money/markets/lib/updateMarketOptionProbabilities'
import db from '../prisma'

async function main() {
try {
const marketsWithoutProbabilities = await db.market.findMany({
where: {
options: {
some: {
probability: null,
},
},
},
})

console.log(`Found ${marketsWithoutProbabilities.length} markets without probabilities.`)

for await (const market of marketsWithoutProbabilities) {
try {
const balances = await getMarketBalances({ marketId: market.id, accountId: market.ammAccountId })

await db.$transaction(async (tx) => {
await updateMarketOptionProbabilities({ tx, balances, marketId: market.id })
})
console.log(`Successfully added probabilities to market with id: ${market.id}`)
} catch (updateError) {
const error = updateError as Error
console.error(`Failed to add probabilities to id: ${market.id}. Error: ${error.message}`)
}
}

const marketsWithoutLiquidity = await db.market.findMany({
where: {
liquidityCount: null,
},
})

console.log(`Found ${marketsWithoutLiquidity.length} markets without liquidity count.`)

for await (const market of marketsWithoutLiquidity) {
try {
const data = await db.transactionEntry.aggregate({
_sum: {
amount: true,
},
where: {
toAccountId: market.clearingAccountId,
assetType: 'CURRENCY',
assetId: 'PRIMARY',
transaction: {
marketId: market.id,
},
},
})

await db.market.update({
where: {
id: market.id,
},
data: {
liquidityCount: data._sum.amount?.toNumber(),
},
})
console.log(`Successfully added liquidity count to market with id: ${market.id}`)
} catch (updateError) {
const error = updateError as Error
console.error(`Failed to add liquidity count to id: ${market.id}. Error: ${error.message}`)
}
}

const marketsWithoutUniqueTraders = await db.market.findMany({
where: {
uniqueTradersCount: null,
},
})

console.log(`Found ${marketsWithoutUniqueTraders.length} markets without unique traders count.`)

for await (const market of marketsWithoutUniqueTraders) {
try {
const data = await db.transaction.groupBy({
by: ['initiatorId'],
where: {
marketId: market.id,
type: 'TRADE_BUY',
},
_count: true,
})

await db.market.update({
where: {
id: market.id,
},
data: {
uniqueTradersCount: data.length,
},
})
console.log(`Successfully added unique traders count to market with id: ${market.id}`)
} catch (updateError) {
const error = updateError as Error
console.error(`Failed to add unique traders count to id: ${market.id}. Error: ${error.message}`)
}
}

const marketsWithoutUniquePromoters = await db.market.findMany({
where: {
uniquePromotersCount: null,
},
})

console.log(`Found ${marketsWithoutUniquePromoters.length} markets without unique promoters count.`)

for await (const market of marketsWithoutUniquePromoters) {
try {
const data = await db.transaction.groupBy({
by: ['initiatorId'],
where: {
marketId: market.id,
type: {
in: ['LIQUIDITY_DEPOSIT', 'LIQUIDITY_INITIALIZE'],
},
},
_count: true,
})

await db.market.update({
where: {
id: market.id,
},
data: {
uniquePromotersCount: data.length,
},
})
console.log(`Successfully added unique promoters count to market with id: ${market.id}`)
} catch (updateError) {
const error = updateError as Error
console.error(`Failed to add unique promoters count to id: ${market.id}. Error: ${error.message}`)
}
}

const marketsWithoutCommentsCount = await db.market.findMany({
where: {
commentCount: null,
},
})

console.log(`Found ${marketsWithoutCommentsCount.length} markets without comments count.`)

for await (const market of marketsWithoutCommentsCount) {
try {
const data = await db.comment.aggregate({
where: {
entityId: market.id,
},
_count: true,
})

await db.market.update({
where: {
id: market.id,
},
data: {
commentCount: data._count,
},
})
console.log(`Successfully added comments count to market with id: ${market.id}`)
} catch (updateError) {
const error = updateError as Error
console.error(`Failed to add comments count to id: ${market.id}. Error: ${error.message}`)
}
}
} catch (fetchError) {
const error = fetchError as Error
console.error(`An error occurred while fetching markets: ${error.message}`)
} finally {
await db.$disconnect()
console.log('Database connection closed.')
}
}

main().catch((e) => {
const error = e as Error
console.error(`Unexpected error: ${error.message}`)
process.exit(1)
})
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { z } from 'zod';

export const MarketOptionScalarFieldEnumSchema = z.enum(['id','name','marketId','color','liquidityProbability','createdAt','updatedAt']);
export const MarketOptionScalarFieldEnumSchema = z.enum(['id','name','marketId','color','liquidityProbability','createdAt','updatedAt','probability']);

export default MarketOptionScalarFieldEnumSchema;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { z } from 'zod';

export const MarketScalarFieldEnumSchema = z.enum(['id','question','description','slug','closeDate','resolvedAt','createdBy','tags','ammAccountId','clearingAccountId','createdAt','updatedAt']);
export const MarketScalarFieldEnumSchema = z.enum(['id','question','description','slug','closeDate','resolvedAt','createdBy','tags','ammAccountId','clearingAccountId','createdAt','updatedAt','commentCount','uniqueTradersCount','uniquePromotersCount','liquidityCount']);

export default MarketScalarFieldEnumSchema;
1 change: 1 addition & 0 deletions packages/database/zod/modelSchema/MarketOptionSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const MarketOptionSchema = z.object({
liquidityProbability: z.instanceof(Prisma.Decimal, { message: "Field 'liquidityProbability' must be a Decimal. Location: ['Models', 'MarketOption']"}),
createdAt: z.coerce.date(),
updatedAt: z.coerce.date(),
probability: z.number().int().nullable(),
})

export type MarketOption = z.infer<typeof MarketOptionSchema>
Expand Down
4 changes: 4 additions & 0 deletions packages/database/zod/modelSchema/MarketSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export const MarketSchema = z.object({
clearingAccountId: z.string(),
createdAt: z.coerce.date(),
updatedAt: z.coerce.date(),
commentCount: z.number().int().nullable(),
uniqueTradersCount: z.number().int().nullable(),
uniquePromotersCount: z.number().int().nullable(),
liquidityCount: z.number().int().nullable(),
})

export type Market = z.infer<typeof MarketSchema>
Expand Down
12 changes: 9 additions & 3 deletions packages/finance/lib/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,19 @@ export function marketOptionBalancesToProbabilities(balances: Array<NetBalance |

return assetBalances.reduce(
(result, assetBalance, i) => {
result[assetBalance.assetId] = calculateProbability({
const probability = calculateProbability({
index: i,
shares: assetBalances.map((balance) => balance.total),
})
.times(100)
.round()
.toNumber()
.toDecimalPlaces(2)

if (probability.isNaN()) {
result[assetBalance.assetId] = 0
} else {
result[assetBalance.assetId] = probability.toNumber()
}

return result
},
{} as Record<string, number>
Expand Down
3 changes: 2 additions & 1 deletion packages/finance/lib/updateBalance.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Decimal from 'decimal.js'
import { TransactionClient } from '@play-money/database'
import { AssetTypeType } from '@play-money/database/zod/inputTypeSchemas/AssetTypeSchema'
import { NetBalance } from './getBalances'

export async function updateBalance({
tx,
Expand Down Expand Up @@ -46,5 +47,5 @@ export async function updateBalance({
subtotals,
createdAt: new Date(),
},
})
}) as unknown as NetBalance
}
4 changes: 2 additions & 2 deletions packages/markets/components/MarketBuyForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import React, { useState, useEffect } from 'react'
import { useForm } from 'react-hook-form'
import z from 'zod'
import { createMarketBuy, getMarketQuote } from '@play-money/api-helpers/client'
import { MarketOption } from '@play-money/database'
import { Button } from '@play-money/ui/button'
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@play-money/ui/form'
import { Input } from '@play-money/ui/input'
import { toast } from '@play-money/ui/use-toast'
import { cn } from '@play-money/ui/utils'
import { ExtendedMarketOption } from '../types'

const FormSchema = z.object({
amount: z.coerce.number().min(1, { message: 'Amount must be greater than zero' }),
Expand All @@ -26,7 +26,7 @@ export function MarketBuyForm({
onComplete,
}: {
marketId: string
option: ExtendedMarketOption
option: MarketOption
hasOutcome?: boolean
onComplete?: () => void
}) {
Expand Down
Loading

0 comments on commit 7ea621f

Please sign in to comment.