INITIAL BALANCE PIZZA BURGER

Description

BALANCES THE OPENING RANGE OF PIZZA BURGERS

Categories & Tags

Comments (0)

0/2000

Loading comments…

Source code

function calculate(bars, ctx) {
  // ═══ USER INPUTS ═══
  const sessionStart = ctx.input('Session Start (HHMM)', '0930');
  const sessionEnd = ctx.input('Session End (HHMM)', '1600');
  const timezone = ctx.input('Timezone Offset (UTC)', '-5', {
    options: [
      { label: 'UTC-10', value: '-10' }, { label: 'UTC-8', value: '-8' },
      { label: 'UTC-7', value: '-7' }, { label: 'UTC-6', value: '-6' },
      { label: 'UTC-5', value: '-5' }, { label: 'UTC-4', value: '-4' },
      { label: 'UTC-3', value: '-3' },
      { label: 'UTC+0', value: '0' },
      { label: 'UTC+1', value: '1' }, { label: 'UTC+2', value: '2' },
      { label: 'UTC+3', value: '3' }, { label: 'UTC+4', value: '4' },
      { label: 'UTC+5', value: '5' }, { label: 'UTC+5:30', value: '5.5' },
      { label: 'UTC+8', value: '8' }, { label: 'UTC+9', value: '9' },
      { label: 'UTC+10', value: '10' },
    ]
  });
  const displayMode = ctx.input('Display Mode', 'today', {
    options: [
      { label: 'Today', value: 'today' },
      { label: 'Two Days', value: 'twoDays' },
      { label: 'All', value: 'all' },
      { label: 'Hidden', value: 'hidden' },
    ]
  });
  const showLabels = ctx.input('Show Labels', true);
  const fontSize = ctx.input('Font Size', 11, { min: 8, max: 16, step: 1 });
  const lineWidth = ctx.input('Line Width', 1, { min: 1, max: 5, step: 1 });
  const lineStyle = ctx.input('Line Style', 'dashed', {
    options: [
      { label: 'Solid', value: 'solid' },
      { label: 'Dashed', value: 'dashed' },
      { label: 'Dotted', value: 'dotted' },
    ]
  });
  const ibColor = ctx.input('IB Color', '#2962ff');
  const extendShade = ctx.input('Extend Shaded Region', false);
  const extLevels = ctx.input('Extension Levels', 2, { min: 0, max: 10, step: 1 });
  const extLevelColor = ctx.input('Extension Level Color', '#ffffff');

  if (displayMode === 'hidden') return;
  if (bars.length < 2) return;

  // ═══ PARSE SESSION TIME ═══
  const startHour = parseInt(sessionStart.substring(0, 2));
  const startMin = parseInt(sessionStart.substring(2, 4));
  const endHour = parseInt(sessionEnd.substring(0, 2));
  const endMin = parseInt(sessionEnd.substring(2, 4));
  const tzOffset = parseFloat(timezone);

  const startMinutes = startHour * 60 + startMin;
  const endMinutes = endHour * 60 + endMin;
  const sessionLengthMinutes = endMinutes - startMinutes;

  // ═══ HELPER: get bar time in UTC-offset minutes ═══
  function barTimeMinutes(i) {
    const d = new Date(bars[i].timestamp);
    const utcMins = d.getUTCHours() * 60 + d.getUTCMinutes();
    // Apply user's timezone offset
    let localMins = utcMins + tzOffset * 60;
    // Normalize to 0-1440
    if (localMins < 0) localMins += 1440;
    if (localMins >= 1440) localMins -= 1440;
    return localMins;
  }

  // ═══ HELPER: check if bar time falls within session ═══
  function isInSession(i) {
    const t = barTimeMinutes(i);
    if (sessionStart <= sessionEnd) {
      return t >= startMinutes && t < endMinutes;
    }
    // Wraps midnight (e.g., 2100-1700 next day)
    return t >= startMinutes || t < endMinutes;
  }

  // ═══ HELPER: check if bar is the first bar of a session ═══
  function isSessionStart(i) {
    if (i === 0) return isInSession(0);
    if (!isInSession(i)) return false;
    return !isInSession(i - 1);
  }

  // ═══ HELPER: estimate bars for session length ═══
  // Approximate bar count from session length minutes
  // We'll estimate based on typical bar duration

  // ═══ Draw session colors ═══
  const bgColors = new Array(bars.length).fill(null);
  for (let i = 0; i < bars.length; i++) {
    if (isInSession(i)) {
      const r = parseInt(ibColor.slice(1, 3), 16);
      const g = parseInt(ibColor.slice(3, 5), 16);
      const b = parseInt(ibColor.slice(5, 7), 16);
      bgColors[i] = 'rgba(' + r + ',' + g + ',' + b + ',0.03)';
    }
  }
  ctx.bgcolor(bgColors);

  // ═══ Find all session starts and track IB objects ═══
  const ibSessions = []; // { startBar, ibHigh, ibLow, ibMid }

  // Detect session starts
  for (let i = 0; i < bars.length; i++) {
    if (isSessionStart(i)) {
      ibSessions.push({
        startBar: i,
        ibHigh: bars[i].high,
        ibLow: bars[i].low,
        ibMid: (bars[i].high + bars[i].low) / 2,
      });
    }
  }

  // Apply display mode filter
  let visibleSessions = ibSessions;
  if (displayMode === 'today') {
    visibleSessions = ibSessions.slice(-1);
  } else if (displayMode === 'twoDays') {
    visibleSessions = ibSessions.slice(-2);
  }

  // ═══ Draw IB levels for each session ═══
  const lastBar = bars.length - 1;

  for (const session of visibleSessions) {
    const s = session.startBar;
    const ibHigh = session.ibHigh;
    const ibLow = session.ibLow;
    const ibMid = session.ibMid;

    // Estimate right edge of the initial box (session length)
    // Use a reasonable estimate: ~bars per hour based on chart
    const barsPerHour = 60; // default estimate
    const estBars = Math.max(1, Math.round((sessionLengthMinutes / 60) * barsPerHour));
    const rightEdge = Math.min(s + estBars, lastBar);

    // Draw IB range box (initial zone)
    if (extendShade) {
      ctx.box(s, ibHigh, lastBar, ibLow, {
        fillColor: 'rgba(' + parseInt(ibColor.slice(1, 3), 16) + ',' +
          parseInt(ibColor.slice(3, 5), 16) + ',' +
          parseInt(ibColor.slice(5, 7), 16) + ',0.12)',
        borderColor: 'transparent',
      });
    } else {
      ctx.box(s, ibHigh, rightEdge, ibLow, {
        fillColor: 'rgba(' + parseInt(ibColor.slice(1, 3), 16) + ',' +
          parseInt(ibColor.slice(3, 5), 16) + ',' +
          parseInt(ibColor.slice(5, 7), 16) + ',0.12)',
        borderColor: 'transparent',
      });
    }

    // IB High line
    ctx.line(s, ibHigh, lastBar, ibHigh, {
      color: ibColor,
      lineWidth: lineWidth,
      lineStyle: lineStyle,
      extend: 'right',
    });

    // IB Low line
    ctx.line(s, ibLow, lastBar, ibLow, {
      color: ibColor,
      lineWidth: lineWidth,
      lineStyle: lineStyle,
      extend: 'right',
    });

    // IB 50% Mid line
    ctx.line(s, ibMid, lastBar, ibMid, {
      color: ibColor,
      lineWidth: lineWidth,
      lineStyle: lineStyle === 'solid' ? 'dashed' : 'dotted',
      extend: 'right',
    });

    // Labels
    if (showLabels) {
      ctx.label(lastBar, ibHigh, ' IB High (' + ibHigh.toFixed(2) + ') ', {
        color: ibColor,
        textColor: '#ffffff',
        size: fontSize,
        position: 'above',
        style: 'none',
      });
      ctx.label(lastBar, ibLow, ' IB Low (' + ibLow.toFixed(2) + ') ', {
        color: ibColor,
        textColor: '#ffffff',
        size: fontSize,
        position: 'below',
        style: 'none',
      });
      ctx.label(lastBar, ibMid, ' 50% (' + ibMid.toFixed(2) + ') ', {
        color: ibColor,
        textColor: '#ffffff',
        size: fontSize,
        position: 'at',
        style: 'none',
      });
    }

    // ═══ Extension Levels ═══
    if (extLevels > 0) {
      const range = ibHigh - ibLow;
      for (let i = 1; i <= extLevels; i++) {
        const ext = (range / 2) * i;

        // Upper extension
        const upperPrice = ibHigh + ext;
        ctx.line(s, upperPrice, lastBar, upperPrice, {
          color: extLevelColor,
          lineWidth: 1,
          lineStyle: 'dotted',
          extend: 'right',
        });
        if (showLabels) {
          ctx.label(lastBar, upperPrice, ' Ext ' + i + ' (' + upperPrice.toFixed(2) + ') ', {
            color: extLevelColor,
            textColor: '#ffffff',
            size: fontSize - 2,
            position: 'above',
            style: 'none',
          });
        }

        // Lower extension
        const lowerPrice = ibLow - ext;
        ctx.line(s, lowerPrice, lastBar, lowerPrice, {
          color: extLevelColor,
          lineWidth: 1,
          lineStyle: 'dotted',
          extend: 'right',
        });
        if (showLabels) {
          ctx.label(lastBar, lowerPrice, ' Ext -' + i + ' (' + lowerPrice.toFixed(2) + ') ', {
            color: extLevelColor,
            textColor: '#ffffff',
            size: fontSize - 2,
            position: 'below',
            style: 'none',
          });
        }
      }
    }

    // ═══ Breakout Detection ═══
    // Check if price broke above IB High or below IB Low for the first time
    const lastClose = bars[lastBar].close;
    if (lastClose > ibHigh && lastBar > s) {
      ctx.shape(lastBar, 'arrow_up', {
        color: '#26a69a',
        position: 'below',
        text: 'IB Breakout ↑',
      });
    } else if (lastClose < ibLow && lastBar > s) {
      ctx.shape(lastBar, 'arrow_down', {
        color: '#ef5350',
        position: 'above',
        text: 'IB Breakdown ↓',
      });
    }
  }
}