Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New NAV Metrics for Payments and Total NAV #597

Merged
merged 2 commits into from
Mar 10, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.jetbrains.exposed.sql.insertIgnore
import org.jetbrains.exposed.sql.javatime.JavaLocalDateTimeColumnType
import org.jetbrains.exposed.sql.javatime.datetime
import org.jetbrains.exposed.sql.transactions.transaction
import java.math.BigDecimal
import java.sql.ResultSet
import java.time.LocalDateTime

Expand Down Expand Up @@ -180,6 +181,55 @@ class NavEventsRecord(id: EntityID<Int>) : IntEntity(id) {
)
}
}

fun navPricesBetweenDays(
startDateTime: LocalDateTime,
endDateTime: LocalDateTime
) = transaction {
val query = """
select c.denom,c.source, c.scope_id,
c.price_amount as current_amount,
c.volume as current_volume,
max(c.block_time) as current_block_time,
p.price_amount as previous_amount,
p.volume as previous_volume,
p.block_time as previous_block_time
from nav_events c,
(select p.scope_id, p.price_amount, p.volume, max(p.block_time) as block_time
from nav_events p
where p.source = 'metadata'
and date_trunc('DAYS', block_time) = ?
group by p.scope_id, p.price_amount, p.volume) as p
where c.source = 'metadata'
and date_trunc('DAYS', c.block_time) = ?
and c.scope_id = p.scope_id
group by c.denom, c.source, c.scope_id, c.price_amount, c.volume, p.price_amount, p.volume, p.block_time
""".trimIndent()

val args = mutableListOf<Pair<ColumnType, *>>(
Pair(JavaLocalDateTimeColumnType(), startDateTime),
Pair(JavaLocalDateTimeColumnType(), endDateTime)
)
query.execAndMap(args) {
val map = mutableMapOf<String, Any?>()
(1..it.metaData.columnCount).forEach { index ->
map[it.metaData.getColumnName(index)] = it.getObject(index)
}
map // return a list of map of column name/value
}
}

fun totalMetadataNavs() = transaction {
val query = """
select sum(price_amount)
from (select scope_id, price_amount, row_number() over (partition by scope_id order by block_height desc) as r
from nav_events where source = 'metadata' and price_amount > 0) s
where r = 1
""".trimIndent()
query.execAndMap {
BigDecimal(it.getString(1))
}.firstOrNull() ?: BigDecimal.ZERO
}
}

var blockHeight by NavEventsTable.blockHeight
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ enum class PulseCacheType {
PULSE_MARKET_CAP_METRIC,
PULSE_TRANSACTION_VOLUME_METRIC,
PULSE_FEES_AUCTIONS_METRIC,
PULSE_RECEIVABLES_METRIC,
PULSE_TODAYS_NAV_METRIC,
PULSE_NAV_DECREASE_METRIC,
PULSE_TOTAL_NAV_METRIC,
PULSE_TRADE_SETTLEMENT_METRIC,
PULSE_TRADE_VALUE_SETTLED_METRIC,
PULSE_PARTICIPANTS_METRIC,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class PulseMetricService(
private val validatorService: ValidatorService,
private val pricingService: PricingService,
private val assetService: AssetService,
private val exchangeGrpcClient: ExchangeGrpcClient
private val exchangeGrpcClient: ExchangeGrpcClient,
) {
protected val logger = logger(PulseMetricService::class)

Expand All @@ -69,6 +69,11 @@ class PulseMetricService(
maximumSize(100)
}.build()

/* so it turns out that the `usd` in metadata nav events
use 3 decimal places - :|
*/
private val scopeNAVDecimal = inversePowerOfTen(3)

val base = UTILITY_TOKEN
val quote = USD_UPPER

Expand Down Expand Up @@ -264,9 +269,9 @@ class PulseMetricService(
* Uses metadata module reported values to calculate receivables since
* all data in metadata today is loan receivables
*/
private fun pulseReceivableValue(): PulseMetric =
private fun pulseTodaysNavs(): PulseMetric =
fetchOrBuildCacheFromDataSource(
type = PulseCacheType.PULSE_RECEIVABLES_METRIC
type = PulseCacheType.PULSE_TODAYS_NAV_METRIC
) { // TODO technically correct assuming only metadata nav events are receivables
NavEventsRecord.getNavEvents(
fromDate = LocalDateTime.now().startOfDay(),
Expand All @@ -278,16 +283,48 @@ class PulseMetricService(
it.scopeId
}
.sumOf { it.priceAmount!! }.toBigDecimal().let {
/* so it turns out that the `usd` in metadata nav events
use 3 decimal places - :|
*/
PulseMetric.build(
base = USD_UPPER,
amount = it.times(inversePowerOfTen(3))
amount = it.times(scopeNAVDecimal)
)
}
}

private fun dailyNavDecrease(): PulseMetric =
fetchOrBuildCacheFromDataSource(
type = PulseCacheType.PULSE_NAV_DECREASE_METRIC
) {
val today = LocalDateTime.now().startOfDay()
NavEventsRecord.navPricesBetweenDays(
startDateTime = today.minusDays(1),
endDateTime = today
).map {
val currentAmount = BigDecimal(it["current_amount"].toString())
val previousAmount = BigDecimal(it["previous_amount"].toString())
val changeAmount = previousAmount.minus(currentAmount)

Triple(currentAmount, previousAmount, changeAmount)
}.filter { it.third >= BigDecimal.ZERO }
.sumOf { it.third }.let {
PulseMetric.build(
base = USD_UPPER,
amount = it.times(scopeNAVDecimal)
)
}
}

private fun totalMetadataNavs(): PulseMetric =
fetchOrBuildCacheFromDataSource(
type = PulseCacheType.PULSE_TOTAL_NAV_METRIC
) {
NavEventsRecord.totalMetadataNavs().let {
PulseMetric.build(
base = USD_UPPER,
amount = it.times(scopeNAVDecimal)
)
}
}

/**
* Retrieves the transaction volume for the last 30 days to build
* metric chart data
Expand Down Expand Up @@ -510,7 +547,11 @@ class PulseMetricService(
PulseCacheType.HASH_SUPPLY_METRIC -> hashMetric(type)
PulseCacheType.PULSE_MARKET_CAP_METRIC -> pulseMarketCap()
PulseCacheType.PULSE_TRANSACTION_VOLUME_METRIC -> transactionVolume()
PulseCacheType.PULSE_RECEIVABLES_METRIC -> pulseReceivableValue()

PulseCacheType.PULSE_TODAYS_NAV_METRIC -> pulseTodaysNavs()
PulseCacheType.PULSE_NAV_DECREASE_METRIC -> dailyNavDecrease()
PulseCacheType.PULSE_TOTAL_NAV_METRIC -> totalMetadataNavs()

PulseCacheType.PULSE_TRADE_SETTLEMENT_METRIC -> pulseTradesSettled()
PulseCacheType.PULSE_TRADE_VALUE_SETTLED_METRIC -> pulseTradeValueSettled()
PulseCacheType.PULSE_PARTICIPANTS_METRIC -> totalParticipants()
Expand Down
Loading