How the AI Traders Decide¶
Learning objectives
After reading this page you will be able to:
- Follow one AI trader from a decision to the orders it sends
- Explain each strategy, execution style, tempo and risk setting
- Predict how a preset will behave, and write your own
Prerequisites: AI Traders.
The loop¶
An AI trader does nothing on a fixed timer. Decisions arrive at random — a
Poisson process at the tempo's decisions_per_min — so a swarm's orders do
not come in bursts. In between, it keeps its house in order. Each time it
wakes, in this order:
- Housekeeping. Cancel resting orders older than
max_order_age_sec, or more thanstale_price_ticksbehind the touch on their own side (a buy order behind the best bid, a sell order behind the best ask). - Slices due of a
twapparent order. - Protection. Place, resize or remove the protective orders for each open position (continuous trading only).
- A decision, if one is due: the strategy names a symbol, a side, an urgency and a size multiplier — an intent — or nothing. The execution style turns the intent into orders, within the risk limits.
During continuous trading the decision rate follows the day's U shape:
about twice the average after the open and before the close, half of it at
midday. A swarm's budget scales every trader's rate so the swarm as a
whole sends the actions per second it was given.
The trader reads the market from the engine's public feed — book snapshots,
trades, the session, halts — and from pm-market-sim the true values and
the news. It sends nothing for a halted symbol, nothing while the market is
closed, and only what each phase allows: resting LIMIT and ICEBERG orders in
the call phases, ATO in the opening auction, ATC in the closing auction,
everything during continuous trading.
Strategies: what to trade¶
A strategy says which way it leans with a score from −1 to 1 and buys with
probability (1 + score × strength) / 2, so it leans without becoming
predictable.
| Strategy | Looks at | Buys when | Parameters |
|---|---|---|---|
noise |
one random symbol | at random (score 0) | urgency |
trend |
sample random symbols, acts on the strongest signal |
its fast average price (30 s) is above its slow one (300 s) | strength, sample, urgency |
reversion |
the same way | the price is below its slow average | strength, sample, urgency |
value |
sample random symbols, acts on the widest gap |
the price is below its view of the true value by more than threshold |
threshold, bias_std, redraw, sample, urgency, rumour_shift |
news |
the news touching its symbols | the headline is good news | lag_sec, lag_sigma, half_life_sec, threshold, urgency, move |
Trend and reversion need about 30 seconds and 20 prices of a symbol before they have an opinion on it. Both scale the difference by the symbol's own recent volatility, so a 1% move means more in a quiet stock than in a wild one. The stronger the signal, the more urgent the intent.
Value sees each symbol's true value through a bias of its own — a
persistent error, drawn per symbol with standard deviation bias_std and
re-drawn now and then (redraw). That is why two value traders disagree,
and trade with each other. It acts once the log gap between its view and the
price exceeds threshold; the wider the gap, the more urgent and the larger
the order, up to three times its usual size. When it crosses the spread it
pays up to its view of the value less threshold — not one tick past the
touch — so a jump in the value is priced in by a few orders rather than a
thousand one-tick steps. An open rumour shifts its view by
rumour_shift × sentiment × credibility.
News hears each headline after a delay of its own (lognormal, median
lag_sec), so news traders do not all hit the book in the same millisecond.
From then on its conviction is the headline's |sentiment| — times the
credibility for a rumour — halving every half_life_sec. While conviction is
above threshold it trades the headline's symbols in the headline's
direction, urgently and in up to three times its usual size, paying up to
move × conviction (a log move) beyond the price it saw when it first acted
on the headline. A confirmation
turns a rumour into news known to be true; a retraction leaves an impulse the
other way, as large as the rumour was believable.
Execution styles: how to trade it¶
| Style | Sends | Parameters |
|---|---|---|
passive |
A LIMIT offset_ticks behind the touch (0 joins it) |
offset_ticks, tif |
marketable |
A LIMIT cross_ticks through the touch; what does not fill rests |
cross_ticks, tif |
sweep |
A MARKET, IOC or FOK order against the other side | sweep_type, cross_ticks |
iceberg |
An ICEBERG behind the touch showing visible_fraction of its size |
offset_ticks, visible_fraction |
twap |
A parent order cut into slices child orders over horizon_sec |
slices, horizon_sec, child_style |
Any style becomes marketable when the intent's urgency reaches
urgency_cross. A crossing order (marketable, IOC, FOK) is priced at the
strategy's limit when it gives one (value and news do), otherwise
cross_ticks past the touch. In the call phases — pre-open and the auctions — a trader
takes part in a decision with probability auction_participation, and then
sends a limit order straddling the reference price (with TIF ATO or ATC in
the auctions), so that buyers and sellers overlap at the uncross. Prices are
kept inside the symbol's price collar and quantities inside its order
limits: an AI trader does not send what the engine would refuse.
Tempo: how often and how big¶
| Tempo | Decisions per minute | Order size | Sizes |
|---|---|---|---|
many-small |
30 | 1–25 | mostly small |
aggressive |
20 | 20–120 | even |
cautious |
6 | 10–60 | even |
few-large |
2 | 150–700 | mostly large |
A preset can give its own instead:
tempo: {decisions_per_min: 12, size_min: 5, size_max: 80, size_distribution: small-heavy}
(balanced, small-heavy or block-heavy). The strategy's size multiplier
applies on top.
Risk: what it will not do¶
| Setting | Effect |
|---|---|
max_position |
No order that could take the position past ± this many shares, counting open orders as if filled |
max_live_orders_per_symbol |
At most this many working orders per symbol |
max_order_age_sec |
Cancel resting orders older than this |
stale_price_ticks |
Cancel resting orders this far behind their own side's touch |
protection, protection_pct |
Protective orders for open positions, protection_pct of the price away |
protection is one of none, stop (a STOP order), stop_limit,
trailing (a TRAILING_STOP) or bracket (an OCO of a take-profit LIMIT and
a STOP). It follows the position: a new fill resizes it, a flat position
removes it.
A trader that gets 25 orders refused within 10 seconds pauses for 5 seconds — the reject breaker — so a misconfigured preset cannot flood the exchange with refused orders. A cancel that lost the race with a fill, or an order in flight when the market closed, does not count.
Writing a preset¶
Start from the closest built-in preset in src/edumatcher/ai_trader/presets/
and change one part at a time:
| You want | Change |
|---|---|
| More flow | tempo |
| Takes liquidity instead of providing it | execution.style: sweep or marketable |
| Holds bigger positions | risk.max_position |
| Reacts to news faster | strategy.lag_sec (news), execution.urgency_cross |
| Disagrees more with other value traders | strategy.bias_std (value) |
Every parameter has a range; a value outside it is refused with the file and the key named. AI Traders shows how to run your preset.
The other bot: pm-mm-bot¶
pm-ai-trader trades like a directional participant. The
market-maker bot has
a different job: it posts two-sided quotes on many symbols from one
participant ID. Run one with the swarm — AI traders alone make a thin, jumpy
book.