Use invested instead of sale value, check if eligible, perform payouts
This commit is contained in:
parent
ef7763eb63
commit
dc7cf8bca6
|
@ -1,21 +1,19 @@
|
||||||
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 { getValues, log, writeAsync } from './utils'
|
import { getValues, log, payUser, writeAsync } from './utils'
|
||||||
import { Bet, LimitBet } from 'common/bet'
|
import { Bet } from 'common/bet'
|
||||||
import { Contract, CPMMContract, FreeResponseContract } from 'common/contract'
|
import { Contract, CPMMContract, FreeResponseContract } from 'common/contract'
|
||||||
import { User } from 'common/user'
|
import { PortfolioMetrics, User } from 'common/user'
|
||||||
import { Dictionary, groupBy, keyBy, minBy, sumBy } from 'lodash'
|
import { Dictionary, groupBy, keyBy, minBy, sumBy } from 'lodash'
|
||||||
import { filterDefined } from 'common/util/array'
|
import { filterDefined } from 'common/util/array'
|
||||||
import { getContractBetMetrics } from 'common/calculate'
|
import { getContractBetMetrics } from 'common/calculate'
|
||||||
import { calculateCpmmSale } from 'common/calculate-cpmm'
|
|
||||||
import { calculateDpmSaleAmount } from 'common/calculate-dpm'
|
|
||||||
|
|
||||||
const firestore = admin.firestore()
|
const firestore = admin.firestore()
|
||||||
|
|
||||||
export const updateLoans = functions
|
export const updateLoans = functions
|
||||||
.runWith({ memory: '1GB', timeoutSeconds: 540 })
|
.runWith({ memory: '1GB', timeoutSeconds: 540 })
|
||||||
// Run every Sunday, at 11:59pm.
|
// Run every Monday morning.
|
||||||
.pubsub.schedule('59 11 * * 0')
|
.pubsub.schedule('0 2 * * 1')
|
||||||
.timeZone('America/Los_Angeles')
|
.timeZone('America/Los_Angeles')
|
||||||
.onRun(updateLoansCore)
|
.onRun(updateLoansCore)
|
||||||
|
|
||||||
|
@ -31,10 +29,20 @@ async function updateLoansCore() {
|
||||||
`Loaded ${users.length} users, ${contracts.length} contracts, and ${bets.length} bets.`
|
`Loaded ${users.length} users, ${contracts.length} contracts, and ${bets.length} bets.`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const eligibleUsers = filterDefined(
|
||||||
|
await Promise.all(
|
||||||
|
users.map((user) =>
|
||||||
|
isUserEligibleForLoan(user).then((isEligible) =>
|
||||||
|
isEligible ? user : undefined
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
const contractsById = keyBy(contracts, (contract) => contract.id)
|
const contractsById = keyBy(contracts, (contract) => contract.id)
|
||||||
const betsByUser = groupBy(bets, (bet) => bet.userId)
|
const betsByUser = groupBy(bets, (bet) => bet.userId)
|
||||||
|
|
||||||
const userLoanUpdates = users
|
const userLoanUpdates = eligibleUsers
|
||||||
.map(
|
.map(
|
||||||
(user) =>
|
(user) =>
|
||||||
getUserLoanUpdates(betsByUser[user.id] ?? [], contractsById).betUpdates
|
getUserLoanUpdates(betsByUser[user.id] ?? [], contractsById).betUpdates
|
||||||
|
@ -53,6 +61,35 @@ async function updateLoansCore() {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
await writeAsync(firestore, betUpdates)
|
await writeAsync(firestore, betUpdates)
|
||||||
|
|
||||||
|
const userPayouts = eligibleUsers
|
||||||
|
.map((user) => {
|
||||||
|
const updates = userLoanUpdates.filter(
|
||||||
|
(update) => update.userId === user.id
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
userId: user.id,
|
||||||
|
delta: sumBy(updates, (update) => update.newLoan),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter((update) => update.delta > 0)
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
userPayouts.map(({ userId, delta }) => payUser(userId, delta))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const isUserEligibleForLoan = async (user: User) => {
|
||||||
|
const [portfolio] = await getValues<PortfolioMetrics>(
|
||||||
|
firestore
|
||||||
|
.collection(`users/${user.id}/portfolioHistory`)
|
||||||
|
.orderBy('timestamp', 'desc')
|
||||||
|
.limit(1)
|
||||||
|
)
|
||||||
|
if (!portfolio) return true
|
||||||
|
|
||||||
|
const { balance, investmentValue } = portfolio
|
||||||
|
return balance + investmentValue > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
const getUserLoanUpdates = (
|
const getUserLoanUpdates = (
|
||||||
|
@ -87,23 +124,11 @@ const getUserLoanUpdates = (
|
||||||
}
|
}
|
||||||
|
|
||||||
const getBinaryContractLoanUpdate = (contract: CPMMContract, bets: Bet[]) => {
|
const getBinaryContractLoanUpdate = (contract: CPMMContract, bets: Bet[]) => {
|
||||||
const { totalShares } = getContractBetMetrics(contract, bets)
|
const { invested } = getContractBetMetrics(contract, bets)
|
||||||
const { YES, NO } = totalShares
|
|
||||||
|
|
||||||
const shares = YES || NO
|
|
||||||
const outcome = YES ? 'YES' : 'NO'
|
|
||||||
|
|
||||||
const unfilledBets: LimitBet[] = []
|
|
||||||
const { saleValue } = calculateCpmmSale(
|
|
||||||
contract,
|
|
||||||
shares,
|
|
||||||
outcome,
|
|
||||||
unfilledBets
|
|
||||||
)
|
|
||||||
const loanAmount = sumBy(bets, (bet) => bet.loanAmount ?? 0)
|
const loanAmount = sumBy(bets, (bet) => bet.loanAmount ?? 0)
|
||||||
const oldestBet = minBy(bets, (bet) => bet.createdTime)
|
const oldestBet = minBy(bets, (bet) => bet.createdTime)
|
||||||
|
|
||||||
const newLoan = calculateNewLoan(saleValue, loanAmount)
|
const newLoan = calculateNewLoan(invested, loanAmount)
|
||||||
if (newLoan <= 0 || !oldestBet) return undefined
|
if (newLoan <= 0 || !oldestBet) return undefined
|
||||||
|
|
||||||
const loanTotal = (oldestBet.loanAmount ?? 0) + newLoan
|
const loanTotal = (oldestBet.loanAmount ?? 0) + newLoan
|
||||||
|
@ -121,10 +146,11 @@ const getFreeResponseContractLoanUpdate = (
|
||||||
contract: FreeResponseContract,
|
contract: FreeResponseContract,
|
||||||
bets: Bet[]
|
bets: Bet[]
|
||||||
) => {
|
) => {
|
||||||
return bets.map((bet) => {
|
const openBets = bets.filter((bet) => bet.isSold || bet.sale)
|
||||||
const saleValue = calculateDpmSaleAmount(contract, bet)
|
|
||||||
|
return openBets.map((bet) => {
|
||||||
const loanAmount = bet.loanAmount ?? 0
|
const loanAmount = bet.loanAmount ?? 0
|
||||||
const newLoan = calculateNewLoan(saleValue, loanAmount)
|
const newLoan = calculateNewLoan(bet.amount, loanAmount)
|
||||||
const loanTotal = loanAmount + newLoan
|
const loanTotal = loanAmount + newLoan
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -139,7 +165,7 @@ const getFreeResponseContractLoanUpdate = (
|
||||||
|
|
||||||
const LOAN_WEEKLY_RATE = 0.05
|
const LOAN_WEEKLY_RATE = 0.05
|
||||||
|
|
||||||
const calculateNewLoan = (saleValue: number, loanTotal: number) => {
|
const calculateNewLoan = (investedValue: number, loanTotal: number) => {
|
||||||
const netValue = saleValue - loanTotal
|
const netValue = investedValue - loanTotal
|
||||||
return netValue * LOAN_WEEKLY_RATE
|
return netValue * LOAN_WEEKLY_RATE
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user