Skill, luck, and a coin flip

There is one investment tip per person willing to give it. At a bar, in a meeting, over dinner: someone states a conviction with the poise of a man who was right once, the table nods politely, and the next course arrives. What bothers me isn’t the poise — it’s that nobody ever asks the only question that would matter, which is how you would know the tip was bad.

Since I never found an elegant way to ask it at the table, I turned it into a tool.

Compare it — yes, but to what?

Objectifying means comparing, and everything hangs on what you compare against.

A raw return says nothing at all. Eighteen per cent over three years is remarkable or frankly mediocre depending on what the rest of the world did during those three years. So: first reference, and the obvious one, the market. Lay a broad index underneath and see whether the strategy beat simply buying a bit of everything and leaving for the holidays.

Except that isn’t enough, and this is where it gets interesting. A strategy that beats the index may well have beaten nothing at all: it may have been lucky. A second reference is missing, and that second reference is chance itself — a portfolio held to exactly the same constraints, the same caps, the same number of positions, but where every pick is drawn at random.

A strategy that beats the index without beating the coin has demonstrated nothing. It got lucky, and luck does not repeat on request. It is the only way I know to separate skill from luck, and it is why the coin flip has been in the service since its very first day, before it had any interface at all.

The attentive reader will recognise the phantom that haunts Midas, where ten agents are judged by the same coin. The difference isn’t technical, it’s a difference in kind: there, it is a guarantee I give — my promise never to show you a flattering curve without its judge beside it. Here, it is an instrument I hand over. You don’t have to take my word for anything; you can run the numbers yourself and contradict me on the spot.

What sits under the hood

The engine already existed. It was running for the Midas agents, in Python, with bt handling the deterministic side. What it lacked was a front door: a service you can question from outside, without cloning a repository or installing a thing.

That door is a small FastAPI application wrapping the engine and exposing it behind two verbs — give me the catalogue of available strategies, run me this backtest. It lives in its own container on Cloud Run, with its own shared key so nobody runs my compute on my behalf.

Three details of the build strike me as more telling than the overall architecture.

The first is about trading calendars. As soon as a universe mixes exchanges, public holidays stop lining up: a German stock has no row on 1 May while New York trades as usual. Left alone, those gaps propagate into the returns and manufacture moves that never happened. So the last known price is carried forward across the missing days — a tiny gesture, a handful of lines, that separates a result from a fiction.

The second concerns the trades shown at the end. To say which were the best and the worst, every sale has to be matched to a purchase, and the matching order changes the answer. First in, first out: the convention is arbitrary, but it is stated, and a stated convention beats a number nobody can account for.

The third is a matter of manners. The strategy catalogue isn’t fetched when the page opens: it is photographed at build time and written straight into the HTML. The form is therefore complete before a single request has left the browser, with its eight strategies, its twelve universes and its date bounds. (Eight, where the repository holds fifteen. The other seven are not an editorial cut, whatever a generous reader might assume — they are pending things, and they will surface when I put time back into the tool.)

What free actually costs

The whole portfolio runs on free tiers, as much on principle as out of frank stinginess. On Cloud Run, free has a precise consequence: when nobody visits, the service shuts down. It isn’t asleep, it no longer exists. The next visitor pays for the restart.

That restart used to take between two and four minutes. The proxy connecting my site to the service gives up after thirty seconds, so the visitor got an error for a request that, on Google’s side, eventually succeeded perfectly well — two or three minutes later, once nobody was watching any more.

I spent a while hunting for the guilty piece of application code before reading the logs in the right order. The server starts in two milliseconds. There was nothing in my code to optimise: the entire delay was transport. The container image was stored in the United States, and the service runs in Europe. Four hundred and fifty-six megabytes hauled across the Atlantic on every wake-up. It was never a weight problem, it was a geography problem — and no amount of optimisation corrects an ocean.

With the registry moved to the right side, and the container relieved along the way of everything doing nothing in it — a compiler no request ever invokes, a caching library caching nothing useful — the wake-up dropped to forty-four seconds.

With one lesson picked up on the way, which I record here because it cost me a failed deployment. I had also deleted the test suites bundled inside the Python packages, a hundred-odd megabytes of obvious dead weight in production. The container refused to start: numpy reaches for its own test module as it imports itself. My forty-three tests passed happily throughout — they run against my local environment, not against the image. No test suite could have caught it. Only a container that starts could.

Measured properly — letting the service shut down, then coming back twenty-one minutes later — the request goes through in thirty-eight seconds. The first visitor to a dead service therefore waits some forty seconds and gets an answer, where they used to get an error. The page still tells them it is waking something up, and retries by itself if the first attempt doesn’t land — because a measurement taken once is not a guarantee, and I would rather have an interface that tells the truth about what it is doing. That is the advertised price of free, and advertising it strikes me as better than dressing it up.

Two products, one small experiment

I had known for a long time that Midas held two distinct things: agents that deliberate on one side, a calculation engine on the other. No technical signal told me that. A discomfort did — no longer being able to describe the project in one sentence.

What was missing wasn’t the decision, it was the occasion. And the occasion, in my case, looks like this: whenever a new model ships, I try it out on a review of my portfolio. It is genuine tidying and an honourable excuse to play with the new toy at the same time, and I see no reason to choose between the two. I compare nothing, I keep no scoreboard; I simply notice that my tools have grown finer as the models improved.

Which produces a loop I find rather fair on the times we live in. Building has become easy with AI, so I start maximalist, with far too many ideas, and I accumulate faster than I tidy. And what eventually makes me tidy is, again, AI — a model that ships and has to be tried on something.

That leaves the confession. I don’t use this tool. The occasion doesn’t come up often, it wasn’t really ready until this morning, and above all it is a small experiment, not an everyday object. I built the instrument that answers the question from the bar, and it changes nothing about what happens at the bar. Which is exactly what I wrote about Untilt: knowing you are biased isn’t enough to stop being biased, and knowing you could objectify something doesn’t make you do it.

An experiment doesn’t have to become a habit to have been worth the trouble.

P.S. — A word that isn’t boilerplate. What the tool displays are past results computed on historical data, and a strategy that would have worked beautifully yesterday says strictly nothing about tomorrow. This is not investment advice, and it is close to the opposite: an instrument for doubting advice, including the advice you give yourself.

Open the app →

My newsletter

A personal letter, in French, on books, ideas, and whatever else holds a curious mind’s attention.