Move metrics calculation to common
This commit is contained in:
parent
af68fa6c42
commit
2f53cef36f
131
common/calculate-metrics.ts
Normal file
131
common/calculate-metrics.ts
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
import { sortBy, sum, sumBy } from 'lodash'
|
||||||
|
import { calculatePayout } from './calculate'
|
||||||
|
import { Bet } from './bet'
|
||||||
|
import { Contract } from './contract'
|
||||||
|
import { PortfolioMetrics, User } from './user'
|
||||||
|
import { DAY_MS } from './util/time'
|
||||||
|
|
||||||
|
const computeInvestmentValue = (
|
||||||
|
bets: Bet[],
|
||||||
|
contractsDict: { [k: string]: Contract }
|
||||||
|
) => {
|
||||||
|
return sumBy(bets, (bet) => {
|
||||||
|
const contract = contractsDict[bet.contractId]
|
||||||
|
if (!contract || contract.isResolved) return 0
|
||||||
|
if (bet.sale || bet.isSold) return 0
|
||||||
|
|
||||||
|
const payout = calculatePayout(contract, bet, 'MKT')
|
||||||
|
const value = payout - (bet.loanAmount ?? 0)
|
||||||
|
if (isNaN(value)) return 0
|
||||||
|
return value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const computeTotalPool = (userContracts: Contract[], startTime = 0) => {
|
||||||
|
const periodFilteredContracts = userContracts.filter(
|
||||||
|
(contract) => contract.createdTime >= startTime
|
||||||
|
)
|
||||||
|
return sum(
|
||||||
|
periodFilteredContracts.map((contract) => sum(Object.values(contract.pool)))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const computeVolume = (contractBets: Bet[], since: number) => {
|
||||||
|
return sumBy(contractBets, (b) =>
|
||||||
|
b.createdTime > since && !b.isRedemption ? Math.abs(b.amount) : 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const calculateCreatorVolume = (userContracts: Contract[]) => {
|
||||||
|
const allTimeCreatorVolume = computeTotalPool(userContracts, 0)
|
||||||
|
const monthlyCreatorVolume = computeTotalPool(
|
||||||
|
userContracts,
|
||||||
|
Date.now() - 30 * DAY_MS
|
||||||
|
)
|
||||||
|
const weeklyCreatorVolume = computeTotalPool(
|
||||||
|
userContracts,
|
||||||
|
Date.now() - 7 * DAY_MS
|
||||||
|
)
|
||||||
|
|
||||||
|
const dailyCreatorVolume = computeTotalPool(
|
||||||
|
userContracts,
|
||||||
|
Date.now() - 1 * DAY_MS
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
daily: dailyCreatorVolume,
|
||||||
|
weekly: weeklyCreatorVolume,
|
||||||
|
monthly: monthlyCreatorVolume,
|
||||||
|
allTime: allTimeCreatorVolume,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const calculateNewPortfolioMetrics = (
|
||||||
|
user: User,
|
||||||
|
contractsById: { [k: string]: Contract },
|
||||||
|
currentBets: Bet[]
|
||||||
|
) => {
|
||||||
|
const investmentValue = computeInvestmentValue(currentBets, contractsById)
|
||||||
|
const newPortfolio = {
|
||||||
|
investmentValue: investmentValue,
|
||||||
|
balance: user.balance,
|
||||||
|
totalDeposits: user.totalDeposits,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
userId: user.id,
|
||||||
|
}
|
||||||
|
return newPortfolio
|
||||||
|
}
|
||||||
|
|
||||||
|
const calculateProfitForPeriod = (
|
||||||
|
startTime: number,
|
||||||
|
descendingPortfolio: PortfolioMetrics[],
|
||||||
|
currentProfit: number
|
||||||
|
) => {
|
||||||
|
const startingPortfolio = descendingPortfolio.find(
|
||||||
|
(p) => p.timestamp < startTime
|
||||||
|
)
|
||||||
|
|
||||||
|
if (startingPortfolio === undefined) {
|
||||||
|
return currentProfit
|
||||||
|
}
|
||||||
|
|
||||||
|
const startingProfit = calculateTotalProfit(startingPortfolio)
|
||||||
|
|
||||||
|
return currentProfit - startingProfit
|
||||||
|
}
|
||||||
|
|
||||||
|
const calculateTotalProfit = (portfolio: PortfolioMetrics) => {
|
||||||
|
return portfolio.investmentValue + portfolio.balance - portfolio.totalDeposits
|
||||||
|
}
|
||||||
|
|
||||||
|
export const calculateNewProfit = (
|
||||||
|
portfolioHistory: PortfolioMetrics[],
|
||||||
|
newPortfolio: PortfolioMetrics
|
||||||
|
) => {
|
||||||
|
const allTimeProfit = calculateTotalProfit(newPortfolio)
|
||||||
|
const descendingPortfolio = sortBy(
|
||||||
|
portfolioHistory,
|
||||||
|
(p) => p.timestamp
|
||||||
|
).reverse()
|
||||||
|
|
||||||
|
const newProfit = {
|
||||||
|
daily: calculateProfitForPeriod(
|
||||||
|
Date.now() - 1 * DAY_MS,
|
||||||
|
descendingPortfolio,
|
||||||
|
allTimeProfit
|
||||||
|
),
|
||||||
|
weekly: calculateProfitForPeriod(
|
||||||
|
Date.now() - 7 * DAY_MS,
|
||||||
|
descendingPortfolio,
|
||||||
|
allTimeProfit
|
||||||
|
),
|
||||||
|
monthly: calculateProfitForPeriod(
|
||||||
|
Date.now() - 30 * DAY_MS,
|
||||||
|
descendingPortfolio,
|
||||||
|
allTimeProfit
|
||||||
|
),
|
||||||
|
allTime: allTimeProfit,
|
||||||
|
}
|
||||||
|
|
||||||
|
return newProfit
|
||||||
|
}
|
|
@ -1,42 +1,27 @@
|
||||||
import * as functions from 'firebase-functions'
|
import * as functions from 'firebase-functions'
|
||||||
import * as admin from 'firebase-admin'
|
import * as admin from 'firebase-admin'
|
||||||
import { groupBy, isEmpty, keyBy, last, sortBy, sum, sumBy } from 'lodash'
|
import { groupBy, isEmpty, keyBy, last } from 'lodash'
|
||||||
import { getValues, log, logMemory, writeAsync } from './utils'
|
import { getValues, log, logMemory, writeAsync } from './utils'
|
||||||
import { Bet } from '../../common/bet'
|
import { Bet } from '../../common/bet'
|
||||||
import { Contract } from '../../common/contract'
|
import { Contract } from '../../common/contract'
|
||||||
import { PortfolioMetrics, User } from '../../common/user'
|
import { PortfolioMetrics, User } from '../../common/user'
|
||||||
import { calculatePayout } from '../../common/calculate'
|
|
||||||
import { DAY_MS } from '../../common/util/time'
|
import { DAY_MS } from '../../common/util/time'
|
||||||
import { getLoanUpdates } from '../../common/loans'
|
import { getLoanUpdates } from '../../common/loans'
|
||||||
|
import {
|
||||||
|
calculateCreatorVolume,
|
||||||
|
calculateNewPortfolioMetrics,
|
||||||
|
calculateNewProfit,
|
||||||
|
computeVolume,
|
||||||
|
} from '../../common/calculate-metrics'
|
||||||
|
|
||||||
const firestore = admin.firestore()
|
const firestore = admin.firestore()
|
||||||
|
|
||||||
const computeInvestmentValue = (
|
export const updateMetrics = functions
|
||||||
bets: Bet[],
|
.runWith({ memory: '2GB', timeoutSeconds: 540 })
|
||||||
contractsDict: { [k: string]: Contract }
|
.pubsub.schedule('every 15 minutes')
|
||||||
) => {
|
.onRun(updateMetricsCore)
|
||||||
return sumBy(bets, (bet) => {
|
|
||||||
const contract = contractsDict[bet.contractId]
|
|
||||||
if (!contract || contract.isResolved) return 0
|
|
||||||
if (bet.sale || bet.isSold) return 0
|
|
||||||
|
|
||||||
const payout = calculatePayout(contract, bet, 'MKT')
|
export async function updateMetricsCore() {
|
||||||
const value = payout - (bet.loanAmount ?? 0)
|
|
||||||
if (isNaN(value)) return 0
|
|
||||||
return value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const computeTotalPool = (userContracts: Contract[], startTime = 0) => {
|
|
||||||
const periodFilteredContracts = userContracts.filter(
|
|
||||||
(contract) => contract.createdTime >= startTime
|
|
||||||
)
|
|
||||||
return sum(
|
|
||||||
periodFilteredContracts.map((contract) => sum(Object.values(contract.pool)))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const updateMetricsCore = async () => {
|
|
||||||
const [users, contracts, bets, allPortfolioHistories] = await Promise.all([
|
const [users, contracts, bets, allPortfolioHistories] = await Promise.all([
|
||||||
getValues<User>(firestore.collection('users')),
|
getValues<User>(firestore.collection('users')),
|
||||||
getValues<Contract>(firestore.collection('contracts')),
|
getValues<Contract>(firestore.collection('contracts')),
|
||||||
|
@ -158,108 +143,3 @@ export const updateMetricsCore = async () => {
|
||||||
)
|
)
|
||||||
log(`Updated metrics for ${users.length} users.`)
|
log(`Updated metrics for ${users.length} users.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const computeVolume = (contractBets: Bet[], since: number) => {
|
|
||||||
return sumBy(contractBets, (b) =>
|
|
||||||
b.createdTime > since && !b.isRedemption ? Math.abs(b.amount) : 0
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const calculateProfitForPeriod = (
|
|
||||||
startTime: number,
|
|
||||||
descendingPortfolio: PortfolioMetrics[],
|
|
||||||
currentProfit: number
|
|
||||||
) => {
|
|
||||||
const startingPortfolio = descendingPortfolio.find(
|
|
||||||
(p) => p.timestamp < startTime
|
|
||||||
)
|
|
||||||
|
|
||||||
if (startingPortfolio === undefined) {
|
|
||||||
return currentProfit
|
|
||||||
}
|
|
||||||
|
|
||||||
const startingProfit = calculateTotalProfit(startingPortfolio)
|
|
||||||
|
|
||||||
return currentProfit - startingProfit
|
|
||||||
}
|
|
||||||
|
|
||||||
const calculateTotalProfit = (portfolio: PortfolioMetrics) => {
|
|
||||||
return portfolio.investmentValue + portfolio.balance - portfolio.totalDeposits
|
|
||||||
}
|
|
||||||
|
|
||||||
const calculateCreatorVolume = (userContracts: Contract[]) => {
|
|
||||||
const allTimeCreatorVolume = computeTotalPool(userContracts, 0)
|
|
||||||
const monthlyCreatorVolume = computeTotalPool(
|
|
||||||
userContracts,
|
|
||||||
Date.now() - 30 * DAY_MS
|
|
||||||
)
|
|
||||||
const weeklyCreatorVolume = computeTotalPool(
|
|
||||||
userContracts,
|
|
||||||
Date.now() - 7 * DAY_MS
|
|
||||||
)
|
|
||||||
|
|
||||||
const dailyCreatorVolume = computeTotalPool(
|
|
||||||
userContracts,
|
|
||||||
Date.now() - 1 * DAY_MS
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
daily: dailyCreatorVolume,
|
|
||||||
weekly: weeklyCreatorVolume,
|
|
||||||
monthly: monthlyCreatorVolume,
|
|
||||||
allTime: allTimeCreatorVolume,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const calculateNewPortfolioMetrics = (
|
|
||||||
user: User,
|
|
||||||
contractsById: { [k: string]: Contract },
|
|
||||||
currentBets: Bet[]
|
|
||||||
) => {
|
|
||||||
const investmentValue = computeInvestmentValue(currentBets, contractsById)
|
|
||||||
const newPortfolio = {
|
|
||||||
investmentValue: investmentValue,
|
|
||||||
balance: user.balance,
|
|
||||||
totalDeposits: user.totalDeposits,
|
|
||||||
timestamp: Date.now(),
|
|
||||||
userId: user.id,
|
|
||||||
}
|
|
||||||
return newPortfolio
|
|
||||||
}
|
|
||||||
|
|
||||||
const calculateNewProfit = (
|
|
||||||
portfolioHistory: PortfolioMetrics[],
|
|
||||||
newPortfolio: PortfolioMetrics
|
|
||||||
) => {
|
|
||||||
const allTimeProfit = calculateTotalProfit(newPortfolio)
|
|
||||||
const descendingPortfolio = sortBy(
|
|
||||||
portfolioHistory,
|
|
||||||
(p) => p.timestamp
|
|
||||||
).reverse()
|
|
||||||
|
|
||||||
const newProfit = {
|
|
||||||
daily: calculateProfitForPeriod(
|
|
||||||
Date.now() - 1 * DAY_MS,
|
|
||||||
descendingPortfolio,
|
|
||||||
allTimeProfit
|
|
||||||
),
|
|
||||||
weekly: calculateProfitForPeriod(
|
|
||||||
Date.now() - 7 * DAY_MS,
|
|
||||||
descendingPortfolio,
|
|
||||||
allTimeProfit
|
|
||||||
),
|
|
||||||
monthly: calculateProfitForPeriod(
|
|
||||||
Date.now() - 30 * DAY_MS,
|
|
||||||
descendingPortfolio,
|
|
||||||
allTimeProfit
|
|
||||||
),
|
|
||||||
allTime: allTimeProfit,
|
|
||||||
}
|
|
||||||
|
|
||||||
return newProfit
|
|
||||||
}
|
|
||||||
|
|
||||||
export const updateMetrics = functions
|
|
||||||
.runWith({ memory: '2GB', timeoutSeconds: 540 })
|
|
||||||
.pubsub.schedule('every 15 minutes')
|
|
||||||
.onRun(updateMetricsCore)
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user