Automated Trading with Claude Code — Part 6/6 — Final

The Feedback Loop

Self-improving trading intelligence — automated retrospectives, adaptive strategy weights, score calibration, and the migration path from manual to fully automated.

Self-Improving 40% → 65% Hit Rate Pattern Memory Migration Path
Automated Trading with Claude Code 6/6
Virtuous Cycle Auto Retro Weight Adjustment Calibration Pattern Memory Migration Path Conclusion

The Self-Improving System

Without feedback, your system is static — rules that worked last month may fail this month as market conditions evolve. The feedback loop creates a virtuous cycle:

Scan
Identify setups
Measure
Track outcomes
Improve
Adjust weights

The target: from ~40% hit rate (random stock picking) to 65%+ hit rate after 8-12 weeks of feedback loop operation. Here's how each component drives improvement.

Automated Retrospective System

Every Friday at 23:00, the retrospective task runs automatically. It evaluates all setups from the past 10 days and produces a comprehensive performance report.

Retrospective Pipeline
async function runRetrospective() {
  // 1. Read all scans from past 10 days
  const scans = readRecentScans(10);
  // Each scan has 10 setups with entry, stop, TP1, TP2, strategy

  // 2. Collect current prices for all setups
  const allTickers = [...new Set(scans.flatMap(s => s.setups.map(t => t.ticker)))];
  const currentData = await QueryData({
    symbols: allTickers,
    types: "quote,bars_daily"
  });

  // 3. Calculate outcome for each setup
  const results = scans.flatMap(scan => scan.setups.map(setup => {
    const bars = currentData[setup.ticker].bars_daily;
    const entryBar = bars.find(b => b.date >= scan.date);
    if (!entryBar) return null;

    const postEntryBars = bars.filter(b => b.date > scan.date);
    const hitTP1 = postEntryBars.some(b => b.high >= setup.tp1);
    const hitTP2 = postEntryBars.some(b => b.high >= setup.tp2);
    const hitStop = postEntryBars.some(b => b.low <= setup.stop);

    // Determine order: which hit first?
    const tp1Day = postEntryBars.findIndex(b => b.high >= setup.tp1);
    const stopDay = postEntryBars.findIndex(b => b.low <= setup.stop);

    let outcome;
    if (hitTP1 && (!hitStop || tp1Day < stopDay)) outcome = "TP1_HIT";
    else if (hitTP2 && (!hitStop || tp1Day < stopDay)) outcome = "TP2_HIT";
    else if (hitStop) outcome = "STOP_HIT";
    else outcome = "OPEN"; // Still within timeframe

    const currentPrice = currentData[setup.ticker].quote.price;
    const pnl = ((currentPrice - setup.entry) / setup.entry * 100).toFixed(1);

    return {
      ...setup,
      scanDate: scan.date,
      outcome,
      currentPrice,
      pnl: parseFloat(pnl),
      hitTP1, hitTP2, hitStop,
      daysHeld: postEntryBars.length
    };
  })).filter(Boolean);

  // 4. Calculate aggregate metrics
  const metrics = {
    totalSetups: results.length,
    hitRateTP1: results.filter(r => r.outcome === "TP1_HIT").length / results.length,
    hitRateTP2: results.filter(r => r.outcome === "TP2_HIT").length / results.length,
    stopRate: results.filter(r => r.outcome === "STOP_HIT").length / results.length,
    avgPnL: results.reduce((sum, r) => sum + r.pnl, 0) / results.length,
    bestPick: results.sort((a, b) => b.pnl - a.pnl)[0],
    worstPick: results.sort((a, b) => a.pnl - b.pnl)[0],
    byStrategy: groupByStrategy(results),
    byRegime: groupByRegime(results)
  };

  // 5. Compute grade
  const grade = computeGrade(metrics);
  // A+: HR > 60%, avg P&L > 3%
  // A:  HR > 55%, avg P&L > 2%
  // B+: HR > 50%, avg P&L > 0%
  // B:  HR > 45%
  // C+: HR > 40%
  // Below: D or F

  // 6. Generate HTML retrospective article
  await generateRetroArticle(results, metrics, grade);

  // 7. Update strategy weights based on results
  await adjustStrategyWeights(metrics.byStrategy);

  return { metrics, grade };
}

Key Performance Indicators

KPIFormulaTarget
Hit Rate (TP1)TP1 hits / total setups≥ 55%
Win/Loss RatioAvg win / Avg loss≥ 1.5
Average P&LMean % change across all setups≥ 2.0%
Max DrawdownWorst setup P&L≥ -8%
Consecutive LossesLongest losing streak≤ 4
Score Correlationr(composite_score, pnl)≥ 0.3

Strategy Weight Adjustment

After each retrospective, strategy weights are updated using a Bayesian-inspired approach. Successful strategies gain weight; underperformers lose weight.

Weight Update Algorithm
function adjustStrategyWeights(byStrategy, currentWeights) {
  const FLOOR = 0.05;   // No strategy below 5%
  const CEILING = 0.40;  // No strategy above 40%
  const LEARNING_RATE = 0.15; // How fast we adapt

  const newWeights = { ...currentWeights };

  for (const [strategy, results] of Object.entries(byStrategy)) {
    const hitRate = results.filter(r => r.outcome.includes("TP")).length / results.length;
    const avgPnL = results.reduce((s, r) => s + r.pnl, 0) / results.length;

    // Performance score: combines hit rate and P&L
    const perfScore = hitRate * 0.6 + Math.min(avgPnL / 10, 0.4) * 0.4;

    // Bayesian update: blend prior weight with observed performance
    const prior = currentWeights[strategy] || 0.15;
    const posterior = prior * (1 - LEARNING_RATE) + perfScore * LEARNING_RATE;

    newWeights[strategy] = Math.max(FLOOR, Math.min(CEILING, posterior));
  }

  // Normalize to sum to 1.0
  const total = Object.values(newWeights).reduce((s, w) => s + w, 0);
  for (const key of Object.keys(newWeights)) {
    newWeights[key] = Math.round((newWeights[key] / total) * 100) / 100;
  }

  return newWeights;
}

// Example evolution over 4 weeks:
// Week 0 (default):
// { oversold_bounce: 0.25, momentum: 0.25, squeeze: 0.15,
//   trend: 0.15, safe_haven: 0.10, value: 0.10 }

// Week 2 (oversold outperforming in risk-off):
// { oversold_bounce: 0.32, momentum: 0.18, squeeze: 0.14,
//   trend: 0.12, safe_haven: 0.15, value: 0.09 }

// Week 4 (market transitions to neutral):
// { oversold_bounce: 0.26, momentum: 0.24, squeeze: 0.16,
//   trend: 0.14, safe_haven: 0.10, value: 0.10 }

Score Calibration

Is the scoring system actually predictive? Calibration analysis answers: do setups with score 90 perform better than those with score 80?

Calibration Analysis
function calibrationAnalysis(allResults) {
  // Group results by score bucket
  const buckets = {};
  for (const r of allResults) {
    const bucket = Math.floor(r.compositeScore / 5) * 5; // 75, 80, 85, 90, 95
    if (!buckets[bucket]) buckets[bucket] = [];
    buckets[bucket].push(r);
  }

  // For each bucket, compute actual hit rate
  const calibration = {};
  for (const [score, results] of Object.entries(buckets)) {
    calibration[score] = {
      count: results.length,
      hitRate: results.filter(r => r.outcome.includes("TP")).length / results.length,
      avgPnL: results.reduce((s, r) => s + r.pnl, 0) / results.length
    };
  }

  // Expected pattern:
  // Score 75-79: hit rate ~40%, avg P&L ~0.5%
  // Score 80-84: hit rate ~50%, avg P&L ~1.5%
  // Score 85-89: hit rate ~57%, avg P&L ~2.5%
  // Score 90-94: hit rate ~65%, avg P&L ~4.0%
  // Score 95-100: hit rate ~72%, avg P&L ~5.5%

  // If score 90 has LOWER hit rate than 80 → layer weights are wrong
  // → re-calibrate by analyzing which layers predict success

  return calibration;
}

function featureImportance(allResults) {
  // Which layer correlates most with positive outcomes?
  const layers = ['technical', 'fundamental', 'social', 'sector', 'macro', 'ai'];

  return layers.map(layer => {
    const scores = allResults.map(r => r.layers[layer]);
    const outcomes = allResults.map(r => r.pnl);
    const correlation = pearsonCorrelation(scores, outcomes);

    return { layer, correlation, importance: Math.abs(correlation) };
  }).sort((a, b) => b.importance - a.importance);

  // Example output:
  // { layer: "technical",   correlation: 0.42, importance: 0.42 }  ← Most predictive
  // { layer: "macro",       correlation: 0.35, importance: 0.35 }
  // { layer: "sector",      correlation: 0.28, importance: 0.28 }
  // { layer: "fundamental", correlation: 0.22, importance: 0.22 }
  // { layer: "ai",          correlation: 0.18, importance: 0.18 }
  // { layer: "social",      correlation: 0.05, importance: 0.05 }  ← Least predictive

  // Action: if social has near-zero correlation,
  // reduce its weight from 10% to 5% and redistribute.
}

Overfitting Warning

With only 40-60 setups per month, calibration data is noisy. Wait for at least 100 data points (6-8 weeks) before making significant weight changes. Small samples + frequent changes = overfitting to noise.

Pattern Memory

The system remembers what worked and what didn't, building a knowledge base over time.

Ticker Blacklist / Goldlist

Pattern Memory System
// Stored in scanner/pattern_memory.json
{
  "blacklist": {
    "SMCI": {
      "reason": "3 consecutive stop hits, accounting concerns",
      "since": "2026-02-15",
      "expires": "2026-03-15",
      "failCount": 3
    },
    "PLTR": {
      "reason": "2 false breakouts, extreme overvaluation",
      "since": "2026-02-20",
      "expires": "2026-03-20",
      "failCount": 2
    }
  },
  "goldlist": {
    "CF": {
      "reason": "2 TP2 hits in 3 weeks, strong insider buying",
      "since": "2026-02-28",
      "winCount": 2,
      "avgPnL": 8.5
    }
  },
  "sectorPatterns": {
    "Biotech": {
      "falseSignalRate": 0.45,
      "note": "High false signal rate — apply 10% score penalty",
      "adjustment": -10
    },
    "Energy": {
      "falseSignalRate": 0.22,
      "note": "Below average false signals in risk-off — boost",
      "adjustment": +5
    }
  },
  "dayOfWeekPatterns": {
    "Monday": { "hitRate": 0.52, "note": "Slightly above average" },
    "Tuesday": { "hitRate": 0.58, "note": "Best day — gap fills" },
    "Wednesday": { "hitRate": 0.45, "note": "Below average — FOMC?" },
    "Thursday": { "hitRate": 0.48, "note": "Average" },
    "Friday": { "hitRate": 0.42, "note": "Worst — weekend risk" }
  },
  "calendarEffects": {
    "opex_week": { "hitRate": 0.38, "note": "Gamma effects reduce accuracy" },
    "earnings_season": { "hitRate": 0.55, "note": "Slightly better — more catalysts" },
    "fomc_week": { "hitRate": 0.40, "note": "Uncertainty reduces hit rate" }
  }
}

How Pattern Memory Feeds Back

During the scanner run, pattern memory modifies candidate scoring:

function applyPatternMemory(candidate, memory) {
  let adjustment = 0;

  // Blacklisted tickers: automatic reject
  if (memory.blacklist[candidate.ticker]) {
    return { ...candidate, rejected: true, reason: "blacklisted" };
  }

  // Goldlist tickers: bonus
  if (memory.goldlist[candidate.ticker]) {
    adjustment += 5;
  }

  // Sector adjustment
  const sectorAdj = memory.sectorPatterns[candidate.sector]?.adjustment || 0;
  adjustment += sectorAdj;

  // Day of week
  const dayAdj = (memory.dayOfWeekPatterns[today].hitRate - 0.50) * 20;
  adjustment += dayAdj;

  // Calendar effects
  if (isOpexWeek() && memory.calendarEffects.opex_week) {
    adjustment -= 3;
  }

  candidate.compositeScore += adjustment;
  candidate.adjustments = { adjustment, reasons: [...] };
  return candidate;
}

From Manual to Fully Automated

Don't go from 0 to 100% automated overnight. Follow this gradual migration path:

Week
1-2

Parallel Run

Run the automated scanner alongside your manual trading. Compare: do the A++ picks align with your own analysis? Track both results in a spreadsheet. Build trust.

Week
3-4

Follow A++ Only

Start taking the top 3 A++ trades. Small position sizes (1-2% of portfolio). Continue manual trading as well. Compare win rates side by side.

Month
2

50/50 Split

Half automated, half manual. First retrospective data starts informing weight adjustments. Pattern memory building. DSL v2.0 deployed based on initial feedback.

Month
3+

Full Automation

Automated setups only. Manual intervention only for system issues. Position sizing rules embedded in alerts. Monthly manual review of overall system performance.

Risk Management Rules (Non-Negotiable)

The Future of Retail Trading

You've built a complete, self-improving, zero-touch trading system. Here's what it gives you:

6
Scoring Layers
58
Data Types
15m
Daily Time
24/7
Monitoring

The edge: Speed of analysis (60 stocks scored in seconds), breadth of data (58 types per stock), emotional discipline (the AI doesn't panic-sell), and continuous improvement (every week the system gets smarter).

What AI can't do: Predict black swans, guarantee profits, or replace risk management. The system finds high-probability setups — but high-probability is not certainty. Always use stops. Always manage position sizes. The best trading system is one you trust enough to follow consistently.

Back to the beginning
Part 1: The Automated Trading Stack
Automated Trading with Claude Code 6/6