How a $80,000 Ghost Price Froze V3.6 for Three Weeks

Key Takeaways

  • Weekly performance data from live crypto trading bots competing head-to-head
  • Fear & Greed Index gates control when bots are allowed to trade
  • Real money results — every number comes from live exchange data

V3.6 Fear & Greed — the CoinClaw bot that gates its trades on market sentiment — stopped trading. Not because the market was wrong. Not because the Fear & Greed index was too high. Because it thought BTC was at $80,000.

BTC was at $68,600.

The Price Sanity Check

Every CoinClaw live bot has a safety function called is_price_sane(). Before placing any order, the bot compares the current exchange price against the last price it recorded. If the gap exceeds 10%, the bot refuses to trade — it assumes something is wrong with the data feed rather than risk placing orders at a bad price.

This is a good safety mechanism. A 10% price move in 15 minutes is almost certainly a data error, not a real market move. The sanity check prevents the bot from buying at an inflated price or selling at a crashed one.

The problem: the function had no concept of time.

Where the $80,000 Came From

V3.6's wallet state file stores last_price — the price from the bot's most recent successful trading cycle. At some point, the state file was written from a developer's machine where BTC was trading at $80,000. That was weeks ago, when BTC was actually near that level.

Since then, BTC dropped to $68,600. That's a 14.4% gap from the stored $80,000. Every time V3.6 ran its 15-minute cycle, it would:

  1. Fetch the current BTC price: $68,600
  2. Compare against last_price: $80,000
  3. Calculate the gap: 14.4%
  4. Reject the price as insane (>10% threshold)
  5. Skip the entire trading cycle

This happened every 15 minutes, 96 times a day, for three weeks. The bot was running. The cron was firing. The logs showed activity. But V3.6 never placed a single order because it was comparing today's real price against a ghost from the past.

Why It Wasn't Caught Sooner

The bot's circuit breaker didn't trip because the bot wasn't losing money — it wasn't trading at all. The price sanity check fires before any order logic, so the bot never reached the point where the circuit breaker would evaluate P&L.

The bot status reporter showed V3.6 as "active" because the cron was running. It didn't distinguish between "running and trading" and "running and rejecting every cycle."

The issue was finally caught during a manual review of bot behaviour — someone noticed V3.6 had zero recent trades despite the Fear & Greed index being in a range that should trigger trading.

The Fix: Stale Price Detection

The fix adds time awareness to is_price_sane(). The function now checks when last_price was last updated. If the state hasn't been updated in more than 24 hours, the function accepts the current price regardless of the gap — because a 14% move over three weeks is completely normal market behaviour, not a data error.

The logic:

# Before: no time awareness
def is_price_sane(current_price, last_price):
    gap = abs(current_price - last_price) / last_price
    return gap < 0.10  # 10% threshold

# After: stale price detection
def is_price_sane(current_price, last_price, last_updated=None):
    gap = abs(current_price - last_price) / last_price
    if gap < 0.10:
        return True
    # If last_price is stale (>24h), accept current price
    if last_updated is None or is_stale(last_updated, hours=24):
        log.warning("Price sanity override — last_price stale, accepting current")
        return True
    return False

When the stale override fires, the bot logs a warning and updates last_price to the current price. On the next cycle, the sanity check works normally again with a fresh baseline.

The fix was applied to all three live bot runners (V3.5, V3.6, V3.7) since they all share the same is_price_sane() function in the base class.

The Broader Lesson

This is a classic example of a safety mechanism that's correct in the narrow case but wrong in the broader context. The price sanity check was designed to catch bad ticks — momentary data errors where the exchange reports a wildly wrong price. For that use case, a 10% threshold with no time component is fine.

But the function was also being used to validate prices after long outages, server migrations, and state file transfers between machines. In those contexts, a large price gap is expected and normal. The function needed to distinguish between "price moved 14% in 15 minutes" (probably bad data) and "price moved 14% in 3 weeks" (completely normal).

The fix is 10 lines of code and 10 tests. The bot was frozen for three weeks.

V3.6 is now trading again. The Fear & Greed index is at 12 — deep in the fear zone that V3.6 is designed to exploit. Whether V3.6's marginal edge (p=0.114) translates to real profits remains to be seen. But at least it's trading.

Advertisement