<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Forex Live Chart</title>
  <!-- Bootstrap 5 -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
  <!-- jQuery -->
  <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
  <!-- Chart.js + date adapter -->
  <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@3.0.0/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
  <style>
    body { padding: 20px; background: #f7f9fc; }
    .card { box-shadow: 0 6px 18px rgba(0,0,0,0.06); }
    #chart-wrap { min-height: 380px; }
    .small-muted { font-size: .9rem; color: #6c757d; }
  </style>
</head>
<body>
  <div class="container">
    <div class="d-flex justify-content-between align-items-center mb-3">
      <h3 class="mb-0">Forex Live Chart</h3>
      <div class="small-muted">Data source: <strong>exchangerate.host</strong> (free)</div>
    </div>

    <div class="row g-3">
      <div class="col-md-9">
        <div class="card">
          <div class="card-body">
            <div class="row g-2 align-items-center mb-3">
              <div class="col-auto">
                <select id="pairSelect" class="form-select">
                  <!-- default suggestions -->
                  <option value="USD/EUR">USD / EUR</option>
                  <option value="USD/INR">USD / INR</option>
                  <option value="EUR/GBP">EUR / GBP</option>
                  <option value="USD/JPY">USD / JPY</option>
                  <option value="XAU/USD">XAU / USD</option>
                  <option value="XAG/USD">XAG / USD</option>
                </select>
              </div>

              <div class="col-auto">
                <button id="btnStart" class="btn btn-success">Start</button>
                <button id="btnStop" class="btn btn-danger">Stop</button>
              </div>

              <div class="col-auto">
                <label class="form-label mb-0 small-muted">Poll interval (sec)</label>
                <input id="pollInterval" type="number" class="form-control" min="1" value="5" style="width:100px;">
              </div>

              <div class="col text-end">
                <div class="h6 mb-0">Pair: <span id="currentPair">USD/EUR</span></div>
                <div class="small-muted">Last price: <strong id="lastPrice">—</strong></div>
                <div class="small-muted">Updated: <span id="lastUpdated">—</span></div>
              </div>
            </div>

            <div id="chart-wrap">
              <canvas id="forexChart"></canvas>
            </div>
          </div>
        </div>
      </div>

      <div class="col-md-3">
        <div class="card mb-3">
          <div class="card-body">
            <h6>Quick Controls</h6>
            <div class="mb-2">
              <label class="form-label">Chart window (minutes)</label>
              <input id="windowMinutes" type="number" class="form-control" value="60" min="1">
            </div>
            <div class="mb-2">
              <label class="form-label">Spread (pips)</label>
              <input id="spreadPips" type="number" class="form-control" value="0.5" step="0.1">
            </div>
            <div class="d-grid">
              <button id="btnReset" class="btn btn-outline-primary">Reset Data</button>
            </div>
          </div>
        </div>

        <div class="card">
          <div class="card-body">
            <h6>Latest Quote</h6>
            <table class="table table-sm mb-0">
              <tr><td>Bid</td><td id="bidPrice">—</td></tr>
              <tr><td>Ask</td><td id="askPrice">—</td></tr>
              <tr><td>Change</td><td id="changePrice">—</td></tr>
              <tr><td>Last updated</td><td id="quoteTime">—</td></tr>
            </table>
            <div class="mt-2 small-muted">
              Note: This demo polls a free public API and appends updates as they arrive.
            </div>
          </div>
        </div>

      </div>
    </div>
  </div>

<script>
$(function(){
  // utils
  function parsePair(pair) {
    // pair like "USD/EUR"
    const parts = pair.split('/');
    return { base: parts[0].trim().toUpperCase(), quote: parts[1].trim().toUpperCase() };
  }

  function formatTime(ts) {
    const d = new Date(ts);
    return d.toLocaleString();
  }

  // chart setup
  const ctx = document.getElementById('forexChart').getContext('2d');
  const data = {
    datasets: [{
      label: 'Price',
      data: [], // {x: timestamp(ms), y: price}
      tension: 0.1,
      pointRadius: 0,
      borderWidth: 2
    }]
  };

  const config = {
    type: 'line',
    data,
    options: {
      animation: false,
      responsive: true,
      maintainAspectRatio: false,
      interaction: { mode: 'nearest', intersect: false },
      plugins: {
        legend: { display: false },
        tooltip: {
          callbacks: {
            label: function(ctx) {
              return ctx.parsed.y;
            }
          }
        }
      },
      scales: {
        x: {
          type: 'time',
          time: { unit: 'minute', tooltipFormat: 'PPpp' },
          ticks: { autoSkip: true, maxTicksLimit: 10 }
        },
        y: {
          beginAtZero: false,
          ticks: { precision: 6 }
        }
      }
    }
  };

  const forexChart = new Chart(ctx, config);

  // state
  let pollTimer = null;
  let lastPrice = null;
  let lastBase = 'USD', lastQuote = 'EUR';
  let isRunning = false;

  // API functions (using exchangerate.host)
  async function fetchHistoricalDaily(base, quote, days=7) {
    // returns array of {t: ms, v: price} ordered ascending
    const end = new Date();
    const start = new Date(end.getTime() - (days-1)*24*60*60*1000);
    const startStr = start.toISOString().slice(0,10);
    const endStr = end.toISOString().slice(0,10);

    const url = `https://api.exchangerate.host/timeseries?start_date=${startStr}&end_date=${endStr}&base=${encodeURIComponent(base)}&symbols=${encodeURIComponent(quote)}`;
    try {
      const resp = await fetch(url);
      const json = await resp.json();
      if (!json || !json.rates) return [];
      const out = [];
      const dates = Object.keys(json.rates).sort();
      for (const d of dates) {
        const v = json.rates[d][quote];
        if (typeof v === 'number') out.push({ t: new Date(d).getTime(), y: v });
      }
      return out;
    } catch (err) {
      console.warn('hist err', err);
      return [];
    }
  }

  async function fetchLatest(base, quote){
    // use /latest endpoint
    // returns {price, timestamp}
    // exchangerate.host returns timestamp as unix time in 'date' field (YYYY-MM-DD) - no time-of-day,
    // so we'll use current client time for timestamp and price from response.
    const url = `https://api.exchangerate.host/latest?base=${encodeURIComponent(base)}&symbols=${encodeURIComponent(quote)}`;
    try {
      const resp = await fetch(url);
      const json = await resp.json();
      if (!json || !json.rates || typeof json.rates[quote] !== 'number') {
        return null;
      }
      return { price: json.rates[quote], ts: Date.now() }; // timestamp now
    } catch (err) {
      console.warn('latest err', err);
      return null;
    }
  }

  // update UI & chart
  function updateQuoteUI(price, previousPrice, ts, spreadPips) {
    $('#lastPrice').text(price.toFixed(6));
    $('#lastUpdated').text(formatTime(ts));
    $('#currentPair').text(`${lastBase}/${lastQuote}`);

    // derive bid/ask with small spread
    // treat spreadPips in pip units: pip depends on pair — we will use 0.0001 for most, 0.01 for JPY & metals
    const isJPY = lastQuote === 'JPY';
    const isMetal = (lastQuote === 'USD' && (lastBase === 'XAU' || lastBase === 'XAG')) || lastBase === 'XAU' || lastBase === 'XAG';
    const pipUnit = isJPY ? 0.01 : (isMetal ? 0.1 : 0.0001);

    const spread = (spreadPips || 0.5) * pipUnit;
    const bid = price - spread/2;
    const ask = price + spread/2;

    $('#bidPrice').text(bid.toFixed(6));
    $('#askPrice').text(ask.toFixed(6));

    const change = previousPrice ? (price - previousPrice) : 0;
    const changePct = previousPrice ? (change/previousPrice*100) : 0;
    $('#changePrice').text(`${change.toFixed(6)} (${changePct.toFixed(4)}%)`);
    $('#quoteTime').text(formatTime(ts));
  }

  function appendPointToChart(point, windowMinutes) {
    // point = {t: ms, y: price}
    data.datasets[0].data.push({ x: point.t, y: point.y });

    // remove old points outside window
    const now = Date.now();
    const windowMs = (windowMinutes||60) * 60*1000;
    data.datasets[0].data = data.datasets[0].data.filter(p => p.x >= (now - windowMs));

    forexChart.update('none');
  }

  // load initial data
  async function loadInitial(pair) {
    const { base, quote } = parsePair(pair);
    lastBase = base; lastQuote = quote;

    // fetch historical daily (7 days) as baseline
    const hist = await fetchHistoricalDaily(base, quote, 7);
    data.datasets[0].data = hist.map(d => ({ x: d.t, y: d.y }));
    forexChart.options.scales.y.ticks.precision = 6;
    forexChart.update();

    // fetch latest to set lastPrice
    const latest = await fetchLatest(base, quote);
    if (latest) {
      lastPrice = latest.price;
      // append only if last timestamp is newer than last hist point
      const lastHistTs = data.datasets[0].data.length ? data.datasets[0].data[data.datasets[0].data.length-1].x : 0;
      if (latest.ts > lastHistTs) {
        appendPointToChart({t: latest.ts, y: latest.price}, Number($('#windowMinutes').val()) );
      }
      updateQuoteUI(latest.price, null, latest.ts, Number($('#spreadPips').val()));
    }
  }

  // poll loop
  async function startPolling() {
    if (isRunning) return;
    isRunning = true;
    $('#btnStart').prop('disabled',true);
    $('#btnStop').prop('disabled',false);

    const poll = async () => {
      const pair = $('#pairSelect').val();
      const { base, quote } = parsePair(pair);

      // if pair changed since start, stop (or we can continue but easier to reload state)
      if (base !== lastBase || quote !== lastQuote) {
        // reinitialize
        await loadInitial(pair);
      }

      const latest = await fetchLatest(base, quote);
      if (latest && typeof latest.price === 'number') {
        // update only on change
        if (lastPrice === null || latest.price !== lastPrice) {
          const prev = lastPrice;
          lastPrice = latest.price;
          appendPointToChart({t: latest.ts, y: latest.price}, Number($('#windowMinutes').val()));
          updateQuoteUI(latest.price, prev, latest.ts, Number($('#spreadPips').val()));

          // ajax callback when price changed
          // demo: send new price to server when changed (user can replace URL)
          // $.post('/price-update', {pair: `${base}/${quote}`, price: latest.price, timestamp: latest.ts});
          // The above is commented out in demo. If you have a back-end, enable it.
        } // else no change => do nothing (saves chart updates)
      }
    };

    // initial immediate poll
    await poll();

    // set interval
    const intervalSec = Math.max(1, Number($('#pollInterval').val() || 5));
    pollTimer = setInterval(async () => {
      // refresh interval in case the user changed input
      const intervalNow = Math.max(1, Number($('#pollInterval').val() || 5));
      if (intervalNow*1000 !== pollTimer._intervalMs) {
        // we don't have such property; simpler approach: clear and restart interval
        clearInterval(pollTimer);
        pollTimer = setInterval(poll, intervalNow*1000);
      } else {
        await poll();
      }
    }, intervalSec*1000);

    // hack: attach the interval time for detection (not relied upon)
    // (Browser intervals don't expose ms; so above re-creation handles changes)
  }

  function stopPolling() {
    if (!isRunning) return;
    isRunning = false;
    $('#btnStart').prop('disabled', false);
    $('#btnStop').prop('disabled', true);
    if (pollTimer) { clearInterval(pollTimer); pollTimer = null; }
  }

  // UI events
  $('#btnStart').on('click', async function(){
    const pair = $('#pairSelect').val();
    $('#currentPair').text(pair);
    await loadInitial(pair);
    startPolling();
  });

  $('#btnStop').on('click', function(){
    stopPolling();
  });

  $('#pairSelect').on('change', async function(){
    const pair = $(this).val();
    $('#currentPair').text(pair);
    // if running, reload initial dataset for new pair
    await loadInitial(pair);
  });

  $('#btnReset').on('click', function(){
    data.datasets[0].data = [];
    forexChart.update();
    lastPrice = null;
  });

  // auto start once
  (async function init(){
    const initialPair = $('#pairSelect').val();
    $('#currentPair').text(initialPair);
    await loadInitial(initialPair);
    startPolling();
  })();

});
</script>

<!-- Bootstrap bundle -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
