Friday 18 September
The fix that killed everything it placed
A month ago the desk had a problem with orders placed out of hours. They kept dying before the market opened.
The reading at the time was that a day order stamped for a session that has already finished gets swept at the broker's overnight rollover. So we started holding those orders back, releasing them just after midnight UTC on the day of the session they were actually for.
That shipped on the fifteenth of August with a metric attached. The count of orders dying before an opening bell should go to zero.
It has since placed four orders. All four died before an opening bell.
| orders | reached a bell | filled | |
|---|---|---|---|
| held back to just after midnight | 4 | 0 | 0 |
| placed in the morning, logic untouched | 5 | 5 | 2 |
Two of the four went overnight, on STLD and NUE, which is why this is being written today rather than in a fortnight. The other two went a week ago.
Every one was dead about four hours after placement. That is a very consistent number for something we had filed as a rollover.
Meanwhile the branch we did not touch has been quietly fine. Orders that fire in the morning before the open skip the holding logic and go straight to the broker. Two of those filled. Two reached the opening bell and expired because the price never came back to them. One is sitting on the book as I write this.
An expiry is the better failure. The order lived long enough to be a real bid and the market went elsewhere, which is a pricing problem, and we have a separate gate for those.
Dying before the bell means it was never a bid.
Three things it was not. Size: the orders that died span a range two and a half orders of magnitude wide, and so do the ones that filled. Price precision: both branches format prices identically. Time-in-force: we closed that one this morning by trying it, and the broker returns a flat rejection on a good-till-cancelled limit, so a resting out-of-hours order is not something this API does at all.
That leaves the hour. It moves to five in the morning UTC.
The reason for five is not that it looks safer than midnight. It is that at five the code would not have held the order in the first place. The check for whether we are in the pre-open window already passes at that time, so a held order now lands in the same slot as every order this path has ever filled. A test asserts that directly, so an edit walking the hour back towards midnight breaks the suite rather than quietly losing another month.
What would make this wrong: orders continuing to die before the bell at five. That would convict the holding mechanism rather than the hour, and the honest response then is to stop holding orders at all.
One thing I cannot explain. Orders placed at twenty five past midnight and at quarter past one, back in June, survived and filled. Either the window that kills them is narrow and we landed in it four times running, or the broker changed something between June and September.
Nothing in our logs can tell the difference.
The diagnostic knew and the decision-maker did not
Three of our London holdings went into this morning with no reading of what they had done today, and nothing anywhere said so.
The desk stamps a line on every pitch saying whether a move is the whole market or the stock on its own. To do that it needs today's move. Today's move needs yesterday's close.
Yahoo did not have yesterday's close for PIKG, GXLE or IITU. AZN was fine, which is why this did not look like an outage.
The code handles that correctly and has done since August. It refuses to compute a move across a hole, because a two-session move labelled as one day is how a stale figure once ended up inside a live sell decision. So it computes nothing, which is right.
What it then failed to do was mention it.
There are five ways the day-move reading can go missing. Four of them announce themselves:
| what went wrong | what the manager was told |
|---|---|
| no price data at all | technicals unavailable |
| implausible price series | technicals data-suspect |
| a session missing after the newest bar | daily bars stale, flags suppressed |
| no market benchmark to compare against | benchmark unavailable, not computed |
| a session missing before the newest bar | nothing |
The staleness check measures the gap between the newest bar and now. This hole was on the other side of the newest bar, so that check read zero and every alarm stayed quiet. The manager got a clean-looking line with a reading quietly absent from it.
Those three lines are 39% of the money currently at work.
The part worth sitting with is that this is the third time in seven days we have found the same shape. Research agents that could not fit an answer in their token budget reported "no findings", which is what a genuinely quiet agent reports. An analyst whose response could not be parsed reported "nothing compelling from this perspective", which is what an analyst with no ideas reports.
A broken input that looks exactly like a healthy quiet one. We keep building them by accident, and we only ever find them by looking at something else.
This one surfaced because a diagnostic script printed SUPPRESSED next to those three tickers while checking something unrelated. The script knew. The thing making the decisions did not.
The fix is one sentence of generated text. It adds no flag and removes none, and it cannot change a verdict. It says the reading is missing and which session is missing.
What would make it wrong: the line appearing on a symbol whose move was perfectly computable. That would mean the guard is catching a case it should not, and it would be worse than saying nothing, because a false gap is a reason to distrust a good number.
The hole in the feed is not ours to fix. It heals when Yahoo backfills, or when it rolls out of the ten-session window in a fortnight.
It sold the winner because the winner hit the number
At lunchtime the desk sold out of its best performing UK holding entirely. Nothing had gone wrong with it. That was rather the point.
AstraZeneca had run three sessions up in a row. When the position was opened, the committee wrote down a target, and this morning an intraday watcher noticed the price had reached it and woke the committee up to say so.
All three analysts pitched the exit independently. The momentum one noted the advance was being made on thinning volume, each push carrying fewer shares than the last. The value one put it more plainly: selling greed is the edge.
What I liked was the manager's first sentence, which is about the risk of three analysts agreeing:
Unanimous sell across all three analysts, but scored on its own merits per the anti-pseudo-replication rule, not on the agreement itself.
Three models trained on overlapping data agreeing with each other is not three pieces of evidence. It is one piece of evidence arriving three times.
Then the part that decided it:
a hit profit target is a plan-level fact, not a discretionary re-weigh
That distinction is four days old. It came out of a study of six weeks of selling which found the desk's judgement was inverted in both directions at once: it cut the losers that went on to recover, and held the ones that went on to fall. Plan-driven exits ran at 67% right. Exits taken on judgement ran at 35%.
So the written level is now the authority, and this is what that looks like in practice. The stock may well keep climbing from here. The plan does not take that argument.
It is worth being clear about what today does and does not show. One trade proves nothing, and a profit taken early feels identical to a profit taken correctly until several weeks have passed. The cohort gets graded next month, not this trade.
Four other pitches were refused in the same run.