Agent skill
tjr-smt-divergence
TJR SMT (Structure Market Technicals) divergence detection between ES and NQ futures - identifies high-probability reversals through structural mismatches during market open
Install this agent skill to your Project
npx add-skill https://github.com/Nice-Wolf-Studio/wolf-skills-marketplace/tree/main/tjr-smt-divergence
SKILL.md
TJR SMT Divergence Detection
Overview
SMT (Structure Market Technicals) divergence is a core component of The Jared Ryan (TJR) trading system. It identifies when two highly correlated instruments (ES and NQ futures) diverge structurally, signaling potential market reversals.
Core Principle: When ES and NQ are highly correlated (r ≥ 0.7) but form divergent market structures (e.g., ES makes higher high while NQ makes lower high), this indicates institutional positioning disagreement and often precedes significant reversals.
Use this skill when:
- Building TJR-based trading bots for ES/NQ futures
- Detecting market reversals during the market open window (9:30-10:30 AM ET)
- Identifying which index (ES or NQ) is leading the market
- Validating directional bias through structural divergence confirmation
Critical Time Window: SMT divergences are most reliable during the NY market open (9:30-10:30 AM ET).
What is SMT Divergence?
Definition
SMT Divergence occurs when two correlated instruments form different structural patterns despite moving together. In TJR, we specifically compare:
- ES (E-mini S&P 500 futures)
- NQ (E-mini Nasdaq-100 futures)
Example Bearish SMT:
ES: Makes HIGHER HIGH (HH)
NQ: Makes LOWER HIGH (LH)
→ Bearish divergence → Expect downward reversal
Example Bullish SMT:
ES: Makes LOWER LOW (LL)
NQ: Makes HIGHER LOW (HL)
→ Bullish divergence → Expect upward reversal
Why SMT Works
Fundamental Basis:
- ES (S&P 500) represents broad market exposure
- NQ (Nasdaq-100) represents tech-heavy exposure
- When they diverge structurally, it signals sector rotation or institutional repositioning
- One index "leads" while the other "lags" → divergence creates trading opportunity
Statistical Validation:
- Requires rolling correlation ≥ 0.7 (70% correlated movement)
- If correlation is too low (<0.7), divergence is noise, not signal
- Time lag tolerance: ≤2 bars between structural formations
Market Structure Classification
To detect SMT, we first classify each instrument's structure using swing points.
Structure Types
Bullish Structures:
- Higher High (HH): Current swing high > previous swing high
- Higher Low (HL): Current swing low > previous swing low
Bearish Structures: 3. Lower High (LH): Current swing high < previous swing high 4. Lower Low (LL): Current swing low < previous swing low
TypeScript Implementation:
enum StructureType {
HIGHER_HIGH = 'HH',
HIGHER_LOW = 'HL',
LOWER_HIGH = 'LH',
LOWER_LOW = 'LL'
}
interface SwingPoint {
type: 'high' | 'low';
index: number; // Candle index
price: number;
timestamp: number;
}
// Classify structure between two swing points
function classifyStructure(
prev: SwingPoint,
current: SwingPoint
): StructureType | null {
if (prev.type !== current.type) {
return null; // Can only compare high-to-high or low-to-low
}
if (prev.type === 'high') {
return current.price > prev.price ? StructureType.HIGHER_HIGH : StructureType.LOWER_HIGH;
} else {
return current.price > prev.price ? StructureType.HIGHER_LOW : StructureType.LOWER_LOW;
}
}
// Label all swing structures in a series
function labelSwingStructures(swings: SwingPoint[]): Map<number, StructureType> {
const structures = new Map<number, StructureType>();
const highs = swings.filter(s => s.type === 'high');
const lows = swings.filter(s => s.type === 'low');
// Compare consecutive highs
for (let i = 1; i < highs.length; i++) {
const structure = classifyStructure(highs[i - 1], highs[i]);
if (structure) {
structures.set(highs[i].index, structure);
}
}
// Compare consecutive lows
for (let i = 1; i < lows.length; i++) {
const structure = classifyStructure(lows[i - 1], lows[i]);
if (structure) {
structures.set(lows[i].index, structure);
}
}
return structures;
}
SMT Divergence Detection Algorithm
Core Logic
Bearish SMT Divergence Patterns:
- ES makes HH while NQ makes LH (or stays flat)
- ES makes HL while NQ makes LL
Bullish SMT Divergence Patterns:
- ES makes LL while NQ makes HL (or stays flat)
- ES makes LH while NQ makes HH
TypeScript Implementation:
interface SMTDivergence {
timestamp: number;
type: 'bullish' | 'bearish';
esStructure: StructureType;
nqStructure: StructureType;
esSwingIndex: number;
nqSwingIndex: number;
correlation: number; // Rolling correlation at time of divergence
confidence: number; // 0-1 score
leadingIndex: 'ES' | 'NQ';
}
function detectSMTDivergences(
esSwings: SwingPoint[],
nqSwings: SwingPoint[],
esCandles: Candle[],
nqCandles: Candle[],
params: {
minCorrelation: number; // e.g., 0.7
correlationWindow: number; // e.g., 60 bars
timeLagTolerance: number; // e.g., 2 bars
}
): SMTDivergence[] {
const divergences: SMTDivergence[] = [];
// Label structures
const esStructures = labelSwingStructures(esSwings);
const nqStructures = labelSwingStructures(nqSwings);
// Get structure entries
const esEntries = Array.from(esStructures.entries());
const nqEntries = Array.from(nqStructures.entries());
// Compare structures within time lag tolerance
for (const [esIndex, esStruct] of esEntries) {
const esTime = esCandles[esIndex].timestamp;
for (const [nqIndex, nqStruct] of nqEntries) {
const nqTime = nqCandles[nqIndex].timestamp;
// Check time lag tolerance (within N bars)
const timeLag = Math.abs(esIndex - nqIndex);
if (timeLag > params.timeLagTolerance) continue;
// Calculate rolling correlation at this point
const correlation = calculateRollingCorrelation(
esCandles.slice(Math.max(0, esIndex - params.correlationWindow), esIndex + 1),
nqCandles.slice(Math.max(0, nqIndex - params.correlationWindow), nqIndex + 1)
);
// Correlation must be strong enough
if (correlation < params.minCorrelation) continue;
// Check for bearish divergence
if (
(esStruct === StructureType.HIGHER_HIGH && nqStruct === StructureType.LOWER_HIGH) ||
(esStruct === StructureType.HIGHER_LOW && nqStruct === StructureType.LOWER_LOW)
) {
divergences.push({
timestamp: esTime,
type: 'bearish',
esStructure: esStruct,
nqStructure: nqStruct,
esSwingIndex: esIndex,
nqSwingIndex: nqIndex,
correlation,
confidence: calculateDivergenceConfidence(esStruct, nqStruct, correlation),
leadingIndex: determineLeadingIndex(esStruct, nqStruct)
});
}
// Check for bullish divergence
if (
(esStruct === StructureType.LOWER_LOW && nqStruct === StructureType.HIGHER_LOW) ||
(esStruct === StructureType.LOWER_HIGH && nqStruct === StructureType.HIGHER_HIGH)
) {
divergences.push({
timestamp: esTime,
type: 'bullish',
esStructure: esStruct,
nqStructure: nqStruct,
esSwingIndex: esIndex,
nqSwingIndex: nqIndex,
correlation,
confidence: calculateDivergenceConfidence(esStruct, nqStruct, correlation),
leadingIndex: determineLeadingIndex(esStruct, nqStruct)
});
}
}
}
return divergences;
}
Rolling Correlation Calculation
Essential for validating that ES and NQ are truly correlated before trusting the divergence.
TypeScript Implementation:
function calculateRollingCorrelation(
esCandles: Candle[],
nqCandles: Candle[]
): number {
if (esCandles.length !== nqCandles.length || esCandles.length < 2) {
return 0;
}
const esPrices = esCandles.map(c => c.close);
const nqPrices = nqCandles.map(c => c.close);
return pearsonCorrelation(esPrices, nqPrices);
}
function pearsonCorrelation(x: number[], y: number[]): number {
const n = x.length;
if (n !== y.length || n === 0) return 0;
const meanX = x.reduce((sum, val) => sum + val, 0) / n;
const meanY = y.reduce((sum, val) => sum + val, 0) / n;
let numerator = 0;
let sumX2 = 0;
let sumY2 = 0;
for (let i = 0; i < n; i++) {
const dx = x[i] - meanX;
const dy = y[i] - meanY;
numerator += dx * dy;
sumX2 += dx * dx;
sumY2 += dy * dy;
}
const denominator = Math.sqrt(sumX2 * sumY2);
return denominator === 0 ? 0 : numerator / denominator;
}
Leading Index Identification
Critical Concept: When structures diverge, one index is "leading" the market direction.
Rules:
- ES Leading: ES makes higher lows while NQ makes lower lows → Bullish bias
- NQ Leading: NQ makes higher lows while ES makes lower lows → Bullish bias (for NQ)
- Bearish equivalent: Opposite patterns
TypeScript Implementation:
function determineLeadingIndex(
esStructure: StructureType,
nqStructure: StructureType
): 'ES' | 'NQ' {
// Bullish divergences
if (esStructure === StructureType.HIGHER_LOW && nqStructure === StructureType.LOWER_LOW) {
return 'ES'; // ES is stronger (making higher lows)
}
if (esStructure === StructureType.LOWER_LOW && nqStructure === StructureType.HIGHER_LOW) {
return 'NQ'; // NQ is stronger
}
// Bearish divergences
if (esStructure === StructureType.HIGHER_HIGH && nqStructure === StructureType.LOWER_HIGH) {
return 'ES'; // ES is weaker (making higher highs while NQ weakens)
}
if (esStructure === StructureType.LOWER_HIGH && nqStructure === StructureType.HIGHER_HIGH) {
return 'NQ'; // NQ is weaker
}
// Default fallback (shouldn't happen with proper filtering)
return 'ES';
}
Market Open Window Filter
SMT divergences are most reliable during the 9:30-10:30 AM ET window after cash market open.
TypeScript Implementation:
function isInMarketOpenWindow(timestamp: number): boolean {
const est = new Date(timestamp.toLocaleString('en-US', {
timeZone: 'America/New_York'
}));
const hour = est.getHours();
const minute = est.getMinutes();
// 9:30 AM to 10:30 AM ET
const startMinutes = 9 * 60 + 30; // 9:30 AM = 570 minutes
const endMinutes = 10 * 60 + 30; // 10:30 AM = 630 minutes
const currentMinutes = hour * 60 + minute;
return currentMinutes >= startMinutes && currentMinutes < endMinutes;
}
// Filter divergences to market open window only
function filterToMarketOpen(divergences: SMTDivergence[]): SMTDivergence[] {
return divergences.filter(div => isInMarketOpenWindow(div.timestamp));
}
Confidence Scoring
Not all SMT divergences are equal. Score confidence based on:
- Correlation strength (higher = better)
- Structure clarity (HH vs LH is clearer than HL vs LL)
- Time window (market open = higher confidence)
TypeScript Implementation:
function calculateDivergenceConfidence(
esStructure: StructureType,
nqStructure: StructureType,
correlation: number
): number {
let confidence = 0.5; // Base confidence
// Bonus for strong correlation
if (correlation >= 0.8) confidence += 0.2;
else if (correlation >= 0.7) confidence += 0.1;
// Bonus for clear high divergences (more obvious than low divergences)
const isHighDivergence =
(esStructure === StructureType.HIGHER_HIGH && nqStructure === StructureType.LOWER_HIGH) ||
(esStructure === StructureType.LOWER_HIGH && nqStructure === StructureType.HIGHER_HIGH);
if (isHighDivergence) confidence += 0.15;
// Bonus for extreme divergence (HH vs LH is more extreme than HL vs LL)
const isExtremeDivergence =
(esStructure === StructureType.HIGHER_HIGH && nqStructure === StructureType.LOWER_HIGH) ||
(esStructure === StructureType.LOWER_LOW && nqStructure === StructureType.HIGHER_LOW);
if (isExtremeDivergence) confidence += 0.15;
return Math.min(confidence, 1.0); // Cap at 1.0
}
Complete SMT Signal Generation
Bringing it all together into a usable signal generator:
TypeScript Implementation:
interface SMTSignal {
timestamp: number;
direction: 'long' | 'short';
divergence: SMTDivergence;
reasoning: string[];
entry?: number; // Suggested entry (midpoint of ES and NQ current prices)
confidence: number;
}
async function generateSMTSignals(
esCandles: Candle[],
nqCandles: Candle[],
sessionStart: Date
): Promise<SMTSignal[]> {
const signals: SMTSignal[] = [];
// 1. Find swing points for both instruments
const esSwings = findSwingPoints(esCandles, 2); // k=2 lookback (from trading-foundations)
const nqSwings = findSwingPoints(nqCandles, 2);
// 2. Detect SMT divergences
const divergences = detectSMTDivergences(
esSwings,
nqSwings,
esCandles,
nqCandles,
{
minCorrelation: 0.7,
correlationWindow: 60,
timeLagTolerance: 2
}
);
// 3. Filter to market open window
const marketOpenDivergences = filterToMarketOpen(divergences);
// 4. Convert divergences to trading signals
for (const div of marketOpenDivergences) {
const esCurrentPrice = esCandles[div.esSwingIndex].close;
const nqCurrentPrice = nqCandles[div.nqSwingIndex].close;
signals.push({
timestamp: div.timestamp,
direction: div.type === 'bullish' ? 'long' : 'short',
divergence: div,
entry: esCurrentPrice, // Use ES price as entry reference
confidence: div.confidence,
reasoning: [
`SMT ${div.type} divergence detected`,
`ES: ${div.esStructure}, NQ: ${div.nqStructure}`,
`Correlation: ${(div.correlation * 100).toFixed(1)}%`,
`Leading index: ${div.leadingIndex}`,
`Market open window (9:30-10:30 AM ET)`
]
});
}
return signals;
}
Integration with Existing Skills
Reusing Trading-Foundations
Swing Point Detection:
// Import from trading-foundations skill
import { findSwingPoints, SwingPoint } from '../trading-foundations';
// Use directly in SMT detection
const esSwings = findSwingPoints(esCandles, 2);
const nqSwings = findSwingPoints(nqCandles, 2);
Reusing Trading-Bot-Development
ES/NQ Data Fetching:
// Import from trading-bot-development
import { DataManager } from '../trading-bot-development';
// Fetch both ES and NQ in parallel
const dataManager = new DataManager(databentoApiKey);
const [esCandles, nqCandles] = await Promise.all([
dataManager.getCandles('ES', '5m', 200),
dataManager.getCandles('NQ', '5m', 200)
]);
Signal Schema Compatibility:
// SMT signals implement the same Signal interface
interface Signal {
strategy: string; // 'tjr-smt'
symbol: string; // 'ES' or 'NQ'
direction: 'long' | 'short';
entry: number;
confidence: number;
reasoning: string[];
timestamp: Date;
metadata?: {
divergence: SMTDivergence;
correlation: number;
leadingIndex: string;
};
}
Discord Bot Integration
Example Slash Command:
import { SlashCommandBuilder, EmbedBuilder } from 'discord.js';
const smtAnalyzeCommand = new SlashCommandBuilder()
.setName('smt-analyze')
.setDescription('Analyze ES vs NQ for SMT divergences');
async function handleSMTAnalyze(interaction) {
await interaction.deferReply();
try {
// Fetch data
const sessionStart = getTodaySessionStart();
const [esCandles, nqCandles] = await Promise.all([
dataManager.getCandles('ES', '5m', 200),
dataManager.getCandles('NQ', '5m', 200)
]);
// Generate signals
const signals = await generateSMTSignals(esCandles, nqCandles, sessionStart);
if (signals.length === 0) {
await interaction.editReply('No SMT divergences detected during market open window.');
return;
}
// Build embed for most recent signal
const latestSignal = signals[signals.length - 1];
const embed = new EmbedBuilder()
.setTitle(`🔀 SMT Divergence Detected`)
.setColor(latestSignal.direction === 'long' ? 0x00FF00 : 0xFF0000)
.addFields([
{
name: 'Direction',
value: latestSignal.direction.toUpperCase(),
inline: true
},
{
name: 'Confidence',
value: `${(latestSignal.confidence * 100).toFixed(0)}%`,
inline: true
},
{
name: 'Leading Index',
value: latestSignal.divergence.leadingIndex,
inline: true
},
{
name: 'Structures',
value: `ES: **${latestSignal.divergence.esStructure}**\nNQ: **${latestSignal.divergence.nqStructure}**`,
inline: false
},
{
name: 'Correlation',
value: `${(latestSignal.divergence.correlation * 100).toFixed(1)}%`,
inline: true
},
{
name: 'Entry Suggestion',
value: latestSignal.entry?.toFixed(2) || 'N/A',
inline: true
},
{
name: 'Reasoning',
value: latestSignal.reasoning.join('\n'),
inline: false
}
])
.setTimestamp()
.setFooter({ text: `${signals.length} divergence(s) found in market open window` });
await interaction.editReply({ embeds: [embed] });
} catch (error) {
await interaction.editReply(`Error: ${error.message}`);
}
}
Common Pitfalls
1. Ignoring Correlation Threshold
- ❌ Detecting divergences when correlation < 0.7
- ✅ Always validate rolling correlation ≥ 0.7 before trusting divergence
2. Wrong Time Window
- ❌ Trading SMT divergences outside 9:30-10:30 AM ET
- ✅ Filter signals to market open window only
3. Mixing Swing Types
- ❌ Comparing ES swing high to NQ swing low
- ✅ Only compare high-to-high or low-to-low
4. Ignoring Time Lag
- ❌ Comparing ES structure at 9:35 to NQ structure at 10:15
- ✅ Enforce time lag tolerance (≤2 bars)
5. No Swing Point Validation
- ❌ Using insufficient lookback (k=1) for swing detection
- ✅ Use k=2 minimum for reliable swing identification
After Using This Skill
If building TJR trading bot:
- Use
tjr-liquidity-detectionskill for liquidity sweep patterns - Use
tjr-session-patternsskill for multi-timeframe confirmation - Use
tjr-multi-timeframe-confluenceskill to combine SMT with other TJR signals
For multi-strategy bots:
- SMT divergence provides directional bias confirmation
- Combine with ICT Fair Value Gaps for precise entry timing
- Use AMT value area for target identification
For backtesting:
- Test SMT signals isolated first (measure win rate, avg return)
- Then test combined with liquidity sweeps
- Track correlation threshold impact on performance
References
TJR Session Analysis:
- Nov 13-14, 2025 session notes (~/Dev/tjr-suite/docs/knowledge/tjr/)
- SMT divergence detection methodology
- ES vs NQ structural analysis patterns
Related Concepts:
- Market structure analysis (trading-foundations)
- Correlation analysis (statistical methods)
- Leading/lagging index relationships
Last Updated: January 2025 Version: 1.0.0 Part of Wolf Skills Marketplace - TJR Series
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
daily-summary
Use when preparing daily standups or status reports - automates PR summary generation with categorization, metrics, and velocity analysis; eliminates manual report compilation and ensures consistent format
wolf-scripts-core
Core automation scripts for archetype selection, evidence validation, quality scoring, and safe bash execution
wolf
Master skill for Wolf Agents institutional knowledge and behavioral patterns (v1.1.0 with skill-chaining)
wolf-archetypes
Behavioral archetypes for automatic agent adaptation based on work type
ict-strategy
Inner Circle Trader (ICT) methodology - smart money concepts, liquidity pools, fair value gaps, order blocks, and market structure for trading bot implementation
databento
Use when working with ES/NQ futures market data, before calling any Databento API - follow mandatory four-step workflow (cost check, availability check, fetch, validate); prevents costly API errors and ensures data quality
Didn't find tool you were looking for?