Agent skill
tjr-multi-timeframe-confluence
TJR (The Jared Ryan) multi-timeframe confluence and integration layer. Use when implementing complete TJR trading system that combines SMT divergence, liquidity detection, and session patterns into unified bias and trade decisions. Reuses: trading-foundations, tjr-smt-divergence, tjr-liquidity-detection, tjr-session-patterns
Install this agent skill to your Project
npx add-skill https://github.com/Nice-Wolf-Studio/wolf-skills-marketplace/tree/main/tjr-multi-timeframe-confluence
SKILL.md
TJR Multi-Timeframe Confluence
Overview
This skill provides reference material for implementing the TJR integration layer that combines all TJR analysis components into a unified trading system. It focuses on:
- Confluence Scoring: Combining signals from SMT, liquidity, and session patterns
- Multi-Timeframe Bias: Calculating directional bias across 1m, 15m, 1h, daily
- Trade Decision Framework: Determining when confluence is sufficient for entry
- Priority Ranking: Scoring setups from A+ (highest) to C (lowest quality)
- Real-Time Monitoring: Continuous analysis and alerting system
When to use this skill: When coding Discord bots that need complete TJR methodology implementation, combining multiple analysis types into actionable trade decisions for ES and NQ futures.
Prerequisites
Before using this skill, you should be familiar with:
- trading-foundations: Candle data models, swing point detection, timeframe resampling
- tjr-smt-divergence: ES/NQ correlation analysis, structure divergence detection
- tjr-liquidity-detection: PDH/PDL sweep detection, FVG patterns, VWAP validation
- tjr-session-patterns: Sweep clustering, session pattern recognition, multi-timeframe analysis
Key Concepts
Confluence Layers
TJR methodology requires alignment across multiple layers for high-probability trades:
Layer 1: Structural (SMT Divergence)
- Weight: 30%
- ES vs NQ divergence detection
- Minimum correlation: 0.7
- Valid during market open window (9:30-10:30 AM ET)
Layer 2: Liquidity (PDH/PDL Sweeps)
- Weight: 25%
- Prior day high/low sweeps with fast reclaim
- Fair Value Gap confirmation
- Volume spike validation
Layer 3: Session Pattern
- Weight: 25%
- London flat → NY reversal
- Steep selloff → bounce
- Accumulation cluster breakout
Layer 4: Multi-Timeframe Alignment
- Weight: 20%
- 1-minute, 15-minute, 1-hour, daily bias agreement
- Higher timeframes override lower timeframes
- Daily bias provides context, not entry timing
Confluence Scoring System
interface ConfluenceScore {
structural: number; // 0-30 points
liquidity: number; // 0-25 points
session: number; // 0-25 points
timeframe: number; // 0-20 points
total: number; // 0-100 points
grade: 'A+' | 'A' | 'B' | 'C' | 'D' | 'F';
}
function calculateConfluence(
smtDivergence: SMTDivergence | null,
liquiditySweep: LiquiditySweep | null,
sessionPattern: SessionPattern | null,
mtfAlignment: MultitimeframeConfidence
): ConfluenceScore {
let score: ConfluenceScore = {
structural: 0,
liquidity: 0,
session: 0,
timeframe: 0,
total: 0,
grade: 'F',
};
// Structural layer (SMT divergence)
if (smtDivergence) {
score.structural = 15; // Base for having SMT
if (smtDivergence.correlation >= 0.85) score.structural += 5; // Strong correlation
if (smtDivergence.leadingIndex !== null) score.structural += 5; // Clear leader
if (isMarketOpenWindow(new Date())) score.structural += 5; // Optimal timing
}
// Liquidity layer (sweeps)
if (liquiditySweep) {
score.liquidity = 15; // Base for having sweep
if (liquiditySweep.reclaimSpeed <= 3) score.liquidity += 5; // Fast reclaim
if (liquiditySweep.volumeSpike) score.liquidity += 5; // Volume confirmation
}
// Session pattern layer
if (sessionPattern) {
score.session = 15; // Base for having pattern
if (sessionPattern.confidence >= 75) score.session += 5; // High confidence pattern
if (sessionPattern.clusters.length >= 3) score.session += 5; // Multiple clusters
}
// Multi-timeframe alignment
score.timeframe = Math.floor(mtfAlignment.total * 0.2); // Convert 0-100 to 0-20
// Calculate total
score.total =
score.structural +
score.liquidity +
score.session +
score.timeframe;
// Assign grade
if (score.total >= 85) score.grade = 'A+';
else if (score.total >= 75) score.grade = 'A';
else if (score.total >= 65) score.grade = 'B';
else if (score.total >= 55) score.grade = 'C';
else if (score.total >= 45) score.grade = 'D';
else score.grade = 'F';
return score;
}
function isMarketOpenWindow(time: Date): boolean {
const hour = time.getUTCHours() - 5; // Convert to ET
const minute = time.getUTCMinutes();
return (hour === 9 && minute >= 30) || (hour === 10 && minute < 30);
}
Multi-Timeframe Bias Calculation
interface TimeframeBias {
timeframe: '1m' | '15m' | '1h' | '1d';
bias: 'bullish' | 'bearish' | 'neutral';
strength: number; // 0-100
reasoning: string[];
}
interface AggregatedBias {
primary: 'bullish' | 'bearish' | 'neutral';
strength: number; // 0-100
timeframes: TimeframeBias[];
agreement: number; // 0-100 (percentage of timeframes agreeing)
}
function calculateTimeframeBias(
candles: Candle[],
timeframe: '1m' | '15m' | '1h' | '1d'
): TimeframeBias {
const reasoning: string[] = [];
let biasScore = 0; // Positive = bullish, negative = bearish
// Calculate swing points
const swings = findSwingPoints(candles, 5);
const recentSwings = swings.slice(-6); // Last 6 swing points
// Check for higher highs / higher lows (bullish) or lower highs / lower lows (bearish)
const highs = recentSwings.filter(s => s.type === 'high').map(s => s.price);
const lows = recentSwings.filter(s => s.type === 'low').map(s => s.price);
if (highs.length >= 2) {
const hhCount = highs.slice(1).filter((h, i) => h > highs[i]).length;
const lhCount = highs.slice(1).filter((h, i) => h < highs[i]).length;
if (hhCount > lhCount) {
biasScore += 20;
reasoning.push(`Higher highs detected on ${timeframe}`);
} else if (lhCount > hhCount) {
biasScore -= 20;
reasoning.push(`Lower highs detected on ${timeframe}`);
}
}
if (lows.length >= 2) {
const hlCount = lows.slice(1).filter((l, i) => l > lows[i]).length;
const llCount = lows.slice(1).filter((l, i) => l < lows[i]).length;
if (hlCount > llCount) {
biasScore += 20;
reasoning.push(`Higher lows detected on ${timeframe}`);
} else if (llCount > hlCount) {
biasScore -= 20;
reasoning.push(`Lower lows detected on ${timeframe}`);
}
}
// Check moving average alignment (simple 20-period)
const closes = candles.slice(-20).map(c => c.close);
const ma20 = closes.reduce((sum, c) => sum + c, 0) / closes.length;
const currentPrice = candles[candles.length - 1].close;
if (currentPrice > ma20) {
biasScore += 15;
reasoning.push(`Price above 20-period MA on ${timeframe}`);
} else {
biasScore -= 15;
reasoning.push(`Price below 20-period MA on ${timeframe}`);
}
// Check recent momentum (last 10 candles)
const recentCandles = candles.slice(-10);
const bullishCandles = recentCandles.filter(c => c.close > c.open).length;
const bearishCandles = recentCandles.filter(c => c.close < c.open).length;
if (bullishCandles > bearishCandles * 1.5) {
biasScore += 15;
reasoning.push(`Strong bullish momentum on ${timeframe} (${bullishCandles}/10 bullish candles)`);
} else if (bearishCandles > bullishCandles * 1.5) {
biasScore -= 15;
reasoning.push(`Strong bearish momentum on ${timeframe} (${bearishCandles}/10 bearish candles)`);
}
// Determine bias and strength
let bias: 'bullish' | 'bearish' | 'neutral';
let strength: number;
if (biasScore >= 20) {
bias = 'bullish';
strength = Math.min(biasScore, 100);
} else if (biasScore <= -20) {
bias = 'bearish';
strength = Math.min(Math.abs(biasScore), 100);
} else {
bias = 'neutral';
strength = 50;
}
return {
timeframe,
bias,
strength,
reasoning,
};
}
function aggregateBias(timeframeBiases: TimeframeBias[]): AggregatedBias {
// Weight timeframes differently
const weights = {
'1m': 0.2,
'15m': 0.3,
'1h': 0.3,
'1d': 0.2,
};
let weightedScore = 0;
timeframeBiases.forEach(tfBias => {
const weight = weights[tfBias.timeframe];
const score = tfBias.bias === 'bullish'
? tfBias.strength
: tfBias.bias === 'bearish'
? -tfBias.strength
: 0;
weightedScore += score * weight;
});
// Determine primary bias
let primary: 'bullish' | 'bearish' | 'neutral';
let strength: number;
if (weightedScore >= 20) {
primary = 'bullish';
strength = Math.min(weightedScore, 100);
} else if (weightedScore <= -20) {
primary = 'bearish';
strength = Math.min(Math.abs(weightedScore), 100);
} else {
primary = 'neutral';
strength = 50;
}
// Calculate agreement percentage
const primaryBiasCount = timeframeBiases.filter(
tfBias => tfBias.bias === primary
).length;
const agreement = (primaryBiasCount / timeframeBiases.length) * 100;
return {
primary,
strength,
timeframes: timeframeBiases,
agreement,
};
}
Trade Decision Framework
interface TradeDecision {
action: 'LONG' | 'SHORT' | 'NO_TRADE';
confidence: number; // 0-100
confluenceScore: ConfluenceScore;
bias: AggregatedBias;
entry: number;
stopLoss: number;
takeProfit: number;
positionSize: number; // contracts
reasoning: string[];
alerts: string[]; // Warning messages
}
interface DecisionCriteria {
minConfluenceGrade: 'A+' | 'A' | 'B' | 'C';
minBiasAgreement: number; // Default: 75%
minBiasStrength: number; // Default: 60
requireSMT: boolean; // Default: false
requireLiquidity: boolean; // Default: true
requireSession: boolean; // Default: false
}
function makeTradeDecision(
symbol: 'ES' | 'NQ',
smtDivergence: SMTDivergence | null,
liquiditySweep: LiquiditySweep | null,
sessionPattern: SessionPattern | null,
bias: AggregatedBias,
currentPrice: number,
criteria: DecisionCriteria = {
minConfluenceGrade: 'B',
minBiasAgreement: 75,
minBiasStrength: 60,
requireSMT: false,
requireLiquidity: true,
requireSession: false,
}
): TradeDecision {
const reasoning: string[] = [];
const alerts: string[] = [];
// Calculate confluence score
const mtfConfidence: MultitimeframeConfidence = {
base: 50,
m1Alignment: bias.timeframes.find(t => t.timeframe === '1m')?.bias === bias.primary ? 10 : 0,
m15Alignment: bias.timeframes.find(t => t.timeframe === '15m')?.bias === bias.primary ? 15 : 0,
h1Alignment: bias.timeframes.find(t => t.timeframe === '1h')?.bias === bias.primary ? 15 : 0,
dailyAlignment: bias.timeframes.find(t => t.timeframe === '1d')?.bias === bias.primary ? 10 : 0,
total: 50,
};
mtfConfidence.total =
mtfConfidence.base +
mtfConfidence.m1Alignment +
mtfConfidence.m15Alignment +
mtfConfidence.h1Alignment +
mtfConfidence.dailyAlignment;
const confluenceScore = calculateConfluence(
smtDivergence,
liquiditySweep,
sessionPattern,
mtfConfidence
);
// Check minimum confluence grade
const gradeOrder = ['F', 'D', 'C', 'B', 'A', 'A+'];
const minGradeIndex = gradeOrder.indexOf(criteria.minConfluenceGrade);
const actualGradeIndex = gradeOrder.indexOf(confluenceScore.grade);
if (actualGradeIndex < minGradeIndex) {
alerts.push(`Confluence grade ${confluenceScore.grade} below minimum ${criteria.minConfluenceGrade}`);
return {
action: 'NO_TRADE',
confidence: 0,
confluenceScore,
bias,
entry: 0,
stopLoss: 0,
takeProfit: 0,
positionSize: 0,
reasoning,
alerts,
};
}
reasoning.push(`Confluence grade: ${confluenceScore.grade} (${confluenceScore.total}/100)`);
// Check bias agreement
if (bias.agreement < criteria.minBiasAgreement) {
alerts.push(`Bias agreement ${bias.agreement.toFixed(0)}% below minimum ${criteria.minBiasAgreement}%`);
return {
action: 'NO_TRADE',
confidence: 0,
confluenceScore,
bias,
entry: 0,
stopLoss: 0,
takeProfit: 0,
positionSize: 0,
reasoning,
alerts,
};
}
reasoning.push(`Bias agreement: ${bias.agreement.toFixed(0)}% (${bias.primary})`);
// Check bias strength
if (bias.strength < criteria.minBiasStrength) {
alerts.push(`Bias strength ${bias.strength.toFixed(0)} below minimum ${criteria.minBiasStrength}`);
return {
action: 'NO_TRADE',
confidence: 0,
confluenceScore,
bias,
entry: 0,
stopLoss: 0,
takeProfit: 0,
positionSize: 0,
reasoning,
alerts,
};
}
reasoning.push(`Bias strength: ${bias.strength.toFixed(0)}/100`);
// Check required components
if (criteria.requireSMT && !smtDivergence) {
alerts.push('SMT divergence required but not present');
return {
action: 'NO_TRADE',
confidence: 0,
confluenceScore,
bias,
entry: 0,
stopLoss: 0,
takeProfit: 0,
positionSize: 0,
reasoning,
alerts,
};
}
if (criteria.requireLiquidity && !liquiditySweep) {
alerts.push('Liquidity sweep required but not present');
return {
action: 'NO_TRADE',
confidence: 0,
confluenceScore,
bias,
entry: 0,
stopLoss: 0,
takeProfit: 0,
positionSize: 0,
reasoning,
alerts,
};
}
if (criteria.requireSession && !sessionPattern) {
alerts.push('Session pattern required but not present');
return {
action: 'NO_TRADE',
confidence: 0,
confluenceScore,
bias,
entry: 0,
stopLoss: 0,
takeProfit: 0,
positionSize: 0,
reasoning,
alerts,
};
}
// All criteria met - determine trade direction
let action: 'LONG' | 'SHORT';
let entry: number;
let stopLoss: number;
let takeProfit: number;
if (bias.primary === 'bullish') {
action = 'LONG';
// Entry: Use session pattern entry if available, else liquidity sweep level
if (sessionPattern) {
entry = sessionPattern.entry;
stopLoss = sessionPattern.stopLoss;
takeProfit = sessionPattern.takeProfit;
reasoning.push(`Using session pattern entry: ${sessionPattern.type}`);
} else if (liquiditySweep) {
entry = liquiditySweep.level + 2; // Enter 2 points above sweep
stopLoss = liquiditySweep.level - 5; // Stop 5 points below
takeProfit = entry + 15; // Target 15 points (3:1 R:R)
reasoning.push('Using liquidity sweep entry');
} else {
entry = currentPrice;
stopLoss = currentPrice - 10;
takeProfit = currentPrice + 20;
reasoning.push('Using current price entry (no specific setup)');
}
} else {
action = 'SHORT';
if (sessionPattern) {
entry = sessionPattern.entry;
stopLoss = sessionPattern.stopLoss;
takeProfit = sessionPattern.takeProfit;
reasoning.push(`Using session pattern entry: ${sessionPattern.type}`);
} else if (liquiditySweep) {
entry = liquiditySweep.level - 2;
stopLoss = liquiditySweep.level + 5;
takeProfit = entry - 15;
reasoning.push('Using liquidity sweep entry');
} else {
entry = currentPrice;
stopLoss = currentPrice + 10;
takeProfit = currentPrice - 20;
reasoning.push('Using current price entry (no specific setup)');
}
}
// Calculate position size (simplified)
const accountBalance = 100000; // Example: $100k account
const riskPercent = 1; // Risk 1% per trade
const dollarRisk = accountBalance * (riskPercent / 100);
const pointRisk = Math.abs(entry - stopLoss);
const pointValue = 50; // $50 per point for ES/NQ
const positionSize = Math.floor(dollarRisk / (pointRisk * pointValue));
// Calculate final confidence
const confidence = Math.min(
(confluenceScore.total + bias.strength + bias.agreement) / 3,
100
);
return {
action,
confidence,
confluenceScore,
bias,
entry,
stopLoss,
takeProfit,
positionSize,
reasoning,
alerts,
};
}
Implementation Patterns
1. Complete TJR Analysis Pipeline
interface TJRAnalysis {
symbol: 'ES' | 'NQ';
timestamp: Date;
smtDivergence: SMTDivergence | null;
liquiditySweep: LiquiditySweep | null;
sessionPattern: SessionPattern | null;
bias: AggregatedBias;
decision: TradeDecision;
}
async function runTJRAnalysis(symbol: 'ES' | 'NQ'): Promise<TJRAnalysis> {
const timestamp = new Date();
// Fetch candles for all timeframes
const m1Candles = await fetchCandles(symbol, '1m', 100);
const m15Candles = await fetchCandles(symbol, '15m', 100);
const h1Candles = await fetchCandles(symbol, '1h', 100);
const dailyCandles = await fetchCandles(symbol, '1d', 30);
// Calculate bias for each timeframe
const m1Bias = calculateTimeframeBias(m1Candles, '1m');
const m15Bias = calculateTimeframeBias(m15Candles, '15m');
const h1Bias = calculateTimeframeBias(h1Candles, '1h');
const dailyBias = calculateTimeframeBias(dailyCandles, '1d');
// Aggregate bias
const bias = aggregateBias([m1Bias, m15Bias, h1Bias, dailyBias]);
// Detect SMT divergence (requires both ES and NQ data)
let smtDivergence: SMTDivergence | null = null;
if (symbol === 'ES') {
const nqM15Candles = await fetchCandles('NQ', '15m', 100);
const esSwings = findSwingPoints(m15Candles, 5);
const nqSwings = findSwingPoints(nqM15Candles, 5);
const divergences = detectSMTDivergences(
esSwings,
nqSwings,
m15Candles,
nqM15Candles,
{
minCorrelation: 0.7,
correlationWindow: 60,
timeLagTolerance: 2,
}
);
smtDivergence = divergences.length > 0 ? divergences[0] : null;
}
// Detect liquidity sweeps
const { pdh, pdl } = getPriorDayLevels(m1Candles);
const sweeps = await detectAllSweeps(m1Candles, pdh, pdl);
const liquiditySweep = sweeps.length > 0 ? sweeps[sweeps.length - 1] : null;
// Detect session patterns
const sessionPattern = await detectPatternsOnTimeframe(m15Candles, '15m');
// Make trade decision
const decision = makeTradeDecision(
symbol,
smtDivergence,
liquiditySweep,
sessionPattern,
bias,
m1Candles[m1Candles.length - 1].close,
{
minConfluenceGrade: 'B',
minBiasAgreement: 75,
minBiasStrength: 60,
requireSMT: false,
requireLiquidity: true,
requireSession: false,
}
);
return {
symbol,
timestamp,
smtDivergence,
liquiditySweep,
sessionPattern,
bias,
decision,
};
}
2. Real-Time Monitoring System
class TJRMonitor {
private analysisInterval: NodeJS.Timeout | null = null;
private lastAnalysis: Map<string, TJRAnalysis> = new Map();
start() {
// Run analysis every 1 minute
this.analysisInterval = setInterval(async () => {
await this.analyze();
}, 60 * 1000);
console.log('TJR Monitor started');
}
stop() {
if (this.analysisInterval) {
clearInterval(this.analysisInterval);
this.analysisInterval = null;
}
console.log('TJR Monitor stopped');
}
private async analyze() {
try {
// Analyze both ES and NQ
const esAnalysis = await runTJRAnalysis('ES');
const nqAnalysis = await runTJRAnalysis('NQ');
// Check for changes
this.checkForAlerts(esAnalysis, 'ES');
this.checkForAlerts(nqAnalysis, 'NQ');
// Store latest analysis
this.lastAnalysis.set('ES', esAnalysis);
this.lastAnalysis.set('NQ', nqAnalysis);
} catch (error) {
console.error('Analysis error:', error);
}
}
private checkForAlerts(analysis: TJRAnalysis, symbol: string) {
const last = this.lastAnalysis.get(symbol);
// New trade signal
if (
analysis.decision.action !== 'NO_TRADE' &&
(!last || last.decision.action === 'NO_TRADE')
) {
this.sendAlert({
type: 'NEW_TRADE',
symbol,
analysis,
});
}
// Confluence grade improvement
if (
last &&
getGradeValue(analysis.decision.confluenceScore.grade) >
getGradeValue(last.decision.confluenceScore.grade)
) {
this.sendAlert({
type: 'CONFLUENCE_UPGRADE',
symbol,
analysis,
});
}
// New SMT divergence
if (analysis.smtDivergence && (!last || !last.smtDivergence)) {
this.sendAlert({
type: 'SMT_DIVERGENCE',
symbol,
analysis,
});
}
// New liquidity sweep
if (
analysis.liquiditySweep &&
(!last ||
!last.liquiditySweep ||
analysis.liquiditySweep.timestamp !== last.liquiditySweep.timestamp)
) {
this.sendAlert({
type: 'LIQUIDITY_SWEEP',
symbol,
analysis,
});
}
// New session pattern
if (
analysis.sessionPattern &&
(!last ||
!last.sessionPattern ||
analysis.sessionPattern.type !== last.sessionPattern.type)
) {
this.sendAlert({
type: 'SESSION_PATTERN',
symbol,
analysis,
});
}
}
private sendAlert(alert: {
type: string;
symbol: string;
analysis: TJRAnalysis;
}) {
console.log(`[${alert.type}] ${alert.symbol}:`, alert.analysis.decision);
// In production, this would send to Discord, Telegram, etc.
}
}
function getGradeValue(grade: string): number {
const gradeValues: Record<string, number> = {
'F': 0,
'D': 1,
'C': 2,
'B': 3,
'A': 4,
'A+': 5,
};
return gradeValues[grade] || 0;
}
3. Discord Bot Integration
import { Client, EmbedBuilder, TextChannel, ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';
class TJRConfluenceBot {
private client: Client;
private channelId: string;
private monitor: TJRMonitor;
constructor(token: string, channelId: string) {
this.client = new Client({
intents: ['Guilds', 'GuildMessages'],
});
this.channelId = channelId;
this.monitor = new TJRMonitor();
this.client.once('ready', () => {
console.log('TJR Confluence Bot ready');
this.monitor.start();
});
// Override sendAlert to use Discord
this.monitor['sendAlert'] = async (alert) => {
await this.sendDiscordAlert(alert);
};
this.client.login(token);
}
private async sendDiscordAlert(alert: {
type: string;
symbol: string;
analysis: TJRAnalysis;
}) {
const channel = await this.client.channels.fetch(this.channelId) as TextChannel;
const { analysis, symbol, type } = alert;
switch (type) {
case 'NEW_TRADE':
await this.sendTradeSignal(channel, symbol, analysis);
break;
case 'CONFLUENCE_UPGRADE':
await this.sendConfluenceUpgrade(channel, symbol, analysis);
break;
case 'SMT_DIVERGENCE':
await this.sendSMTAlert(channel, symbol, analysis);
break;
case 'LIQUIDITY_SWEEP':
await this.sendLiquidityAlert(channel, symbol, analysis);
break;
case 'SESSION_PATTERN':
await this.sendSessionPatternAlert(channel, symbol, analysis);
break;
}
}
private async sendTradeSignal(
channel: TextChannel,
symbol: string,
analysis: TJRAnalysis
) {
const { decision, bias } = analysis;
const embed = new EmbedBuilder()
.setTitle(`🎯 TJR TRADE SIGNAL - ${symbol}`)
.setColor(decision.action === 'LONG' ? 0x00FF00 : 0xFF0000)
.setDescription(`**${decision.action}** signal with **${decision.confluenceScore.grade}** grade confluence`)
.addFields(
{
name: '📊 Confluence Breakdown',
value:
`Structural: ${decision.confluenceScore.structural}/30\n` +
`Liquidity: ${decision.confluenceScore.liquidity}/25\n` +
`Session: ${decision.confluenceScore.session}/25\n` +
`Timeframe: ${decision.confluenceScore.timeframe}/20\n` +
`**Total: ${decision.confluenceScore.total}/100 (${decision.confluenceScore.grade})**`,
inline: true,
},
{
name: '🎲 Multi-Timeframe Bias',
value:
`Primary: **${bias.primary.toUpperCase()}**\n` +
`Strength: ${bias.strength.toFixed(0)}/100\n` +
`Agreement: ${bias.agreement.toFixed(0)}%\n` +
`1m: ${bias.timeframes.find(t => t.timeframe === '1m')?.bias || 'N/A'}\n` +
`15m: ${bias.timeframes.find(t => t.timeframe === '15m')?.bias || 'N/A'}\n` +
`1h: ${bias.timeframes.find(t => t.timeframe === '1h')?.bias || 'N/A'}\n` +
`Daily: ${bias.timeframes.find(t => t.timeframe === '1d')?.bias || 'N/A'}`,
inline: true,
},
{
name: '💰 Trade Parameters',
value:
`Entry: ${decision.entry.toFixed(2)}\n` +
`Stop: ${decision.stopLoss.toFixed(2)}\n` +
`Target: ${decision.takeProfit.toFixed(2)}\n` +
`Risk: ${Math.abs(decision.entry - decision.stopLoss).toFixed(2)} pts\n` +
`Reward: ${Math.abs(decision.takeProfit - decision.entry).toFixed(2)} pts\n` +
`R:R: ${(Math.abs(decision.takeProfit - decision.entry) / Math.abs(decision.entry - decision.stopLoss)).toFixed(2)}:1\n` +
`Size: ${decision.positionSize} contracts`,
inline: false,
},
{
name: '🔍 Analysis Components',
value:
`SMT Divergence: ${analysis.smtDivergence ? '✅' : '❌'}\n` +
`Liquidity Sweep: ${analysis.liquiditySweep ? '✅' : '❌'}\n` +
`Session Pattern: ${analysis.sessionPattern ? `✅ (${analysis.sessionPattern.type})` : '❌'}`,
inline: false,
},
{
name: '📝 Reasoning',
value: decision.reasoning.join('\n'),
inline: false,
}
)
.setTimestamp();
// Add action buttons
const row = new ActionRowBuilder<ButtonBuilder>()
.addComponents(
new ButtonBuilder()
.setCustomId(`take_trade_${symbol}_${decision.action}`)
.setLabel(`Take ${decision.action} Trade`)
.setStyle(decision.action === 'LONG' ? ButtonStyle.Success : ButtonStyle.Danger),
new ButtonBuilder()
.setCustomId(`view_charts_${symbol}`)
.setLabel('View Charts')
.setStyle(ButtonStyle.Primary),
new ButtonBuilder()
.setCustomId(`dismiss_${symbol}`)
.setLabel('Dismiss')
.setStyle(ButtonStyle.Secondary)
);
await channel.send({ embeds: [embed], components: [row] });
}
private async sendConfluenceUpgrade(
channel: TextChannel,
symbol: string,
analysis: TJRAnalysis
) {
const embed = new EmbedBuilder()
.setTitle(`⬆️ Confluence Upgrade - ${symbol}`)
.setColor(0xFFFF00)
.setDescription(`Confluence grade improved to **${analysis.decision.confluenceScore.grade}** (${analysis.decision.confluenceScore.total}/100)`)
.setTimestamp();
await channel.send({ embeds: [embed] });
}
private async sendSMTAlert(
channel: TextChannel,
symbol: string,
analysis: TJRAnalysis
) {
const smt = analysis.smtDivergence!;
const embed = new EmbedBuilder()
.setTitle(`📈 SMT Divergence Detected - ${symbol}`)
.setColor(0xFF00FF)
.addFields(
{ name: 'Type', value: smt.type.toUpperCase(), inline: true },
{ name: 'Correlation', value: smt.correlation.toFixed(2), inline: true },
{ name: 'Leading Index', value: smt.leadingIndex || 'N/A', inline: true },
{ name: 'ES Structure', value: smt.esStructure, inline: true },
{ name: 'NQ Structure', value: smt.nqStructure, inline: true }
)
.setTimestamp();
await channel.send({ embeds: [embed] });
}
private async sendLiquidityAlert(
channel: TextChannel,
symbol: string,
analysis: TJRAnalysis
) {
const sweep = analysis.liquiditySweep!;
const embed = new EmbedBuilder()
.setTitle(`💧 Liquidity Sweep - ${symbol}`)
.setColor(0x00FFFF)
.addFields(
{ name: 'Type', value: sweep.type.replace('_', ' ').toUpperCase(), inline: true },
{ name: 'Level', value: sweep.level.toFixed(2), inline: true },
{ name: 'Direction', value: sweep.direction.toUpperCase(), inline: true },
{ name: 'Reclaim Speed', value: `${sweep.reclaimSpeed} bars`, inline: true },
{ name: 'Volume Spike', value: sweep.volumeSpike ? '✅' : '❌', inline: true },
{ name: 'Confidence', value: `${sweep.confidence}%`, inline: true }
)
.setTimestamp();
await channel.send({ embeds: [embed] });
}
private async sendSessionPatternAlert(
channel: TextChannel,
symbol: string,
analysis: TJRAnalysis
) {
const pattern = analysis.sessionPattern!;
const embed = new EmbedBuilder()
.setTitle(`🔔 Session Pattern - ${symbol}`)
.setColor(0xFFA500)
.addFields(
{ name: 'Pattern', value: pattern.type.replace(/_/g, ' ').toUpperCase(), inline: true },
{ name: 'Direction', value: pattern.direction.toUpperCase(), inline: true },
{ name: 'Confidence', value: `${pattern.confidence}%`, inline: true },
{ name: 'Timeframe', value: pattern.timeframe, inline: true },
{ name: 'Clusters', value: `${pattern.clusters.length}`, inline: true },
{ name: 'Reasoning', value: pattern.reasoning.join('\n'), inline: false }
)
.setTimestamp();
await channel.send({ embeds: [embed] });
}
}
// Usage
const bot = new TJRConfluenceBot(
process.env.DISCORD_TOKEN!,
process.env.CHANNEL_ID!
);
Priority Ranking System
interface PriorityRank {
rank: 'A+' | 'A' | 'B' | 'C' | 'D';
score: number; // 0-100
factors: {
confluenceGrade: number; // 0-25
timeframeAlignment: number; // 0-25
sessionTiming: number; // 0-20
riskReward: number; // 0-15
smtPresence: number; // 0-15
};
}
function calculatePriorityRank(
analysis: TJRAnalysis,
currentTime: Date
): PriorityRank {
let factors = {
confluenceGrade: 0,
timeframeAlignment: 0,
sessionTiming: 0,
riskReward: 0,
smtPresence: 0,
};
// Confluence grade (0-25 points)
const gradePoints: Record<string, number> = {
'A+': 25,
'A': 20,
'B': 15,
'C': 10,
'D': 5,
'F': 0,
};
factors.confluenceGrade = gradePoints[analysis.decision.confluenceScore.grade] || 0;
// Timeframe alignment (0-25 points)
factors.timeframeAlignment = Math.floor(analysis.bias.agreement * 0.25);
// Session timing (0-20 points)
const hour = currentTime.getUTCHours() - 5;
const minute = currentTime.getUTCMinutes();
if ((hour === 9 && minute >= 30) || (hour === 10 && minute < 30)) {
factors.sessionTiming = 20; // Market open window (best)
} else if (hour >= 9 && hour < 12) {
factors.sessionTiming = 15; // Morning session
} else if (hour >= 14 && hour < 16) {
factors.sessionTiming = 10; // Afternoon session
} else {
factors.sessionTiming = 5; // Other times
}
// Risk/reward (0-15 points)
if (analysis.decision.action !== 'NO_TRADE') {
const risk = Math.abs(analysis.decision.entry - analysis.decision.stopLoss);
const reward = Math.abs(analysis.decision.takeProfit - analysis.decision.entry);
const rr = reward / risk;
if (rr >= 3) factors.riskReward = 15;
else if (rr >= 2) factors.riskReward = 10;
else if (rr >= 1.5) factors.riskReward = 5;
}
// SMT presence (0-15 points)
if (analysis.smtDivergence) {
factors.smtPresence = 10; // Base
if (analysis.smtDivergence.correlation >= 0.85) {
factors.smtPresence += 5; // Strong correlation bonus
}
}
// Calculate total score
const score =
factors.confluenceGrade +
factors.timeframeAlignment +
factors.sessionTiming +
factors.riskReward +
factors.smtPresence;
// Determine rank
let rank: 'A+' | 'A' | 'B' | 'C' | 'D';
if (score >= 85) rank = 'A+';
else if (score >= 75) rank = 'A';
else if (score >= 65) rank = 'B';
else if (score >= 55) rank = 'C';
else rank = 'D';
return {
rank,
score,
factors,
};
}
Summary
This skill provides the complete TJR integration layer that combines all analysis components into actionable trade decisions. Key takeaways:
- Confluence Scoring: 4-layer system (structural, liquidity, session, timeframe) totaling 100 points
- Multi-Timeframe Bias: Weighted aggregation across 1m, 15m, 1h, daily with agreement percentage
- Trade Decision Framework: Configurable criteria for minimum confluence, bias strength, required components
- Priority Ranking: A+ to D grading based on confluence, alignment, timing, R:R, SMT presence
- Real-Time Monitoring: Continuous analysis with alerts for new signals, upgrades, divergences
When to use: Building production Discord trading bots that implement the complete TJR methodology with multi-component confluence validation and real-time monitoring.
Reuses from other skills:
trading-foundations: Candle models, swing points, timeframe resamplingtjr-smt-divergence: ES/NQ divergence detection, correlation analysistjr-liquidity-detection: PDH/PDL sweeps, FVG patterns, VWAP validationtjr-session-patterns: Sweep clustering, session pattern recognition, multi-timeframe analysis
Example Usage
async function main() {
// Run complete TJR analysis
const esAnalysis = await runTJRAnalysis('ES');
const nqAnalysis = await runTJRAnalysis('NQ');
// Calculate priority ranks
const esPriority = calculatePriorityRank(esAnalysis, new Date());
const nqPriority = calculatePriorityRank(nqAnalysis, new Date());
console.log('ES Analysis:');
console.log(` Decision: ${esAnalysis.decision.action}`);
console.log(` Confluence: ${esAnalysis.decision.confluenceScore.grade} (${esAnalysis.decision.confluenceScore.total}/100)`);
console.log(` Bias: ${esAnalysis.bias.primary} (${esAnalysis.bias.strength}/100, ${esAnalysis.bias.agreement.toFixed(0)}% agreement)`);
console.log(` Priority: ${esPriority.rank} (${esPriority.score}/100)`);
console.log();
console.log('NQ Analysis:');
console.log(` Decision: ${nqAnalysis.decision.action}`);
console.log(` Confluence: ${nqAnalysis.decision.confluenceScore.grade} (${nqAnalysis.decision.confluenceScore.total}/100)`);
console.log(` Bias: ${nqAnalysis.bias.primary} (${nqAnalysis.bias.strength}/100, ${nqAnalysis.bias.agreement.toFixed(0)}% agreement)`);
console.log(` Priority: ${nqPriority.rank} (${nqPriority.score}/100)`);
// Start Discord bot with real-time monitoring
const bot = new TJRConfluenceBot(
process.env.DISCORD_TOKEN!,
process.env.CHANNEL_ID!
);
}
main().catch(console.error);
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?