The checks are a small set of hard limits evaluated in the order path, and the
rejection must be specific enough to act on.
Client FUND-17, limits in force
max order value 2,000,000 GBP
max order quantity 100,000 shares
max order as % of 20-day ADV 5 %
price collar vs last trade ±10 %
max net long position, this name 5,000,000 GBP
daily gross traded value 25,000,000 GBP
restricted list instrument must not be on it
Incoming: BUY 400,000 VOD.L @ 79.90, last trade 72.35
order value 400,000 x 0.7990 = 319,600 PASS
order quantity 400,000 > 100,000 FAIL
% of ADV 400,000 / 6,100,000 = 6.6% FAIL
price collar 79.90 vs 72.35 = +10.4% FAIL
net long after 2,910,000 + 319,600 PASS
daily gross 18,400,000 + 319,600 PASS
restricted list not present PASS
Reject: 35=8 39=8 103=99 58=QTY_LIMIT,ADV_LIMIT,PRICE_COLLAR
The design point is that these are cheap, absolute and in the synchronous path.
Every one is a comparison against a number held in memory, deliberately so,
because a check requiring a database call or a service hop would have to be
skipped under load — and load is when a malfunctioning algorithm is most likely
to be firing.
Reporting all three failures rather than the first is a deliberate choice. A
trader who fixes the quantity and resubmits into a price collar rejection will
resubmit again, and each cycle is more messages at a moment when the system is
already unhappy. Returning the full set costs nothing and converts three round
trips into one.
The limits themselves are a mixture of two different intentions and it is worth
saying so. Order value, quantity and the price collar are fat-finger checks: they
catch a mistyped order that no legitimate strategy would send. Position and daily
gross limits are credit and mandate checks: they are about how much this client
is permitted to do in total, and they need to be updated by fills as the day
progresses, which makes them stateful and therefore the harder half to get right
under concurrency.
The check nobody mentions is the message rate limit per session, which is what
actually stops a looping algorithm, since a runaway sending one valid small order
per microsecond passes every limit above until the position check catches up.