tap
Back

yahoo-finance/quote

yahoo-financeRead-only

Yahoo Finance 股票行情

finance.yahoo.com
Last 7 days
0
Last 30 days
0
All time
0
yahoo-finance/quote.js
/* @meta
{
  "name": "yahoo-finance/quote",
  "description": "Yahoo Finance 股票行情",
  "domain": "finance.yahoo.com",
  "args": {
    "symbol": {"required": true, "description": "Stock ticker symbol, e.g. AAPL, MSFT, TSLA"}
  },
  "readOnly": true,
  "example": "tap site yahoo-finance/quote AAPL"
}
*/

async function(args) {
  if (!args.symbol) return {error: 'Missing argument: symbol', hint: 'Please provide a stock ticker symbol, e.g. AAPL'};

  var symbol = args.symbol.toUpperCase().trim();

  // Try v7 quote API first
  var url = 'https://query1.finance.yahoo.com/v7/finance/quote?symbols=' + encodeURIComponent(symbol);
  var resp;
  var data;

  try {
    resp = await fetch(url, {
      headers: {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
        'Accept': 'application/json'
      }
    });

    if (resp.ok) {
      data = await resp.json();
      var results = data && data.quoteResponse && data.quoteResponse.result;
      if (results && results.length > 0) {
        var q = results[0];
        return formatQuote(q);
      }
    }
  } catch(e) {
    // v7 failed (likely CORS), fall through to alternatives
  }

  // Fallback: try v8 chart API
  try {
    var chartUrl = 'https://query1.finance.yahoo.com/v8/finance/chart/' + encodeURIComponent(symbol) + '?interval=1d&range=1d';
    resp = await fetch(chartUrl, {
      headers: {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
        'Accept': 'application/json'
      }
    });

    if (resp.ok) {
      data = await resp.json();
      var chart = data && data.chart && data.chart.result && data.chart.result[0];
      if (chart) {
        return formatChart(chart, symbol);
      }
    }
  } catch(e) {
    // v8 also failed, fall through
  }

  // Fallback: scrape from Yahoo Finance page via page context
  try {
    var pageUrl = 'https://finance.yahoo.com/quote/' + encodeURIComponent(symbol) + '/';
    resp = await fetch(pageUrl, {
      headers: {
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
        'Accept': 'text/html'
      }
    });

    if (resp.ok) {
      var html = await resp.text();

      // Try to extract JSON data embedded in the page
      var jsonMatch = html.match(/root\.App\.main\s*=\s*(\{.*?\});\s*\n/);
      if (jsonMatch) {
        var appData = JSON.parse(jsonMatch[1]);
        var stores = appData && appData.context && appData.context.dispatcher && appData.context.dispatcher.stores;
        if (stores && stores.QuoteSummaryStore && stores.QuoteSummaryStore.price) {
          var price = stores.QuoteSummaryStore.price;
          return {
            symbol: symbol,
            name: price.shortName || price.longName || symbol,
            price: price.regularMarketPrice && price.regularMarketPrice.raw,
            change: price.regularMarketChange && price.regularMarketChange.raw,
            changePercent: price.regularMarketChangePercent && price.regularMarketChangePercent.raw
              ? (price.regularMarketChangePercent.raw * 100).toFixed(2) + '%' : null,
            currency: price.currency,
            exchange: price.exchangeName,
            marketState: price.marketState,
            url: pageUrl
          };
        }
      }

      // Try to extract from newer page format (FinanceConfigStore or similar)
      var titleMatch = html.match(/<title>([^<]+)<\/title>/);
      var priceMatch = html.match(/data-testid="qsp-price"[^>]*>([^<]+)</);
      var changeMatch = html.match(/data-testid="qsp-price-change"[^>]*>([^<]+)</);
      var changePctMatch = html.match(/data-testid="qsp-price-change-percent"[^>]*>([^<]+)</);

      if (priceMatch) {
        return {
          symbol: symbol,
          name: titleMatch ? titleMatch[1].split('(')[0].trim() : symbol,
          price: priceMatch[1].replace(/,/g, ''),
          change: changeMatch ? changeMatch[1] : null,
          changePercent: changePctMatch ? changePctMatch[1] : null,
          source: 'page-scrape',
          url: pageUrl
        };
      }

      return {error: 'Could not parse quote data from page', symbol: symbol, url: pageUrl};
    }
  } catch(e) {
    // All methods failed
  }

  return {error: 'Failed to fetch quote for ' + symbol, hint: 'All API methods failed. The symbol may be invalid or Yahoo Finance may be blocking requests.'};

  function formatQuote(q) {
    return {
      symbol: q.symbol,
      name: q.shortName || q.longName || q.symbol,
      price: q.regularMarketPrice,
      change: q.regularMarketChange != null ? q.regularMarketChange.toFixed(2) : null,
      changePercent: q.regularMarketChangePercent != null ? q.regularMarketChangePercent.toFixed(2) + '%' : null,
      open: q.regularMarketOpen,
      high: q.regularMarketDayHigh,
      low: q.regularMarketDayLow,
      prevClose: q.regularMarketPreviousClose,
      volume: q.regularMarketVolume,
      marketCap: formatLargeNumber(q.marketCap),
      pe: q.trailingPE != null ? q.trailingPE.toFixed(2) : null,
      eps: q.epsTrailingTwelveMonths != null ? q.epsTrailingTwelveMonths.toFixed(2) : null,
      week52High: q.fiftyTwoWeekHigh,
      week52Low: q.fiftyTwoWeekLow,
      avgVolume: q.averageDailyVolume3Month,
      currency: q.currency,
      exchange: q.fullExchangeName || q.exchange,
      marketState: q.marketState,
      quoteType: q.quoteType,
      url: 'https://finance.yahoo.com/quote/' + q.symbol + '/'
    };
  }

  function formatChart(chart, sym) {
    var meta = chart.meta || {};
    var indicators = chart.indicators;
    var quoteData = indicators && indicators.quote && indicators.quote[0];
    var timestamps = chart.timestamp || [];

    var lastIdx = timestamps.length - 1;
    var currentPrice = meta.regularMarketPrice || (quoteData && quoteData.close && quoteData.close[lastIdx]);
    var prevClose = meta.previousClose || meta.chartPreviousClose;
    var change = (currentPrice != null && prevClose != null) ? (currentPrice - prevClose) : null;
    var changePct = (change != null && prevClose) ? ((change / prevClose) * 100) : null;

    return {
      symbol: meta.symbol || sym,
      name: meta.shortName || meta.longName || sym,
      price: currentPrice != null ? Number(currentPrice.toFixed(2)) : null,
      change: change != null ? change.toFixed(2) : null,
      changePercent: changePct != null ? changePct.toFixed(2) + '%' : null,
      open: quoteData && quoteData.open ? quoteData.open[0] : null,
      high: quoteData && quoteData.high ? Math.max.apply(null, quoteData.high.filter(function(v) { return v != null; })) : null,
      low: quoteData && quoteData.low ? Math.min.apply(null, quoteData.low.filter(function(v) { return v != null; })) : null,
      prevClose: prevClose,
      volume: meta.regularMarketVolume || (quoteData && quoteData.volume ? quoteData.volume[lastIdx] : null),
      currency: meta.currency,
      exchange: meta.exchangeName,
      marketState: meta.marketState,
      source: 'chart-api',
      url: 'https://finance.yahoo.com/quote/' + (meta.symbol || sym) + '/'
    };
  }

  function formatLargeNumber(v) {
    if (v == null) return null;
    if (v >= 1e12) return (v / 1e12).toFixed(2) + 'T';
    if (v >= 1e9) return (v / 1e9).toFixed(2) + 'B';
    if (v >= 1e6) return (v / 1e6).toFixed(2) + 'M';
    if (v >= 1e3) return (v / 1e3).toFixed(2) + 'K';
    return v.toString();
  }
}
Updated Mar 31, 2026Created Mar 31, 2026SHA-256: 8fbf335f74eb