Glanceway Glanceway
جميع المصادر

Stock Price Monitor

المالية v1.0.0

Monitor global stock prices in your menu bar. Displays real-time price, change amount, and change percentage. Powered by Finnhub (free API key required).

@codytseng #stock #finance #market

الإعدادات

الاسم المفتاح النوع مطلوب القيمة الافتراضية الوصف
Finnhub API Token FINNHUB_TOKEN secret نعم Free API token from https://finnhub.io
Stock Symbols SYMBOLS list نعم Stock symbols (e.g. AAPL, TSLA, MSFT)

الشيفرة المصدرية

version: 1.0.0
name: Stock Price Monitor
description: |
  Monitor global stock prices in your menu bar.
  Displays real-time price, change amount, and change percentage.
  Powered by Finnhub (free API key required).
author: codytseng
author_url: https://github.com/codytseng
category: Finance
tags:
  - stock
  - finance
  - market

config:
  - key: FINNHUB_TOKEN
    name: Finnhub API Token
    type: secret
    required: true
    description: "Free API token from https://finnhub.io"
  - key: SYMBOLS
    name: Stock Symbols
    type: list
    required: true
    description: "Stock symbols (e.g. AAPL, TSLA, MSFT)"

module.exports = async (api) => {
  const token = api.config.get("FINNHUB_TOKEN");
  const symbolsRaw = api.config.get("SYMBOLS") ?? [];

  async function fetchData() {
    const symbols = symbolsRaw.map((s) => s.toUpperCase());

    if (symbols.length === 0) {
      api.emit([]);
      return;
    }

    const items = [];

    for (const symbol of symbols) {
      const quoteRes = await api.fetch(
        `https://finnhub.io/api/v1/quote?symbol=${symbol}&token=${token}`,
      );

      if (!quoteRes.ok || !quoteRes.json || quoteRes.json.c === 0) {
        api.log("warn", `Failed to fetch quote for ${symbol}`);
        continue;
      }

      const q = quoteRes.json;

      // fetch and cache company name
      let name = api.storage.get(`name:${symbol}`);
      if (!name) {
        const profileRes = await api.fetch(
          `https://finnhub.io/api/v1/stock/profile2?symbol=${symbol}&token=${token}`,
        );
        if (profileRes.ok && profileRes.json?.name) {
          name = profileRes.json.name;
          api.storage.set(`name:${symbol}`, name);
        }
      }

      const price = q.c.toFixed(2);
      const change = q.d.toFixed(2);
      const changePct = q.dp.toFixed(2);
      const arrow = q.d > 0 ? "↑" : q.d < 0 ? "↓" : "";
      const sign = q.d > 0 ? "+" : "";

      items.push({
        id: symbol,
        title: name ? `${name} (${symbol})` : symbol,
        subtitle: `${price} ${arrow} ${sign}${change} (${sign}${changePct}%)`,
        url: `https://finnhub.io/stock/${symbol}`,
      });
    }

    api.emit(items);
  }

  await fetchData();

  return {
    refresh: fetchData,
  };
};