Exchange Rate Intelligence — FX Trends, Strength & Risk Signals
Pricing
from $2.00 / 1,000 rate fetcheds
Exchange Rate Intelligence — FX Trends, Strength & Risk Signals
Turn ECB exchange rates into decisions. Get per-currency trend, volatility, risk and shock signals, a currency-strength ranking, historical context versus 25 years, an event feed and recommended actions across 30+ currencies. Includes monitoring, screener and watchlist modes. No API key.
Pricing
from $2.00 / 1,000 rate fetcheds
Rating
0.0
(0)
Developer
Ryan Clinton
Maintained by CommunityActor stats
0
Bookmarked
5
Total users
0
Monthly active users
7 days ago
Last modified
Categories
Share
FX Monitoring & Decision Intelligence Engine

Most FX APIs return rates. This actor returns findings. One API call across 30+ currencies, and you get back the answers, not the raw data:
| Question | Answer |
|---|---|
| Strongest currency | USD |
| Weakest currency | JPY |
| Most unusual move | USD/JPY (top-3% move in 25 years) |
| Top event | USD/JPY printed a new 127-day high |
| Impact | critical |
| Recommended action | review-exposure |
One API call. Six answers. Built on the European Central Bank's official daily reference rates (via the free, open-source Frankfurter API), 30+ currencies back to 1999, no API key, no charts to read.
Before vs after
Without this actor, an FX API hands you:
{ "USDJPY": 156.33, "USDCHF": 0.8921, "EURUSD": 1.0844 }
...and you still have to calculate volatility, rank the currencies, spot the anomalies, compare against history, and build the alerts yourself.
With this actor:
{ "keyFinding": "USD is strongest", "topEvent": "USD/JPY new high", "impactBand": "critical", "recommendedAction": "review-exposure" }
The work is already done.
What questions can this answer?
Treasury — "Has FX risk increased?" -> { "persona": "treasury" } returns riskEscalation, hedgeAttention, volatility-expansion events, and a review-hedging / review-exposure action.
E-commerce — "Do I need to review pricing?" -> { "persona": "ecommerce" } returns pricingPressure, costImpactBand, and a review-pricing action. Add exposureAmount for the actual cost change in your base currency.
Finance — "Which currencies moved materially this month?" -> the run summary ranks every pair by move and flags the material ones with keyFindings and an impactBand.
Trading — "Which pairs deserve attention?" -> { "outputMode": "screener" } returns top breakouts, strongest/weakest currencies, most-unusual moves, and emerging risks.
Operations — "What changed since yesterday?" -> { "watchlistName": "my-fx" } on a schedule returns trend-reversals, regime changes, new highs and a stateChangeSeverity per pair.
Most FX APIs forget yesterday. This one remembers.
Almost every exchange-rate tool is a snapshot: it tells you today's state and nothing about how you got here. Set a watchlistName and this actor keeps state across runs, so each run answers "what changed?":
{ "previousRegime": "ranging", "currentRegime": "breakout", "stateChangeSeverity": 8.7 }
That is a different category of product. Snapshots tell you where you are; state tells you what just moved.
Why this is different

| Capability | FX APIs | Charting tools | Treasury systems | This actor |
|---|---|---|---|---|
| Historical rates | ✓ | ✓ | ✓ | ✓ |
| Trend & volatility analysis | ✗ | ✓ | partial | ✓ |
| Historical context (percentile, rarity) | ✗ | partial | partial | ✓ |
| Event detection | ✗ | partial | partial | ✓ |
| Watchlist memory (cross-run) | ✗ | partial | ✓ | ✓ |
| Decision guidance | ✗ | ✗ | partial | ✓ |
| API-first / automatable | ✓ | ✗ | ✗ | ✓ |
Why the signals are trustworthy
Finance buyers are sceptical, and they should be. Every result here is built from:
- ECB official daily reference rates
- 25+ years of history
- Historical percentiles and volatility regimes
- Drawdown, recovery and shock-event detection
- Cross-run state and change detection
No AI. No opinions. No forecasts. Every score, band, event and action is computed by a pure deterministic function: the same input always produces the same output, and every number is reproducible and auditable. The actor tells you what happened and what to review — never what will happen.
What it replaces
Instead of:
- Exporting rates to a spreadsheet
- Building your own volatility and drawdown calculations
- Maintaining currency watchlists by hand
- Comparing this month's move to historical periods manually
- Assembling a top-mover dashboard
...run one actor.
The intelligence stack
DATA rates, series, snapshots|ANALYTICS trend, momentum, volatility, drawdown, VaR|INTELLIGENCE events, attention, unusualness, historical context|MONITORING watchlists, screeners, state changes, alerts|DECISION impact score, recommended action, persona views, exposure
Every field below belongs to one of these layers. Most actors stop at ANALYTICS; this one runs the whole stack from a single fetch.

What you get from one run
For a time-series query, every pair comes back as one analysis record, read top-down — decisions first, the maths underneath:
- Decision —
impactScore+impactBand,recommendedAction(+ reasons),signal(+ asignalReason[]chain),behaviour, and anfxHealthScore(A-F). The fields you act on. - Monitoring — an
events[]feed (typed, severity-scored, with paste-ready headlines),attentionScore,unusualnessScore, and — on a watchlist run — what changed since last run (temporalSignals,stateChangeSeverity, regime transitions). - Context —
historicalContext(is this move big versus 25 years?),marketRegime,volatilityTrend, recovery and shock-event detection (it auto-flags days like the Nov-2022 BoJ yen intervention). - Risk — historical Value-at-Risk (
var95Pct/var99Pct), Expected Shortfall, max drawdown, and anexposureImpacton your own position when you passexposureAmount. - The underlying stats — every record also carries the raw analytics (trend slope + R², momentum, mean/median, support/resistance, percentiles) for anyone who wants them. See the output example below.
Plus a run-level summary record: a ranked currency-strength meter, keyFindings[], topEvents[], mustWatch[], and a volatility-regime read — mirrored to the SUMMARY key-value-store key. Everything is deterministic: same input, same numbers, every time.

Three retrieval modes
| Mode | What it returns |
|---|---|
latest | Today's most recent ECB reference rates for your base, with inverse quotes. |
date | Rates for one specific historical date (YYYY-MM-DD). |
timeseries | The full daily series between two dates, analyzed into per-currency decision records plus a run summary. This is where the intelligence layer runs. |
In latest and date modes the actor returns a single snapshot record (rates map + per-currency quotes with inverse rates). The volatility, trend, and signal analysis needs a series, so it runs in timeseries mode.
Output modes (timeseries)
Control how much you get back with outputMode:
analysis(default) — one decision record per currency pair, sorted biggest-mover-first (rank 1 leads the preview), plus the run summary. Raw daily rows are not included unless you ask for them.full— everything inanalysisplus one raw rate row per trading day (recordType: "timeseries-point").dashboard— a single digest object (headline numbers + a compact per-pair table) for BI tiles, exec alerts, or an agent that wants one object instead of a paged dataset.report— a single templated executive report:executiveSummary,keyFindings,topMovers,riskAlerts,trendHighlights,strongestCurrencies. Deterministic, no LLM, drops straight into Slack / email / a Dify node.screener— "Bloomberg top movers for FX": the basket sorted into the lists a monitor scans —most-unusual,emerging-risks(watchlist runs only — pairs becoming important),top-breakouts,top-reversals,strongest-currencies,weakest-currencies,volatility-expansions,most-improved,most-deteriorated. Onescreener-categoryrecord per non-empty list.
And outputProfile controls field verbosity on analysis records: minimal (pair, change, regime, volatility band, signal, rating, summary), standard (default), or full (every field including anomaly points and the confidence breakdown). Set includeDailyRates: true to attach the raw daily rows in analysis mode.
Advanced intelligence (opt-in)
Four cross-market layers, all computed from the same single API response (no extra calls, no extra charge):
- Currency strength ranking (always on in timeseries) — a ForexFactory-style strength meter in the summary record. Every currency in your basket is scored 0-100 and ranked by its average appreciation against all the others, so you see which currency led and which lagged at a glance.
- Correlation matrix (
includeCorrelations: true) — Pearson correlation of daily returns between every pair, classifiedstrong-positive/moderate-positive/uncorrelated/moderate-negative/strong-negative. Use it for hedging and diversification: pairs that move together do not diversify risk. - Multi-timeframe (
multiTimeframe: true) — a trailing 30 / 90 / 180 / 365-day signal on each pair, so you can tell whether a trend is consistent across horizons or only a recent move. - Seasonality (
includeSeasonality: true) — strongest and weakest month per currency with a consistency score, mined from up to 25 years of ECB history. Needs at least 3 years in your range; returns null otherwise (never fabricated). - Cross rates (
generateCrossRates: true) — also analyze the cross pairs between your target currencies (EUR/GBP, GBP/JPY, …), not just base-vs-target, derived from the same matrix. - Benchmark (
benchmark: "EUR") — attachrelativePerformancePctto each pair: how that currency performed against your chosen benchmark currency over the window. - Historical context (
includeHistoricalContext: true) — the "is this move big?" engine. For each pair,movePercentile+rarityrank the current move against every same-length window since 1999, andvolatilityMultiplecompares current volatility to its 25-year median. One extra fetch of the full series. - Currency network (
includeNetwork: true) — each currency's role in the correlation network:hub(moves with many),connected(a few), orisolated, plus itsconnectedCurrencies. This is correlation degree (who moves with whom), not causality: ECB daily fixings carry no intraday lead-lag, so the actor never claims "X drives Y" it can't observe. - Personas (
persona: "treasury" | "ecommerce" | "research" | "trading") — the same analytics, reshaped into the flags each buyer acts on. Treasury getsriskEscalation+hedgeAttention; e-commerce getspricingPressure+costImpactBand; trading getssetup+tradeAttention; research gets regime + persistence focus. Emitted as apersonaViewblock per pair.
Input parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
base | String | No | EUR | Base currency code (ISO 4217). All rates are relative to 1 unit of this currency. |
symbols | String | No | (all) | Comma-separated target currencies (e.g. USD,GBP,JPY). Empty = all 30+ supported. |
mode | String | No | latest | latest, date, or timeseries. |
date | String | Conditional | — | Required for date mode. YYYY-MM-DD. |
startDate | String | Conditional | — | Required for timeseries mode. YYYY-MM-DD. |
endDate | String | Conditional | — | Required for timeseries mode. YYYY-MM-DD. |
outputMode | String | No | analysis | analysis, full, dashboard, report, or screener (timeseries only). |
persona | String | No | generic | treasury, ecommerce, research, trading — reshape the output per buyer. |
includeNetwork | Boolean | No | false | Currency correlation-network roles (hub/connected/isolated). |
exposureAmount | Integer | No | — | Notional held in each target currency; returns exposureImpact (historical value change in base). |
outputProfile | String | No | standard | minimal, standard, or full field verbosity on analysis records. |
includeDailyRates | Boolean | No | false | Also emit raw daily rate rows in analysis mode. |
watchlistName | String | No | — | Track this base + currency set across scheduled runs. See "Scheduled monitoring" below. |
multiTimeframe | Boolean | No | false | Add trailing 30/90/180/365-day signals to each pair. |
includeCorrelations | Boolean | No | false | Emit a cross-pair correlation matrix record. |
includeSeasonality | Boolean | No | false | Emit monthly seasonality per currency (needs 3+ years). |
generateCrossRates | Boolean | No | false | Also analyze cross pairs between your target currencies. |
benchmark | String | No | — | Currency code to benchmark each pair against (relativePerformancePct). |
includeHistoricalContext | Boolean | No | false | Percentile each move + volatility against all windows since 1999 (one extra fetch). |
Input example — analyze a quarter
{"base": "USD","symbols": "EUR,GBP,JPY,CHF","mode": "timeseries","startDate": "2024-01-01","endDate": "2024-03-31"}
Input example — one historical date
{"base": "USD","symbols": "EUR,JPY,CAD,AUD","mode": "date","date": "2023-07-15"}
Output example — analysis record
{"recordType": "analysis","schemaVersion": "1.6.0","base": "EUR","currency": "JPY","pair": "EUR/JPY","startDate": "2024-01-01","endDate": "2024-06-28","observations": 127,"startRate": 156.33,"endRate": 171.94,"high": 171.94,"low": 155.68,"changePct": 9.99,"baseDirection": "appreciating","rangePct": 9.89,"currentPercentile": 100,"annualizedVolatilityPct": 6.82,"volatilityBand": "moderate","maxDrawdownPct": 2.17,"trend": { "direction": "up", "strength": "strong", "slopePctPerDay": 0.06, "rSquared": 0.927 },"signal": "strong-appreciation","signalReason": ["Move of +9.99% (≥5% = strong).", "Trend up/strong (R²=0.927).", "Volatility band moderate."],"marketRegime": "trending","volatilityTrend": "stable","risk": { "var95Pct": -0.92, "var99Pct": -1.64, "expectedShortfall95Pct": -1.44 },"levels": { "support": 155.68, "resistance": 171.94, "distanceToSupportPct": 10.4, "distanceToResistancePct": 0 },"scores": { "trendScore": 93, "stabilityScore": 61, "riskScore": 44, "fxHealthScore": 73, "healthGrade": "B" },"trendPersistence": { "positiveDaysPct": 58.7, "longestRunDays": 11 },"recovery": { "recoveryPct": 100, "recoveryDays": 0, "fullyRecovered": true },"shocks": { "majorEvents": [], "shockCount": 0, "shockFrequencyPerYear": 0, "shockSeverityScore": 0 },"rank": 1,"attentionScore": 78,"attentionReason": ["window-high", "strength-laggard", "large-move"],"impactScore": 77,"impactBand": "high","recommendedAction": "monitor-closely","actionReasons": ["window-high", "material-move"],"behaviour": "unusually-strong","events": [{ "type": "window-high", "severity": 6, "headline": "EUR/JPY closed at the top of its 127-day range.", "detail": "currentPercentile 100." }],"historicalContext": { "movePercentile": 92, "rarity": "uncommon", "yearsCompared": 25.9, "volatilityHistoricalMedian": 9.01, "volatilityMultiple": 0.76 },"summary": "EUR appreciating vs JPY (+9.99% over 127 trading days). Annualized volatility 6.82% (moderate), trend up/strong. Signal: strong-appreciation.","whyThisMatters": "Holding EUR gained purchasing power against JPY over this window; JPY-denominated costs got cheaper."}
Output example — run summary
{"recordType": "summary","base": "EUR","tradingDays": 127,"currenciesAnalyzed": 5,"strongestForBase": { "currency": "JPY", "changePct": 9.99 },"weakestForBase": { "currency": "USD", "changePct": -3.12 },"mostVolatile": { "currency": "JPY", "annualizedVolatilityPct": 6.82 },"leastVolatile": { "currency": "GBP", "annualizedVolatilityPct": 3.19 },"avgVolatilityPct": 5.37,"regimeNote": "Normal regime — typical major-currency volatility.","summary": "Over 127 trading days, EUR strengthened most vs JPY (+9.99%) and weakened most vs USD (-3.12%). Normal regime."}
Use cases
- Treasury and FX risk — see which exposures got more volatile and which way they trended, with a regime read, without building a pricing model.
- Forex trend research — rank currency pairs by move size and trend strength over any window back to 1999.
- Cross-border pricing — detect when a currency drifted enough to reprice an international catalogue, with the percentage and direction precomputed.
- Financial reporting and reconciliation — pull the exact ECB reference rate for a transaction date, or a clean daily series for the period.
- Portfolio valuation — convert foreign holdings at the official reference rate and flag pairs in a stressed volatility regime.
- Scheduled monitoring — run on a schedule with a watchlist name and get alerted (via the temporal signals) when a pair's trend or volatility regime shifts.
Use in Dify
Drop this actor into Dify workflows via the Apify plugin's Run Actor node. Each currency pair returns scored and classified as structured JSON: a signal verdict (strong-appreciation / stable / volatile-no-trend and so on), a volatilityBand (low / moderate / high / extreme), and a baseDirection your downstream node branches on. A raw exchange-rate API pointed at the same data returns numbers; this returns decisions.
- Actor ID:
ryanclinton/exchange-rate-history - Sample input (monitor a base currency's majors over a quarter):
{"base": "USD","symbols": "EUR,GBP,JPY,CHF","mode": "timeseries","startDate": "2024-01-01","endDate": "2024-03-31","outputMode": "analysis"}
A Dify if/else node can route on the stable enums without parsing any prose. For example:
marketRegime == "unstable"orvolatilityBand == "extreme"-> route to a "hedge / review exposure" branch.signal == "strong-appreciation"orsignal == "strong-depreciation"-> route to a "reprice / alert" branch.marketRegime == "breakout"-> route to a "watch closely" branch.baseDirection == "stable"-> route to a "no action" branch.
Branch on recommendedAction to route work directly (review-hedging -> treasury queue, review-pricing -> pricing queue, investigate -> analyst), on impactBand to gate by severity (critical/high only), on events[].type + events[].severity to alert on specific changes, or on behaviour / unusualnessScore. Run outputMode: "screener" on a schedule to drop a ready-to-read top-movers board into Slack, or set persona: "treasury" so a downstream node branches on personaView.hedgeAttention. The events[].headline, summary and whyThisMatters strings are usable verbatim in a downstream LLM prompt or a Slack message: no field-joining or rewriting needed. Use outputMode: "dashboard" for a single digest object, or outputMode: "report" for a ready-to-send executive summary.
Scheduled monitoring (watchlist)
Set watchlistName to track the same base + currency set across runs. The actor stores a snapshot per pair in a named key-value store and, on the next run, attaches a temporalSignals block to each analysis record:
trend:rising/falling/stable/newpreviousEndRate,rateDeltaPct— how the rate moved since last runpreviousSignal,regimeChange— whether the verdict flippedvolatilityShift— change in annualized volatilitypreviousRegime,regimeChange— the prior regime and whether it shiftedstateChangeSeverity(0-10) — how much the pair's state moved since last runrunsSeen,firstSeenAt,lastSeenAt
On a watchlist run the cross-run events (trend-reversal, regime-change, new-high, new-low, volatility-expansion, …) are appended to the pair's top-level events[] feed alongside the single-run events.
First run: every pair carries trend: "new" and null deltas (no prior snapshot to diff against), and events[] holds only the single-run events. From the second run onward the cross-run events and deltas populate. Leave watchlistName empty for one-off, stateless analysis.
A scheduled run with a watchlist is the monitoring product: point an alert at events[] (filtered by severity) and you get notified the moment a pair reverses trend, breaks to a new high, or its volatility expands.
Integrations
- Google Sheets — export the dataset for pivot tables and charting.
- Zapier / Make / n8n — branch on the
signal/volatilityBand/baseDirectionenums to trigger alerts. - Webhooks / Slack — paste the
summarystring straight into a notification. - Apify Scheduling — run daily after 16:00 CET (when the ECB publishes), with a watchlist for cross-run drift.
Python
from apify_client import ApifyClientclient = ApifyClient("YOUR_API_TOKEN")run = client.actor("ryanclinton/exchange-rate-history").call(run_input={"base": "USD","symbols": "EUR,GBP,JPY","mode": "timeseries","startDate": "2024-06-01","endDate": "2024-06-30",})for item in client.dataset(run["defaultDatasetId"]).iterate_items():if item.get("recordType") == "analysis":print(item["pair"], item["signal"], item["annualizedVolatilityPct"])
How it works
- Parse input — base, target currencies, mode, dates, output controls.
- Build the Frankfurter URL —
/latest,/{date}, or/{startDate}..{endDate}, withbaseand optionalsymbols. - Fetch once — a single HTTP GET (no auth), with timeout and retry/backoff on transient upstream errors.
- Pivot and analyze (timeseries) — turn the date-keyed matrix into per-currency ordered series, then compute volatility, trend, momentum, drawdown, percentile, anomalies, and the signal verdict for each.
- Rank and summarize — sort pairs biggest-mover-first, build the run summary and regime read, and mirror it to the
SUMMARYkey-value-store key. - Emit — push the analysis records (or the dashboard digest), respecting the output mode and profile.
Performance & cost
| Metric | Snapshot (latest/date) | Time series |
|---|---|---|
| API calls per run | 1 | 1 |
| Memory | 256 MB | 256 MB |
| Typical run time | 3-8 s | 5-15 s |
| Third-party API cost | Free | Free |
Pricing is pay-per-event: $0.02 per currency analysed — one charge for each target currency you get a full decision record for (trend, volatility, risk, signal, recommended action and the rest). A 5-currency analysis costs $0.10; a 10-currency screener costs $0.20; a single snapshot (latest/date mode) charges once at $0.02 for all the currencies in it. You pay per decision, not per day: the length of the date range, the historical-context engine's 25-year lookback, and opt-in cross-rates all add no extra charge. Charged only after data is successfully returned — never on an error or an empty result.
What this does NOT do
- Not real-time or intraday. The ECB publishes once per business day around 16:00 CET. For tick-level or live spot data, use a dedicated forex feed.
- Not a forecast. Trend, momentum, and percentile describe what already happened. There is no projection or prediction, by design.
- Not crypto. Frankfurter covers ECB fiat currencies only. For crypto, use CoinGecko Crypto Data.
- Not a trading signal. The
signalverdict is a descriptive classification of historical movement, not financial advice. Always verify with your bank or broker before trading. - ECB coverage only. ~30 currencies. Exotic or less-traded currencies the ECB does not track are unavailable. ECB reference rates are not a transaction guarantee.
FAQ
Which currencies are supported? All currencies the ECB tracks: USD, GBP, JPY, CHF, AUD, CAD, CNY, HKD, NZD, SEK, KRW, SGD, NOK, MXN, INR, ZAR, TRY, BRL, DKK, PLN, THB, IDR, HUF, CZK, ILS, PHP, RON, ISK, MYR, BGN, and more. 30+ in total.
Do I need an API key? No. The underlying Frankfurter API is free and keyless. The actor works out of the box.
How is volatility calculated? Standard deviation of daily log returns over the window, annualized by √252. It is reported as a percentage and bucketed into low (<5%), moderate (5-10%), high (10-20%), and extreme (≥20%).
What does the signal verdict mean?
A deterministic classification combining the total move and the volatility/trend shape, from strong-appreciation through stable to strong-depreciation, plus volatile-no-trend for choppy pairs and insufficient-data for windows under 10 trading days.
Why is confidence capped on short ranges? A handful of data points cannot support a trustworthy volatility or trend read, so windows under 10 trading days are capped at medium confidence and flagged. Widen the range for high-confidence analysis.
How far back does the data go? To 1999-01-04, when the ECB began publishing daily reference rates.
Can I get the raw daily rates too?
Yes. Set includeDailyRates: true or outputMode: "full" to attach one raw row per trading day alongside the analysis records.
Related actors
| Actor | Description |
|---|---|
| Currency Exchange Rate Tracker | Latest real-time rates from multiple providers. Use for current rates without historical analysis. |
| CoinGecko Crypto Data | Cryptocurrency prices, market caps, and exchange rates. |
| BLS Economic Data | US labor and price statistics that move alongside currencies. |
| World Bank Development Indicators | Macroeconomic indicators that correlate with currency movements. |