In-game economy monitoring with streaming SQL means continuously computing currency flow, item trade velocity, and inflation signals from a live event stream. RisingWave ingests economic events from Kafka and maintains materialized views that alert studios within seconds when exploit-driven inflation, illicit gold farming, or price crashes threaten the game's balance.
Why Virtual Economies Break — and Why It Matters
A well-designed in-game economy is a competitive moat. Players engage longer when crafting, trading, and investing feel rewarding. But virtual economies are fragile: a single duplication exploit can hyperinflate item prices overnight, making legitimately earned currency worthless. Gold-farming bots drain resources. Flash crashes in player-to-player markets destroy trust.
Studios that detect these signals in batch reports are always reacting to yesterday's crisis. By the time the analysis arrives, the damage is irreversible: real-money purchases feel wasted, dedicated players feel cheated, and communities fragment. Streaming SQL lets you watch the economy the way a central bank watches inflation — continuously, with pre-defined thresholds that trigger action before conditions spiral out of control.
Connecting to Economy Event Streams
Game economy events — gold earned, gold spent, item trades, crafting actions — need to be captured as a stream:
CREATE SOURCE economy_events (
event_id VARCHAR,
player_id BIGINT,
event_type VARCHAR,
item_id VARCHAR,
quantity INT,
gold_delta BIGINT,
trade_partner BIGINT,
source VARCHAR,
server_id VARCHAR,
event_time TIMESTAMPTZ
)
WITH (
connector = 'kafka',
topic = 'game.economy.events',
properties.bootstrap.server = 'kafka:9092',
scan.startup.mode = 'latest'
)
FORMAT PLAIN ENCODE JSON;
Also load the item catalog as a reference table from your operational database:
CREATE SOURCE item_catalog (
item_id VARCHAR PRIMARY KEY,
item_name VARCHAR,
item_type VARCHAR,
base_gold_value BIGINT,
rarity VARCHAR,
tradeable BOOLEAN
)
WITH (
connector = 'mysql-cdc',
hostname = 'mysql.internal',
port = '3306',
username = 'rwuser',
password = '${secret}',
database.name = 'game_db',
table.name = 'item_catalog'
);
Tracking Gold Velocity and Supply
The first signal of an economy exploit is a sudden spike in gold creation. Build a windowed aggregation that tracks gold inflows and outflows across the entire economy:
CREATE MATERIALIZED VIEW economy_velocity AS
SELECT
window_start,
window_end,
server_id,
SUM(gold_delta) FILTER (WHERE gold_delta > 0) AS gold_created,
ABS(SUM(gold_delta) FILTER (WHERE gold_delta < 0)) AS gold_destroyed,
SUM(gold_delta) AS net_gold_change,
COUNT(DISTINCT player_id) FILTER (WHERE gold_delta > 0) AS active_earners,
MAX(gold_delta) AS max_single_gain,
AVG(gold_delta) FILTER (WHERE gold_delta > 0) AS avg_gold_gain
FROM TUMBLE(economy_events, event_time, INTERVAL '5 minutes')
WHERE event_type IN ('gold_earn', 'gold_spend', 'quest_reward', 'trade')
GROUP BY window_start, window_end, server_id;
An anomaly appears as net_gold_change spiking far above the historical baseline or max_single_gain exceeding any value attainable through normal gameplay.
Detecting Price Inflation in Player Markets
Player-to-player trades reveal real market prices. Build a materialized view that tracks item price trends:
CREATE MATERIALIZED VIEW item_price_tracker AS
SELECT
window_start,
window_end,
e.item_id,
c.item_name,
c.rarity,
c.base_gold_value,
COUNT(*) AS trade_volume,
AVG(e.gold_delta / NULLIF(e.quantity, 0)) AS avg_price_per_unit,
MAX(e.gold_delta / NULLIF(e.quantity, 0)) AS max_price_per_unit,
MIN(e.gold_delta / NULLIF(e.quantity, 0)) AS min_price_per_unit,
ROUND(
(AVG(e.gold_delta / NULLIF(e.quantity, 0)) - c.base_gold_value)::DECIMAL
/ NULLIF(c.base_gold_value, 0) * 100, 2
) AS price_premium_pct
FROM TUMBLE(economy_events, event_time, INTERVAL '15 minutes') e
JOIN item_catalog FOR SYSTEM_TIME AS OF NOW() c
ON e.item_id = c.item_id
WHERE e.event_type = 'trade'
AND c.tradeable = true
GROUP BY window_start, window_end, e.item_id, c.item_name, c.rarity, c.base_gold_value
HAVING COUNT(*) >= 5;
price_premium_pct measures how far current trade prices deviate from the designer's intended base value. A reading of 500% on a common item is a strong inflation signal. A reading of -80% on a rare item suggests someone is dumping a duplicated supply.
Identifying Suspicious Transfer Networks
Gold-farming operations often move wealth through a network of accounts before cashing out. Detect high-volume transfer chains:
CREATE MATERIALIZED VIEW suspicious_transfers AS
SELECT
window_start,
window_end,
player_id,
COUNT(*) FILTER (WHERE event_type = 'trade' AND gold_delta < 0) AS trades_sent,
COUNT(*) FILTER (WHERE event_type = 'trade' AND gold_delta > 0) AS trades_received,
SUM(gold_delta) FILTER (WHERE event_type = 'trade') AS net_trade_gold,
COUNT(DISTINCT trade_partner) AS unique_partners
FROM TUMBLE(economy_events, event_time, INTERVAL '1 hour')
WHERE event_type = 'trade'
GROUP BY window_start, window_end, player_id
HAVING COUNT(DISTINCT trade_partner) > 20
OR ABS(SUM(gold_delta) FILTER (WHERE event_type = 'trade')) > 1000000;
Comparison: Economy Monitoring Approaches
| Approach | Detection Speed | Economy Signal Coverage | False Positive Rate | Implementation Effort |
| Manual GM review | Days | Low | Low | High (manual) |
| Daily batch reports | 24 hours | Medium | Low | Medium |
| Rule-based alerting (single metric) | Minutes | Low | High | Medium |
| RisingWave streaming SQL (multi-signal) | Sub-minute | High | Low | Low |
Sinking Economy Alerts
When economy anomalies are detected, route them to your operations team immediately:
CREATE SINK economy_alerts_sink
FROM economy_velocity
WITH (
connector = 'kafka',
topic = 'game.economy.alerts',
properties.bootstrap.server = 'kafka:9092'
)
FORMAT UPSERT ENCODE JSON (
force_append_only = true
);
Pair this with a PagerDuty or Slack webhook integration in your alerting infrastructure to ensure economy managers are notified within seconds of a threshold breach.
FAQ
Q: How do I set meaningful alert thresholds if I don't know what "normal" looks like yet? A: Run the economy velocity view in observation mode for two to four weeks before enabling alerting. Export the data via the JDBC sink into a BI tool to understand your baseline distribution. Then set thresholds at three standard deviations above the historical mean.
Q: Can RisingWave monitor multiple game servers or shards simultaneously?
A: Yes. Include server_id in your GROUP BY clauses to maintain per-shard metrics while also computing economy-wide aggregates in a separate view. Both views update from the same Kafka source.
Q: What happens to economy monitoring during a major content patch that legitimately changes gold rates?
A: Update the base_gold_value in your operational item catalog. The CDC connector propagates the change to RisingWave within milliseconds, so price_premium_pct is automatically recalibrated.
Q: Can I use this pipeline to balance the economy proactively, not just detect problems? A: Yes. Many studios feed economy velocity data directly into automated reward rate adjustment systems. When the economy shows deflationary signals, the system temporarily boosts quest rewards; during inflation, it tightens them.
Q: How do I archive economy data for post-incident forensic analysis? A: Add a second sink from your economy events source to an Iceberg table. This preserves the raw event log indefinitely for reconstruction and forensic queries, while RisingWave handles the real-time monitoring layer.
Protect What Players Have Earned
A healthy game economy is a form of player respect — every item traded represents real time and effort. With RisingWave, your economy monitoring is as live as the economy itself.
Start at https://docs.risingwave.com/get-started and join the conversation at https://risingwave.com/slack.

