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
|
balance: number
|
||||||
createdTime: number
|
createdTime: number
|
||||||
lastUpdatedTime: number
|
lastUpdatedTime: number
|
||||||
|
totalPnLCached: number
|
||||||
|
creatorVolumeCached: number
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,3 +10,4 @@ export * from './stripe'
|
||||||
export * from './sell-bet'
|
export * from './sell-bet'
|
||||||
export * from './create-contract'
|
export * from './create-contract'
|
||||||
export * from './update-contract-metrics'
|
export * from './update-contract-metrics'
|
||||||
|
export * from './update-user-metrics'
|
||||||
|
|
|
@ -4,6 +4,14 @@ import Stripe from 'stripe'
|
||||||
|
|
||||||
import { payUser } from './utils'
|
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, {
|
const stripe = new Stripe(functions.config().stripe.apikey, {
|
||||||
apiVersion: '2020-08-27',
|
apiVersion: '2020-08-27',
|
||||||
typescript: true,
|
typescript: true,
|
||||||
|
@ -111,12 +119,15 @@ const issueMoneys = async (session: any) => {
|
||||||
const { userId, manticDollarQuantity } = session.metadata
|
const { userId, manticDollarQuantity } = session.metadata
|
||||||
const payout = Number.parseInt(manticDollarQuantity)
|
const payout = Number.parseInt(manticDollarQuantity)
|
||||||
|
|
||||||
await firestore.collection('stripe-transactions').add({
|
const transaction: StripeTransaction = {
|
||||||
userId,
|
userId,
|
||||||
manticDollarQuantity: payout, // save as number
|
manticDollarQuantity: payout, // save as number
|
||||||
sessionId,
|
sessionId,
|
||||||
session,
|
session,
|
||||||
})
|
timestamp: Date.now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
await firestore.collection('stripe-transactions').add(transaction)
|
||||||
|
|
||||||
await payUser(userId, payout)
|
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?
|
// TODO: use Firestore timestamp?
|
||||||
createdTime: Date.now(),
|
createdTime: Date.now(),
|
||||||
lastUpdatedTime: Date.now(),
|
lastUpdatedTime: Date.now(),
|
||||||
|
totalPnLCached: 0,
|
||||||
|
creatorVolumeCached: 0,
|
||||||
}
|
}
|
||||||
await setUser(fbUser.uid, user)
|
await setUser(fbUser.uid, user)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user