Skip to main content

Crypto Bot - Two Bugs That Cost Me $100 (And How I Fixed Them in One Day)

Published: December 10, 20254 min read
#Crypto#Agent#Progress#Analytics

Two Bugs That Cost Me $100 (And How I Fixed Them in One Day)

December 9, 2025

My trading bot ran for 14 hours yesterday. It lost $100 and taught me two lessons worth far more.

The Setup

I'm building an AI-powered trading system. It uses DeepSeek for market analysis, validates proposals through a rules engine, and executes trades on perpetual futures. Paper trading for now, but the lessons are real.

After 14 hours of trading, I had 5 trades, a 32% win rate, and a $100 hole in my paper account. Not great. But the interesting part wasn't the losses - it was why they happened.

Bug #1: The Ghost Positions

What I saw: Valid trades getting auto-closed as "orphans" every time the container restarted.

What I thought: Maybe the reconciliation logic is too aggressive?

What was actually happening: The perpetual adapter's position dictionary was empty on startup. Not because positions didn't exist - because I never loaded them.

Here's the embarrassing part. My spot trading adapter had this:

def __init__(self, ...):
    if paper_trading:
        self._paper_positions = {}
        if self.db:
            self._load_paper_positions_from_db()  # <-- This existed

My perpetual adapter had this:

def __init__(self, ...):
    if paper_trading:
        self._paper_positions = {}
        # <-- This method didn't exist

Same codebase. Same feature. One worked, one didn't. I'd added the perpetual adapter later and simply forgot to add the persistence method.

The fix: 15 lines of code to load positions from the database on startup.

The lesson: When you add a new path through your system (spot vs perpetual), verify both paths have the same capabilities. Silent gaps are the worst bugs because they don't throw errors - they just quietly lose data.

Bug #2: The Late Entry Problem

What I saw: The AI entering LONG positions that immediately went against me.

What I thought: Maybe the AI's analysis is wrong?

What was actually happening: The AI's analysis was fine. The timing was terrible.

I pulled up the charts. SOL had rallied 10% and peaked at 12:00 UTC. My bot entered LONG at 20:39 UTC - eight hours after the peak. ETH was the same story. The rallies were over. I was buying the top.

My validator had all these checks:

  • RSI < 70? Check (RSI was 65)
  • ADX > 15? Check (ADX was 28)
  • Entry within 3% of proposed price? Check

Everything passed. But price was 6% above the 20-day moving average. The rally was exhausted. The validator didn't know to check for that.

The AI prompt even said: "Avoid Chasing: Don't enter if price >5% above fair value."

But that was just text. Advice. A suggestion. The validator didn't enforce it.

The fix: One new check in the validator:

# Check 10: Price Extension Filter
if proposal.direction == 'LONG' and price_extension > 4%:
    return Rejected("Price extended - chasing rally")

The lesson: Soft guidance isn't enforcement. If a rule matters, it needs to be in the validator with a hard rejection. The AI will eventually ignore suggestions. Code doesn't ignore code.

The Meta-Lesson

Both bugs had the same root cause: I assumed things worked because I didn't see them fail.

The perpetual adapter "worked" because trades opened fine. I didn't test restarts.

The entry timing "worked" because trades got approved. I didn't check if approvals were good.

The fix for both was the same: make the invisible visible.

For persistence: load positions on startup and log what you loaded.

For entry timing: add a hard check that rejects bad entries with a clear message.

Now when a LONG gets rejected for being 5% above SMA, I see it in the logs:

Rules rejection: Price 5.2% above SMA20 (>4% - chasing rally)

That's not a bug. That's the system doing its job.

What I'm Doing Next

Rather than guess at the "perfect" take-profit level, I added analytics to track:

  • TP1 hit rate by coin (is SOL underperforming?)
  • TP1 hit rate by market regime (do weak trends need tighter targets?)
  • "Near miss" analysis (how often does price get close to TP but reverse?)

In two weeks, I'll have data. Data beats intuition.

The Numbers

  • Time lost to debugging: 3 hours
  • Code written to fix both bugs: ~50 lines
  • Paper money lost: $100
  • Confidence in the system: Higher than before the bugs

Finding bugs is progress. Fixing them is learning. The bot is better today than it was yesterday.

That's the game.


Building Trader-7 in public. Follow along at jamiewatters.work

Share this post