DEV Community

Cover image for Can Central Bank Press Releases Predict FX Volatility? A Bolivian Case Study in Honest Quant Research
Monica Orellana
Monica Orellana

Posted on

Can Central Bank Press Releases Predict FX Volatility? A Bolivian Case Study in Honest Quant Research

Imagine you are the finance lead of a small importer in Bolivia. You have signed a USD 100,000 contract for machinery, payment is due in about three months, and the country has just moved from a fixed exchange rate to a flexible one. Then the Banco Central de Bolivia (BCB) publishes another multi-page PDF press release.

Is it noise, or a warning? Should you buy dollars today?

I turned that question into a small research project: can the language of BCB communications, read automatically, tell us anything about exchange-rate volatility in the days that follow? And whatever the answer, how should an SME size a cash buffer under a flexible regime?

The short version: the NLP signal turned out to be weak and fragile, and I report it that way. The risk-scenario tool built alongside it is more useful, but only if read for what it is. This post walks through the pipeline, the results, and what I would change next. Code: purischaky/BCB_news_sentiment.

TL;DR

  • Pipeline: scrape BCB press releases, extract PDF text, deduplicate, tag with a zero-shot multilingual NLI model, run an event study against daily exchange-rate returns, then simulate cash buffers with Monte Carlo.
  • Finding 1: the association between classifier confidence and the change in FX volatility is weakly positive (r = 0.31, 95% CI [-0.00, 0.56], n = 41). It does not survive scrutiny.
  • Finding 2: for a USD 100,000 invoice, a zero-drift simulation implies a 95% buffer of roughly Bs 340,000 at a 60-business-day horizon, rising to about Bs 775,000 if volatility doubles. These are stress illustrations, not forecasts.
  • Takeaway: the most valuable part of the project was learning where not to trust my own result.

Context: a regime change

According to the repository's institutional notes, Bolivia kept an official rate of Bs 6.96 per USD from 2011 until 28 June 2026. A flexible regime took effect on 29 June 2026 under Ministerial Resolution 245/2026 and BCB Board Resolution 88/2026. On 24 September 2026, BCB Board Resolution 142/2026 repealed RD 88/2026 and issued an updated exchange-operations regulation. Under the new rules the official rate (TCO) is the amount-weighted median of banks' USD purchases from clients and the BCB, excluding interbank trades.

That last detail matters later: a weighted median is smoother than a market quote, so it can understate short-term volatility.

The pipeline

├── data/
│   ├── raw/          # scraped communications and exchange-rate snapshot
│   └── processed/    # deduplicated documents and classifier output
├── docs/             # data notes: series definition and open questions
├── outputs/
│   ├── figures/
│   └── reports/
├── scripts/          # diagnostics and README table generator
├── src/              # pipeline modules
└── tests/            # pytest
Enter fullscreen mode Exit fullscreen mode

1. Collection and cleaning

  • Resilient scraping. A requests session with automatic retries and backoff, a polite delay between PDF downloads, and incremental JSON-lines writes while scraping runs.
  • Spanish date parsing. Publication dates arrive in formats such as "Viernes, 06 Marzo, 2026" or "14 de octubre de 2026". A regex parser normalizes whitespace and punctuation and maps Spanish month names. Items whose date cannot be parsed are skipped, not guessed.
  • Deduplication. The same press release often appears under two sections with different URLs, so URL alone is not a reliable key. Documents are deduplicated on date + normalized title (accents stripped, "comunicado -" style prefixes removed), keeping the press-note version first.
  • Hand-added documents. Some announcements do not appear under the standard category listings. I added those PDFs through a separate script that skips URLs already collected. This matters for the results, as discussed below.

2. NLP tagging

I used MoritzLaurer/mDeBERTa-v3-base-mnli-xnli for zero-shot classification of each text chunk (about 1,000 characters) into four candidate topics:

  1. currency restriction / exchange controls
  2. monetary or exchange-regime adjustment
  3. reserves and liquidity stability
  4. administrative or routine communication

Per document, intensity_score is the maximum top-label confidence across chunks. Across the 42 documents the labels split 18 reserves and liquidity, 12 regime adjustment, 6 routine and 6 currency restriction.

BCB communications by zero-shot topic label

Precision about what this score is matters, because it is easy to oversell:

  • It is a topic classifier's confidence, not a measure of hawkishness or policy stance.
  • It is not calibrated, and because it takes a maximum over chunks it tends to be higher for longer documents.
  • A confident "administrative" label scores as high as a confident "currency restriction" label.
  • It has not yet been validated against human labels.

The pipeline also stores an is_hawkish flag, but it simply marks documents whose top label is the restriction or adjustment topic with confidence of at least 0.50. It flags a topic, not a tightening direction. I kept the field names for compatibility with saved data and documented what they actually mean.

3. Event-study design

For each announcement date T:

  • Pre window: the 20 business-day log returns ending on T.
  • Post window: the 5 business-day log returns strictly after T. Both windows must be complete.
  • Outcome: log(post-window volatility / pre-window volatility).
  • Several documents on the same date count as one event, using the highest score.

Measuring volatility relative to its own recent level avoids comparing calm and turbulent periods directly. I fixed these windows before looking at results.

Results: a weak, fragile signal

I checked whether classifier confidence is associated with a change in volatility after an announcement, using 41 event dates.

NLP classifier confidence vs change in FX volatility

Sample n Pearson r 95% CI Permutation p
Full sample 41 0.306 [-0.002, 0.561] 0.050
Before flexible regime 30 0.305 [-0.062, 0.600] 0.104
Flexible regime 11 0.432 [-0.226, 0.820] 0.196
Excluding Nov 2025 - Jan 2026 26 0.175 [-0.228, 0.527] 0.398

The Spearman correlation on the full sample is 0.22. Read literally, this is a weak positive association sitting at the edge of conventional significance, with a confidence interval that touches zero. Four samples are reported, so a borderline result in one of them should not be read as a discovery.

Why I would not lean on it

1. The sample can only detect large effects. With 41 events, the smallest correlation detectable at 80% power and a 5% level is about 0.43. The flexible-regime subsample has only 11 events, and its interval runs from -0.23 to 0.82.

2. The events are not independent. Announcements cluster: 26 of the 40 gaps between consecutive event dates are five business days or fewer. The 5-day post window of one event overlaps the pre window of the next. A standard permutation test assumes exchangeable observations, so the effective sample is smaller than 41, and the p-value is optimistic.

3. A handful of events in a suspect period drive the result. The highest volatility ratios come from four events on 4-7 November 2025, inside a window (roughly November 2025 to January 2026) where the series shows daily moves of 10% to 24% whose origin I could not explain. Drop those four events and r falls from 0.31 to about 0.18, the same as the "excluding Nov-Jan" row. I chose that exclusion after looking at the exchange-rate plot, so I treat it as a sensitivity check, not a primary test.

Exchange-rate series and daily returns

4. The sample is partly hand-curated. Every one of the 11 flexible-regime event dates, plus three earlier ones, appears in the list of PDFs I added manually. My inclusion rule was not pre-specified, so some selection effect is possible, and it falls on exactly the regime that matters most.

5. Two different series are pooled. Before 29 June 2026 the series is not the official TCO and its definition is unverified.

And even a real association would not be causal. Central banks plausibly publish more exchange-rate material when markets are already turbulent, which is reverse causality.

From prediction to risk preparation

If text confidence cannot reliably forecast volatility, a CFO is no better off waiting for a signal. A different question is still answerable: given the volatility we have actually observed under the flexible regime, how much liquidity should an importer hold?

The scenario engine is a zero-drift geometric Brownian motion with 10,000 paths and a fixed seed, so the report and the plots always agree.

  • Spot: the last observed rate, Bs 11.84 per USD.
  • Daily volatility: 1.99%, estimated from log returns since 29 June 2026.
  • Buffer: the 95th-percentile terminal rate times the contract amount, minus the cost at spot.
  • Stress scenarios: volatility multiplied by 1.5 and 2.0. These multipliers are illustrative. They are not estimated from the NLP signal, and the NLP score does not feed the simulation. The two parts of the project are independent.

Simulated exchange-rate distribution at 60 business days

USD 100,000 contract, spot Bs 11.84. Horizons are counted in business days.

Scenario Horizon (business days) Daily vol. 95% VaR rate (Bs/USD) Required buffer (Bs)
Baseline 30 1.99% 14.16 232,350
Stress x1.5 (illustrative) 30 2.99% 15.49 365,102
Stress x2.0 (illustrative) 30 3.98% 16.94 510,297
Baseline 60 1.99% 15.23 339,050
Stress x1.5 (illustrative) 60 2.99% 17.27 543,410
Stress x2.0 (illustrative) 60 3.98% 19.59 775,190
Baseline 90 1.99% 16.11 426,512
Stress x1.5 (illustrative) 90 2.99% 18.78 694,321
Stress x2.0 (illustrative) 90 3.98% 21.91 1,006,665

How to read the matrix

Use calendar time carefully. A payment due in 90 calendar days is about 64 business days, so the 60-day row is the closer match, not the 90-day row. Re-running the same engine and seed at 64 business days gives a 95% rate of roughly 15.40 and a baseline buffer of about Bs 356,000, around 30% of the Bs 1.18 million spot cost. At 2.0x volatility it rises to roughly Bs 820,000. (A 30-calendar-day contract is about 21 business days.)

The volatility input is unstable. The 1.99% estimate comes from about 85 business days that include an unusual spike in September 2026. In the event-study data, the 20-day windows ending in July and early September show daily volatility of about 0.6% to 1.0%, while windows ending on 25 September and 14 October show 3.9% and 2.9%. The regime-wide average sits between two very different states, so the "baseline" is more fragile than its tidy label suggests.

The simulator is deliberately simple. It assumes zero drift in log terms, normally distributed returns, constant volatility, no mean reversion and no policy intervention. Compounding 2% daily volatility for months produces wide distributions; the simulated lower tail even falls below the former fixed rate. Treat the buffers as a conservative upper bound from a simple model, and as a way to ask "what happens to my liquidity need if volatility doubles?", not as a forecast or a hedging recommendation.

What I would do next

  1. Validate the classifier against a hand-labelled sample. It is the most valuable next step, and everything NLP-related depends on it.
  2. Measure stance directly, for example with an NLI hypothesis such as "the central bank is tightening access to foreign currency", rather than topic confidence.
  3. Fix the event design: use non-overlapping events or a block permutation, and add a placebo test on matched non-announcement dates to ask whether volatility changes around announcements are unusual at all.
  4. Pre-specify the inclusion rule for manually added documents.
  5. Improve the simulator: bootstrap observed returns for fatter tails, test sensitivity to the volatility window, and add horizons in calendar-day terms.
  6. Confirm timing conventions, in particular whether the exchange-rate date is the date a rate applies or the date it is published, which could shift windows by one day.

What this project taught me

  • Write down what your variable actually is. A zero-shot topic confidence is not a sentiment score, and naming it correctly changes the whole interpretation.
  • Overlapping windows quietly shrink your sample. Forty-one events are not forty-one independent tests.
  • Sensitivity checks defined after seeing the data are still useful, as long as you label them. The Nov-Jan exclusion tells you where the result lives.
  • Separate the signal from the tool. The risk matrix stands on its own assumptions; it does not become more credible because an NLP model sits upstream.
  • Make the numbers reproducible. Fixed seeds, a one-command pipeline, and README tables generated from the output files mean the figures cannot drift from the text.

Engineering approach and AI disclosure

  • Human-defined: the research question, institutional context, scenario design, event-study specification, and interpretation of results.
  • AI-assisted: drafting of scraping and classification boilerplate, plotting styles, test scaffolding, an audit of my first version (v0.1), and step-by-step guidance through the v0.2 fixes. I ran all code locally and reproduced every number; the audit's claims were checked against the data before I acted on them.

Stack: Python, Hugging Face Transformers (PyTorch backend), pandas, NumPy (Fisher intervals and permutation tests implemented directly), pdfplumber, BeautifulSoup, requests, matplotlib and seaborn, pytest.

This is an exploratory, educational and portfolio project, tied to the 23 October 2026 data snapshot. It is not financial advice and does not predict future market behavior.

Let's connect

If you work on messy document pipelines, careful empirical testing, or the intersection of NLP and economics, I would be glad to compare notes. The full code, data notes and reproduction steps are in the repository:

github.com/purischaky/BCB_news_sentiment

Top comments (0)