A smart notification system that monitors real-time exchange rates—alerting you when opportunities arise. Support for stocks and crypto coming soon.
- 📊 Real-time Exchange Rate Monitoring - Tracks 5 currency pairs
- 🔔 Cross-Platform Notifications - Native alerts on macOS, Linux, and Windows
- macOS now prioritizes
terminal-notifierand usesafplayas a fallback to play system sound effects, ensuring audible alerts
- macOS now prioritizes
- 🤖 Dynamic Thresholds - Auto-calculated based on historical data (10th percentile)
- 🔑 Secure API Key Management - Uses
.envfile for credentials - ⚡ Efficient Resource Usage - Minimal CPU and network overhead
- 🎯 Intelligent Alerts - Only notifies for significant rate changes (~20% of opportunities)
- AUD/CNY (Australian Dollar to Chinese Yuan)
- CHF/AUD (Swiss Franc to Australian Dollar)
- USD/AUD (US Dollar to Australian Dollar)
- AUD/HKD (Australian Dollar to Hong Kong Dollar)
- HKD/JPY (Hong Kong Dollar to Japanese Yen)
- Python 3.6+
- Internet connection
- No API key required! (uses free Frankfurter API)
- Optional: Backup API key from ExchangeRate-API.com for redundancy
| Platform | Notification Method | Additional Requirements |
|---|---|---|
| macOS | Native (osascript) | ✅ Built-in, no extras needed |
| Linux | notify-send | Install libnotify-bin (usually pre-installed) |
| Windows | plyer library | Auto-installed with setup |
cd SimpleReminder
# Use Python 3.11 (recommended). With pyenv, ensure `.python-version` is respected.
# If you prefer a manual setup, run the commands below.
./setup.shThe setup script will:
- ✅ Create virtual environment
- ✅ Install all dependencies
- ✅ Check for .env file (optional)
If you prefer to set up manually:
cd SimpleReminder# Create virtual environment with Python 3.11
python3.11 -m venv venv
# Activate virtual environment
source venv/bin/activate# Install all dependencies at once
pip install -r requirements.txt
# Or install manually
pip install requests python-dotenvThe script works out-of-the-box with no configuration! It uses the free Frankfurter API.
For extra reliability, you can optionally configure a backup API:
- Visit https://www.exchangerate-api.com/
- Sign up for a free account (1,500 requests/month)
- Copy your API key
- Create a
.envfile:
# Copy the template
cp env_template.txt .env
# Edit .env and add your API key
nano .envYour .env file should look like:
EXCHANGE_RATE_API_KEY=your_actual_api_key_here
Note: If you skip this step, the script will still work perfectly using only Frankfurter API.
Instead of manually setting thresholds, the system can automatically calculate them based on historical data.
- Analyzes past year of exchange rate data
- Calculates 10th/90th percentile thresholds
- Notifies only for exceptional rates (bottom 10% or top 10%)
- Updates monthly to adapt to market trends
✅ Auto-adaptive - Follows market trends automatically
✅ Data-driven - Based on 365 days of real data, not guesswork
✅ Smart notifications - Captures ~20% of best opportunities
✅ Low maintenance - Update once per month
cd /Users/ellery/Development/SimpleReminder
# Activate virtual environment
source venv/bin/activate
# Generate dynamic thresholds (run once, then monthly)
python update_thresholds.pyOutput example:
🔄 Updating Dynamic Thresholds
📊 Method: 10th Percentile
📅 Lookback: 365 days
======================================================================
📈 Processing AUD/CNY...
📥 Fetching 365 days of historical data...
✅ Thresholds: 4.5234 - 4.8765
📊 Based on 260 data points
💡 Historical range: 4.4123 - 4.9876
📈 Processing CHF/AUD...
✅ Thresholds: 1.5123 - 1.8543
...
✅ Thresholds updated and saved to data/thresholds.json
Automatic reminder: The monitor will remind you on the 1st of each month to update thresholds.
Additionally, if you don't update on the 1st, the monitor will send a weekly reminder until thresholds are updated for the current month.
Manual update anytime:
python update_thresholds.pyEdit update_thresholds.py to customize:
PERCENTILE = 10 # 10th/90th percentile (20% opportunities)
LOOKBACK_DAYS = 365 # One year of dataPercentile guide:
5%= ~18 notifications/year (very selective, best opportunities only)10%= ~36 notifications/year (balanced, recommended) ⭐15%= ~55 notifications/year (more frequent)20%= ~73 notifications/year (very frequent)
# Make sure virtual environment is activated
source venv/bin/activate
# Run the script
python exchange_rate_reminder.py
# Or run directly with venv Python (without activation)
./venv/bin/python exchange_rate_reminder.py
# Optional: manage macOS autostart (launchd)
# Install and load on login
./venv/bin/python exchange_rate_reminder.py --install-autostart
# Check status
./venv/bin/python exchange_rate_reminder.py --autostart-status
# Remove autostart
./venv/bin/python exchange_rate_reminder.py --remove-autostart- Loads dynamic thresholds (if available) or uses static defaults
- Checks exchange rates every 45 minutes (~4,000 API calls/month)
- Uses Frankfurter API as primary source (5,000 requests/month)
- Automatically switches to backup API if primary fails
- Compares rates against thresholds (10th/90th percentile)
- Sends cross-platform notifications when rates cross thresholds
- Logs all rates to console with timestamps
- Reminds you to update thresholds on the 1st of each month
When AUD/CNY drops below 4.50:
📉 Low Rate Alert: AUD/CNY = 4.45
AUD/CNY has fallen below your minimum threshold (4.50)
Edit the currency pairs in update_thresholds.py:
CURRENCY_PAIRS = [
"AUD/CNY",
"CHF/AUD",
"USD/AUD",
"AUD/HKD",
"HKD/JPY",
# Add your own pairs here
]Then run python update_thresholds.py to generate thresholds.
If you prefer manual control, edit exchange_rate_reminder.py:
# Static thresholds (in load_thresholds() function)
return {
"AUD/CNY": {"min": 4.50, "max": 4.90}, # Your custom values
"CHF/AUD": {"min": 1.50, "max": 1.90},
# ... more pairs
}Note: Manual thresholds are used as fallback when data/thresholds.json doesn't exist.
Modify the CHECK_INTERVAL_MINUTES in the Config class:
class Config:
CHECK_INTERVAL_MINUTES = 45 # Current: 45 minutes
# For 30 min: = 30
# For 1 hour: = 60
# For 2 hours: = 120Note: With Frankfurter API (~5,000 requests/month), recommended intervals:
- 45 min = ~128 API calls/day (~3,840/month) ✅ Optimal (current) ⭐
- 30 min = ~240 API calls/day (~7,200/month)
⚠️ Exceeds limit - 1 hour = ~120 API calls/day (~3,600/month) ✅ Safe
- 2 hours = ~60 API calls/day (~1,800/month) ✅ Conservative
- Free Tier: ~5,000 requests/month (no registration needed)
- Each check: 4-5 API calls (one per base currency)
- Safe daily usage: ~165 requests/day = 33 checks/day
| Interval | Daily Checks | Daily API Calls | Monthly Total | Status |
|---|---|---|---|---|
| 45 min ⭐ | 32 | 128 | ~3,840 | ✅ Optimal (current) |
| 1 hour | 24 | 96-120 | ~2,880-3,600 | ✅ Safe |
| 2 hours | 12 | 48-60 | ~1,440-1,800 | ✅ Conservative |
| 30 min | 48 | 192-240 | ~5,760-7,200 | ❌ Exceeds limit |
Only used when Frankfurter API fails. Same rate calculations apply.
# Make sure you're in the project directory
cd SimpleReminder
# Run in background using venv Python
nohup ./venv/bin/python exchange_rate_reminder.py > exchange_rate.log 2>&1 &
# Check if it's running
ps aux | grep exchange_rate
# View logs
tail -f exchange_rate.log
# Stop the background process (if needed)
pkill -f exchange_rate_reminder.py- You can either use the built-in flags:
./venv/bin/python exchange_rate_reminder.py --install-autostart- Or manually create
~/Library/LaunchAgents/com.user.exchangerate.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.user.exchangerate</string>
<key>ProgramArguments</key>
<array>
<string>/FULL/PATH/TO/SimpleReminder/venv/bin/python</string>
<string>/FULL/PATH/TO/SimpleReminder/exchange_rate_reminder.py</string>
</array>
<key>WorkingDirectory</key>
<string>/FULL/PATH/TO/SimpleReminder</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardErrorPath</key>
<string>/FULL/PATH/TO/SimpleReminder/error.log</string>
<key>StandardOutPath</key>
<string>/FULL/PATH/TO/SimpleReminder/output.log</string>
</dict>
</plist>Note: Replace /FULL/PATH/TO/SimpleReminder with your actual project path.
- Load the service:
launchctl load ~/Library/LaunchAgents/com.user.exchangerate.plist- This is normal on first run! The script uses static defaults
- Run
python update_thresholds.pyto generate dynamic thresholds - After generating, restart the monitor to use dynamic thresholds
- This is normal! The script works fine without a backup API
- If you want to add redundancy, create a
.envfile with your ExchangeRate-API key - The warning only appears when the primary (Frankfurter) API fails
- This is a helpful reminder, not an error
- Run
python update_thresholds.pyto update thresholds - Skip it if you don't need to update this month
macOS:
- Grant Terminal (or Python) notification permissions:
- System Preferences → Notifications → Terminal → Allow notifications
- Or: System Settings → Notifications → Terminal
Linux:
- Install notification daemon if missing:
# Ubuntu/Debian sudo apt-get install libnotify-bin # Fedora/RHEL sudo dnf install libnotify # Arch Linux sudo pacman -S libnotify
Windows:
- Ensure
plyeris installed:pip install plyer
- Check Windows notification settings (allow app notifications)
- Check your API key is valid
- Verify you haven't exceeded the free tier limit (1,500/month)
- Check internet connection
# Activate virtual environment first
source venv/bin/activate
# Then install dependencies
pip install -r requirements.txtThe system uses the most reliable notification method for each platform:
macOS (Darwin)
- ✅ Uses
terminal-notifierwhen available for native notifications (better visibility) - ✅ Falls back to AppleScript
osascriptif not available - ✅ Plays a reliable chime using
afplayto ensure an audible cue even when the notifier sound is muted
Linux
- ✅ Uses
notify-sendcommand - ✅ Works with GNOME, KDE, XFCE, and other desktop environments
- ℹ️ Requires
libnotify-bin(usually pre-installed)
Windows
- ✅ Uses
plyerlibrary for native Windows 10/11 toast notifications - ✅ Automatic fallback to console output if unavailable
- ℹ️ Installed automatically during setup
Graceful Degradation
- If native notifications fail on any platform, the system falls back to console output; on macOS an additional
afplaysound is attempted - Monitoring continues uninterrupted
SimpleReminder/
├── venv/ # Virtual environment (do not commit!)
├── exchange_rate_reminder.py # Main monitoring script (cross-platform)
├── threshold_calculator.py # Dynamic threshold calculation engine
├── update_thresholds.py # Monthly threshold update script
├── requirements.txt # Python dependencies
├── setup.sh # Quick setup script
├── data/ # Data directory
│ ├── thresholds.json # Calculated thresholds (auto-generated)
│ └── README.md # Data directory info
├── .env # Your API key (optional, do not commit!)
├── env_template.txt # Template for .env file
├── .gitignore # Git ignore rules
├── QUICKSTART.md # Quick start guide
├── CHANGES.md # Change log
└── README.md # This file
⚠️ Never commit your.envfile to Git⚠️ Never share your API key publicly- ✅ The
.gitignorefile prevents accidental commits - ✅ Use
env_template.txtas reference for others
If you need more frequent checks, consider upgrading:
- Pro Plan ($10/month): 30,000 requests, hourly updates
- Business Plan ($30/month): 125,000 requests, 5-min updates
Visit ExchangeRate-API.com pricing for details.
Free to use and modify for personal projects.
# 1. Update thresholds with latest historical data
python update_thresholds.py
# 2. Restart the monitor (if running)
pkill -f exchange_rate_reminder.py
./venv/bin/python exchange_rate_reminder.pyJust let it run! The monitor will:
- ✅ Check rates every 2 hours
- ✅ Send notifications for exceptional rates
- ✅ Remind you to update on the 1st
The system uses the 10th and 90th percentiles as thresholds:
- 10th percentile: Rate was lower than this only 10% of the time in the past year
- 90th percentile: Rate was higher than this only 10% of the time in the past year
Past year AUD/CNY rates: 4.40 to 4.95
- Lowest 10% of rates: 4.40 - 4.55 → 10th percentile = 4.55
- Highest 10% of rates: 4.85 - 4.95 → 90th percentile = 4.85
Result: You get notified when rates enter the best 20% of opportunities (top 10% + bottom 10%)
- Uses
terminal-notifierfor native banners and falls back to AppleScript if needed. - Plays a reliable chime via
afplay(default sound: Glass) so alerts are audible.
| Method | AUD/CNY Example | Notifications/Year |
|---|---|---|
| Manual | 4.50 - 4.90 | Unpredictable |
| 10% Percentile | 4.55 - 4.85 | ~73 days (20%) |
| 5% Percentile | 4.48 - 4.92 | ~37 days (10%) |
Found a bug or want to add features? Feel free to submit issues or pull requests!
Happy Exchange Rate Monitoring! 💱
