<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Johan Wirakarsa, Ph.D.</title>
    <description>The latest articles on DEV Community by Johan Wirakarsa, Ph.D. (@johanwirakarsaphd).</description>
    <link>https://dev.to/johanwirakarsaphd</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4125367%2F10c8c378-9cef-4004-b067-68033a311142.png</url>
      <title>DEV Community: Johan Wirakarsa, Ph.D.</title>
      <link>https://dev.to/johanwirakarsaphd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/johanwirakarsaphd"/>
    <language>en</language>
    <item>
      <title>Modeling Liquidity Without a Misleading Score — Johan Wirakarsa, Ph.D.</title>
      <dc:creator>Johan Wirakarsa, Ph.D.</dc:creator>
      <pubDate>Tue, 15 Sep 2026 03:51:39 +0000</pubDate>
      <link>https://dev.to/johanwirakarsaphd/modeling-liquidity-without-a-misleading-score-johan-wirakarsa-phd-238c</link>
      <guid>https://dev.to/johanwirakarsaphd/modeling-liquidity-without-a-misleading-score-johan-wirakarsa-phd-238c</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flriwr1wp0a65uwc99khs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flriwr1wp0a65uwc99khs.png" alt=" " width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;from dataclasses import dataclass&lt;br&gt;
from enum import Enum&lt;/p&gt;

&lt;p&gt;class FundingState(str, Enum):&lt;br&gt;
    OPEN = "open"&lt;br&gt;
    SELECTIVE = "selective"&lt;br&gt;
    CONSTRAINED = "constrained"&lt;/p&gt;

&lt;p&gt;@dataclass(frozen=True)&lt;br&gt;
class MarketDepth:&lt;br&gt;
    spread_bps: float&lt;br&gt;
    executable_units: int&lt;/p&gt;

&lt;p&gt;@dataclass(frozen=True)&lt;br&gt;
class FundingAccess:&lt;br&gt;
    state: FundingState&lt;br&gt;
    annual_cost_pct: float&lt;/p&gt;

&lt;p&gt;@dataclass(frozen=True)&lt;br&gt;
class BalanceSheetCash:&lt;br&gt;
    cash_units: float&lt;br&gt;
    near_term_obligations: float&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def coverage(self) -&amp;gt; float:
    if self.near_term_obligations &amp;lt;= 0:
        raise ValueError("obligations must be positive")
    return self.cash_units / self.near_term_obligations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;@dataclass(frozen=True)&lt;br&gt;
class LiquiditySnapshot:&lt;br&gt;
    market: MarketDepth&lt;br&gt;
    funding: FundingAccess&lt;br&gt;
    cash: BalanceSheetCash&lt;/p&gt;

&lt;p&gt;sample = LiquiditySnapshot(&lt;br&gt;
    MarketDepth(12.0, 800),&lt;br&gt;
    FundingAccess(FundingState.SELECTIVE, 6.2),&lt;br&gt;
    BalanceSheetCash(150.0, 100.0),&lt;br&gt;
)&lt;/p&gt;

&lt;p&gt;print(sample.funding.state.value)&lt;br&gt;
print(f"{sample.cash.coverage():.2f}x")&lt;/p&gt;

&lt;p&gt;The output is selective and 1.50x.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

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

&lt;p&gt;Disclaimer: This example is for software-design education only. It uses synthetic data and is not financial or investment advice.&lt;/p&gt;

</description>
      <category>dataclasses</category>
      <category>dataengineering</category>
      <category>python</category>
      <category>johanwirakarsaphd</category>
    </item>
  </channel>
</rss>
