Does foreign flow lead or follow the Brazilian market?
The "foreign flow" chart almost never runs alone in the news: it comes with the causal reading attached — "foreigners are back, the market goes up". This page measures what can be measured from a source that holds up, and publishes the whole result, including the part that undoes the reading.
Before the number: what is being measured
This is not B3's chart. That one is the non-resident buy/sell balance on the exchange floor. This is capital crossing the border, from the Central Bank's balance-of-payments foreign exchange records. They correlate and tell similar stories; they are not the same number.
Why we don't use B3's: as of 16/08/2026 that was seven paths tested without finding a stable, machine-readable public source. The last one was the Daily Bulletin (BDI) — a JavaScript-rendered application whose output is PDF, and whose own site warns that bulletins older than 20 days live only in the archive. A scraper on top of that would break without warning on every layout change, and the history would have to be rebuilt from PDFs. That is the opposite of what this database sets out to be.
The two series
| Flow | foreign portfolio investment in equities, monthly net, US$ millions — methodology |
| Market | total return of our market index, dividends reinvested — methodology |
The monthly return uses the last index point in each month and is computed in logs, which is what adds up over time and keeps a +50% followed by a −33% symmetric.
Why in two currencies
The flow is in dollars; the index, in reais. A year of +20% in reais with the dollar up 25% is a loss for whoever brought dollars in — and it is the dollar result that decides whether that capital stays. Publishing only the local-currency version would answer, with a correct number, a question that is not the foreign investor's.
The exchange rate is the daily USD sell rate (SGS series 1, identified by name in the Central Bank's open-data catalogue). We store the daily series and define month-end ourselves rather than using the ready-made monthly one: the return that matters is the index level on the last trading day of the month converted at that day's rate. A monthly average answers a different question, and the gap between the two shows up precisely in months of sharp turns — the months that matter most in a flow analysis.
The conversion happens on the level, before dividing, never on the finished return. The two routes give close but different numbers, and the wrong one goes unnoticed because it stays plausible. Invariant I-X07 locks this.
The result
Snapshot of 16/08/2026, with 122 months (2016-05 to 2026-06). The figures below are a frozen copy, for citation. The table that recomputes every month lives at /macro/fluxo-e-mercado, and that one governs — this page describes the METHOD. (One of our own comparison pages aged silently because it carried hand-typed numbers; the date above exists because of that.)
| Lag | Reading | In R$ (Pearson · Spearman) | In US$ (Pearson · Spearman) | Months |
|---|---|---|---|---|
| -3 | return → flow +3m | +0.02 · +0.03 | -0.01 · -0.03 | 119 |
| -2 | return → flow +2m | -0.07 · -0.11 | -0.09 · -0.13 | 120 |
| -1 | return → flow +1m | +0.14 · +0.10 | +0.20 · +0.16 | 121 |
| 0 | same month | +0.47 · +0.38 | +0.51 · +0.46 | 122 |
| +1 | flow → return +1m | +0.03 · -0.06 | +0.05 · -0.03 | 123 |
| +2 | flow → return +2m | -0.14 · -0.16 | -0.07 · -0.11 | 124 |
| +3 | flow → return +3m | -0.05 · -0.04 | +0.01 · +0.02 | 124 |
Pearson measures a linear relationship; Spearman measures the same thing over ranks, and it runs alongside on purpose: one panic month (March 2020) dominates Pearson by itself, and any divergence between the two is the information that the result rests on few months.
How to read it (valid while the order of magnitude holds)
Same month: +0.47 (R$) and +0.51 (US$). Strong — and almost unavoidable. Both are measured over the same month, so the correlation is equally consistent with the buying contributing to the price and with the price attracting the buying: monthly data cannot separate the two. It says nothing about prediction, which is exactly how the news chart is usually read.
Flow first, return next month: +0.03. Essentially zero — and on ranks it turns slightly negative. Across these months, knowing that foreigners had come in bore almost no linear relation to the next month's return.
Return first, flow next month: +0.14 (R$) and +0.20 (US$). Larger than the other direction, in both currencies and both measures. The pattern is more consistent with flow that follows the return than with flow that leads it — which is what return chasing looks like, and identifies no cause.
The asymmetry between −1 and +1 is this page's finding, and it inverts the headline. But the finding is the ORDER, not the size: without 2020 the return→flow correlation falls from +0.20 to +0.09. The direction survives every cut; the magnitude rests on one year.
Limits, plainly
- Correlation is not cause, and a lag table is not a causality test — not even Granger's. It is cross-correlation, and nothing here controls for US rates, commodity prices or political risk. Global risk falls, the market rises AND foreigners buy, with neither causing the other.
- One country, ~10 years, and quite different regimes inside the window (pandemic, global rate cycle, two elections). The market index starts in 2016 and that is what bounds the window. Enough to refuse a claim; not enough to establish a rule.
- Correlations are published as effect sizes, with no p-value. On an autocorrelated series the naive p-value is far too optimistic. The one place a p-value does appear is the inflow-versus-outflow comparison, which is a yes/no question — and there the permutation shuffles blocks contiguous in time, for the same reason.
- This is a descriptive map, not a decision rule. Money that came in is not a buy signal.
Invariants
| id | what it locks |
|---|---|
| I-X05 | every FX quote is positive and inside a possible range |
| I-X06 | no FX jump above 15% between neighbouring trading days |
| I-X07 | return in US$ = return in R$ − change in the exchange rate |
| I-X08 | every published correlation carries its own n, and n is never small enough to be noise |
| I-X09 | the same-month correlation is the largest of all |
| I-X10 | each lag's n is exactly the overlap between flow and shifted return |
| I-X11 | no n exceeds the length of the shorter series |
| I-X12 | the permutation preserves group sizes and shuffles blocks contiguous in time |
Two defects an external review found
On 17/08/2026 a reviewer called the n impossible: "123 pairs in a 122-month
series". It was not a bug — the BCB flow runs 1995 to 2026-06 and the
market index 2016-05 to 2026-08, so a lag can use a flow month before the
overlap or a return month after it. But he was right about what matters: a
number that looks impossible, on a page that sells auditability, is a defect
for as long as the proof lives only in the author's head. Hence I-X10 and
I-X11, which recompute every count by an independent route.
The second finding was a real bug, and it came from a side question: "does the permutation preserve group sizes?". It did — but the "contiguous blocks" were cut from a list ordered by label, not by calendar, because the code concatenated inflows and outflows before blocking. Blocks that are not temporal preserve no temporal dependence, which is the only reason they exist.
Worse: the symptom had been published as a virtue. The p-value's insensitivity to block length, which I presented as robustness, was a consequence of the defect. After the fix p runs from 0.95 to 0.93 across blocks of 1 to 24 months — the conclusion did not change, but the robustness only started meaning something now. I-X12 locks both properties, and the function returns the ordering it actually used: the first version of the test rebuilt the pairing on its own and passed cleanly with the defect deliberately reintroduced.
I-X09 is not a guess about the market: it is an assembly test. Flow and return in the same month share the cause, so lag zero has to dominate. If a distant lag comes out larger, the date alignment is off — a shifted month, a reference period read as the next one, an exchange rate matched to the wrong month. It is the same class of error that produced "Technology +2809% in a week" in the sector index, and that was only caught because someone asked whether the number was possible.
I-X08 exists because a correlation is the easiest number to publish without context: |0.5| over 8 months and |0.5| over 120 months are different results and print identically. If the sample shrinks, the row disappears from the page instead of shipping with an asterisk.