> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ngnmarket.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Set Up Price Alerts

> Poll stock prices and trigger notifications when a target price or percentage move is hit.

This guide shows how to build a price alert system — poll the NGN Market API on an interval, compare prices against user-defined thresholds, and fire a notification (webhook, email, or push) when a condition is met.

**Endpoint used:**

* `GET /companies/:symbol` (Starter plan) — single stock quote with current price

## Alert types

You can model any of these conditions:

| Type                     | Example                               |
| :----------------------- | :------------------------------------ |
| Price rises above target | Notify when DANGCEM > ₦320            |
| Price falls below target | Notify when GTCO \< ₦55               |
| Price moves up by %      | Notify when MTNN gains 5% today       |
| Price moves down by %    | Notify when ZENITHBANK drops 3% today |

## Step 1: Define your alerts

```javascript JavaScript theme={null}
const alerts = [
  { id: 1, symbol: 'DANGCEM',    condition: 'above', target: 320.00  },
  { id: 2, symbol: 'GTCO',       condition: 'below', target: 55.00   },
  { id: 3, symbol: 'MTNN',       condition: 'change_above', target: 5  }, // +5% today
  { id: 4, symbol: 'ZENITHBANK', condition: 'change_below', target: -3 }, // -3% today
];
```

```python Python theme={null}
alerts = [
    {'id': 1, 'symbol': 'DANGCEM',    'condition': 'above',        'target': 320.00},
    {'id': 2, 'symbol': 'GTCO',       'condition': 'below',        'target': 55.00 },
    {'id': 3, 'symbol': 'MTNN',       'condition': 'change_above', 'target': 5     },
    {'id': 4, 'symbol': 'ZENITHBANK', 'condition': 'change_below', 'target': -3    },
]
```

## Step 2: Fetch a quote

<CodeGroup>
  ```javascript JavaScript theme={null}
  const API_KEY = 'ngm_live_YOUR_KEY';

  async function getQuote(symbol) {
    const res = await fetch(
      `https://api.ngnmarket.com/v1/companies/${symbol}`,
      { headers: { Authorization: `Bearer ${API_KEY}` } }
    );
    const { data } = await res.json();
    return data; // current_price, price_change_percent, last_updated, etc.
  }
  ```

  ```python Python theme={null}
  import requests

  API_KEY = 'ngm_live_YOUR_KEY'

  def get_quote(symbol):
      res = requests.get(
          f'https://api.ngnmarket.com/v1/companies/{symbol}',
          headers={'Authorization': f'Bearer {API_KEY}'},
      )
      return res.json()['data']
  ```
</CodeGroup>

### Sample quote response (trimmed)

```json theme={null}
{
  "symbol": "DANGCEM",
  "company_name": "Dangote Cement Plc",
  "current_price": 322.50,
  "price_change": 20.00,
  "price_change_percent": 6.61,
  "last_updated": "2026-04-21T11:40:00.000Z"
}
```

## Step 3: Evaluate alert conditions

<CodeGroup>
  ```javascript JavaScript theme={null}
  function isTriggered(alert, quote) {
    const { condition, target } = alert;
    const { current_price, price_change_percent } = quote;

    switch (condition) {
      case 'above':        return current_price >= target;
      case 'below':        return current_price <= target;
      case 'change_above': return price_change_percent >= target;
      case 'change_below': return price_change_percent <= target;
      default:             return false;
    }
  }
  ```

  ```python Python theme={null}
  def is_triggered(alert, quote):
      condition = alert['condition']
      target    = alert['target']
      price     = quote['current_price']
      change    = quote['price_change_percent']

      if condition == 'above':        return price >= target
      if condition == 'below':        return price <= target
      if condition == 'change_above': return change >= target
      if condition == 'change_below': return change <= target
      return False
  ```
</CodeGroup>

## Step 4: Send a notification

Replace `notify()` with whatever delivery mechanism you use — webhook, email, SMS, or push.

<CodeGroup>
  ```javascript JavaScript theme={null}
  async function notify(alert, quote) {
    const message = buildMessage(alert, quote);

    // Option A: webhook
    await fetch(process.env.WEBHOOK_URL, {
      method:  'POST',
      headers: { 'Content-Type': 'application/json' },
      body:    JSON.stringify({ text: message, alert, quote }),
    });

    // Option B: log to console (replace with your email/SMS/push SDK)
    console.log(`[ALERT] ${message}`);
  }

  function buildMessage({ condition, target, symbol }, { current_price, price_change_percent }) {
    if (condition === 'above')        return `${symbol} hit ₦${current_price} — above your ₦${target} target`;
    if (condition === 'below')        return `${symbol} dropped to ₦${current_price} — below your ₦${target} target`;
    if (condition === 'change_above') return `${symbol} is up ${price_change_percent.toFixed(2)}% today (target: +${target}%)`;
    if (condition === 'change_below') return `${symbol} is down ${price_change_percent.toFixed(2)}% today (target: ${target}%)`;
  }
  ```

  ```python Python theme={null}
  import os, httpx

  def notify(alert, quote):
      message = build_message(alert, quote)

      # Option A: webhook
      httpx.post(os.environ['WEBHOOK_URL'], json={
          'text': message, 'alert': alert, 'quote': quote
      })

      # Option B: print (replace with your email/SMS/push SDK)
      print(f"[ALERT] {message}")

  def build_message(alert, quote):
      cond   = alert['condition']
      target = alert['target']
      sym    = alert['symbol']
      price  = quote['current_price']
      change = quote['price_change_percent']

      if cond == 'above':        return f"{sym} hit ₦{price} — above your ₦{target} target"
      if cond == 'below':        return f"{sym} dropped to ₦{price} — below your ₦{target} target"
      if cond == 'change_above': return f"{sym} is up {change:.2f}% today (target: +{target}%)"
      if cond == 'change_below': return f"{sym} is down {change:.2f}% today (target: {target}%)"
  ```
</CodeGroup>

## Step 5: Run the checker on an interval

<CodeGroup>
  ```javascript JavaScript theme={null}
  const triggered = new Set(); // prevent repeat notifications per session

  async function checkAlerts() {
    for (const alert of alerts) {
      // Skip already-triggered alerts
      if (triggered.has(alert.id)) continue;

      const quote = await getQuote(alert.symbol);
      if (isTriggered(alert, quote)) {
        await notify(alert, quote);
        triggered.add(alert.id); // fire once per session
      }
    }
  }

  // Check every 5 minutes during market hours
  const INTERVAL = 5 * 60 * 1000;
  checkAlerts();
  const timer = setInterval(checkAlerts, INTERVAL);
  ```

  ```python Python theme={null}
  import time, threading

  triggered = set()

  def check_alerts():
      for alert in alerts:
          if alert['id'] in triggered:
              continue
          quote = get_quote(alert['symbol'])
          if is_triggered(alert, quote):
              notify(alert, quote)
              triggered.add(alert['id'])

  def run_checker(interval_seconds=300):
      while True:
          check_alerts()
          time.sleep(interval_seconds)

  # Run in background thread
  thread = threading.Thread(target=run_checker, daemon=True)
  thread.start()
  ```
</CodeGroup>

## Persistent alerts with a database

For a production system where alert state survives restarts, store alerts and their triggered status in a database:

```javascript JavaScript theme={null}
// Pseudocode — adapt to your ORM/database
async function checkAlerts() {
  const activeAlerts = await db.alerts.findAll({ where: { triggered: false } });

  for (const alert of activeAlerts) {
    const quote = await getQuote(alert.symbol);
    if (isTriggered(alert, quote)) {
      await notify(alert, quote);
      await db.alerts.update(
        { triggered: true, triggered_at: new Date(), triggered_price: quote.current_price },
        { where: { id: alert.id } }
      );
    }
  }
}
```

## Tips

**Debounce noisy conditions.** A stock can briefly cross a threshold and come back. Consider requiring the condition to hold for two consecutive checks before firing.

**Reset daily alerts at midnight WAT.** `change_above` / `change_below` alerts are based on `price_change_percent`, which resets each session. Clear those triggered flags at the start of each trading day.

**Only check during trading hours.** Prices don't move outside NGX hours (Mon–Fri, 09:00–16:00 WAT), so skip checks then to save quota.

```javascript JavaScript theme={null}
function isMarketOpen() {
  const now     = new Date();
  const day     = now.getUTCDay();
  if (day === 0 || day === 6) return false;
  const watTime = (now.getUTCHours() + 1) * 100 + now.getUTCMinutes();
  return watTime >= 900 && watTime < 1600; // 09:00–16:00
}
```

<CardGroup cols={2}>
  <Card title="Company detail reference" icon="book" href="/api-reference/companies/detail">
    All fields returned by `GET /companies/:symbol`
  </Card>

  <Card title="Live price ticker guide" icon="bolt" href="/guides/live-price-ticker">
    How to keep prices updating automatically
  </Card>
</CardGroup>
