Skip to content

Repository files navigation

💵 USD Strength Tracker

Tracks the US dollar against world currencies and emails you when the dollar is favorable for travel — i.e. stronger than its own recent average for a given country's currency.

The website has five tabs:

  1. Currency strength — world heatmap (toggle: currency timing ↔ affordability)
    • equal-weighted USD index chart (1M/3M/6M/1Y) + a per-currency table with a price-level (PPP) column. Plus a monthly email alert when watched currencies become favorable.
  2. Best value now — a composite score per country blending affordability (PPP), dollar strength vs history, safety, and weather for a chosen month; ranked list + map.
  3. Best time by country — pick a country (grouped by region) for a 12-month climate-comfort chart and its best months to visit.
  4. Best places by month — pick a month → world heatmap of nicest weather.
  5. Travel advisories — world heatmap + list of current US State Dept advisory levels (1–4) per country.

Data sources (all free, no API key): currency = fxratesapi.com, PPP = World Bank, climate = Open-Meteo, advisories = travel.state.gov. The slow-moving datasets are precomputed into public/:

python3 -m fxtracker.build_climate   # -> public/climate.json (weather comfort)
python3 -m fxtracker.build_ppp       # -> public/ppp.json (PPP conversion factors)

Re-run those to refresh. Price level = PPP factor ÷ live exchange rate (below ~1 means cheaper than the US for a dollar holder).

No build step, no pip install. Pure Python standard library (Python 3.9+). FX data comes from the free, key-less fxratesapi.com API (~180 currencies; ~1 year of history).


Quick start

cd "fx-tracker"
./run.sh            # or: python3 server.py

Open http://localhost:8000. Click Settings to set your watchlist, threshold, and email — then Save settings.

To test alerts without the dashboard:

python3 check.py --dry-run   # prints what it would alert, sends nothing
python3 check.py             # actually sends email (if configured)

How "favorable" is decided

For each currency the app expresses the rate as units of foreign currency per 1 USD, so a higher number means a stronger dollar.

  • Baseline = the average rate over the last N days (default 365).
  • Strength = how far today's rate is above that baseline, in percent.
  • A currency is favorable when strength ≥ your threshold (default +2%).
  • Range position shows where today sits between the window's low and high (100th percentile = dollar at its strongest in the window).

Tune baseline_days and threshold_pct in Settings (or config.json).


Email setup (Gmail example)

  1. Enable 2-Step Verification on your Google account.
  2. Create an App Password (Google Account → Security → App passwords). It's a 16-character code — use it, not your normal password.
  3. In Settings, enable email and fill in:
    • SMTP host smtp.gmail.com, port 587
    • Username / From / To = your Gmail address
    • Password = the app password
  4. Save, then click Check & email now to send a test (only sends if any watched currency is currently favorable).

Keeping the password out of the file: leave the password field blank and set an environment variable instead:

export FX_SMTP_PASSWORD="your-app-password"
python3 check.py

Any SMTP provider that supports STARTTLS works (Fastmail, Outlook, etc.) — just change host/port.


Scheduling monthly checks

Alerts are set up to fire once a month — good cadence for trip planning. The alert_cooldown_hours default (720h ≈ 30 days) also guarantees a currency that stays favorable won't email you twice in the same month.

Option A — cron

crontab -e

Add (runs on the 1st of every month at 9:00 AM; adjust the path):

0 9 1 * *  cd "/Users/doug/Desktop/Claude Workspace/fx-tracker" && FX_SMTP_PASSWORD="your-app-password" /usr/bin/python3 check.py >> check.log 2>&1

Option B — macOS launchd

Create ~/Library/LaunchAgents/com.fxtracker.check.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.fxtracker.check</string>
  <key>ProgramArguments</key>
  <array>
    <string>/usr/bin/python3</string>
    <string>/Users/doug/Desktop/Claude Workspace/fx-tracker/check.py</string>
  </array>
  <key>WorkingDirectory</key><string>/Users/doug/Desktop/Claude Workspace/fx-tracker</string>
  <key>EnvironmentVariables</key><dict><key>FX_SMTP_PASSWORD</key><string>your-app-password</string></dict>
  <key>StartCalendarInterval</key><dict><key>Day</key><integer>1</integer><key>Hour</key><integer>9</integer><key>Minute</key><integer>0</integer></dict>
  <key>StandardOutPath</key><string>/tmp/fxtracker.log</string>
  <key>StandardErrorPath</key><string>/tmp/fxtracker.err</string>
</dict></plist>

Then: launchctl load ~/Library/LaunchAgents/com.fxtracker.check.plist

The alert cooldown (default 720h ≈ 30 days) prevents the same currency from emailing you more than once a month.


Files

fx-tracker/
├── server.py            # web dashboard + JSON API (stdlib http.server)
├── check.py             # scheduled alert job
├── config.json          # your settings (created/edited via the dashboard)
├── state.json           # last-alert timestamps (auto-created)
├── run.sh               # convenience launcher
├── fxtracker/
│   ├── rates.py         # fetch rates + score favorability
│   ├── mailer.py        # SMTP email + alert formatting
│   └── store.py         # JSON config/state persistence
└── public/
    ├── index.html / app.js / styles.css   # dashboard front-end
    └── world.geojson    # slimmed Natural Earth country shapes (for the heatmap)

The heatmap uses an equirectangular SVG projection drawn from world.geojson (Natural Earth 110m, public domain) — no mapping library. The currency→country mapping lives in CUR_BY_ISO in public/app.js.


Notes & limitations

  • Coverage: fxratesapi.com provides ~180 currencies — essentially every country with a traded currency, including all of Latin America, Africa, the Middle East, and Asia. History is limited to ~1 year on the free tier, so the chart windows cap at 1Y. The only network code lives in fxtracker/rates.py (fetch_json + the three get_* helpers); the rest is provider-agnostic, so swapping providers again is a one-file change.
  • The "overall USD strength" index is computed on a stable majors basket (MAJORS in rates.py), not all 180 — otherwise hyperinflation currencies (ARS, VES, etc.) would distort the headline. The map and table use full coverage.
  • No key, so no uptime guarantee. For a production/always-on deploy, consider a keyed provider; the swap is isolated to rates.py.
  • Not financial advice. Mid-market reference rates differ from what your bank or card charges. Confirm before booking.
  • macOS certs: python.org's Python sometimes lacks CA certs; rates.py auto-finds a bundle (env SSL_CERT_FILE, certifi, or the system store) with verification left on.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages