Quant Python: Architecting Autonomous Trading Systems

Quant Python: Architecting Autonomous Trading Systems

Day 31 · Core Order Architecture · Python 3.11+ · Alpaca Paper Trading

Python Quant's avatar
Python Quant
Apr 07, 2026
∙ Paid

The “Round to Two Decimals” Trap

Every junior quant makes this mistake exactly once.

They build a momentum signal. It fires. They compute a limit price from the mid-quote, call round(signal_price, 2), and submit. Backtests are clean. Paper trading looks fine. Then they hit a biotech name trading at $0.47 with a tick size of $0.0001, or an ETF in a crossed-market condition at 9:30:01 AM. The order rejects. Or worse — it fills instantly at a price 4 bps worse than intended, because their “limit” order was actually crossing the spread.

Here is what that looks like in code:

# JUNIOR TRAP: Do not ship this
def submit_entry(symbol: str, signal_price: float, qty: int):
    limit = round(signal_price, 2)          # ← float rounding, no tick awareness
    if limit < get_current_price(symbol):   # ← synchronous price fetch in hot path
        client.submit_order(                # ← no spread check, no filter validation
            symbol=symbol,
            qty=qty,
            side='buy',
            type='limit',
            limit_price=limit,
            time_in_force='day'
        )

Three failure vectors in twelve lines of code. Let’s take them apart.


The Three Failure Modes

Failure 1: Floating-Point Tick Quantization

round(price, 2) is not tick quantization. It is decimal truncation. When tick_size=0.01:

>>> 0.07 / 0.01
6.999999999999999          # Python float, not 7
>>> round(0.07, 2)
0.07                       # Looks right, but...
>>> 0.07 == 7 * 0.01
False                      # IEEE 754 drift

Alpaca and most exchanges validate price modulo tick_size. A price of $142.135 against tick_size=0.01 yields 142.135 % 0.01 ≈ 4.97e-15 in float arithmetic — which looks non-zero to the exchange’s integer comparator and triggers a 422 Unprocessable Entity. You need Decimal arithmetic with an explicit rounding mode.

In plain English: IEEE 754 is the standard for how computers store decimal numbers in binary. The number 0.07 cannot be stored exactly in binary — just like 1/3 has no exact decimal form. That tiny error (6.999... instead of 7.0) is enough to fail an exchange’s price filter.

Failure 2: Spread Crossing — The Accidental Market Order

If your buy limit price is greater than or equal to the current ask, your order fills immediately at the ask (or sweeps levels above it). You have submitted a limit order with a price ceiling that is already satisfied. This is:

  • Fine if intentional (an IOC aggressive fill)

  • A latent slippage bomb if your signal_price logic is computing above the ask systematically

The exchange will not reject it. You will see it in your fills, not your logs. That is what makes it dangerous.

Failure 3: Price Filter Blindness

Alpaca enforces per-symbol price filters. Submitting a price outside [min_price, max_price] or with the wrong tick_size returns a 422 that your try/except swallows, leaving your position unhedged while you think you are filled. In a volatility event, you will do this 40 times before the rate limiter kicks you off.


User's avatar

Continue reading this post for free, courtesy of Python Quant.

Or purchase a paid subscription.
© 2026 Python Quant · Privacy ∙ Terms ∙ Collection notice
Start your SubstackGet the app
Substack is the home for great culture