Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Monetary policy engine: Difference between revisions

From Prosperity SMP Wiki
Initial content
 
Rewrite from locked v2 spec + deployed code audit
 
Line 1: Line 1:
The '''monetary policy engine''' is a custom-built automated economic management system on Prosperity SMP. It tracks every dollar created and destroyed on the server, computes real-time inflation metrics, and generates policy recommendations to maintain a healthy economy. It is believed to be the first implementation of classical monetary theory applied to a live Minecraft server economy.
The '''monetary policy engine''' is a custom-built automated economic management system developed specifically for Prosperity SMP. It tracks every dollar created and destroyed on the server, decomposes the money supply into policy-relevant buckets, computes a real-time inflation signal from the [[Consumer Price Index]], and generates advisory recommendations to maintain a healthy economy. The entire system — from event capture to controller logic — was purpose-built for this server.


== Overview ==
== Design philosophy ==
The engine applies the '''quantity theory of money''' the same framework used by real-world central banks — to the server economy:
Real-world central banks use the relationship between money supply, spending velocity, prices, and output to guide policy. The Prosperity SMP engine draws on the same principles but takes a deliberately simpler approach: rather than trying to estimate all four variables (which is unreliable with a small player base), it '''directly observes price changes''' through the [[Consumer Price Index|CPI]] and reacts proportionally.


<blockquote>
The engine was designed through a five-round iterative critique between two AI systems (Claude and ChatGPT). The original v1 draft attempted to directly compute inflation pressure from money supply growth minus output growth. This was rejected during the critique — velocity and output are too noisy with 10-15 players. The v2 design watches CPI directly and uses money supply data for dashboard diagnostics rather than direct control.
'''M x V = P x Q'''
 
Where M = money supply, V = velocity (turnover rate), P = price level ([[Consumer Price Index|CPI]]), and Q = real output (goods traded).
</blockquote>
 
If money supply grows faster than real output, prices must rise (inflation). If it grows slower, prices fall (deflation). The engine measures these forces in real time and recommends adjustments to keep the economy stable.


== Architecture ==
== Architecture ==
The system operates across two custom-built layers, both developed specifically for Prosperity SMP.
The system operates across two custom-built layers.


=== Skript capture layer ===
=== Skript capture layer (Minecraft server) ===
Custom Skript files running on the Minecraft server capture every money flow event in real time:
Three custom Skript files capture every money flow event in real time:


* '''treasury-grant.sk''' — a single entry point for all money creation events. Every script that pays a player routes through this wrapper, ensuring no money enters circulation without being tracked.
* '''treasury-grant.sk''' — a single entry point for all money creation. Every script that pays a player routes through this wrapper, ensuring nothing enters circulation untracked.
* '''money-supply.sk''' — classifies every Vault transaction into a five-category taxonomy, computes money supply decomposition with exponential activity decay, and runs stock-flow reconciliation.
* '''money-supply.sk''' — classifies every Vault transaction into a five-category taxonomy, computes money supply decomposition with exponential activity decay, runs stock-flow reconciliation, and exports JSON snapshots every 5 minutes.
* '''cpi-basket.sk''' — computes a winsorized geometric mean Consumer Price Index from real player-to-player trade data, with built-in manipulation defenses.
* '''cpi-basket.sk''' — computes a winsorized geometric mean [[Consumer Price Index]] from real player-to-player trade data, with manipulation defenses and per-item confidence scoring.


=== Admin Hub controller layer ===
=== Admin Hub controller layer (TypeScript) ===
A TypeScript module in the custom Next.js Admin Hub ingests Skript-exported JSON and runs the policy controller:
A custom module in the Next.js Admin Hub ingests the Skript-exported JSON and runs the policy controller:


* Computes EWMA-smoothed inflation gap
* Loads current money supply, CPI, lagged CPI, reconciliation state, and money flow events
* Evaluates confidence from data maturity, sample depth, and participant diversity
* Computes the inflation gap (observed CPI growth vs target)
* Applies proportional control with locked safety bounds
* Evaluates a confidence score from four data quality components
* Generates recommendations with reasoning traces and uncertainty bands
* Applies a proportional controller with locked safety bounds
* Persists an audit log for historical analysis and future regression
* Aggregates source-flow ratios (sink intensity, bank drain, job faucet)
* Generates a recommendation with reasoning trace and uncertainty band
* Persists an hourly audit log for historical analysis


== Event taxonomy ==
== Event taxonomy ==
Line 36: Line 32:
{| class="wikitable" style="width:100%;"
{| class="wikitable" style="width:100%;"
|-
|-
! Category !! Definition !! Effect on money supply
! Category !! Definition !! Effect on total supply
|-
|-
| '''CREATE''' || Money appears from nowhere (job wages, contract rewards, crate cash, /eco give) || Increases total supply
| '''CREATE''' || Money appears from nowhere (job wages, contract rewards, crate cash, /eco give) || Increases supply
|-
|-
| '''DESTROY''' || Money disappears (admin shop purchases, fees, /eco take) || Decreases total supply
| '''DESTROY''' || Money disappears (admin shop purchases, fees, /eco take) || Decreases supply
|-
|-
| '''TRANSFER_TO_PUBLIC''' || System account pays a player ([[Simulated ChestShop|Sim Shop]] payouts) || No change (reserve to active)
| '''TRANSFER_TO_PUBLIC''' || System pays player ([[Simulated ChestShop|Sim Shop]] payouts) || No change
|-
|-
| '''TRANSFER_FROM_PUBLIC''' || Player pays a system account (rent to system landlord) || No change (active to reserve)
| '''TRANSFER_FROM_PUBLIC''' || Player pays system (rent to system landlord) || No change
|-
|-
| '''TRANSFER_PRIVATE''' || Player pays another player ([[ChestShop]] sales, /pay) || No change (changes hands)
| '''TRANSFER_PRIVATE''' || Player pays player ([[ChestShop]] sales, /pay) || No change
|}
|}


Only CREATE and DESTROY events change the total money supply. Transfers redistribute existing money.
Currently tracked sources: Jobs payouts, /eco give and /eco take, [[Contracts|civic contract]] rewards, [[Simulated ChestShop]] payouts, and [[Admin shop]] (EconomyShopGUI) transactions. All hooks are live and producing data.


== Money supply decomposition ==
== Money supply decomposition ==
The engine does not treat all money equally. It decomposes the total into three buckets:
The engine decomposes total money into three buckets with different policy relevance:


{| class="wikitable"
{| class="wikitable"
|-
|-
! Bucket !! Definition !! Policy relevance
! Bucket !! What it contains !! Why it matters
|-
|-
| '''M_active''' || Balances of recently active players, weighted by exponential decay || The money actively chasing goods — the primary inflation signal
| '''M_active''' || Balances of players who logged in within 14 days, weighted by exponential decay || Money actively chasing goods — the primary inflation-relevant pool
|-
|-
| '''M_dormant''' || Balances of players inactive for 14+ days || Not circulating; a returning player creates a liquidity shock
| '''M_dormant''' || Balances of inactive players || Not circulating; a returning player creates a "liquidity shock"
|-
|-
| '''M_reserve''' || CentralBank and system accounts || Sterilized reserve — only inflationary when disbursed
| '''M_reserve''' || CentralBank and system accounts || Sterilized reserve — only enters circulation when disbursed
|}
|}


The effective money supply formula:
The effective supply formula:


: '''M_effective = M_active + (0.20 x M_dormant) + (lambda x M_reserve)'''
: '''M_effective = M_active + (0.20 x M_dormant) + (lambda x M_reserve)'''


Where lambda forecasts how much reserve money will enter circulation in the next 7 days. The activity decay uses an exponential curve:
Where lambda estimates how much reserve money will enter circulation in the next 7 days. The activity decay uses:


: '''weight = max(0.05, e^(-days_since_login / 14))'''
: '''weight = max(0.05, e^(-days_since_login / 14))'''


This prevents the engine from panicking when a large dormant balance reactivates.
This decomposition appears on the Admin Hub dashboard. The controller itself uses CPI data (not M directly) for recommendations — M decomposition is a diagnostic signal for admins, not a control input in v1.


== Stock-flow reconciliation ==
== Stock-flow reconciliation ==
An hourly integrity check verifies that the tracked events match reality:
An hourly integrity check ensures tracked events match reality:


: '''Change in M_total = SUM of CREATE - SUM of DESTROY'''
: '''Change in M_total = SUM of CREATE events - SUM of DESTROY events'''


If observed and predicted totals diverge by more than 2%, the engine disables all recommendations and flags the discrepancy. This prevents acting on bad data.
If the observed total diverges from the predicted total by more than 2%, the engine disables all recommendations. This is the "hard gate" — it prevents acting on bad data. As of deploy, reconciliation passes consistently with error under 0.01%.


== Controller design ==
== Controller: the actual formula ==
The v1 controller uses a '''proportional-only''' formula with conservative tuning:
The v1 controller is a '''CPI-gap proportional controller'''. It directly observes price changes and reacts:


: '''new_multiplier = old_multiplier x (1 - confidence x K_p x gap)'''
: '''gap = (current_basket_index / lagged_7d_basket_index) - 1 - target_growth'''
: '''new_multiplier = old_multiplier x (1 - confidence x 0.20 x gap)'''


Where:
In words: the engine computes how fast the CPI basket is growing relative to the admin-set target (+0.5% per week default). If prices are rising too fast, it recommends lowering the Sim Shop multiplier. If too slow, it recommends raising it. The magnitude is scaled by data confidence.
* '''gap''' = observed weekly CPI growth minus admin-set target (decimal form)
* '''K_p''' = 0.20 (proportional gain constant)
* '''confidence''' = data quality score from 0 to 1


Safety bounds:
=== Locked constants ===
* Maximum weekly change: +/- 5%
{| class="wikitable"
* Multiplier range: 0.7 to 1.3
|-
* Dead-band: if gap is smaller than 0.3%, the engine outputs NO_OP
! Parameter !! Value !! Meaning
|-
| K_p || 0.20 || Proportional gain (intentionally conservative — actuator response is unknown)
|-
| Target growth || +0.5%/week || Default healthy inflation rate
|-
| Dead-band || +/- 0.3% || Below this gap magnitude, engine outputs NO_OP
|-
| Max weekly change || +/- 5% || Hard clamp on multiplier movement per week
|-
| Multiplier bounds || 0.7 to 1.3 || Absolute floor and ceiling
|-
| Min confidence (display) || 0.30 || Below this, recommendations are suppressed
|-
| Min confidence (auto-apply) || 0.70 || v2 auto-apply threshold (not active in v1)
|}


The controller is '''advisory only''' in v1. Recommendations require manual admin approval. Auto-application requires confidence above 0.70 for four consecutive weeks.
=== Why proportional-only ===
The spec includes provisions for adding derivative (D) and integral (I) control terms later, forming a full PID controller. These are deferred because:
* The D term requires smoothed historical data (EWMA, not yet accumulated — needs ~14 days)
* The I term can accumulate error during bad-data periods and needs anti-windup
* The actuator gain (how much multiplier change produces how much CPI change) is unknown until perturbation experiments run at day 30+
 
v1 ships the simplest controller that makes correct directional decisions. More sophisticated control is a future upgrade backed by accumulated data.


== Confidence scoring ==
== Confidence scoring ==
The confidence score is a geometric mean of four components, each bounded between 0.1 and 1.0:
{| class="wikitable"
{| class="wikitable"
|-
|-
! Component !! What it measures !! Maximum (1.0) when
! Component !! Measures !! Maximum when
|-
|-
| Basket coverage || Fraction of CPI basket items with valid data || All basket items have enough trades
| Basket coverage || Fraction of CPI basket items with valid trade data || All items pass depth gates
|-
|-
| Sample depth || Average transactions per basket item || 50+ transactions per item
| Sample depth || Average transactions per basket item || 50+ transactions per item
|-
|-
| Diversity || Number of unique buyers and sellers || 8+ unique participants
| Diversity || Unique buyers + sellers across basket || 8+ unique participants
|-
|-
| Maturity || Time since server deploy || 90+ days of operation
| Maturity || Days since server deploy || 90+ days
|}
|}


: '''confidence = (coverage x depth x diversity x maturity) ^ (1/4)'''
: '''confidence = (coverage x depth x diversity x maturity) ^ (1/4)'''


The geometric mean prevents any single component from dominating — all four must be healthy for confidence to be high.
The geometric mean ensures all four components must be healthy. Each is floored at 0.1 to prevent any single zero from killing the score entirely.


== NO_OP conditions ==
== NO_OP conditions ==
The engine outputs no action when any of:
The engine outputs "no action" when any of:


# Confidence below 0.30
# Confidence below 0.30
# Gap within dead-band (+/- 0.3%)
# Gap within dead-band (+/- 0.3%)
# Reconciliation failing
# Reconciliation failing (hard gate)
# Fewer than 5 active players
# Fewer than 5 active players in 7 days
# Previous action not yet observed (one-week cooldown)
# Previous action not yet observed (one-week cooldown)
# New-player spike (3+ new players in 48 hours)
# New-player spike (3+ new players in 48 hours)
# Within first 14 days of operation (Phase A bootstrap)
# Within first 14 days of operation (Phase A bootstrap)


NO_OP is the most common output on a small server. The engine is designed to be cautious.
NO_OP is the '''most common output''' on a young server. The engine is designed to be cautious — doing nothing is better than acting on insufficient data.


== Bootstrap schedule ==
== Bootstrap schedule ==
Line 141: Line 154:
| Day 14-30 || Advisory enabled with confidence scaling. No auto-apply.
| Day 14-30 || Advisory enabled with confidence scaling. No auto-apply.
|-
|-
| Day 30-90 || Perturbation experiments to measure actuator response.
| Day 30-90 || Category-level perturbation experiments to measure actuator response.
|-
|-
| Day 90+ || Auto-apply eligible if confidence sustained above 0.70.
| Day 90+ || Auto-apply eligible if confidence sustained above 0.70.
Line 147: Line 160:


== Source-flow ratios ==
== Source-flow ratios ==
The engine computes diagnostic ratios displayed in the Admin Hub Treasury panel:
The controller also computes diagnostic ratios displayed on the Admin Hub:


{| class="wikitable"
{| class="wikitable"
|-
|-
! Ratio !! What it measures
! Ratio !! Formula !! What it tells admins
|-
| sink_ratio || (DESTROY + TRANSFER_FROM_PUBLIC) / M_active || How fast money leaves active circulation
|-
|-
| '''sink_ratio''' || How fast money is removed from circulation
| bank_drain_ratio || TRANSFER_TO_PUBLIC / M_active || How fast the bank injects liquidity
|-
|-
| '''bank_drain_ratio''' || How fast the bank injects liquidity into player hands
| jobs_faucet_ratio || CREATE from Jobs / M_active || Job wage intensity vs the active money pool
|-
|-
| '''jobs_faucet_ratio''' || Job wage intensity relative to active money pool
| net supply change || CREATE - DESTROY over 7 days || Bottom-line money supply movement
|}
|}


== Design process ==
== Design lineage ==
The engine was designed through a five-round iterative critique between two AI systems (Claude and ChatGPT), correcting three material design flaws in the original draft:
The engine spec was refined through five rounds of Claude/ChatGPT iterative critique, correcting three material flaws in the original v1 draft:


# CentralBank balance incorrectly counted as active money (corrected to sterilized reserve with lambda-weighted forecast)
# '''CentralBank counted as active money''' — corrected to sterilized reserve with lambda-weighted disbursement forecast
# Policy response was inverted — raising the target during inflation is accommodation, not control (corrected to proportional tightening)
# '''Policy response was inverted''' — the v1 draft raised the inflation target during high pressure (accommodation); corrected to proportional tightening toward a fixed target
# "Deflation via fewer purchases" was a misnomer — reducing the faucet slows growth but does not contract existing supply (corrected to "disinflation")
# '''"Deflation via fewer purchases" was mislabeled''' — reducing the faucet slows growth (disinflation) but does not contract existing supply (real deflation requires sinks)


The full technical spec is maintained in the server's internal documentation.
The locked v2 spec is maintained in the server's internal documentation.


== See also ==
== See also ==
* [[Consumer Price Index]] — the CPI basket the engine consumes
* [[Consumer Price Index]] — the CPI basket the engine observes
* [[Economy]] — how money flows through the server
* [[Economy]] — how money flows through the server
* [[Simulated ChestShop]] — the bank-funded market activity system
* [[Simulated ChestShop]] — the custom market activity system
* [[Admin shop]] — server-run shop (a money sink)
* [[Admin shop]] — server-run shop (a money sink)
* [[Prestige]] — endgame money sink
* [[Prestige]] — endgame money sink at scale


[[Category:Systems]]
[[Category:Systems]]

Latest revision as of 16:57, 27 May 2026

The monetary policy engine is a custom-built automated economic management system developed specifically for Prosperity SMP. It tracks every dollar created and destroyed on the server, decomposes the money supply into policy-relevant buckets, computes a real-time inflation signal from the Consumer Price Index, and generates advisory recommendations to maintain a healthy economy. The entire system — from event capture to controller logic — was purpose-built for this server.

Design philosophy

Real-world central banks use the relationship between money supply, spending velocity, prices, and output to guide policy. The Prosperity SMP engine draws on the same principles but takes a deliberately simpler approach: rather than trying to estimate all four variables (which is unreliable with a small player base), it directly observes price changes through the CPI and reacts proportionally.

The engine was designed through a five-round iterative critique between two AI systems (Claude and ChatGPT). The original v1 draft attempted to directly compute inflation pressure from money supply growth minus output growth. This was rejected during the critique — velocity and output are too noisy with 10-15 players. The v2 design watches CPI directly and uses money supply data for dashboard diagnostics rather than direct control.

Architecture

The system operates across two custom-built layers.

Skript capture layer (Minecraft server)

Three custom Skript files capture every money flow event in real time:

  • treasury-grant.sk — a single entry point for all money creation. Every script that pays a player routes through this wrapper, ensuring nothing enters circulation untracked.
  • money-supply.sk — classifies every Vault transaction into a five-category taxonomy, computes money supply decomposition with exponential activity decay, runs stock-flow reconciliation, and exports JSON snapshots every 5 minutes.
  • cpi-basket.sk — computes a winsorized geometric mean Consumer Price Index from real player-to-player trade data, with manipulation defenses and per-item confidence scoring.

Admin Hub controller layer (TypeScript)

A custom module in the Next.js Admin Hub ingests the Skript-exported JSON and runs the policy controller:

  • Loads current money supply, CPI, lagged CPI, reconciliation state, and money flow events
  • Computes the inflation gap (observed CPI growth vs target)
  • Evaluates a confidence score from four data quality components
  • Applies a proportional controller with locked safety bounds
  • Aggregates source-flow ratios (sink intensity, bank drain, job faucet)
  • Generates a recommendation with reasoning trace and uncertainty band
  • Persists an hourly audit log for historical analysis

Event taxonomy

Every balance change on the server is classified into exactly one of five categories:

Category Definition Effect on total supply
CREATE Money appears from nowhere (job wages, contract rewards, crate cash, /eco give) Increases supply
DESTROY Money disappears (admin shop purchases, fees, /eco take) Decreases supply
TRANSFER_TO_PUBLIC System pays player (Sim Shop payouts) No change
TRANSFER_FROM_PUBLIC Player pays system (rent to system landlord) No change
TRANSFER_PRIVATE Player pays player (ChestShop sales, /pay) No change

Currently tracked sources: Jobs payouts, /eco give and /eco take, civic contract rewards, Simulated ChestShop payouts, and Admin shop (EconomyShopGUI) transactions. All hooks are live and producing data.

Money supply decomposition

The engine decomposes total money into three buckets with different policy relevance:

Bucket What it contains Why it matters
M_active Balances of players who logged in within 14 days, weighted by exponential decay Money actively chasing goods — the primary inflation-relevant pool
M_dormant Balances of inactive players Not circulating; a returning player creates a "liquidity shock"
M_reserve CentralBank and system accounts Sterilized reserve — only enters circulation when disbursed

The effective supply formula:

M_effective = M_active + (0.20 x M_dormant) + (lambda x M_reserve)

Where lambda estimates how much reserve money will enter circulation in the next 7 days. The activity decay uses:

weight = max(0.05, e^(-days_since_login / 14))

This decomposition appears on the Admin Hub dashboard. The controller itself uses CPI data (not M directly) for recommendations — M decomposition is a diagnostic signal for admins, not a control input in v1.

Stock-flow reconciliation

An hourly integrity check ensures tracked events match reality:

Change in M_total = SUM of CREATE events - SUM of DESTROY events

If the observed total diverges from the predicted total by more than 2%, the engine disables all recommendations. This is the "hard gate" — it prevents acting on bad data. As of deploy, reconciliation passes consistently with error under 0.01%.

Controller: the actual formula

The v1 controller is a CPI-gap proportional controller. It directly observes price changes and reacts:

gap = (current_basket_index / lagged_7d_basket_index) - 1 - target_growth
new_multiplier = old_multiplier x (1 - confidence x 0.20 x gap)

In words: the engine computes how fast the CPI basket is growing relative to the admin-set target (+0.5% per week default). If prices are rising too fast, it recommends lowering the Sim Shop multiplier. If too slow, it recommends raising it. The magnitude is scaled by data confidence.

Locked constants

Parameter Value Meaning
K_p 0.20 Proportional gain (intentionally conservative — actuator response is unknown)
Target growth +0.5%/week Default healthy inflation rate
Dead-band +/- 0.3% Below this gap magnitude, engine outputs NO_OP
Max weekly change +/- 5% Hard clamp on multiplier movement per week
Multiplier bounds 0.7 to 1.3 Absolute floor and ceiling
Min confidence (display) 0.30 Below this, recommendations are suppressed
Min confidence (auto-apply) 0.70 v2 auto-apply threshold (not active in v1)

Why proportional-only

The spec includes provisions for adding derivative (D) and integral (I) control terms later, forming a full PID controller. These are deferred because:

  • The D term requires smoothed historical data (EWMA, not yet accumulated — needs ~14 days)
  • The I term can accumulate error during bad-data periods and needs anti-windup
  • The actuator gain (how much multiplier change produces how much CPI change) is unknown until perturbation experiments run at day 30+

v1 ships the simplest controller that makes correct directional decisions. More sophisticated control is a future upgrade backed by accumulated data.

Confidence scoring

Component Measures Maximum when
Basket coverage Fraction of CPI basket items with valid trade data All items pass depth gates
Sample depth Average transactions per basket item 50+ transactions per item
Diversity Unique buyers + sellers across basket 8+ unique participants
Maturity Days since server deploy 90+ days
confidence = (coverage x depth x diversity x maturity) ^ (1/4)

The geometric mean ensures all four components must be healthy. Each is floored at 0.1 to prevent any single zero from killing the score entirely.

NO_OP conditions

The engine outputs "no action" when any of:

  1. Confidence below 0.30
  2. Gap within dead-band (+/- 0.3%)
  3. Reconciliation failing (hard gate)
  4. Fewer than 5 active players in 7 days
  5. Previous action not yet observed (one-week cooldown)
  6. New-player spike (3+ new players in 48 hours)
  7. Within first 14 days of operation (Phase A bootstrap)

NO_OP is the most common output on a young server. The engine is designed to be cautious — doing nothing is better than acting on insufficient data.

Bootstrap schedule

Period Behavior
Day 0-14 Advisory disabled. Dashboards live. Data collection running.
Day 14-30 Advisory enabled with confidence scaling. No auto-apply.
Day 30-90 Category-level perturbation experiments to measure actuator response.
Day 90+ Auto-apply eligible if confidence sustained above 0.70.

Source-flow ratios

The controller also computes diagnostic ratios displayed on the Admin Hub:

Ratio Formula What it tells admins
sink_ratio (DESTROY + TRANSFER_FROM_PUBLIC) / M_active How fast money leaves active circulation
bank_drain_ratio TRANSFER_TO_PUBLIC / M_active How fast the bank injects liquidity
jobs_faucet_ratio CREATE from Jobs / M_active Job wage intensity vs the active money pool
net supply change CREATE - DESTROY over 7 days Bottom-line money supply movement

Design lineage

The engine spec was refined through five rounds of Claude/ChatGPT iterative critique, correcting three material flaws in the original v1 draft:

  1. CentralBank counted as active money — corrected to sterilized reserve with lambda-weighted disbursement forecast
  2. Policy response was inverted — the v1 draft raised the inflation target during high pressure (accommodation); corrected to proportional tightening toward a fixed target
  3. "Deflation via fewer purchases" was mislabeled — reducing the faucet slows growth (disinflation) but does not contract existing supply (real deflation requires sinks)

The locked v2 spec is maintained in the server's internal documentation.

See also