Automated Trading with Claude Code — Part 3/6

The AI Analysis Engine

Multi-layer scoring across 6 dimensions — technical, fundamental, social, sector, macro, and AI conviction — to identify A++ setups that check every box.

6 Scoring Layers A++ Threshold: 92+ AI Conviction Claude Code Skill
Automated Trading with Claude Code 3/6
Framework Technical Fundamental Social Sector Macro AI Layer Composite Skill File

The 6-Layer Scoring Framework

Every setup candidate passes through 6 independent scoring layers. Each layer produces a score from 0 to 100. The composite score is a weighted average. For A++ classification, all 6 layers must pass their minimum thresholds.

Layer 1: Technical Analysis — 30% weight

RSI, MACD, moving averages, volume, support/resistance, pattern recognition. Confluence count determines the score. Minimum threshold: 70.

Layer 2: Fundamental Quality — 20% weight

Revenue growth, margin trajectory, cash flow quality, balance sheet health, valuation vs peers. Minimum threshold: 65.

Layer 3: Social Sentiment — 10% weight

StockTwits bull ratio, Reddit mentions, news sentiment, pump&dump detection. Minimum threshold: 50.

Layer 4: Sector Momentum — 15% weight

Sector ETF trend, relative strength vs SPY, rotation model position. Minimum threshold: 60.

Layer 5: Macro Regime — 15% weight

Strategy-regime alignment, VIX compatibility, dollar impact. Minimum threshold: 60.

Layer 6: AI Conviction — 10% weight

LLM narrative analysis, red flag detection, historical pattern matching. Minimum threshold: 65.

92+
A++ Threshold
85+
A+ Threshold
70+
A Threshold
<70
Rejected

Layer 1: Technical Scoring Engine

The technical layer evaluates price action, momentum, volume, and pattern quality. The core concept is confluence — the more independent signals align, the higher the score.

Signal Detection

Technical Scoring Algorithm
function scoreTechnical(data) {
  const signals = [];
  let score = 50; // Base score

  // === RSI Analysis ===
  if (data.rsi14 < 30) {
    signals.push({ name: "RSI Oversold", weight: 15, type: "bullish" });
    score += 15;
  } else if (data.rsi14 > 70) {
    signals.push({ name: "RSI Overbought", weight: -10, type: "bearish" });
    score -= 10;
  } else if (data.rsi14 > 45 && data.rsi14 < 65) {
    signals.push({ name: "RSI Healthy Zone", weight: 5, type: "neutral" });
    score += 5;
  }

  // === MACD Analysis ===
  if (data.macdHistogram > 0 && data.prevMacdHistogram <= 0) {
    signals.push({ name: "MACD Bullish Cross", weight: 12, type: "bullish" });
    score += 12;
  } else if (data.macdHistogram > data.prevMacdHistogram && data.macd > 0) {
    signals.push({ name: "MACD Expanding", weight: 8, type: "bullish" });
    score += 8;
  } else if (data.macdHistogram < 0 && data.macdHistogram < data.prevMacdHistogram) {
    signals.push({ name: "MACD Deteriorating", weight: -8, type: "bearish" });
    score -= 8;
  }

  // === Moving Average Alignment ===
  const maAlignment = [
    data.price > data.ema20,
    data.ema20 > data.ema50,
    data.ema50 > data.ema200,
    data.price > data.sma200
  ].filter(Boolean).length;

  if (maAlignment === 4) {
    signals.push({ name: "Perfect MA Alignment", weight: 15, type: "bullish" });
    score += 15;
  } else if (maAlignment >= 3) {
    signals.push({ name: "Strong MA Alignment", weight: 8, type: "bullish" });
    score += 8;
  } else if (maAlignment <= 1) {
    signals.push({ name: "Bearish MA Structure", weight: -10, type: "bearish" });
    score -= 10;
  }

  // Golden cross / Death cross detection
  if (data.ema50 > data.ema200 && data.prevEma50 <= data.prevEma200) {
    signals.push({ name: "Golden Cross", weight: 10, type: "bullish" });
    score += 10;
  }

  // === Volume Confirmation ===
  const relativeVolume = data.volume / data.avgVolume;
  if (relativeVolume > 2.0) {
    signals.push({ name: "Volume Surge (2x+)", weight: 12, type: "bullish" });
    score += 12;
  } else if (relativeVolume > 1.5) {
    signals.push({ name: "Above Avg Volume", weight: 6, type: "bullish" });
    score += 6;
  } else if (relativeVolume < 0.5) {
    signals.push({ name: "Low Volume Warning", weight: -5, type: "bearish" });
    score -= 5;
  }

  // === Support/Resistance Proximity ===
  const nearestSupport = data.supports?.[0];
  const nearestResistance = data.resistances?.[0];

  if (nearestSupport) {
    const distToSupport = (data.price - nearestSupport.price) / data.price;
    if (distToSupport < 0.03 && distToSupport > 0) {
      signals.push({ name: "Near Strong Support", weight: 10, type: "bullish" });
      score += 10;
    }
  }

  if (nearestResistance) {
    const distToResistance = (nearestResistance.price - data.price) / data.price;
    if (distToResistance < 0.02) {
      signals.push({ name: "At Resistance", weight: -5, type: "bearish" });
      score -= 5;
    }
  }

  // === Bollinger Band Analysis ===
  if (data.price < data.bbLower) {
    signals.push({ name: "Below Lower BB", weight: 8, type: "bullish" });
    score += 8;
  }
  if (data.bbWidth < 5) {
    signals.push({ name: "BB Squeeze", weight: 7, type: "neutral" });
    score += 7; // Potential energy
  }

  // === ATR-Based Stop Quality ===
  const atrStop = data.price - (1.5 * data.atr14);
  const stopDistance = (data.price - atrStop) / data.price;
  if (stopDistance < 0.05) {
    signals.push({ name: "Tight Stop Possible (<5%)", weight: 5, type: "bullish" });
    score += 5;
  }

  // === Confluence Count ===
  const bullishSignals = signals.filter(s => s.type === "bullish").length;
  const bearishSignals = signals.filter(s => s.type === "bearish").length;

  // Bonus for high confluence
  if (bullishSignals >= 5) score += 10;
  else if (bullishSignals >= 4) score += 5;

  // Penalty for mixed signals
  if (bullishSignals > 0 && bearishSignals >= 3) score -= 10;

  return {
    score: Math.max(0, Math.min(100, score)),
    signals,
    confluence: bullishSignals,
    bearishCount: bearishSignals
  };
}

Why Confluence Matters

A single indicator (e.g., RSI oversold) is correct about 55% of the time. Two aligned signals push accuracy to ~62%. Four or more aligned signals can reach 70-75% win rate. The scoring engine quantifies this: more independent confirming signals = higher score = higher probability.

Layer 2: Fundamental Scoring

The fundamental layer assesses the quality of the underlying business. A great technical setup on a fundamentally weak stock is a trap. This layer separates quality setups from value traps.

Fundamental Scoring Algorithm
function scoreFundamental(data) {
  let score = 50;
  const notes = [];

  // === Revenue Growth ===
  const revenueGrowthYoY = data.financials?.revenueGrowthYoY;
  if (revenueGrowthYoY > 25) { score += 15; notes.push("Strong revenue growth >25%"); }
  else if (revenueGrowthYoY > 10) { score += 8; notes.push("Healthy revenue growth"); }
  else if (revenueGrowthYoY > 0) { score += 3; }
  else if (revenueGrowthYoY < -10) { score -= 10; notes.push("Revenue declining"); }

  // === Margin Trajectory ===
  const grossMargin = data.financials?.grossMargin;
  const operatingMargin = data.financials?.operatingMargin;
  if (grossMargin > 0.60) { score += 8; notes.push("High gross margin >60%"); }
  else if (grossMargin > 0.40) { score += 4; }
  else if (grossMargin < 0.20) { score -= 5; }

  if (operatingMargin > 0.20) { score += 7; notes.push("Strong operating leverage"); }
  else if (operatingMargin < 0) { score -= 8; notes.push("Operating losses"); }

  // === Cash Flow Quality ===
  const fcf = data.financials?.freeCashFlow;
  const revenue = data.financials?.revenue;
  if (fcf && revenue && fcf > 0) {
    const fcfMargin = fcf / revenue;
    if (fcfMargin > 0.15) { score += 10; notes.push("Excellent FCF margin >15%"); }
    else if (fcfMargin > 0.05) { score += 5; }
  } else if (fcf < 0) {
    score -= 8; notes.push("Negative free cash flow");
  }

  // === Balance Sheet Health ===
  const currentRatio = data.stats?.currentRatio;
  const debtEquity = data.stats?.debtEquity;
  if (currentRatio > 2.0) { score += 5; }
  else if (currentRatio < 1.0) { score -= 8; notes.push("Liquidity risk"); }

  if (debtEquity < 0.5) { score += 5; notes.push("Low debt"); }
  else if (debtEquity > 2.0) { score -= 7; notes.push("High leverage"); }

  // === Earnings Surprise History ===
  const earnings = data.earnings_quarterly || [];
  const lastFourBeats = earnings.slice(0, 4)
    .filter(e => e.epsActual > e.epsEstimate).length;
  if (lastFourBeats === 4) { score += 10; notes.push("4/4 earnings beats"); }
  else if (lastFourBeats >= 3) { score += 5; }
  else if (lastFourBeats <= 1) { score -= 5; }

  // === Valuation ===
  const forwardPE = data.stats?.forwardPE;
  const peg = data.stats?.peg;
  if (peg && peg > 0 && peg < 1.0) { score += 8; notes.push("PEG < 1 (undervalued)"); }
  else if (peg > 3.0) { score -= 5; notes.push("Expensive PEG"); }

  if (forwardPE && forwardPE < 15) { score += 5; }
  else if (forwardPE > 50) { score -= 5; }

  // === Insider Activity ===
  const insiderBuys = (data.insider_transactions || [])
    .filter(t => t.type === "buy" && daysSince(t.date) < 90);
  if (insiderBuys.length >= 3) {
    score += 10; notes.push("Significant insider buying");
  } else if (insiderBuys.length >= 1) {
    score += 5;
  }

  // === Special Cases: Pre-Revenue Biotech ===
  if (!revenue || revenue === 0) {
    score = 40; // Reset base for pre-revenue
    const cashRunway = data.financials?.cashAndEquivalents;
    const burnRate = data.financials?.operatingCashFlow;
    if (cashRunway && burnRate && burnRate < 0) {
      const months = (cashRunway / Math.abs(burnRate)) * 3;
      if (months > 24) { score += 20; notes.push("Cash runway >2 years"); }
      else if (months > 12) { score += 10; }
      else { score -= 15; notes.push("Cash runway <12 months — dilution risk"); }
    }
  }

  return {
    score: Math.max(0, Math.min(100, score)),
    notes
  };
}

Layer 3: Social Sentiment Engine

Social sentiment acts as a contrarian indicator and momentum amplifier. Extreme bullish sentiment often signals tops; extreme bearish sentiment can signal bottoms. But when sentiment aligns with technicals, it amplifies conviction.

Social Scoring Algorithm
function scoreSocial(data) {
  let score = 50;
  const flags = [];

  const sentiment = data.sentiment_overall;
  if (!sentiment) return { score: 50, flags: ["No sentiment data"] };

  // === Overall Sentiment Score ===
  // sentiment.score ranges from -1.0 (extreme bearish) to +1.0 (extreme bullish)
  const sentScore = sentiment.score;

  if (sentScore > 0.7) {
    // Extreme bullish = contrarian warning
    score -= 5;
    flags.push("EXTREME BULLISH — contrarian caution");
  } else if (sentScore > 0.3) {
    score += 10;
    flags.push("Moderately bullish sentiment");
  } else if (sentScore < -0.5) {
    // Extreme bearish + technical oversold = strong buy signal
    score += 15;
    flags.push("EXTREME BEARISH — potential contrarian buy");
  } else if (sentScore < -0.2) {
    score += 5;
    flags.push("Mildly bearish sentiment");
  }

  // === StockTwits Analysis ===
  const st = data.sentiment_stocktwits;
  if (st) {
    const bullRatio = st.bullish / (st.bullish + st.bearish || 1);
    if (bullRatio > 0.80) {
      score -= 5; flags.push("StockTwits overcrowded bull");
    } else if (bullRatio > 0.55 && bullRatio < 0.75) {
      score += 5; flags.push("Healthy StockTwits bull bias");
    }
    // Volume of messages = attention
    if (st.messages_24h > 500) {
      score += 3; flags.push("High social volume");
    }
  }

  // === Reddit/WSB Analysis ===
  const reddit = data.sentiment_reddit;
  if (reddit && reddit.mentions_24h > 50) {
    score += 3;
    if (reddit.mentions_24h > 200) {
      flags.push("HIGH REDDIT ATTENTION — pump risk");
      // Pump & Dump detection
      if (pumpDumpCheck(data)) {
        score -= 20;
        flags.push("PUMP & DUMP DETECTED — REJECT");
      }
    }
  }

  // === News Sentiment ===
  const news = data.news || [];
  const recentNews = news.filter(n => daysSince(n.date) < 3);
  const posNews = recentNews.filter(n => n.sentiment === "positive").length;
  const negNews = recentNews.filter(n => n.sentiment === "negative").length;
  if (posNews > negNews + 2) { score += 5; }
  else if (negNews > posNews + 2) { score -= 5; }

  return { score: Math.max(0, Math.min(100, score)), flags };
}

function pumpDumpCheck(data) {
  // 6 criteria for pump & dump detection
  let redFlags = 0;
  if (data.marketCap < 500000000) redFlags++;      // Small cap
  if (data.relativeVolume > 5) redFlags++;          // Abnormal volume
  if (data.changePercent5d > 30) redFlags++;        // Parabolic move
  if (data.shortPercent > 20) redFlags++;           // High short interest
  if (data.insiderSelling90d > 3) redFlags++;       // Insiders dumping
  if (data.sentiment_reddit?.mentions_24h > 300) redFlags++;  // WSB hype
  return redFlags >= 4; // 4 out of 6 = likely pump & dump
}

Layer 4: Sector Momentum

Trading a stock whose sector has headwinds is swimming against the current. This layer checks if the sector supports or undermines the trade thesis.

Sector Scoring Algorithm
// Sector ETF mapping
const SECTOR_ETFS = {
  "Technology": "XLK",     "Healthcare": "XLV",
  "Financials": "XLF",     "Energy": "XLE",
  "Industrials": "XLI",    "Materials": "XLB",
  "Consumer Discretionary": "XLY",  "Consumer Staples": "XLP",
  "Utilities": "XLU",      "Real Estate": "XLRE",
  "Communication Services": "XLC"
};

function scoreSector(tickerData, sectorData) {
  let score = 50;
  const notes = [];

  const sectorETF = SECTOR_ETFS[tickerData.sector];
  if (!sectorETF || !sectorData) return { score: 50, notes: ["No sector data"] };

  // === Sector Trend (5d, 20d, 60d) ===
  const s5d = sectorData.change5d;
  const s20d = sectorData.change20d;
  const s60d = sectorData.change60d;

  if (s5d > 0 && s20d > 0 && s60d > 0) {
    score += 15; notes.push("Sector in uptrend (all timeframes)");
  } else if (s5d > 0 && s20d > 0) {
    score += 8; notes.push("Sector short-term positive");
  } else if (s5d < 0 && s20d < 0 && s60d < 0) {
    score -= 15; notes.push("Sector in downtrend");
  } else if (s5d < -2 && s20d > 0) {
    score += 5; notes.push("Sector pulling back in uptrend");
  }

  // === Relative Strength vs SPY ===
  const sectorRS = sectorData.change20d - sectorData.spyChange20d;
  if (sectorRS > 3) {
    score += 10; notes.push("Sector outperforming SPY by >3%");
  } else if (sectorRS > 0) {
    score += 5;
  } else if (sectorRS < -5) {
    score -= 10; notes.push("Sector lagging SPY significantly");
  }

  // === Rotation Model ===
  // Early cycle: financials, consumer discretionary
  // Mid cycle: tech, industrials
  // Late cycle: energy, materials, healthcare
  // Recession: utilities, consumer staples
  const regimeFavoredSectors = {
    "RISK_ON": ["Technology", "Consumer Discretionary", "Financials"],
    "NEUTRAL": ["Technology", "Industrials", "Healthcare"],
    "EARLY_RISK_OFF": ["Energy", "Materials", "Healthcare", "Utilities"],
    "RISK_OFF": ["Utilities", "Consumer Staples", "Healthcare"],
    "RECOVERY": ["Financials", "Technology", "Industrials"]
  };

  const favored = regimeFavoredSectors[tickerData.currentRegime] || [];
  if (favored.includes(tickerData.sector)) {
    score += 10; notes.push("Sector favored in current regime");
  }

  // === Money Flow ===
  if (sectorData.volumeTrend === "rising" && s5d > 0) {
    score += 5; notes.push("Rising volume + rising price in sector");
  }

  return { score: Math.max(0, Math.min(100, score)), notes };
}

Layer 5: Macro Regime Compatibility

This layer checks whether the trade strategy matches the current macro regime. A momentum breakout setup in a Risk-Off environment gets penalized; an oversold bounce gets boosted.

Macro Compatibility Matrix
const STRATEGY_REGIME_COMPATIBILITY = {
  // Strategy: { regime: compatibility_score }
  "momentum_breakout": {
    "RISK_ON": 95, "NEUTRAL": 70, "EARLY_RISK_OFF": 30,
    "RISK_OFF": 10, "RECOVERY": 60
  },
  "trend_following": {
    "RISK_ON": 90, "NEUTRAL": 75, "EARLY_RISK_OFF": 40,
    "RISK_OFF": 20, "RECOVERY": 55
  },
  "oversold_bounce": {
    "RISK_ON": 50, "NEUTRAL": 65, "EARLY_RISK_OFF": 90,
    "RISK_OFF": 60, "RECOVERY": 95
  },
  "volatility_squeeze": {
    "RISK_ON": 70, "NEUTRAL": 80, "EARLY_RISK_OFF": 50,
    "RISK_OFF": 30, "RECOVERY": 70
  },
  "safe_haven": {
    "RISK_ON": 20, "NEUTRAL": 40, "EARLY_RISK_OFF": 85,
    "RISK_OFF": 95, "RECOVERY": 30
  },
  "value_insiders": {
    "RISK_ON": 60, "NEUTRAL": 70, "EARLY_RISK_OFF": 75,
    "RISK_OFF": 65, "RECOVERY": 80
  }
};

function scoreMacro(strategy, regime, marketData) {
  let score = STRATEGY_REGIME_COMPATIBILITY[strategy]?.[regime] || 50;

  // VIX adjustment
  if (marketData.vix > 30 && strategy !== "safe_haven") score -= 10;
  if (marketData.vix < 15 && strategy === "momentum_breakout") score += 5;

  // Dollar adjustment (strong dollar hurts intl/commodity stocks)
  if (marketData.dxyChange5d > 2 && ["energy", "materials", "intl"].includes(strategy))
    score -= 5;

  // Yield curve (inverted = recession risk)
  if (marketData.yieldSpread < 0) {
    if (strategy === "safe_haven") score += 5;
    else score -= 5;
  }

  return { score: Math.max(0, Math.min(100, score)) };
}

Layer 6: AI Conviction Layer

The final layer is the AI "gut check." Claude Code synthesizes all available data and generates a conviction score. This catches things the quantitative layers might miss: narrative inconsistencies, upcoming catalysts, sector-specific risks.

AI Conviction Prompt (Claude Code Skill)
# AI Conviction Assessment

You are scoring setup conviction for {TICKER} ({STRATEGY}).

## Available Data
- Technical score: {tech_score}/100 (confluence: {confluence})
- Fundamental score: {fund_score}/100
- Social score: {social_score}/100
- Sector score: {sector_score}/100
- Macro score: {macro_score}/100

## Recent Context
- News: {recent_news_summary}
- Earnings: {next_earnings_date}
- Insider activity: {insider_summary}
- Sector trend: {sector_trend}

## Your Task
1. Does the narrative make sense? (Is there a coherent reason this stock
   should move in the expected direction?)
2. Are there red flags the numbers miss?
   - Pending litigation, FDA decision, regulatory action
   - Key person departure, accounting concerns
   - Sector-specific risks (tariffs, regulation)
3. Is the timing right?
   - Earnings in < 5 days = higher volatility risk
   - FOMC meeting this week = macro uncertainty
   - OpEx Friday = gamma effects
4. Historical pattern matching:
   - Has this setup worked before in similar conditions?

## Output
{
  "conviction": 0-100,
  "thesis": "2-3 sentences explaining why",
  "redFlags": ["list of concerns"],
  "catalysts": ["list of positive catalysts"],
  "timingRisk": "low|medium|high"
}

Why an AI Layer?

Quantitative screens are powerful but blind to context. An AI layer can catch things like: "CF Industries has great technicals, but the Biden administration just announced fertilizer subsidies are ending" — something no RSI or MACD indicator would detect. It's the difference between data and understanding.

The Composite Score

Composite Score Calculation
function calculateComposite(layers) {
  const weights = {
    technical:    0.30,
    fundamental:  0.20,
    social:       0.10,
    sector:       0.15,
    macro:        0.15,
    aiConviction: 0.10
  };

  const thresholds = {
    technical:    70,
    fundamental:  65,
    social:       50,
    sector:       60,
    macro:        60,
    aiConviction: 65
  };

  // Weighted average
  let composite = 0;
  for (const [layer, weight] of Object.entries(weights)) {
    composite += layers[layer] * weight;
  }

  // Check all minimum thresholds
  const failedLayers = Object.entries(thresholds)
    .filter(([layer, min]) => layers[layer] < min)
    .map(([layer]) => layer);

  // Classification
  let grade;
  if (composite >= 92 && failedLayers.length === 0) grade = "A++";
  else if (composite >= 85 && failedLayers.length === 0) grade = "A+";
  else if (composite >= 78 && failedLayers.length <= 1) grade = "A";
  else if (composite >= 70) grade = "B+";
  else grade = "REJECT";

  return {
    composite: Math.round(composite * 10) / 10,
    grade,
    failedLayers,
    layers,
    pass: grade !== "REJECT"
  };
}

Example: Scoring CF Industries (Early Risk-Off)

Technical (confluence: 5)
88
Fundamental
82
Social Sentiment
65
Sector Momentum
91
Macro Regime
90
AI Conviction
85
COMPOSITE
86.2

Grade: A+ — All layers above threshold. Oversold bounce strategy, highly compatible with Early Risk-Off regime. Strong fundamentals in fertilizer sector with energy tailwinds.

Building a Claude Code Skill

The entire scoring engine is packaged as a Claude Code skill — a reusable prompt template that can be invoked from CLAUDE.md or directly.

.claude/skills/analyze-setup.md
# Analyze Setup — Multi-Layer Scoring

## Input
Ticker: {TICKER}
Strategy: {STRATEGY} (oversold_bounce | momentum_breakout | trend_following |
                       volatility_squeeze | safe_haven | value_insiders)

## Steps

### 1. Collect Data
Call QueryData with:
- symbols: [{TICKER}, sector_etf]
- types: quote,technicals,support_resistance,trading_signals,
         sentiment_overall,sentiment_stocktwits,insider_transactions,
         financials,earnings_quarterly,stats,news

Call GetMarketOverview for regime context.

### 2. Score Each Layer
Apply the 6-layer scoring framework:
- Technical: RSI, MACD, MA alignment, volume, S/R, confluence
- Fundamental: Growth, margins, FCF, balance sheet, earnings, valuation
- Social: Sentiment score, StockTwits, Reddit, news, P&D detection
- Sector: ETF trend, relative strength, rotation model
- Macro: Strategy-regime compatibility, VIX, DXY, yield curve
- AI Conviction: Narrative coherence, red flags, catalysts, timing

### 3. Compute Composite
Weighted average (30/20/10/15/15/10).
Check all minimums. Assign grade (A++/A+/A/B+/REJECT).

### 4. Generate Trade Parameters (if A+ or A++)
- Entry: current price or key level
- Stop: nearest support - ATR buffer, or 1.5x ATR below entry
- TP1: first resistance or +1R
- TP2: second resistance or +2R
- R/R ratio: (TP1 - Entry) / (Entry - Stop)
- Horizon: swing (3-10 days) or position (2-6 weeks)

### 5. Output
Return structured JSON:
{
  "ticker": "CF",
  "strategy": "oversold_bounce",
  "composite": 86.2,
  "grade": "A+",
  "layers": { ... },
  "entry": 78.50,
  "stop": 74.20,
  "tp1": 84.00,
  "tp2": 89.50,
  "rr": 1.28,
  "thesis": "...",
  "confirmations": [...],
  "invalidations": [...],
  "timing": "swing 5-8 days",
  "sector": "Materials",
  "geo": "US"
}

Skills are Declarative

Notice there's zero code to maintain. The skill is a markdown prompt that tells Claude Code what to do. It orchestrates MCP calls, applies scoring logic, and generates structured output. When you want to change the scoring weights or add a new layer, just edit the markdown.

Part 4 of 6
The Alert DSL — Programmable Trading Triggers
Automated Trading with Claude Code 3/6