Exact Order Amounts: Decimal Steps, Rounding and Real Residual Balances

한국어로 읽기

TECH INSIGHT / EXPLAINED

Use an explicit numeric contract at the order and ledger boundary. Binary floats remain useful for statistical analysis, while exact decimal amounts or scaled integers make exchange increments and accounting rules easier to enforce.

Three colleagues compare approximate numbers with exact quantity steps and price boundaries.
AI-generated educational illustration. Three colleagues compare approximate numbers with exact quantity steps and price boundaries.

Representation is different from display

Python float usually uses binary64, which cannot exactly represent 0.1. Formatting a float can hide a residue without correcting every earlier comparison or rounding decision. The practical error depends on magnitude and operations; there is no justified claim that it costs a fixed amount per day. Decimal constructed from a float preserves that float’s approximation, so parse original decimal text or scaled integers at the boundary.

A step is not just a decimal-place count

Decimal.quantize aligns an exponent. It does not round arbitrary values to multiples of 0.05, 0.25 or 5. The example takes an exact rational quotient to find the number of whole steps, then uses sufficient Decimal precision for multiplication. It supports nonnegative quantities and bounded decimal inputs, not every numeric representation.

from decimal import Decimal, localcontext
from fractions import Fraction

def floor_step(value_text, step_text):
    if not isinstance(value_text,str) or not isinstance(step_text,str):
        raise ValueError("decimal strings required")
    v, step = Decimal(value_text), Decimal(step_text)
    if not v.is_finite() or not step.is_finite() or v < 0 or step <= 0:
        raise ValueError("finite nonnegative value and positive step required")
    # Bound untrusted input complexity for this teaching example.
    for x in (v,step):
        if len(x.as_tuple().digits)>40 or abs(x.as_tuple().exponent)>40:
            raise ValueError("outside supported precision range")
    ratio = Fraction(v)/Fraction(step)  # exact integer quotient
    ticks = ratio.numerator//ratio.denominator
    with localcontext() as ctx:
        ctx.prec = max(28,len(str(ticks))+len(step.as_tuple().digits)+2)
        return Decimal(ticks)*step

assert Decimal("0.1")+Decimal("0.2") == Decimal("0.3")
assert Decimal("1.23").quantize(Decimal("0.05")) == Decimal("1.23")
assert floor_step("1.23","0.05") == Decimal("1.20")
assert floor_step("0.30000000000000004","0.001") == Decimal("0.300")
print(format(floor_step("1.23","0.05"),"f"))  # 1.20

Rounding direction expresses an order constraint

Rounding a buy limit down preserves a maximum acceptable price; rounding a sell limit up preserves a minimum acceptable price. Both can reduce execution probability. Moving a limit toward the opposing quote may improve marketability but changes the economic constraint. There is no universal “round against yourself for safety” rule.

Rounding quantity down avoids exceeding the raw quantity, but you still need a fee reserve, available balance and the venue’s min/max quantity and notional checks. A raw price converted to int before rounding has already lost its fractional part. If price bands change tick size, validate the resulting price under the band it lands in.

Treat venue rules as versioned data

For Binance Spot, distinguish price/quantity filters and the relevant notional and market-order rules. Read the actual filter type and documented fields; do not guess that similarly named fields from other APIs are interchangeable. Cache rules with retrieval time and handle a rejection by revalidating, not silently retrying a larger order.

On-chain base units can use integers, but each token has its own scale and arithmetic still needs overflow and division-rounding rules. In Go, big.Float remains binary; big.Rat represents rational values. Choosing a numeric type does not eliminate the need to specify rounding at every conversion.

Do not erase real dust with an arbitrary epsilon

Exact zero comparison is valid for exact integers or Decimal ledger quantities. Approximate analytical floats can use a scale-aware tolerance, but “below the minimum tradable amount” is a business state, not necessarily zero ownership. Reconcile residual balances explicitly. Serialize a validated amount using the endpoint’s accepted format; decimal places, increment and total value are separate requirements.

Sources and further reading

Python floating point · Python Decimal · Exact rational values · Binance Spot filter specification

Content and examples reviewed: 2026-09-29

댓글