The labels were the hard part
I have a huge number of rows of NSE data and spent most of a year on feature engineering before admitting the real problem was upstream. Deciding what a training example should be labelled — given that execution takes most of a second and costs must clear before a trade is worth taking — turned out to be the whole game.
Several billion rows across thousands of tickers over ten months. The obvious thing to do with a dataset that size is to build features and train something. That is what I did for a long time, and it was the wrong emphasis. The binding constraint was never the features or the architecture. It was the label — the question of what number should sit in the target column of a training example, and why.
The label has to survive your execution latency
The first thing that breaks the naive approach is timing.
My order round trip is roughly 400 to 900 milliseconds. The data arrives at about one sample per second. So a label like "the price five seconds from now" is not actually available to me — by the time I act on a signal, most of a second has already gone. The realistic version is "the price five seconds from now, minus the half-second to one second I spent getting filled."
This is not a rounding detail. It is the difference between a strategy that works in backtest and one that works live, and it disqualifies an entire horizon range. Anything under about ten seconds is noise for a system with my latency. No amount of model quality recovers it, because the information is being consumed by the execution gap rather than by any failure of prediction.
That single constraint eliminates most of what makes tick data seem exciting. The sub-second structure is real and it is not mine to trade.
The label has to clear costs before it is worth predicting
The second constraint is the cost floor, and it is the one I find people skip most often.
A label of the form "did the price go up" is nearly useless, because the majority of upward moves are smaller than the round trip cost of capturing them. The correct label is not directional. It is: did the price move far enough, in my favour, within a window I can actually hold, to clear the spread and the full cost stack. That threshold is computable exactly rather than assumed, which is why the cost work had to come first.
This reframes the target from a regression on returns into something closer to a filtered binary event, and it makes the class balance brutal. Most moments are not trade opportunities. A labelling scheme that admits this produces a dataset where the positive class is rare — which is correct, and which most people then "fix" with resampling, destroying the very property that made the label honest.
Excursions, not returns
What eventually worked as a labelling primitive was the excursion pair: for a hypothetical entry at each point, the maximum favourable excursion and maximum adverse excursion over a forward window. How far in your favour did it go, and how far against you, before the window closed.
This is a better description of a trade than forward return, for a reason that is obvious once stated: a trade that ends flat after going 2% in your favour and a trade that ends flat after going 2% against you are the same forward return and completely different events. One was an opportunity you mistimed. The other nearly stopped you out. Any label that maps them to the same value has thrown away the information an exit model needs.
Generalising this gives a family of labels rather than one: excursion measured over multiple horizons, normalised by prevailing volatility, thresholded against the exact break-even move for the intended position size.
The aggregation question I got wrong
At one point I asked whether the bar aggregation step was necessary at all. Why build minute bars, why not compute excursion labels directly on raw ticks? Surely aggregation throws away information.
The answer, which I did not expect, is that skipping aggregation makes things worse in both directions.
It is slower, obviously — forward-looking excursion computation means scanning ahead from every row, and doing that on 200 million rows instead of 9.4 million is twenty times the work for no gain. But the conceptual problem is the real one. At one sample per second, consecutive ticks are enormously redundant. The price at second five and second six are usually identical. Labelling both gives you two nearly identical training examples with nearly identical targets. That is not more information. It is the same information repeated, which inflates your apparent sample size while adding correlation to a training set whose independence you were probably already assuming.
So aggregation is a compression step, roughly twenty to one in my case, and it is doing real statistical work rather than merely saving compute.
The valid form of the question is different, and better: are fixed-time bars the right unit? They are not obviously optimal. You can aggregate by volatility, opening a new bar whenever cumulative absolute movement exceeds a threshold, so each bar represents equal activity rather than equal time. You can aggregate by volume, so each bar represents equal participation. You can sample on events — a spread widening past a threshold, order book imbalance flipping sign. All of these give you bars that are more uniform in information content than clock time does, and clock time is only the default because it is the easiest to implement.
Hindsight labels and the causality trap
The most seductive idea in this whole area, and the one that needs the most care.
Given a full day of prices, you can compute the optimal set of trades by dynamic programming — the best k entry-exit pairs, exactly. I built this. It produces beautiful plots. It tells you the theoretical maximum available on that day, which is genuinely useful as a denominator: it tells you what fraction of the available money any real strategy captured.
The trap is using it as a label. Those trades are computed with full knowledge of the future. A model trained to reproduce them is being asked to predict something that was defined using information it will never have. It will learn to be confident in exactly the situations where hindsight makes the answer obvious and the present makes it ambiguous.
Converting a hindsight-optimised oracle into a causal labelling engine — one that assigns labels using only data available at that moment — is the real problem, and it is not a small modification. It is a different algorithm.
Meta-labelling, and where the human effort goes
The structure I ended up with has two stages. A primary rule proposes candidate trades. A second model then classifies each candidate as worth taking or not.
The honest part of this story is where the labour lives. Building the meta-labelling dataset meant hours of manually tweaking heuristics to filter hundreds of candidate trades down to a handful, then applying the cost threshold to cut further. That is not glamorous and it does not automate. It is the part where domain judgement enters, and it is why I no longer believe the framing where you point a large model at raw data and let it discover structure.
The opinion
In applied machine learning on market data, label design is where nearly all the leverage and nearly all the self-deception live — and the reason people work on features and architectures instead is that those have public leaderboards and label design does not.
You can tell a model is being asked the wrong question when improving it does not improve anything downstream. That symptom is common and it is almost always diagnosed as a modelling problem, because modelling is where the tools are. But a model can only be as good as the correspondence between its target and the thing you actually want. If the label ignores your latency, the model is predicting moves you cannot reach. If it ignores costs, it is predicting moves that are not worth reaching. If it comes from hindsight optimisation, it is predicting something that does not exist causally.
None of those failures show up in validation loss. All of them show up in the P&L, months later, as a model with excellent metrics that makes no money — which is a failure mode I have written about separately, and which I now think is usually a labelling failure wearing a modelling costume.