> youcanbuildthings.com
tutorials books topics about
analysis from: Polymarket Profits 2

How to Detect Edge Decay in Your Trading Bot

by J Cook · 8 min read·

Summary:

  1. Edge decay is math, not failure. Every profitable strategy gets less profitable as bots enter.
  2. Five metrics tell you when decay is happening before your P&L turns red.
  3. The PlatformChangeMonitor class catches fee updates and tick-size changes as leading indicators.
  4. Stop, pivot, adapt, or add — a decision framework that tells you what to do with a decaying strategy.

The arbitrage scanner that printed money last month is making half as much this month. Fill rates on your limit orders dropped from 80% to 55%. Your win rate is the same but the average profit per trade is shrinking. That is edge decay, and the last month of Polymarket changelog entries proves it is happening faster than ever.

Four platform changes in the past 11 days that would each break a bot running on last-month’s assumptions: new pagination endpoints, fee structure V2 with updated per-category rates, a change to how the closed query parameter defaults, and a new feeSchedule object that replaces the old fee fields. A bot that skipped these updates is trading against stale rules and losing money to mechanics it cannot see.

What is edge decay?

Edge decay phases chart showing profit per trade over 12 months starting at around $15 in the Discovery phase (0-3 months, high and stable), drifting down through Imitation (3-6 months, gentle drift), falling sharply in Crowding (6-9 months, sharp drop to around $2), and flatlining in Equilibrium (9-12 months), with a red STOP or PIVOT marker at the 9-month boundary when decay exceeds 50 percent

Edge decay is the process by which a profitable trading strategy loses profitability over time as competing bots enter the market, platform rules change, or the underlying inefficiency closes. Every edge has an expiration date.

Decay happens in four phases, and knowing which phase your strategy is in tells you how much longer it will print money:

  1. Discovery. A few traders find the strategy. Nobody else runs it. Returns are high and stable.
  2. Imitation. Others figure it out. Reddit posts, books, and courses spread the knowledge. Competition increases.
  3. Crowding. Hundreds of bots run the same play. Gaps close in milliseconds instead of seconds. Slow bots go negative.
  4. Equilibrium. Only the most efficient operators profit. Margins are thin. The bar to enter profitably is high.

Most Polymarket strategies from Polymarket Profits 2 are in Phase 1 or Phase 2 right now. Your job is not to fight decay — that is a losing battle. Your job is to detect which phase each strategy is in and rotate capital before the edge turns negative.

How do you detect edge decay from your trade log?

You track the average profit per trade over a rolling 30-day window and compare it to the prior 30-day window. A 30%+ drop is a red flag. Here is the function:

def detect_edge_decay(trade_log, window=30):
    """Detect whether average profit per trade is declining.

    trade_log   List of dicts with a 'pnl' key in dollar terms.
    window      Number of trades in each comparison window.

    Returns dict with recent_avg, previous_avg, decay_pct, and
    alert (True when decay > 30%).
    """
    if len(trade_log) < window * 2:
        return None  # Not enough data yet

    recent = trade_log[-window:]
    previous = trade_log[-window * 2:-window]

    recent_avg = sum(t["pnl"] for t in recent) / len(recent)
    previous_avg = sum(t["pnl"] for t in previous) / len(previous)

    if previous_avg == 0:
        decay_pct = 0
    else:
        decay_pct = (previous_avg - recent_avg) / abs(previous_avg) * 100

    return {
        "recent_avg_pnl": recent_avg,
        "previous_avg_pnl": previous_avg,
        "decay_pct": decay_pct,
        "alert": decay_pct > 30,
    }

Profit per trade is the primary metric, but it is a lagging indicator. By the time your trade log shows decay, the edge has been shrinking for weeks. The other four metrics catch it earlier.

The 30% alert threshold is a starting default, not a statistical guarantee. Tune it to your trade volume and PnL variance: if your strategy has high variance (wide win/loss spread), a 30% drop over 30 trades may be noise, and you want a wider window or a tighter threshold. Run the decay check against your own historical log before trusting the default.

Which five metrics do you track?

You track profit per trade, limit order fill rate, time to fill, win rate stability, and platform-change events. The first four are lagging signals from your own data. The fifth is a leading signal from Polymarket itself.

MetricSignalWhat it means when it drops
Avg profit per tradeLaggingSpreads are tightening, edge is closing
Limit order fill rateLaggingOther bots are grabbing liquidity before you
Time to fillLaggingYou are losing the speed race
Win rate stabilityLaggingMarket structure is changing
Platform-change eventsLeadingMetadata flipped, recalculate edge now

Two or more lagging signals moving in the same direction is the condition for reducing allocation. A single platform-change event is the condition for pausing new entries until you have recalculated edge on every active position.

How do you catch platform changes before they hit your P&L?

You diff the market metadata fields your strategies depend on and fire an alert when any of them change. This is the leading indicator that the other four metrics cannot give you.

import time


class PlatformChangeMonitor:
    """Watch Polymarket metadata for the kinds of changes that
    decay your edge instantly: fee activations, rate changes,
    tick size changes, negRisk flag flips, category reassignments.

    Tracks BOTH the legacy fee fields (feeRateBps, feesEnabled)
    AND the new feeSchedule object introduced Mar 31, 2026. Either
    field disappearing or changing is a signal to recalculate edge.
    """
    TRACKED_FIELDS = [
        "feeRateBps",        # legacy
        "feesEnabled",       # legacy
        "feeSchedule",       # new (Mar 31, 2026)
        "minimum_tick_size",
        "negRisk",
        "category",
    ]

    def __init__(self):
        # token_id -> {field_name: last_seen_value}
        self.snapshots = {}

    def snapshot(self, market):
        """Record current metadata for a market."""
        token_id = market.get("tokens", [{}])[0].get("token_id")
        if not token_id:
            return
        self.snapshots[token_id] = {
            f: market.get(f) for f in self.TRACKED_FIELDS
        }

    def check_for_changes(self, markets):
        """Compare current metadata to last snapshot.

        Returns a list of detected changes. Run on every scan
        cycle, or at least every 5 minutes.
        """
        changes = []
        for market in markets:
            token_id = (
                market.get("tokens", [{}])[0].get("token_id")
            )
            if not token_id:
                continue
            if token_id not in self.snapshots:
                self.snapshot(market)
                continue

            previous = self.snapshots[token_id]
            current = {
                f: market.get(f) for f in self.TRACKED_FIELDS
            }
            for field in self.TRACKED_FIELDS:
                if previous.get(field) != current.get(field):
                    changes.append({
                        "token_id": token_id,
                        "field": field,
                        "previous": previous.get(field),
                        "current": current.get(field),
                        "question": market.get("question", "")[:60],
                        "timestamp": time.time(),
                    })
            self.snapshots[token_id] = current
        return changes

Plug this into your main scanner loop. Any non-empty return means: stop opening new positions in that market, re-run edge calculation on every active position, and log the change.

How fast do Polymarket platform changes actually happen?

Faster than most bot operators realize. Pulled from the official Polymarket changelog, here are the changes in the 11 days before this article was written:

DateChangeImpact on bots
Apr 10, 2026New keyset pagination endpoints (/markets/keyset, /events/keyset)Offset-based pagination will be deprecated; old code will break
Apr 9, 2026GET /markets closed parameter defaults to falseBots relying on old default now miss resolved markets
Mar 31, 2026Fee calculation now uses feeSchedule objectHardcoded feeRateBps parsers break silently
Mar 30, 2026Fee Structure V2 — updated rates per categoryEvery bot sizing against old fee rates is mispricing trades

Four changes in 11 days. Three of them directly affect the metadata your bot depends on. If you deployed your scanner on March 15 and have not updated it since, the bot is now trading against rules that do not exist. The PlatformChangeMonitor above catches the last two automatically. The pagination change you have to read the changelog for.

When should you stop, pivot, adapt, or add?

You apply the four-way decision framework based on what the decay signals are telling you.

DecisionConditionAction
StopDecay exceeds 50% over 30 days AND cause is structural (permanent competition, fee increase)Kill the strategy. Redeploy capital elsewhere.
PivotSame strategy works on different marketsPoint the scanner at fresh categories (new launches, niche markets)
AdaptA parameter change restores profitabilityRaise min spread, tighten depth filter, switch taker to maker
AddNew edge appears alongside the decaying oneRun both in parallel while the old one still has some edge

The trap most bot operators fall into: they refuse to stop. They burned weeks building the strategy and cannot bring themselves to kill it. Do not confuse sunk cost with current edge. A strategy that worked for three months and now loses money is a sunk cost. The next month of losses is your future to choose.

Pivoting is the most underused option. Your arbitrage scanner that is dying on BTC time-series markets because 50 other bots run the same play? The code works. The markets are the problem. Point it at a newly launched category where you are the first bot in and you are back in Phase 1.

Here is the framework as a function you can call from your weekly monitoring job:

def decide_strategy_action(decay_report, fill_rate, cause):
    """Recommend stop, pivot, adapt, or add for a decaying strategy.

    decay_report   Output of detect_edge_decay() or None.
    fill_rate      Current limit-order fill rate, 0.0-1.0.
    cause          "competition" | "fees" | "market_shift" | "unknown".
    """
    if decay_report is None:
        return ("MONITOR", "Not enough trades yet.")

    decay_pct = decay_report["decay_pct"]

    if decay_pct > 50 and cause in ("competition", "fees"):
        return ("STOP", f"Structural decay {decay_pct:.0f}%. Redeploy capital.")
    if fill_rate < 0.4 and cause == "competition":
        return ("PIVOT", "Fills collapsing. Point scanner at new categories.")
    if decay_pct > 30 and cause == "market_shift":
        return ("ADAPT", "Raise min spread, tighten filters, switch maker/taker.")
    if decay_pct > 15:
        return ("ADD", "Run a complementary strategy in parallel.")
    return ("MONITOR", f"Decay {decay_pct:.0f}% is within noise.")

Call it every Sunday with fresh trade-log data. It turns “I have a gut feeling something is off” into a specific recommendation you can argue with.

The PlatformChangeMonitor and detect_edge_decay() functions sit alongside the paper-trading trainer from Chapter 3 of Polymarket Profits 2. Every strategy that runs through the trainer also feeds its trades into these two detectors. When the trainer starts showing you declining profit per trade, decay has already begun, and the monitor tells you whether it was competition or a platform change before P&L makes it obvious.

What should you actually do?

  • Run detect_edge_decay() on every strategy weekly. Put it in a cron job, send yourself an email alert when decay_pct > 30.
  • Run PlatformChangeMonitor.check_for_changes() on every scan cycle. Platform changes are the one leading indicator, and it costs you nothing to check every 60 seconds.
  • Read the Polymarket changelog once a week. The metadata monitor catches field flips, but API-level changes (new endpoints, deprecated parameters) require human review.
  • Never refuse to stop a decaying strategy. Apply the framework honestly. If decay is structural and adaptation does not work, kill it and move on.
  • Always have the next strategy in paper trading. The time to find your next edge is while the current one is still profitable, not after it dies.

bottom_line

  • Edge decay is guaranteed, not avoidable. Build the monitoring system before you need it.
  • Platform changes are the only leading indicator you get for free. Use them.
  • Stop is an option. A strategy that made you money last year owes you nothing this year. Kill it and move on.

Frequently Asked Questions

What is edge decay in a trading bot?+

Edge decay is the gradual loss of profitability as competing bots enter the same market or as platform rules change. A strategy that returned 8% a month last quarter might return 2% this quarter and lose money next quarter as the edge erodes.

How do you know when your trading bot's edge has decayed?+

Track five metrics: average profit per trade, fill rate on limit orders, time to fill, win-rate stability, and platform-change events. Two or more declining together, or a single platform change, is the signal to reduce allocation or pause the strategy.

Should you shut down a decaying strategy or try to fix it?+

Apply the stop/pivot/adapt/add framework. Stop if decay exceeds 50% and the cause is structural. Pivot if the same code works on different markets. Adapt if a parameter change restores profitability. Add a complementary strategy if the current one still has some edge.