Schedule cloud function to update user metrics: totalPnL, creatorVolume
This commit is contained in:
parent
673c432bb9
commit
56c7e2597d
|
@ -7,4 +7,6 @@ export type User = {
|
|||
balance: number
|
||||
createdTime: number
|
||||
lastUpdatedTime: number
|
||||
totalPnLCached: number
|
||||
creatorVolumeCached: number
|
||||
}
|
||||
|
|
|
@ -10,3 +10,4 @@ export * from './stripe'
|
|||
export * from './sell-bet'
|
||||
export * from './create-contract'
|
||||
export * from './update-contract-metrics'
|
||||
export * from './update-user-metrics'
|
||||
|
|
|
@ -4,6 +4,14 @@ import Stripe from 'stripe'
|
|||
|
||||
import { payUser } from './utils'
|
||||
|
||||
export type StripeTransaction = {
|
||||
userId: string
|
||||
manticDollarQuantity: number
|
||||
sessionId: string
|
||||
session: any
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
const stripe = new Stripe(functions.config().stripe.apikey, {
|
||||
apiVersion: '2020-08-27',
|
||||
typescript: true,
|
||||
|
@ -111,12 +119,15 @@ const issueMoneys = async (session: any) => {
|
|||
const { userId, manticDollarQuantity } = session.metadata
|
||||
const payout = Number.parseInt(manticDollarQuantity)
|
||||
|
||||
await firestore.collection('stripe-transactions').add({
|
||||
const transaction: StripeTransaction = {
|
||||
userId,
|
||||
manticDollarQuantity: payout, // save as number
|
||||
sessionId,
|
||||
session,
|
||||
})
|
||||
timestamp: Date.now(),
|
||||
}
|
||||
|
||||
await firestore.collection('stripe-transactions').add(transaction)
|
||||
|
||||
await payUser(userId, payout)
|
||||
|
||||
|
|
85
functions/src/update-user-metrics.ts
Normal file
85
functions/src/update-user-metrics.ts
Normal file
|
@ -0,0 +1,85 @@
|
|||
import * as functions from 'firebase-functions'
|
||||
import * as admin from 'firebase-admin'
|
||||
import * as _ from 'lodash'
|
||||
|
||||
import { getValues } from './utils'
|
||||
import { Contract } from '../../common/contract'
|
||||
import { Bet } from '../../common/bet'
|
||||
import { User } from '../../common/user'
|
||||
import { calculatePayout } from '../../common/calculate'
|
||||
import { StripeTransaction } from '.'
|
||||
|
||||
const firestore = admin.firestore()
|
||||
|
||||
export const updateUserMetrics = functions.pubsub
|
||||
.schedule('every 15 minutes')
|
||||
.onRun(async () => {
|
||||
const [users, contracts] = await Promise.all([
|
||||
getValues<User>(firestore.collection('users')),
|
||||
getValues<Contract>(firestore.collection('contracts')),
|
||||
])
|
||||
|
||||
const contractsDict = _.fromPairs(
|
||||
contracts.map((contract) => [contract.id, contract])
|
||||
)
|
||||
|
||||
await Promise.all(
|
||||
users.map(async (user) => {
|
||||
const investmentValue = await computeInvestmentValue(
|
||||
user,
|
||||
contractsDict
|
||||
)
|
||||
const deposits = await getValues<StripeTransaction>(
|
||||
firestore
|
||||
.collection('stripe-transactions')
|
||||
.where('userId', '==', user.id)
|
||||
)
|
||||
const totalDeposits =
|
||||
1000 + _.sumBy(deposits, (deposit) => deposit.manticDollarQuantity)
|
||||
const totalValue = user.balance + investmentValue
|
||||
|
||||
const totalPnL = totalValue - totalDeposits
|
||||
|
||||
const creatorVolume = await computeTotalVolume(user, contractsDict)
|
||||
|
||||
return firestore.collection('users').doc(user.id).update({
|
||||
totalPnLCached: totalPnL,
|
||||
creatorVolumeCached: creatorVolume,
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
const computeInvestmentValue = async (
|
||||
user: User,
|
||||
contractsDict: _.Dictionary<Contract>
|
||||
) => {
|
||||
const query = firestore.collectionGroup('bets').where('userId', '==', user.id)
|
||||
const bets = await getValues<Bet>(query)
|
||||
|
||||
return _.sumBy(bets, (bet) => {
|
||||
const contract = contractsDict[bet.contractId]
|
||||
if (!contract || contract.isResolved) return 0
|
||||
if (bet.sale || bet.isSold) return 0
|
||||
|
||||
return calculatePayout(contract, bet, 'MKT')
|
||||
})
|
||||
}
|
||||
|
||||
const computeTotalVolume = async (
|
||||
user: User,
|
||||
contractsDict: _.Dictionary<Contract>
|
||||
) => {
|
||||
const creatorContracts = Object.values(contractsDict).filter(
|
||||
(contract) => contract.creatorId === user.id
|
||||
)
|
||||
const volumes = await Promise.all(creatorContracts.map(computeVolume))
|
||||
return _.sum(volumes)
|
||||
}
|
||||
|
||||
const computeVolume = async (contract: Contract) => {
|
||||
const bets = await getValues<Bet>(
|
||||
firestore.collection(`contracts/${contract.id}/bets`)
|
||||
)
|
||||
return _.sumBy(bets, (bet) => Math.abs(bet.amount))
|
||||
}
|
|
@ -74,6 +74,8 @@ export function listenForLogin(onUser: (user: User | null) => void) {
|
|||
// TODO: use Firestore timestamp?
|
||||
createdTime: Date.now(),
|
||||
lastUpdatedTime: Date.now(),
|
||||
totalPnLCached: 0,
|
||||
creatorVolumeCached: 0,
|
||||
}
|
||||
await setUser(fbUser.uid, user)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user