nautilus_live/book/mod.rs
1// -------------------------------------------------------------------------------------------------
2// Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3// https://nautechsystems.io
4//
5// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6// You may not use this file except in compliance with the License.
7// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Shared order book synchronization, recovery, and conformance checks.
17//!
18//! - [`sync`] holds the per-book synchronization lifecycle and its contract.
19//! - [`recovery`] owns recovery episodes, the retry budget, and the retry ceiling that follows it.
20//! - [`snapshot`] coordinates subscription write confirmation with snapshot acceptance and deadlines.
21//! - [`BookSequenceOutcome`] and [`BookSyncSignal`] describe validation decisions and monitoring
22//! signals without carrying venue-specific sequence fields or channel types.
23//! - `conformance` (test support) checks emitted book output against the book stream contract.
24//!
25//! # Recovery Lifecycle
26//!
27//! The adapter validates incoming book frames and marks gaps through [`BookSync`](sync::BookSync),
28//! which requests recovery while nothing owns the book. A request grants no ownership: the adapter
29//! then claims an episode and runs it. The recovery runner requests replacement snapshots through
30//! an adapter-supplied operation until the adapter accepts one or the episode is cancelled; a book
31//! never ends in a terminal failure state. A confirmed write opens the snapshot gate; only an
32//! accepted fresh snapshot completes recovery.
33//!
34//! # Adapters
35//!
36//! Adapters retain sequence rules, venue positions, buffers, socket routing, wire commands,
37//! acknowledgement correlation, and error classification. They keep [`BookSync`](sync::BookSync)
38//! under their existing lock or owning task.
39//!
40//! A monitoring signal does not itself start recovery; the adapter decides how to respond.
41
42pub mod recovery;
43pub mod snapshot;
44pub mod sync;
45
46#[cfg(any(test, feature = "test-support"))]
47pub mod conformance;
48
49use nautilus_common::live::dst::time::Duration;
50use nautilus_model::identifiers::InstrumentId;
51
52/// Default wait for an initial, post-reconnect, or recovery order book snapshot, in seconds.
53///
54/// Adapters use this as their `book_snapshot_timeout_secs` default; the config
55/// value remains tunable per deployment.
56pub const DEFAULT_BOOK_SNAPSHOT_TIMEOUT_SECS: u64 = 10;
57
58/// Decision from an adapter's book sequence validation.
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub enum BookSequenceOutcome {
61 /// Process the book snapshot or incremental update.
62 Accept,
63 /// Discard the frame without starting another recovery.
64 Suppress,
65 /// Discard the frame and request a fresh snapshot.
66 Recover,
67}
68
69/// Condition reported while monitoring book synchronization.
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum BookSyncSignalKind {
72 /// No accepted book update arrived within the stale-feed threshold.
73 Stale { elapsed: Duration },
74 /// The expected book snapshot did not arrive before its deadline.
75 SnapshotMissing,
76}
77
78/// A monitoring signal for one book.
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub struct BookSyncSignal {
81 pub instrument_id: InstrumentId,
82 pub kind: BookSyncSignalKind,
83}
84
85impl BookSyncSignal {
86 /// Logs the signal as a warning.
87 pub fn log(&self) {
88 let instrument_id = self.instrument_id;
89
90 match self.kind {
91 BookSyncSignalKind::Stale { elapsed } => {
92 log::warn!(
93 "Book feed stale for {instrument_id}: no update for {:.3}s",
94 elapsed.as_secs_f64()
95 );
96 }
97 BookSyncSignalKind::SnapshotMissing => {
98 log::warn!("Book snapshot not received for {instrument_id} after recovery request");
99 }
100 }
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use rstest::rstest;
107
108 use super::*;
109
110 #[rstest]
111 fn default_book_snapshot_timeout_is_ten_seconds() {
112 assert_eq!(DEFAULT_BOOK_SNAPSHOT_TIMEOUT_SECS, 10);
113 }
114}