Move loans calculation to /common
This commit is contained in:
parent
a142093494
commit
e9d72e57a4
89
common/loans.ts
Normal file
89
common/loans.ts
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import { Dictionary, groupBy, sumBy, minBy } from 'lodash'
|
||||||
|
import { Bet } from './bet'
|
||||||
|
import { getContractBetMetrics } from './calculate'
|
||||||
|
import { Contract, CPMMContract, FreeResponseContract, MultipleChoiceContract } from './contract'
|
||||||
|
import { filterDefined } from './util/array'
|
||||||
|
|
||||||
|
export const getUserLoanUpdates = (
|
||||||
|
bets: Bet[],
|
||||||
|
contractsById: Dictionary<Contract>
|
||||||
|
) => {
|
||||||
|
const betsByContract = groupBy(bets, (bet) => bet.contractId)
|
||||||
|
const contracts = filterDefined(
|
||||||
|
Object.keys(betsByContract).map((contractId) => contractsById[contractId])
|
||||||
|
)
|
||||||
|
|
||||||
|
const betUpdates = filterDefined(
|
||||||
|
contracts
|
||||||
|
.map((c) => {
|
||||||
|
if (c.mechanism === 'cpmm-1') {
|
||||||
|
return getBinaryContractLoanUpdate(c, betsByContract[c.id])
|
||||||
|
} else if (
|
||||||
|
c.outcomeType === 'FREE_RESPONSE' ||
|
||||||
|
c.outcomeType === 'MULTIPLE_CHOICE'
|
||||||
|
)
|
||||||
|
return getFreeResponseContractLoanUpdate(c, betsByContract[c.id])
|
||||||
|
else {
|
||||||
|
// Unsupported contract / mechanism for loans.
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flat()
|
||||||
|
)
|
||||||
|
|
||||||
|
const totalNewLoan = sumBy(betUpdates, (loanUpdate) => loanUpdate.loanTotal)
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalNewLoan,
|
||||||
|
betUpdates,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getBinaryContractLoanUpdate = (contract: CPMMContract, bets: Bet[]) => {
|
||||||
|
const { invested } = getContractBetMetrics(contract, bets)
|
||||||
|
const loanAmount = sumBy(bets, (bet) => bet.loanAmount ?? 0)
|
||||||
|
const oldestBet = minBy(bets, (bet) => bet.createdTime)
|
||||||
|
|
||||||
|
const newLoan = calculateNewLoan(invested, loanAmount)
|
||||||
|
if (isNaN(newLoan) || newLoan <= 0 || !oldestBet) return undefined
|
||||||
|
|
||||||
|
const loanTotal = (oldestBet.loanAmount ?? 0) + newLoan
|
||||||
|
|
||||||
|
return {
|
||||||
|
userId: oldestBet.userId,
|
||||||
|
contractId: contract.id,
|
||||||
|
betId: oldestBet.id,
|
||||||
|
newLoan,
|
||||||
|
loanTotal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFreeResponseContractLoanUpdate = (
|
||||||
|
contract: FreeResponseContract | MultipleChoiceContract,
|
||||||
|
bets: Bet[]
|
||||||
|
) => {
|
||||||
|
const openBets = bets.filter((bet) => bet.isSold || bet.sale)
|
||||||
|
|
||||||
|
return openBets.map((bet) => {
|
||||||
|
const loanAmount = bet.loanAmount ?? 0
|
||||||
|
const newLoan = calculateNewLoan(bet.amount, loanAmount)
|
||||||
|
const loanTotal = loanAmount + newLoan
|
||||||
|
|
||||||
|
if (isNaN(newLoan) || newLoan <= 0) return undefined
|
||||||
|
|
||||||
|
return {
|
||||||
|
userId: bet.userId,
|
||||||
|
contractId: contract.id,
|
||||||
|
betId: bet.id,
|
||||||
|
newLoan,
|
||||||
|
loanTotal,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const LOAN_WEEKLY_RATE = 0.05
|
||||||
|
|
||||||
|
const calculateNewLoan = (investedValue: number, loanTotal: number) => {
|
||||||
|
const netValue = investedValue - loanTotal
|
||||||
|
return netValue * LOAN_WEEKLY_RATE
|
||||||
|
}
|
|
@ -1,17 +1,12 @@
|
||||||
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 { Dictionary, groupBy, keyBy, minBy, sumBy } from 'lodash'
|
import { groupBy, keyBy, sumBy } from 'lodash'
|
||||||
import { getValues, log, payUser, writeAsync } from './utils'
|
import { getValues, log, payUser, writeAsync } from './utils'
|
||||||
import { Bet } from '../../common/bet'
|
import { Bet } from '../../common/bet'
|
||||||
import {
|
import { Contract } from '../../common/contract'
|
||||||
Contract,
|
|
||||||
CPMMContract,
|
|
||||||
FreeResponseContract,
|
|
||||||
MultipleChoiceContract,
|
|
||||||
} from '../../common/contract'
|
|
||||||
import { PortfolioMetrics, User } from '../../common/user'
|
import { PortfolioMetrics, User } from '../../common/user'
|
||||||
import { filterDefined } from '../../common/util/array'
|
import { filterDefined } from '../../common/util/array'
|
||||||
import { getContractBetMetrics } from '../../common/calculate'
|
import { getUserLoanUpdates } from '../../common/loans'
|
||||||
import { createLoanIncomeNotification } from './create-notification'
|
import { createLoanIncomeNotification } from './create-notification'
|
||||||
|
|
||||||
const firestore = admin.firestore()
|
const firestore = admin.firestore()
|
||||||
|
@ -112,87 +107,3 @@ const isUserEligibleForLoan = async (user: User) => {
|
||||||
const { balance, investmentValue } = portfolio
|
const { balance, investmentValue } = portfolio
|
||||||
return balance + investmentValue > 0
|
return balance + investmentValue > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
const getUserLoanUpdates = (
|
|
||||||
bets: Bet[],
|
|
||||||
contractsById: Dictionary<Contract>
|
|
||||||
) => {
|
|
||||||
const betsByContract = groupBy(bets, (bet) => bet.contractId)
|
|
||||||
const contracts = filterDefined(
|
|
||||||
Object.keys(betsByContract).map((contractId) => contractsById[contractId])
|
|
||||||
)
|
|
||||||
|
|
||||||
const betUpdates = filterDefined(
|
|
||||||
contracts
|
|
||||||
.map((c) => {
|
|
||||||
if (c.mechanism === 'cpmm-1') {
|
|
||||||
return getBinaryContractLoanUpdate(c, betsByContract[c.id])
|
|
||||||
} else if (
|
|
||||||
c.outcomeType === 'FREE_RESPONSE' ||
|
|
||||||
c.outcomeType === 'MULTIPLE_CHOICE'
|
|
||||||
)
|
|
||||||
return getFreeResponseContractLoanUpdate(c, betsByContract[c.id])
|
|
||||||
else {
|
|
||||||
// Unsupported contract / mechanism for loans.
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.flat()
|
|
||||||
)
|
|
||||||
|
|
||||||
const totalNewLoan = sumBy(betUpdates, (loanUpdate) => loanUpdate.loanTotal)
|
|
||||||
|
|
||||||
return {
|
|
||||||
totalNewLoan,
|
|
||||||
betUpdates,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getBinaryContractLoanUpdate = (contract: CPMMContract, bets: Bet[]) => {
|
|
||||||
const { invested } = getContractBetMetrics(contract, bets)
|
|
||||||
const loanAmount = sumBy(bets, (bet) => bet.loanAmount ?? 0)
|
|
||||||
const oldestBet = minBy(bets, (bet) => bet.createdTime)
|
|
||||||
|
|
||||||
const newLoan = calculateNewLoan(invested, loanAmount)
|
|
||||||
if (isNaN(newLoan) || newLoan <= 0 || !oldestBet) return undefined
|
|
||||||
|
|
||||||
const loanTotal = (oldestBet.loanAmount ?? 0) + newLoan
|
|
||||||
|
|
||||||
return {
|
|
||||||
userId: oldestBet.userId,
|
|
||||||
contractId: contract.id,
|
|
||||||
betId: oldestBet.id,
|
|
||||||
newLoan,
|
|
||||||
loanTotal,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getFreeResponseContractLoanUpdate = (
|
|
||||||
contract: FreeResponseContract | MultipleChoiceContract,
|
|
||||||
bets: Bet[]
|
|
||||||
) => {
|
|
||||||
const openBets = bets.filter((bet) => bet.isSold || bet.sale)
|
|
||||||
|
|
||||||
return openBets.map((bet) => {
|
|
||||||
const loanAmount = bet.loanAmount ?? 0
|
|
||||||
const newLoan = calculateNewLoan(bet.amount, loanAmount)
|
|
||||||
const loanTotal = loanAmount + newLoan
|
|
||||||
|
|
||||||
if (isNaN(newLoan) || newLoan <= 0) return undefined
|
|
||||||
|
|
||||||
return {
|
|
||||||
userId: bet.userId,
|
|
||||||
contractId: contract.id,
|
|
||||||
betId: bet.id,
|
|
||||||
newLoan,
|
|
||||||
loanTotal,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const LOAN_WEEKLY_RATE = 0.05
|
|
||||||
|
|
||||||
const calculateNewLoan = (investedValue: number, loanTotal: number) => {
|
|
||||||
const netValue = investedValue - loanTotal
|
|
||||||
return netValue * LOAN_WEEKLY_RATE
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user