Real-time currency exchange rate monitor with browser push notifications.
There are many FX monitoring tools out there, but most require a phone app, email subscriptions, or third-party services that handle your data. This project is built for self-hosting — your subscription data stays on your own server.
More importantly, many office workers can't check their phones during work hours, and logging into external websites may be against company policy. Browser Web Push works differently: notifications are delivered directly by the browser in the background, no app or email needed. You get alerted the moment a rate target is hit, right on your work computer, without opening anything.
- Live FX rate charts (Frankfurter, Yahoo Finance)
- Price alerts via Web Push (Chrome, Firefox, Safari)
- Each alert notifies once; user can reactivate from the My Alerts panel
- Rate limiting and input validation
All configuration is via environment variables. Copy .env.example to .env and fill in:
| Variable | Description | Default |
|---|---|---|
VAPID_PRIVATE_KEY_PATH |
Path to VAPID private key PEM file | vapid_private.pem |
VAPID_PUBLIC_KEY |
VAPID public key (base64url) | — |
VAPID_EMAIL |
Contact email for push service | mailto:admin@example.com |
POLL_INTERVAL |
Alert check interval in seconds | 60 |
DB_PATH |
SQLite database file path | fx_monitor.db |
pip install pywebpush
python3 -c "
from py_vapid import Vapid
import base64
v = Vapid()
v.generate_keys()
pub = base64.urlsafe_b64encode(v.public_key.public_bytes(
__import__('cryptography.hazmat.primitives.serialization', fromlist=['Encoding']).Encoding.X962,
__import__('cryptography.hazmat.primitives.serialization', fromlist=['PublicFormat']).PublicFormat.UncompressedPoint
)).decode().rstrip('=')
v.save_key('vapid_private.pem')
print('VAPID_PUBLIC_KEY=' + pub)
"# 1. Create data directory
mkdir -p data
# 2. Copy and fill in config
cp .env.example .env
# 3. Build and start
docker compose up -dData is persisted in ./data/ and the VAPID key is mounted read-only from ./vapid_private.pem.
mkdir -p data
pip install -r requirements.txt
uvicorn main:app --host 0.0.0.0 --port 8000To add a new data source, create sources/your_source.py:
from sources.base import RateSource
class YourSource(RateSource):
name = "Your Source"
async def get_currencies(self) -> list[dict]: ...
async def get_rate(self, base, quote) -> float: ...
async def get_history(self, base, quote, start, end, interval="1d") -> list[dict]: ...It will be auto-discovered and appear in the UI dropdown.