Skip to content

Commit

Permalink
feat: add unrealized P&L (#197)
Browse files Browse the repository at this point in the history
* feat: add unrealized P&L
Fixes #174

* chore: preprod envs index from june 10th midnight
  • Loading branch information
filo87 authored Jun 10, 2024
1 parent 345f743 commit 43f43b0
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 4 deletions.
2 changes: 1 addition & 1 deletion chains-cfg/demo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ network:
file: ./dist/chaintypes.js
dataSources:
- kind: substrate/Runtime
startBlock: 2291900 # April 1st 2024
startBlock: 2758750 # June 10th 2024
2 changes: 1 addition & 1 deletion chains-cfg/development.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ network:
file: ./dist/chaintypes.js
dataSources:
- kind: substrate/Runtime
startBlock: 6870 # block first pool was created at
startBlock: 1093800 # June 10th 2024
filter:
modulo: 1000
2 changes: 1 addition & 1 deletion chains-evm/eth/demo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ network:
endpoint: "https://eth-sepolia.api.onfinality.io/rpc?apikey=${ONFINALITY_API_KEY}"
dataSources:
- kind: ethereum/Runtime
startBlock: 5602400 # April 1st 2024
startBlock: 6074950 # June 10th 2024
options:
address: '0x5c8657b827a138D52a4e3f03683A28B1FaD86893'
11 changes: 11 additions & 0 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ type Pool @entity {
sumPoolFeesPendingAmount: BigInt #Applies to All

sumRealizedProfitFifoByPeriod: BigInt
sumUnrealizedProfitAtMarketPrice: BigInt
sumUnrealizedProfitAtNotional: BigInt

# Cumulated transaction data since pool creation
sumBorrowedAmount: BigInt
Expand Down Expand Up @@ -126,6 +128,8 @@ type PoolSnapshot @entity {
sumPoolFeesPendingAmount: BigInt #Applies to All

sumRealizedProfitFifoByPeriod: BigInt
sumUnrealizedProfitAtMarketPrice: BigInt
sumUnrealizedProfitAtNotional: BigInt

# Cumulated transaction data since pool creation
sumBorrowedAmount: BigInt
Expand Down Expand Up @@ -415,6 +419,7 @@ type Asset @entity {
presentValue: BigInt
currentPrice: BigInt
outstandingQuantity: BigInt
notional: BigInt

actualMaturityDate: Date
timeToMaturity: Int
Expand All @@ -436,6 +441,9 @@ type Asset @entity {
writtenOffAmountByPeriod: BigInt
penaltyInterestRatePerSec: BigInt

unrealizedProfitAtMarketPrice: BigInt
unrealizedProfitAtNotional: BigInt

positions: [AssetPosition] @derivedFrom(field: "asset")
}

Expand Down Expand Up @@ -471,6 +479,9 @@ type AssetSnapshot @entity {
writtenOffPercentageByPeriod: BigInt
writtenOffAmountByPeriod: BigInt
penaltyInterestRatePerSec: BigInt

unrealizedProfitAtMarketPrice: BigInt
unrealizedProfitAtNotional: BigInt
}

type AssetPosition @entity {
Expand Down
2 changes: 2 additions & 0 deletions src/helpers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ export interface LoanPricing extends Enum {
asExternal: {
priceId: CfgOracleKey
maxBorrowAmount: LoanExternalPricingMaxBorrowAmount
notional: u128,
maxPriceVariation: u128
}
}

Expand Down
10 changes: 9 additions & 1 deletion src/mappings/handlers/blockHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
Tranche,
TrancheSnapshot,
} from '../../types/models'
import { AssetPositionService } from '../services/assetPositionService'

const timekeeper = TimekeeperService.init()

Expand Down Expand Up @@ -61,11 +62,18 @@ async function _handleBlock(block: SubstrateBlock): Promise<void> {
// Asset operations
const activeLoanData = await pool.getPortfolio()
pool.resetOffchainCashValue()
pool.resetUnrealizedProfit()
for (const loanId in activeLoanData) {
const asset = await AssetService.getById(pool.id, loanId)
await asset.updateActiveAssetData(activeLoanData[loanId])
await pool.increaseInterestAccrued(asset.interestAccruedByPeriod)
await asset.updateUnrealizedProfit(
await AssetPositionService.computeUnrealizedProfitAtPrice(asset.id, asset.currentPrice),
await AssetPositionService.computeUnrealizedProfitAtPrice(asset.id, asset.notional)
)
await asset.save()
await pool.increaseInterestAccrued(asset.interestAccruedByPeriod)
if (asset.isNonCash())
pool.increaseUnrealizedProfit(asset.unrealizedProfitAtMarketPrice, asset.unrealizedProfitAtNotional)
if (asset.actualMaturityDate < block.timestamp) pool.increaseDebtOverdue(asset.outstandingDebt)
if (asset.isOffchainCash()) pool.increaseOffchainCashValue(asset.presentValue)
}
Expand Down
2 changes: 2 additions & 0 deletions src/mappings/handlers/loansHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ async function _handleLoanCreated(event: SubstrateEvent<LoanCreatedEvent>) {

const isInternal = loanInfo.pricing.isInternal
const internalLoanPricing = isInternal ? loanInfo.pricing.asInternal : null
const externalLoanPricing = !isInternal ? loanInfo.pricing.asExternal : null

const assetType: AssetType =
isInternal && internalLoanPricing.valuationMethod.isCash ? AssetType.OffchainCash : AssetType.Other
Expand Down Expand Up @@ -71,6 +72,7 @@ async function _handleLoanCreated(event: SubstrateEvent<LoanCreatedEvent>) {
maturityDate: loanInfo.schedule.maturity.isFixed
? new Date(loanInfo.schedule.maturity.asFixed.date.toNumber() * 1000)
: null,
notional: !isInternal ? externalLoanPricing.notional.toBigInt() : null,
}

await asset.updateAssetSpecs(assetSpecs)
Expand Down
19 changes: 19 additions & 0 deletions src/mappings/services/assetPositionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export class AssetPositionService extends AssetPosition {
`Selling positions for ${assetId} ` +
`sellingQuantity: ${sellingQuantity.toString(10)} sellingPrice: ${sellingPrice.toString(10)}`
)
if (sellingQuantity <= BigInt(0)) return BigInt(0)
const positions = await this.getByAssetId(assetId)
positions.sort((a, b) => b.timestamp.valueOf() - a.timestamp.valueOf())

Expand Down Expand Up @@ -62,4 +63,22 @@ export class AssetPositionService extends AssetPosition {
await Promise.all(dbUpdates)
return profitFromSale - costOfBuy
}

static async computeUnrealizedProfitAtPrice(assetId: string, sellingPrice: bigint) {
if (!sellingPrice || sellingPrice <= BigInt(0)) return BigInt(0)
logger.info(`Computing unrealizedProfit at price ${sellingPrice} for asset ${assetId}`)
const sellingPositions = await this.getByAssetId(assetId)
const sellingQuantity = sellingPositions.reduce<bigint>(
(result, position) => result + position.holdingQuantity,
BigInt(0)
)
const profitFromSale = nToBigInt(bnToBn(sellingPrice).mul(bnToBn(sellingQuantity)).div(WAD))
const costOfBuy = nToBigInt(
sellingPositions.reduce<BN>(
(totalCost, line) => totalCost.add(bnToBn(line.purchasePrice).mul(bnToBn(line.holdingQuantity).div(WAD))),
new BN(0)
)
)
return profitFromSale - costOfBuy
}
}
13 changes: 13 additions & 0 deletions src/mappings/services/assetService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export class AssetService extends Asset {
asset.presentValue = BigInt(0)
asset.outstandingQuantity = BigInt(0)
asset.currentPrice = BigInt(0)
asset.notional = BigInt(0)
asset.writeOffPercentage = BigInt(0)
asset.totalBorrowed = BigInt(0)
asset.totalRepaid = BigInt(0)
Expand All @@ -56,6 +57,9 @@ export class AssetService extends Asset {
asset.writtenOffAmountByPeriod = BigInt(0)
asset.interestAccruedByPeriod = BigInt(0)

asset.unrealizedProfitAtMarketPrice = BigInt(0)
asset.unrealizedProfitAtNotional = BigInt(0)

return asset
}

Expand Down Expand Up @@ -207,6 +211,14 @@ export class AssetService extends Asset {
`currentPrice: ${latestSettlementPrice.toString(10)} for asset ${this.id}`
)
}

public updateUnrealizedProfit(atMarketPrice: bigint, atNotional: bigint) {
logger.info(
`Updating unrealizedProfit for asset ${this.id} atMarketPrice: ${atMarketPrice} atNotional: ${atNotional}`
)
this.unrealizedProfitAtMarketPrice = atMarketPrice
this.unrealizedProfitAtNotional = atNotional
}
}

interface AssetSpecs {
Expand All @@ -217,6 +229,7 @@ interface AssetSpecs {
discountRate?: bigint
maturityDate?: Date
currentPrice?: bigint
notional?: bigint
}

interface AssetIpfsMetadata {
Expand Down
15 changes: 15 additions & 0 deletions src/mappings/services/poolService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ export class PoolService extends Pool {
this.sumPoolFeesPaidAmountByPeriod = BigInt(0)
this.deltaPortfolioValuationByPeriod = BigInt(0)
this.sumInterestAccruedByPeriod = BigInt(0)

this.sumRealizedProfitFifoByPeriod = BigInt(0)
this.sumUnrealizedProfitAtMarketPrice = BigInt(0)
this.sumUnrealizedProfitAtNotional = BigInt(0)

this.sumBorrowedAmount = BigInt(0)
this.sumRepaidAmount = BigInt(0)
Expand Down Expand Up @@ -402,6 +405,18 @@ export class PoolService extends Pool {
logger.info(`Increasing umRealizedProfitFifoByPeriod for pool ${this.id} by ${amount.toString(10)}`)
this.sumRealizedProfitFifoByPeriod += amount
}

public resetUnrealizedProfit() {
logger.info(`Resetting unrealizedProfit for pool ${this.id}`)
this.sumUnrealizedProfitAtMarketPrice = BigInt(0)
this.sumUnrealizedProfitAtNotional = BigInt(0)
}

public increaseUnrealizedProfit(atMarket: bigint, atNotional: bigint) {
logger.info(`Increasing unrealizedProfit for pool ${this.id} atMarket: ${atMarket} notional: ${atNotional}`)
this.sumUnrealizedProfitAtMarketPrice += atMarket
this.sumUnrealizedProfitAtNotional += atNotional
}
}

export interface ActiveLoanData {
Expand Down

0 comments on commit 43f43b0

Please sign in to comment.