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:
- 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.
- 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.
- Best time by country — pick a country (grouped by region) for a 12-month climate-comfort chart and its best months to visit.
- Best places by month — pick a month → world heatmap of nicest weather.
- 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).
cd "fx-tracker"
./run.sh # or: python3 server.pyOpen 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)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).
- Enable 2-Step Verification on your Google account.
- Create an App Password (Google Account → Security → App passwords). It's a 16-character code — use it, not your normal password.
- In Settings, enable email and fill in:
- SMTP host
smtp.gmail.com, port587 - Username / From / To = your Gmail address
- Password = the app password
- SMTP host
- 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.pyAny SMTP provider that supports STARTTLS works (Fastmail, Outlook, etc.) — just change host/port.
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.
crontab -eAdd (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
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.
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.
- 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 threeget_*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
(
MAJORSinrates.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.pyauto-finds a bundle (envSSL_CERT_FILE,certifi, or the system store) with verification left on.