Multi-layer scoring across 6 dimensions — technical, fundamental, social, sector, macro, and AI conviction — to identify A++ setups that check every box.
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.
RSI, MACD, moving averages, volume, support/resistance, pattern recognition. Confluence count determines the score. Minimum threshold: 70.
Revenue growth, margin trajectory, cash flow quality, balance sheet health, valuation vs peers. Minimum threshold: 65.
StockTwits bull ratio, Reddit mentions, news sentiment, pump&dump detection. Minimum threshold: 50.
Sector ETF trend, relative strength vs SPY, rotation model position. Minimum threshold: 60.
Strategy-regime alignment, VIX compatibility, dollar impact. Minimum threshold: 60.
LLM narrative analysis, red flag detection, historical pattern matching. Minimum threshold: 65.
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.
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
};
}
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.
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.
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
};
}
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 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 };
}
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.
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)) };
}
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 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"
}
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.
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"
};
}
Grade: A+ — All layers above threshold. Oversold bounce strategy, highly compatible with Early Risk-Off regime. Strong fundamentals in fertilizer sector with energy tailwinds.
The entire scoring engine is packaged as a Claude Code skill — a reusable prompt template that can be invoked from CLAUDE.md or directly.
# 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"
}
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.
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.