CalSurprise/README.md

108 Zeilen
6,5 KiB
Markdown

2026-09-20 10:03:37 +00:00
# CalSurprise
An economic calendar release read as a number rather than a category: actual
minus forecast, standardised by each indicator's own dispersion, anchored to
the minute bar the market actually reacted in, and regressed against price to
measure what the event does to a symbol.
Companion code for the MQL5 article: https://www.mql5.com/en/articles/24850
## What it does
A calendar filter usually asks whether news is due. That treats every CPI
print as the same object, when the thing the market trades is the gap between
the number and what was expected of it. A release of 0.4% is enormous where
consensus was 0.1% and unremarkable where consensus was 0.4%, and the two are
indistinguishable to anything keyed on the event id alone.
The gap itself is not comparable across indicators. Nonfarm Payrolls surprises
are measured in thousands of jobs, Core CPI in tenths of a percent, and the
ISM surveys in index points, so `CCalendarSurprise` divides each one by the
dispersion of that indicator's own past surprises. The result is a z, and a z
of 2 means the same thing on every event in the table. Three standardisers are
available; the expanding and robust ones look only at releases before the one
being scored, so a z printed on the day carries no look-ahead.
Two things have to be right before any of that is trustworthy, and both were
found the hard way. The first is that the terminal returns calendar timestamps
in GMT until it connects and in server time afterwards, flipping silently mid
run. On a GMT+3 server that is a 180 minute error with nothing logged, so
`CalendarProbe` gates every reader on the connection flag plus two matching
back-to-back reads. It holds no state between calls, which is what lets an
indicator use it from `OnCalculate` where `Sleep` is refused.
The second is daylight saving. A historical stamp expressed with today's
server offset lands an hour from its own bar whenever the release sits across
a changeover. `CCalendarClock` recovers the broker's schedule from price
instead of assuming it: each release votes for the offset of the largest
absolute minute return near its stamp, but only when that move clears eight
times the median minute of its window, since a quiet print has nothing to say
about where the clock sits. The votes are pooled into one twelve-month table.
Pooling is the part that matters. An earlier version calibrated per event and
was visibly wrong, because one event offers about nine releases a month and
the two ISM events ended up disagreeing about which months shift despite
releasing on the same clock. Alignment also needs no forecast, only an actual,
so the voting pool is larger than the pool usable for standardisation.
Anchoring is worth more than any modelling choice here. Over eleven US events,
four symbols and four horizons, the number of cells clearing |t| >= 1.96 rises
from 16 to 49 of 176 when the offset is applied, and mean |t| rises from 0.91
to 1.41. Nothing else changed between those two runs.
The consensus feed also carries forecasts that cannot be real. Among the
payrolls rows is a consensus of 78,356 thousand jobs, against a median
absolute surprise of 121. A median and MAD scale stops rows like that
inflating sigma, but it does not remove them, and left in they arrive at
several hundred sigma and one of them alone dominates a regression. Both the
robust scale and an explicit impossible-value gate are needed; discovering
that took three rounds of an engine that reported everything as flat.
What the measurement finds is mixed, and the negative half is the more
interesting one. Core CPI m/m is significant on all four symbols, with the
sign correctly inverting on USDJPY where the dollar is the base currency:
-8.50 pips per sigma on EURUSD at t = -4.33, -9.54 on GBPUSD, +12.16 on
USDJPY, -196.6 on XAUUSD. Nonfarm Payrolls, the most watched release on the
calendar, is flat on every symbol and every horizon tested, with a maximum
|t| of 1.46, while still moving EURUSD 21.5 pips on average. It moves price
and the direction is not predictable from the surprise. That result survived
all three data fixes above, so it is reported as a finding rather than
explained away.
There is no Expert Advisor here and no claim about returns. The library
measures a response and says when it cannot find one.
## Layout
```
Include/CalSurprise/CalSurpriseTypes.mqh shared vocabulary: event, release and response structures, standardiser and rejection enums
Include/CalSurprise/CalSurpriseClock.mqh CCalendarClock: the broker's daylight saving schedule recovered from price by pooled voting
Include/CalSurprise/CalSurpriseCore.mqh CCalendarSurprise: settling gate, first-print loading, robust standardisation, response regression
Scripts/CalSurprise/SurpriseLab.mq5 the study: calibrates the clock, then writes the clock table, per-event summary and response table as CSV
Indicators/CalSurprise/SurpriseMeter.mq5 separate-window histogram of the standardised surprise per bar, magnitude and event id exposed for iCustom
Indicators/CalSurprise/SurprisePanel.mq5 chart panel of upcoming releases with their measured response profile, dimming rows whose band spans zero
```
Run `SurpriseLab.mq5` first. It writes `surprise_lab.csv` to the common files
folder and reproduces every number quoted above. Its `InpMode` default must
stay `SURPRISE_STD_ROBUST`: the expanding mean and standard deviation run is
materially weaker on the same data, Core CPI on EURUSD coming out at t = -2.40
rather than -4.33, because the impossible forecasts are still inflating sigma.
Setting `InpAnchor` to false disables the clock and reproduces the raw-stamp
comparison.
Substituting your own events is a single edit. `EventIds[]` in `SurpriseLab`
and `InpEvents` on both indicators are plain lists of calendar event ids, and
nothing downstream knows what an event means. The eleven shipped are the US
releases carrying both an actual and a forecast since May 2017, which is where
the terminal's forecast history begins; rate decisions carry no forecast at
all and cannot be used. Horizons are `InpHorizons`, in minutes.
## Disclaimer
Educational code. This measures whether a release moves a symbol, which is not
the same as a way to trade it: the significant cells describe an average
response over a sample, not an entry, and nothing here accounts for the spread
widening and execution risk around a release. The most famous event on the
calendar comes out unmeasurable. Test on your own data, broker and server
clock before drawing conclusions, since the alignment the library recovers is
a property of the broker you run it against.