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

Stars 2
Forks 0

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:

  1. Confluence Scoring: Combining signals from SMT, liquidity, and session patterns
  2. Multi-Timeframe Bias: Calculating directional bias across 1m, 15m, 1h, daily
  3. Trade Decision Framework: Determining when confluence is sufficient for entry
  4. Priority Ranking: Scoring setups from A+ (highest) to C (lowest quality)
  5. 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

typescript
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

typescript
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

typescript
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

typescript
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

typescript
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

typescript
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

typescript
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:

  1. Confluence Scoring: 4-layer system (structural, liquidity, session, timeframe) totaling 100 points
  2. Multi-Timeframe Bias: Weighted aggregation across 1m, 15m, 1h, daily with agreement percentage
  3. Trade Decision Framework: Configurable criteria for minimum confluence, bias strength, required components
  4. Priority Ranking: A+ to D grading based on confluence, alignment, timing, R:R, SMT presence
  5. 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 resampling
  • tjr-smt-divergence: ES/NQ divergence detection, correlation analysis
  • tjr-liquidity-detection: PDH/PDL sweeps, FVG patterns, VWAP validation
  • tjr-session-patterns: Sweep clustering, session pattern recognition, multi-timeframe analysis

Example Usage

typescript
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);

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results