HAMM SAMMICH

Description

Ham on the chart

Comments (0)

0/2000

Loading comments…

Source code

// HAM3 - Clean Session Levels Indicator with Key Levels
// Shows only the most recent Asian, London, NY, and Previous Day session highs/lows
// Plus VWAP and Daily Pivot Points

function calculate(bars, ctx) {
  // User inputs
  const showAsian = ctx.input('Show Asian Session', true);
  const showLondon = ctx.input('Show London Session', true);
  const showNY = ctx.input('Show NY Session', true);
  const showPrevDay = ctx.input('Show Previous Day', true);
  const showVWAP = ctx.input('Show VWAP', true);
  const showPivots = ctx.input('Show Daily Pivots', true);
  
  const asianStart = ctx.input('Asian Start (ET)', 20, { min: 0, max: 23, step: 1 });
  const asianEnd = ctx.input('Asian End (ET)', 0, { min: 0, max: 23, step: 1 });
  const londonStart = ctx.input('London Start (ET)', 2, { min: 0, max: 23, step: 1 });
  const londonEnd = ctx.input('London End (ET)', 5, { min: 0, max: 23, step: 1 });
  const nyStart = ctx.input('NY Start (ET)', 9, { min: 0, max: 23, step: 1 });
  const nyEnd = ctx.input('NY End (ET)', 12, { min: 0, max: 23, step: 1 });
  
  const asianColor = ctx.input('Asian Color', '#9c27b0');
  const londonColor = ctx.input('London Color', '#2196f3');
  const nyColor = ctx.input('NY Color', '#4caf50');
  const prevDayColor = ctx.input('Prev Day Color', '#ff9800');
  const vwapColor = ctx.input('VWAP Color', '#ff5722');
  
  // Pivot colors - bright dark blue for R series, bright green for S series, pink for pivot
  const rColor = ctx.input('R Series Color', '#2962ff'); // Bright dark blue
  const sColor = ctx.input('S Series Color', '#00e676'); // Bright green
  const pivotColor = ctx.input('Pivot Line Color', '#e91e63'); // Pink
  
  const lineWidth = ctx.input('Line Width', 3, { min: 1, max: 10, step: 1 });
  const pivotLineWidth = ctx.input('Pivot Line Width', 4, { min: 1, max: 8, step: 1 }); // Increased
  const lookbackDays = ctx.input('Lookback Days', 5, { min: 1, max: 30, step: 1 });
  const labelSize = ctx.input('Label Size', 12, { min: 8, max: 20, step: 1 });
  
  // Session check helper — handles midnight wrap
  function inSessionRange(barIndex, startHour, endHour) {
    const h = ctx.time.hourIn(barIndex, 'America/New_York');
    if (startHour < endHour) return h >= startHour && h < endHour;
    return h >= startHour || h < endHour;
  }
  
  // Lookback cutoff
  let dayCount = 0, cutoffBar = 0;
  for (let i = bars.length - 1; i >= 1; i--) {
    if (ctx.time.isNewSession(i)) { 
      dayCount++; 
      if (dayCount > lookbackDays) { 
        cutoffBar = i; 
        break; 
      } 
    }
  }
  
  // Track only the most recent session of each type
  let latestAsian = null, latestLondon = null, latestNY = null;
  let prevDayHigh = null, prevDayLow = null, prevDayClose = null;
  let prevDayHighBar = null, prevDayLowBar = null;
  let currentDayHigh = null, currentDayLow = null, currentDayClose = null;
  let currentDayHighBar = null, currentDayLowBar = null;
  
  // Track if we're currently in each session
  let asianStarted = false, londonStarted = false, nyStarted = false;
  
  for (let i = cutoffBar; i < bars.length; i++) {
    const inAsian = inSessionRange(i, asianStart, asianEnd);
    const inLondon = inSessionRange(i, londonStart, londonEnd);
    const inNY = inSessionRange(i, nyStart, nyEnd);
    const isNewDay = ctx.time.isNewSession(i);
    
    // Day tracking - only track the most recent previous day
    if (isNewDay) {
      // Store the previous day's levels before resetting
      if (currentDayHigh !== null && showPrevDay) {
        prevDayHigh = currentDayHigh;
        prevDayLow = currentDayLow;
        prevDayClose = currentDayClose;
        prevDayHighBar = currentDayHighBar;
        prevDayLowBar = currentDayLowBar;
      }
      
      // Reset current day tracking
      currentDayHigh = bars[i].high;
      currentDayLow = bars[i].low;
      currentDayClose = bars[i].close;
      currentDayHighBar = i;
      currentDayLowBar = i;
    } else if (currentDayHigh !== null) {
      if (bars[i].high > currentDayHigh) { 
        currentDayHigh = bars[i].high; 
        currentDayHighBar = i; 
      }
      if (bars[i].low < currentDayLow) { 
        currentDayLow = bars[i].low; 
        currentDayLowBar = i; 
      }
      currentDayClose = bars[i].close; // Update close as we go
    }
    
    // Asian session tracking - only track the most recent COMPLETED session
    if (inAsian) {
      if (!asianStarted) {
        // Start of new Asian session
        latestAsian = { high: bars[i].high, low: bars[i].low, highBar: i, lowBar: i };
        asianStarted = true;
      } else {
        // Continue tracking current Asian session
        if (bars[i].high > latestAsian.high) { 
          latestAsian.high = bars[i].high; 
          latestAsian.highBar = i; 
        }
        if (bars[i].low < latestAsian.low) { 
          latestAsian.low = bars[i].low; 
          latestAsian.lowBar = i; 
        }
      }
    } else if (asianStarted) {
      // Asian session just ended - keep it as the latest
      asianStarted = false;
    }
    
    // London session tracking - only track the most recent COMPLETED session
    if (inLondon) {
      if (!londonStarted) {
        // Start of new London session
        latestLondon = { high: bars[i].high, low: bars[i].low, highBar: i, lowBar: i };
        londonStarted = true;
      } else {
        // Continue tracking current London session
        if (bars[i].high > latestLondon.high) { 
          latestLondon.high = bars[i].high; 
          latestLondon.highBar = i; 
        }
        if (bars[i].low < latestLondon.low) { 
          latestLondon.low = bars[i].low; 
          latestLondon.lowBar = i; 
        }
      }
    } else if (londonStarted) {
      // London session just ended - keep it as the latest
      londonStarted = false;
    }
    
    // NY session tracking - only track the most recent COMPLETED session
    if (inNY) {
      if (!nyStarted) {
        // Start of new NY session
        latestNY = { high: bars[i].high, low: bars[i].low, highBar: i, lowBar: i };
        nyStarted = true;
      } else {
        // Continue tracking current NY session
        if (bars[i].high > latestNY.high) { 
          latestNY.high = bars[i].high; 
          latestNY.highBar = i; 
        }
        if (bars[i].low < latestNY.low) { 
          latestNY.low = bars[i].low; 
          latestNY.lowBar = i; 
        }
      }
    } else if (nyStarted) {
      // NY session just ended - keep it as the latest
      nyStarted = false;
    }
  }
  
  // Build sessions array with only the most recent sessions
  const sessions = [];
  
  if (showAsian && latestAsian) {
    sessions.push({ 
      type: 'Asia', 
      high: latestAsian.high, 
      low: latestAsian.low, 
      endBar: bars.length - 1, 
      highBar: latestAsian.highBar, 
      lowBar: latestAsian.lowBar 
    });
  }
  
  if (showLondon && latestLondon) {
    sessions.push({ 
      type: 'London', 
      high: latestLondon.high, 
      low: latestLondon.low, 
      endBar: bars.length - 1, 
      highBar: latestLondon.highBar, 
      lowBar: latestLondon.lowBar 
    });
  }
  
  if (showNY && latestNY) {
    sessions.push({ 
      type: 'NY', 
      high: latestNY.high, 
      low: latestNY.low, 
      endBar: bars.length - 1, 
      highBar: latestNY.highBar, 
      lowBar: latestNY.lowBar 
    });
  }
  
  // Add previous day levels if available
  if (prevDayHigh !== null && showPrevDay) {
    sessions.push({ 
      type: 'PD', 
      high: prevDayHigh, 
      low: prevDayLow, 
      endBar: bars.length - 1, 
      highBar: prevDayHighBar, 
      lowBar: prevDayLowBar 
    });
  }
  
  // Draw session levels with solid lines and labels
  const endDraw = bars.length - 1;
  
  for (const sess of sessions) {
    const color = 
      sess.type === 'Asia' ? asianColor :
      sess.type === 'London' ? londonColor :
      sess.type === 'NY' ? nyColor :
      prevDayColor;
    
    const labelText = sess.type === 'PD' ? 'Prev Day' : sess.type;
    
    // Draw high line - solid and thick
    ctx.line(sess.highBar, sess.high, endDraw, sess.high, {
      color: color,
      lineWidth: lineWidth,
      lineStyle: 'solid'
    });
    
    // Add label for high line at the right edge
    ctx.label(endDraw, sess.high, labelText + ' High', {
      color: color,
      textColor: '#ffffff',
      size: labelSize,
      style: 'bubble'
    });
    
    // Draw low line - solid and thick
    ctx.line(sess.lowBar, sess.low, endDraw, sess.low, {
      color: color,
      lineWidth: lineWidth,
      lineStyle: 'solid'
    });
    
    // Add label for low line at the right edge
    ctx.label(endDraw, sess.low, labelText + ' Low', {
      color: color,
      textColor: '#ffffff',
      size: labelSize,
      style: 'bubble'
    });
  }
  
  // Add Previous Day Close if available
  if (prevDayClose !== null && showPrevDay) {
    ctx.hline(prevDayClose, {
      color: prevDayColor,
      lineWidth: lineWidth - 1,
      lineStyle: 'dashed'
    });
    ctx.label(endDraw, prevDayClose, 'Prev Close', {
      color: prevDayColor,
      textColor: '#ffffff',
      size: labelSize,
      style: 'bubble'
    });
  }
  
  // Calculate and draw VWAP
  if (showVWAP && bars.length > 0) {
    // Calculate VWAP for the current day
    let cumulativeTPV = 0; // Typical Price * Volume
    let cumulativeVolume = 0;
    const vwapValues = new Array(bars.length).fill(null);
    
    for (let i = 0; i < bars.length; i++) {
      const typicalPrice = (bars[i].high + bars[i].low + bars[i].close) / 3;
      const volume = bars[i].volume || 1; // Use 1 if no volume data
      
      cumulativeTPV += typicalPrice * volume;
      cumulativeVolume += volume;
      
      if (cumulativeVolume > 0) {
        vwapValues[i] = cumulativeTPV / cumulativeVolume;
      }
    }
    
    // Draw VWAP line
    ctx.plot(vwapValues, 'VWAP', {
      color: vwapColor,
      lineWidth: lineWidth,
      lineStyle: 'solid'
    });
    
    // Add VWAP label at the right edge
    if (vwapValues[endDraw] !== null) {
      ctx.label(endDraw, vwapValues[endDraw], 'VWAP', {
        color: vwapColor,
        textColor: '#ffffff',
        size: labelSize,
        style: 'bubble'
      });
    }
  }
  
  // Calculate and draw Daily Pivot Points
  if (showPivots && prevDayHigh !== null && prevDayLow !== null && prevDayClose !== null) {
    // Standard Pivot Point Calculation
    const pivot = (prevDayHigh + prevDayLow + prevDayClose) / 3;
    const r1 = (2 * pivot) - prevDayLow;
    const s1 = (2 * pivot) - prevDayHigh;
    const r2 = pivot + (prevDayHigh - prevDayLow);
    const s2 = pivot - (prevDayHigh - prevDayLow);
    const r3 = prevDayHigh + 2 * (pivot - prevDayLow);
    const s3 = prevDayLow - 2 * (prevDayHigh - pivot);
    
    const pivotLevels = [
      { price: r3, label: 'R3', color: rColor },
      { price: r2, label: 'R2', color: rColor },
      { price: r1, label: 'R1', color: rColor },
      { price: pivot, label: 'Pivot', color: pivotColor },
      { price: s1, label: 'S1', color: sColor },
      { price: s2, label: 'S2', color: sColor },
      { price: s3, label: 'S3', color: sColor }
    ];
    
    // Draw pivot levels with solid lines and larger width
    for (const level of pivotLevels) {
      ctx.hline(level.price, {
        color: level.color,
        lineWidth: pivotLineWidth, // Larger width
        lineStyle: 'solid'
      });
      
      // Add label at the right edge
      ctx.label(endDraw, level.price, level.label, {
        color: level.color,
        textColor: '#ffffff',
        size: labelSize,
        style: 'bubble'
      });
    }
  }
}