Skip to content

Static typing

Static typing checks source code against type rules. See why Python annotations, runtime behavior and validation remain separate safeguards.

Static typing checks whether expressions, arguments and return values fit a type system by analysing source code before execution. In quant software, it can expose a string flowing into an integer calculation before a research run or trade workflow reaches that call.
Python remains dynamically typed. Its optional type annotations supply information for static analysis; adding them does not make the interpreter enforce the declared types. Python's typing system supports gradual typing: checked code can coexist with code whose types are unknown. Python's typing specification explains these distinctions.

One function, two different questions

Consider a deliberately small function:

def twice(x: int) -> int: """Add a value to itself.""" return x + x print(twice(2), type(twice(2)).__name__) print(repr(twice("2")), type(twice("2")).__name__) # 4 int # '22' str
The first call fits the declared contract. The second supplies a string where int is expected. A configured static checker can flag that mismatch when it analyses this call; its coverage and settings matter. This example shows runtime output, rather than a checker diagnostic.
At runtime, Python executes x + x. Integer addition produces 4; string concatenation produces '22'. No annotation-driven conversion or rejection occurs. The Python typing documentation explicitly separates annotations from runtime enforcement. These outputs were checked with Python 3.14.4.
Static checking compares the string argument in twice("2") with the declared int type. At runtime, twice(2) returns integer 4 and twice("2") returns string "22". Explicit validation is a separate check.
Static checking compares the string argument in twice("2") with the declared int type. At runtime, twice(2) returns integer 4 and twice("2") returns string "22". Explicit validation is a separate check.
Static analysis checks the source against a declared contract. Runtime execution follows the actual objects' operations; a validation guard must be written or supplied separately.

A valid type can still be an invalid trade input

Suppose a fictional workflow only accepts a positive whole number of lots and then doubles it. A negative integer satisfies the type int but violates that business rule. An explicit runtime guard can enforce the rule:
def double_lots(lots: int) -> int: """Double a positive exact-integer lot count.""" if type(lots) is not int: raise TypeError("lots must be an exact int") if lots <= 0: raise ValueError("lots must be positive") return lots + lots print(double_lots(3)) # 6 for candidate in ("3", -3): try: double_lots(candidate) except (TypeError, ValueError) as error: print(f"{type(error).__name__}: {error}") # TypeError: lots must be an exact int # ValueError: lots must be positive
The exact-type policy here deliberately excludes booleans and integer subclasses. Python's bool is a subclass of int, so a broader isinstance(lots, int) check alone would admit True. That is a runtime policy choice, separate from the signature. Python's data model documents the relationship.
Likewise, a plain float annotation cannot establish whether a rate is annual or monthly, a price uses the intended currency, or observations arrive in timestamp order. Those contracts need suitable domain representations, validation and tests.

Make checking part of the workflow

Annotations become useful safeguards when a checker actually analyses the relevant code. Add checks to the editor and continuous integration, decide how much untyped code is allowed, and inspect diagnostics. Any represents a statically unknown type and limits what the checker can establish. A clean report must be interpreted in light of that coverage. The current typing specification describes this gradual boundary.
Keep runtime validation where outside data enters the system, and use unit tests for numerical results and business rules. Static checking can catch incompatible calls without exercising them; tests check selected executions. Each contributes different evidence about the program.
For a first step, Functions develops explicit input–output contracts; the full primer lesson is free with sign-in. Data Ingestion and APIs shows boundary validation for market-data payloads; the full lesson requires membership and has a public preview.

Sources

Last updated
  • python
  • type systems
  • software quality
  • quant development
← Back to the glossaryBack to top ↑