When Indicators Are Not Functions: Defining Quant Operators in Rust
Source: Dev.to
Where the problem comes from
Most technical indicators today are modeled as functions: they take an array and return a value or another array.
This breaks down once indicators need:
- Persistent state (rolling, EMA)
- Sensitivity to time ordering
- Consistent semantics across batch and streaming
- Composition and scheduling
A pure‑function model cannot express these constraints. In Rust, the limitation becomes explicit:
- Ownership forces state responsibility to be explicit
- Implicit globals are unacceptable
- Performance requires clear execution boundaries
Core Definition: Operator
In this document, an Operator is defined as:
A schedulable execution unit that processes input data under explicit execution semantics.
The term “function” is intentionally avoided.
An operator must:
- Have explicit input/output semantics
- Be invoked as a semantic execution unit
- Own its state boundaries explicitly
- Fail in a structured, observable way
An operator must never:
- Make decisions about scheduling
- Manage system resources
- Perform system‑level recovery
Execution as a semantic unit
An Execution is defined as:
An indivisible invocation of an operator under a given input and execution context.
Key properties:
- Execution is semantic, not syntactic
- Operators may execute multiple times
- Relationships between executions are out of scope here
Failure is not panic
Operator failure is not a panic. It is a first‑class execution result that may propagate outward. Whether a failure halts the system is the responsibility of the caller.
Minimal Reference Implementation (v0.1)
The following code exists to prove that the above definitions are implementable in Rust.
pub trait Operator {
type Error;
fn execute(&mut self, input: Input) -> Result;
}
Notes:
&mut selfexplicitly allows internal state.- No time, batch, or scheduling semantics are included yet.
- The implementation is intentionally minimal.
A trivial stateless example:
pub struct Sum;
impl Operator for Sum {
type Error = ();
fn execute(&mut self, input: &[f64]) -> Result {
Ok(input.iter().sum())
}
}
Its purpose is conceptual, not functional.
Non‑goals
This article intentionally does not define:
- State models
- Time semantics
- Batch consistency
- DataFrame or Polars integration
Engineering repository:
Contact: