of_signals 0.2.0

Signal modules and gating policy for the Orderflow engine
Documentation

of_signals

of_signals contains strategy modules that transform analytics snapshots into stable directional state. It is intentionally separated from ingestion/runtime plumbing so strategy logic remains easy to test and evolve.

Core API

  • Trait: [SignalModule]
  • Gate result: [SignalGateDecision]
  • Built-in modules:
    • [DeltaMomentumSignal]
    • [VolumeImbalanceSignal]
    • [CumulativeDeltaSignal]
    • [AbsorptionSignal]
    • [ExhaustionSignal]
    • [SweepDetectionSignal]
    • [CompositeSignal]

New In 0.2.0

Relative to the 0.1.x line, of_signals now includes a broader built-in catalog:

  • [VolumeImbalanceSignal]
  • [CumulativeDeltaSignal]
  • [AbsorptionSignal]
  • [ExhaustionSignal]
  • [SweepDetectionSignal]
  • [CompositeSignal]

The original trait contract stayed stable, so downstream custom modules do not need a migration.

Public API Inventory

Public types:

  • [SignalGateDecision]
  • [SignalModule]
  • [DeltaMomentumSignal]
  • [VolumeImbalanceSignal]
  • [CumulativeDeltaSignal]
  • [AbsorptionSignal]
  • [ExhaustionSignal]
  • [SweepDetectionSignal]
  • [CompositeSignal]

Public constructors:

  • [DeltaMomentumSignal::new]
  • [VolumeImbalanceSignal::new]
  • [CumulativeDeltaSignal::new]
  • [AbsorptionSignal::new]
  • [ExhaustionSignal::new]
  • [SweepDetectionSignal::new]
  • [CompositeSignal::new]

[SignalModule] trait methods:

  • on_analytics(&AnalyticsSnapshot)
  • snapshot() -> SignalSnapshot
  • quality_gate(DataQualityFlags) -> SignalGateDecision

Signal output uses of_core::SignalSnapshot and states such as LongBias, ShortBias, Neutral, and Blocked.

SignalModule Contract

[SignalModule] is the extension point for strategy logic.

  • on_analytics(&AnalyticsSnapshot) consumes the latest analytics state and updates internal signal state.
  • snapshot() returns the last computed SignalSnapshot.
  • quality_gate(DataQualityFlags) tells the runtime whether the signal should be blocked under the current feed-quality conditions.

Recommended implementation rules:

  • keep updates deterministic so replay and live runs match
  • include human-readable reason text in the snapshot when practical
  • use confidence consistently so downstream hosts can compare modules
  • block aggressively on stale, gap, or degraded feed conditions when a strategy should not trade through uncertainty

Constructor Parameter Reference

  • [DeltaMomentumSignal::new] takes an absolute delta threshold.
  • [VolumeImbalanceSignal::new] takes an absolute session buy_volume - sell_volume threshold.
  • [CumulativeDeltaSignal::new] takes an absolute cumulative_delta threshold.
  • [AbsorptionSignal::new] takes:
    • threshold: directional pressure required before checking for absorption
    • price_band: max distance from POC/value location used by the heuristic
  • [ExhaustionSignal::new] takes an absolute delta threshold for stalled reversal detection.
  • [SweepDetectionSignal::new] takes:
    • threshold: directional delta threshold
    • breakout_ticks: minimum break outside value area
  • [CompositeSignal::new] takes owned child modules and aggregates their votes.

Delta Momentum Strategy

[DeltaMomentumSignal] is a reference implementation that:

  • emits LongBias when delta >= threshold
  • emits ShortBias when delta <= -threshold
  • emits Neutral otherwise
  • emits Blocked in runtime when quality gate fails

Volume Imbalance Strategy

[VolumeImbalanceSignal] is a reference implementation that:

  • compares session buy_volume - sell_volume against an absolute threshold
  • emits LongBias when buy pressure dominates
  • emits ShortBias when sell pressure dominates
  • remains Neutral while the session imbalance stays inside the configured band

Cumulative Delta Strategy

[CumulativeDeltaSignal] is a session-bias module that:

  • compares cumulative_delta against an absolute threshold
  • emits LongBias when session delta remains strongly positive
  • emits ShortBias when session delta remains strongly negative
  • remains Neutral while cumulative delta stays inside the configured band

Absorption Strategy

[AbsorptionSignal] is a heuristic module that:

  • looks for strong directional delta that fails to move price away from POC
  • emits LongBias on sell absorption near POC
  • emits ShortBias on buy absorption near POC

Exhaustion Strategy

[ExhaustionSignal] is a heuristic reversal module that:

  • looks for strong directional delta that stalls back near POC
  • emits ShortBias when buying appears exhausted
  • emits LongBias when selling appears exhausted

Sweep Detection Strategy

[SweepDetectionSignal] is a breakout module that:

  • looks for strong delta alongside a break outside value area
  • emits LongBias on upside sweeps
  • emits ShortBias on downside sweeps

Composite Strategy

[CompositeSignal] combines multiple child modules and:

  • updates each child on the same analytics snapshot
  • emits the majority directional view when one side has more votes
  • remains Neutral when there is no directional majority

Output Interpretation

All built-in modules return SignalSnapshot, which downstream runtimes and bindings expose unchanged.

  • state is the durable directional state
  • confidence is a normalized score chosen by the module
  • reason is short human-readable rationale
  • quality_flags echoes the quality context that contributed to blocking or caution

Built-in modules are intentionally heuristic rather than venue-specific alpha models. They are meant as production-ready references and defaults, not as the only strategy approach.

Quick Example

use of_core::{AnalyticsSnapshot, SignalState};
use of_signals::{DeltaMomentumSignal, SignalModule};

let mut signal = DeltaMomentumSignal::new(100);
signal.on_analytics(&AnalyticsSnapshot {
    delta: 150,
    ..Default::default()
});

let snapshot = signal.snapshot();
assert!(matches!(snapshot.state, SignalState::LongBias));

Alternative Module Example

use of_core::{AnalyticsSnapshot, SignalState};
use of_signals::{SignalModule, VolumeImbalanceSignal};

let mut signal = VolumeImbalanceSignal::new(100);
signal.on_analytics(&AnalyticsSnapshot {
    buy_volume: 350,
    sell_volume: 200,
    ..Default::default()
});

let snapshot = signal.snapshot();
assert!(matches!(snapshot.state, SignalState::LongBias));

Composite Example

use of_core::{AnalyticsSnapshot, SignalState};
use of_signals::{
    CompositeSignal, CumulativeDeltaSignal, DeltaMomentumSignal, SignalModule,
    VolumeImbalanceSignal,
};

let mut signal = CompositeSignal::new(vec![
    Box::new(DeltaMomentumSignal::new(100)),
    Box::new(VolumeImbalanceSignal::new(100)),
    Box::new(CumulativeDeltaSignal::new(150)),
]);
signal.on_analytics(&AnalyticsSnapshot {
    delta: 200,
    cumulative_delta: 250,
    buy_volume: 400,
    sell_volume: 100,
    ..Default::default()
});

let snapshot = signal.snapshot();
assert!(matches!(snapshot.state, SignalState::LongBias));

Quality Gate Example

use of_core::DataQualityFlags;
use of_signals::{DeltaMomentumSignal, SignalGateDecision, SignalModule};

let signal = DeltaMomentumSignal::default();
let gate = signal.quality_gate(DataQualityFlags::SEQUENCE_GAP);
assert_eq!(gate, SignalGateDecision::Block);

Implementing Your Own Signal Module

Implement [SignalModule] and keep it:

  • deterministic (important for replay parity)
  • explicit about confidence and reason fields
  • strict about quality gating for unsafe feed states
  • compatible with the stable SignalSnapshot contract so bindings and FFI callers keep working

Real-World Use Cases

1. Gate entries on feed quality

Even a simple threshold signal becomes materially safer when quality_gate(...) blocks action during stale, gap, or degraded feed states.

2. Build multi-factor orderflow strategies

Use several modules together to express:

  • context: CumulativeDeltaSignal
  • trigger: SweepDetectionSignal
  • reversal filter: AbsorptionSignal or ExhaustionSignal

3. Keep strategy logic deterministic in replay

Because modules consume normalized analytics instead of live provider payloads, the same module can be replayed over historical sessions and compared to live behavior with much less drift.

Strategy Pattern: Composite Confirmation

A practical strategy stack often looks like:

  1. CumulativeDeltaSignal for directional regime bias
  2. VolumeImbalanceSignal for immediate orderflow pressure
  3. SweepDetectionSignal for breakout confirmation
  4. CompositeSignal to require majority confirmation

Detailed Example: Build A Composite Intraday Bias Module

use of_core::{AnalyticsSnapshot, DataQualityFlags, SignalState};
use of_signals::{
    CompositeSignal, CumulativeDeltaSignal, SweepDetectionSignal, SignalGateDecision,
    SignalModule, VolumeImbalanceSignal,
};

fn main() {
    let mut signal = CompositeSignal::new(vec![
        Box::new(CumulativeDeltaSignal::new(400)),
        Box::new(VolumeImbalanceSignal::new(150)),
        Box::new(SweepDetectionSignal::new(200, 4)),
    ]);

    let analytics = AnalyticsSnapshot {
        buy_volume: 900,
        sell_volume: 500,
        delta: 400,
        cumulative_delta: 650,
        last_price: 505_075,
        point_of_control: 505_000,
        value_area_low: 504_750,
        value_area_high: 505_050,
        ..Default::default()
    };

    if signal.quality_gate(DataQualityFlags::NONE) == SignalGateDecision::Pass {
        signal.on_analytics(&analytics);
        let snapshot = signal.snapshot();
        if matches!(snapshot.state, SignalState::LongBias) {
            println!("long bias: {}", snapshot.reason);
        }
    }
}

Detailed Example: Write Your Own Signal Module

use of_core::{AnalyticsSnapshot, DataQualityFlags, SignalSnapshot, SignalState};
use of_signals::{SignalGateDecision, SignalModule};

struct POCReclaimSignal {
    last: SignalSnapshot,
}

impl Default for POCReclaimSignal {
    fn default() -> Self {
        Self {
            last: SignalSnapshot {
                module_id: "poc_reclaim_v1",
                state: SignalState::Neutral,
                confidence_bps: 0,
                quality_flags: 0,
                reason: "init".to_string(),
            },
        }
    }
}

impl SignalModule for POCReclaimSignal {
    fn on_analytics(&mut self, analytics: &AnalyticsSnapshot) {
        let state = if analytics.delta > 200 && analytics.point_of_control >= analytics.value_area_low {
            SignalState::LongBias
        } else if analytics.delta < -200 && analytics.point_of_control <= analytics.value_area_high {
            SignalState::ShortBias
        } else {
            SignalState::Neutral
        };

        self.last = SignalSnapshot {
            module_id: "poc_reclaim_v1",
            state,
            confidence_bps: 7000,
            reason: "POC reclaim heuristic".to_string(),
            quality_flags: 0,
        };
    }

    fn snapshot(&self) -> SignalSnapshot {
        self.last.clone()
    }

    fn quality_gate(&self, flags: DataQualityFlags) -> SignalGateDecision {
        if flags.intersects(
            DataQualityFlags::STALE_FEED
                | DataQualityFlags::SEQUENCE_GAP
                | DataQualityFlags::ADAPTER_DEGRADED,
        ) {
            SignalGateDecision::Block
        } else {
            SignalGateDecision::Pass
        }
    }
}