# STB Live Data Feeds — Deployment Guide

This document covers the four WordPress mu-plugins that power live market data on the STB site. Hand this to the IT/dev team when migrating from the staging server to production.

---

## What this replaces

These four mu-plugins replaced a mix of Python scripts on `/opt/`, system cron jobs, and Apache-served static files. **Everything is now self-contained inside WordPress.** No Python required. No system cron required (one optional entry recommended).

---

## The four mu-plugins

All files live in `wp-content/mu-plugins/`. Must-use plugins load automatically — no activation needed.

| File | Purpose | Refresh | REST endpoint |
|---|---|---|---|
| `stb-live-prices.php` | Forex / metals / crypto / indices prices | 5 min | `/wp-json/stb/v1/prices` |
| `stb-live-sentiment.php` | Technical-analysis sentiment for 8 instruments | 15 min | `/wp-json/stb/v1/sentiment` |
| `stb-economic-calendar.php` | Economic calendar events (2-week window) | 30 min | `/wp-json/stb/v1/calendar` |
| `stb-cpt-taxonomy.php` | Custom post type registrations (unrelated to feeds, but already there) | — | — |

---

## Data sources (upstream)

| Plugin | Primary source | Fallback |
|---|---|---|
| Prices | `api.stbbrokers.com/live-prices` (10 symbols: forex, metals, crypto) | TradingView scanner (5 symbols: indices + oil) |
| Sentiment | TradingView scanner technical analysis (8 symbols) | None — endpoint serves last-known if call fails |
| Calendar | `investing.com/economic-calendar/Service/getCalendarFilteredData` | None — endpoint serves last-known if call fails |

**IP allow-list requirement:** The Prices plugin calls `api.stbbrokers.com` which uses IP allow-listing. The production server's outbound IP must be whitelisted by the team behind `api.stbbrokers.com` (Jordan / IT). If it isn't whitelisted, prices feed will only show TradingView-sourced symbols (indices + oil) — forex/metals/crypto will be empty until the IP is added.

---

## Deployment to a new server

### Step 1. Copy the mu-plugins

Copy these four files into `wp-content/mu-plugins/` on the production server:

- `stb-live-prices.php`
- `stb-live-sentiment.php`
- `stb-economic-calendar.php`
- `stb-cpt-taxonomy.php`

That's it for the plugin side. WordPress loads them automatically. Each plugin self-schedules its own WP-Cron job on the first page-load and runs a warm-up fetch within seconds.

### Step 2. Verify the REST endpoints respond

After the first page-load of the site (or wait 30 seconds), hit these URLs:

```bash
curl https://<your-domain>/wp-json/stb/v1/prices
curl https://<your-domain>/wp-json/stb/v1/sentiment
curl https://<your-domain>/wp-json/stb/v1/calendar
```

All three should return JSON with `"ok": true` (prices/sentiment) or `"total": > 0` (calendar) within the first minute.

### Step 3. Set up real cron (recommended, not strictly required)

WordPress's default cron (`WP-Cron`) only fires when a visitor opens a page. On a low-traffic site, scheduled refreshes can be late or skipped. Best practice for production:

**3a.** Add this line to `wp-config.php` (place it **before** `require_once ABSPATH . 'wp-settings.php';`):

```php
define( 'DISABLE_WP_CRON', true );
```

This stops WordPress from triggering cron on page-load.

**3b.** Add one system cron entry. Run `crontab -e` as root (or as the web-server user) and append:

```cron
*/5 * * * * /usr/local/bin/wp cron event run --due-now --allow-root --path=/var/www/<your-wp-path> > /dev/null 2>&1
```

Adjust `/var/www/<your-wp-path>` to wherever WordPress is installed.

**Alternative if `wp-cli` is not installed**, use `curl` instead:

```cron
*/5 * * * * curl -fsS -o /dev/null https://<your-domain>/wp-cron.php?doing_wp_cron
```

Either approach works. `wp-cli` is faster and avoids HTTP overhead.

### Step 4. (Optional) Disable verbose Apache access logging for `wp-cron.php`

If using the `curl` approach above, you'll get a log entry every 5 minutes. To filter it out:

```apache
SetEnvIf Request_URI "^/wp-cron\.php" dontlog
CustomLog /var/log/apache2/access.log combined env=!dontlog
```

---

## Health checks

### Admin status pages

Once logged in to `/wp-admin/`:

- **Tools → STB Economic Calendar** — shows last fetch time, event count, runtime, next scheduled run, JSON file location, and a "Run fetch now" button.

(Prices and Sentiment don't have status pages yet — add later if needed. For now use the REST endpoints directly.)

### Quick CLI health check

```bash
# All three feeds in one go
for path in prices sentiment calendar; do
  echo -n "$path: "
  curl -s "https://<your-domain>/wp-json/stb/v1/$path" \
    | python3 -c "import sys,json; d=json.load(sys.stdin); print(f'count={d.get(\"count\") or d.get(\"total\")}, stale={d.get(\"stale\")}, updated_at={d.get(\"updated_at\")}')"
done
```

### WP-Cron status

```bash
# List all scheduled events
wp cron event list --allow-root --path=/var/www/<your-wp-path> | grep stb_

# Should show 3 events: stb_lp_cron (5 min), stb_sent_cron (15 min), stb_cal_cron (30 min)
```

### Manual cron trigger (if something looks stuck)

```bash
wp cron event run stb_lp_cron   --allow-root --path=/var/www/<your-wp-path>
wp cron event run stb_sent_cron --allow-root --path=/var/www/<your-wp-path>
wp cron event run stb_cal_cron  --allow-root --path=/var/www/<your-wp-path>
```

---

## Troubleshooting

### Prices endpoint returns indices/oil only (no forex/metals/crypto)

Cause: server's outbound IP not on the `api.stbbrokers.com` allow-list.

Fix: get the server's outbound IP (`curl ifconfig.me`) and send it to the team managing `api.stbbrokers.com` for whitelisting.

### Endpoint returns `"stale": true`

Cause: upstream source (STB API / TradingView / Investing) failed on the last fetch attempt.

This is **expected behaviour** — the plugin serves the last-known data as a graceful fallback rather than breaking the UI. The next cron tick (5/15/30 min later) will try again. If it stays stale for > 1 hour, check:

```bash
# For prices
wp option get stb_prices_last_stb_error --allow-root --path=/var/www/<wp-path>
wp option get stb_prices_last_ok_v2     --allow-root --path=/var/www/<wp-path>

# For calendar
wp option get stb_cal_last_error --allow-root --path=/var/www/<wp-path>
wp option get stb_cal_last_ok    --allow-root --path=/var/www/<wp-path>
```

### Calendar JSON file `wp-content/uploads/stb-ecal.json` not being created

Cause: `wp-content/uploads/` isn't writable by the web-server user.

Fix:
```bash
chown -R www-data:www-data /var/www/<wp-path>/wp-content/uploads
chmod -R 755 /var/www/<wp-path>/wp-content/uploads
```

This file is dual-purpose: the REST endpoint works without it, but the tools page client-side code currently reads the static file. Keeping both ensures back-compat.

### Tools page still shows the old prices

Cause: browser cached the old `prices.js` or `tools.php` page.

Fix: hard-refresh (`Ctrl+Shift+R` / `Cmd+Shift+R`), or clear any caching plugin's cache. The plugin enqueues `prices.js` with a version query string from theme version, so bumping theme version in `style.css` will invalidate cleanly.

### Reverting to the old Python agent (Economic Calendar only)

If for any reason you need to fall back to the Python script:

```bash
# 1. Remove the WP plugin
rm /var/www/<wp-path>/wp-content/mu-plugins/stb-economic-calendar.php

# 2. Re-enable the Python cron entry (it's still on disk, just commented out)
crontab -l | sed 's/^# DISABLED.*replaced by WP mu-plugin.*— //' | crontab -

# 3. Run once immediately
python3 /opt/stb_ecal_fetch.py
```

The Python script `/opt/stb_ecal_fetch.py` and the original crontab backup `/root/crontab.bak-20260512` are left on the staging server for this purpose. Both can be deleted after the team is happy with the WP version (recommended: keep for 30 days).

---

## What runs where (after migration)

```
WordPress (every page-load) → loads 4 mu-plugins on init
        │
        ├─ stb-live-prices.php
        │   └─ schedules stb_lp_cron (5 min)
        │       └─ fetches api.stbbrokers.com + TradingView scanner
        │           └─ caches in transient, exposes /wp-json/stb/v1/prices
        │
        ├─ stb-live-sentiment.php
        │   └─ schedules stb_sent_cron (15 min)
        │       └─ fetches TradingView technical scanner
        │           └─ caches in transient, exposes /wp-json/stb/v1/sentiment
        │
        ├─ stb-economic-calendar.php
        │   └─ schedules stb_cal_cron (30 min)
        │       └─ fetches investing.com
        │           └─ writes wp-content/uploads/stb-ecal.json
        │           └─ exposes /wp-json/stb/v1/calendar
        │
        └─ stb-cpt-taxonomy.php (unchanged)

System cron (every 5 min)
        └─ wp cron event run --due-now → triggers any of the above that are due
```

No Python. No `/opt/` scripts. No external services apart from the upstream APIs the plugins call themselves.

---

## Contact

If something breaks during migration and the steps above don't help, you'll need someone with WordPress / PHP familiarity to debug. The plugins are self-contained, well-commented, and follow standard WordPress patterns — any senior WP developer can pick them up.
