DEV Community

Johan Wirakarsa, Ph.D.
Johan Wirakarsa, Ph.D.

Posted on

Modeling Liquidity Without a Misleading Score — Johan Wirakarsa, Ph.D.

Consider a data model with one convenient field: liquidity_score. It is easy to sort and chart, but difficult to interpret. Does it describe market depth, funding access, or cash on a balance sheet? Those mechanisms can move independently. One score hides the question the data should answer.

A better schema keeps the distinctions visible. This Python example uses only the standard library. Its values are synthetic and demonstrate structure, not real market conditions.

from dataclasses import dataclass
from enum import Enum

class FundingState(str, Enum):
OPEN = "open"
SELECTIVE = "selective"
CONSTRAINED = "constrained"

@dataclass(frozen=True)
class MarketDepth:
spread_bps: float
executable_units: int

@dataclass(frozen=True)
class FundingAccess:
state: FundingState
annual_cost_pct: float

@dataclass(frozen=True)
class BalanceSheetCash:
cash_units: float
near_term_obligations: float

def coverage(self) -> float:
    if self.near_term_obligations <= 0:
        raise ValueError("obligations must be positive")
    return self.cash_units / self.near_term_obligations
Enter fullscreen mode Exit fullscreen mode

@dataclass(frozen=True)
class LiquiditySnapshot:
market: MarketDepth
funding: FundingAccess
cash: BalanceSheetCash

sample = LiquiditySnapshot(
MarketDepth(12.0, 800),
FundingAccess(FundingState.SELECTIVE, 6.2),
BalanceSheetCash(150.0, 100.0),
)

print(sample.funding.state.value)
print(f"{sample.cash.coverage():.2f}x")

The output is selective and 1.50x.

This object never pretends that the measurements share a unit. A spread change should not silently offset cash coverage simply because both values were normalized and averaged.

Typed fields also make contracts reviewable. A static type checker can catch incorrect shapes, external input can be parsed through FundingState, and validation can stay close to each dimension. A production version should model missing observations explicitly instead of filling them silently.

If a composite indicator is required, I would calculate it in a separate, versioned function and retain the inputs, units, weights, and assumptions. Aggregation then becomes an explicit output, not the source of truth.

Liquidity is context, not a verdict. The schema should represent its mechanisms first and postpone interpretation until the consuming application has a defined question.

Disclaimer: This example is for software-design education only. It uses synthetic data and is not financial or investment advice.

Top comments (0)