bothan_lib/registry/
signal.rs

1// ! Signal definitions for registry entries.
2//!
3//! This module provides the core structure for defining signals in the registry system.
4//! A signal represents a specific asset or data point that can be computed from various
5//! sources, processed according to a defined strategy, and optionally transformed through
6//! post-processing steps.
7//!
8//! The module provides:
9//!
10//! - The [`Signal`] struct which defines the complete specification for a signal
11//! - Methods for creating and managing signal definitions
12//!
13//! # Signal Structure
14//!
15//! Each signal consists of:
16//!
17//! - Source queries that specify where to obtain input data
18//! - A processor that defines how to combine data from different sources
19//! - Optional post-processors that apply transformations to the processed value
20//!
21//! Signals form the fundamental building blocks of the registry system, enabling
22//! the computation of asset information from various data sources in a structured
23//! and configurable way.
24
25use bincode::{Decode, Encode};
26use serde::{Deserialize, Serialize};
27
28use crate::registry::post_processor::PostProcessor;
29use crate::registry::processor::Processor;
30use crate::registry::source::SourceQuery;
31
32/// A complete definition for computing a signal value.
33///
34/// The `Signal` struct encapsulates all the information required to compute a signal value:
35/// the sources to query for input data, the processing strategy to apply, and any
36/// post-processing transformations to perform on the result.
37///
38/// Signals are the fundamental building blocks of the registry system, allowing for
39/// flexible and configurable computation of asset information from various data sources.
40///
41/// # Components
42///
43/// * `source_queries` - Definitions of where and how to obtain input data
44/// * `processor` - Strategy for combining data from different sources
45/// * `post_processors` - Optional transformations to apply to the processed value
46///
47/// # Examples
48///
49/// ```
50/// use bothan_lib::registry::signal::Signal;
51/// use bothan_lib::registry::source::{SourceQuery, OperationRoute, Operation};
52/// use bothan_lib::registry::processor::{Processor, median::MedianProcessor};
53/// use bothan_lib::registry::post_processor::{PostProcessor, tick::TickPostProcessor};
54///
55/// // Create a BTC-USD signal that uses data from multiple sources
56/// let signal = Signal::new(
57///     // Source queries
58///     vec![
59///         SourceQuery::new(
60///             "binance".to_string(),
61///             "btcusdt".to_string(),
62///             vec![
63///                 // Apply USDT-USD conversion route
64///                 OperationRoute::new("USDT-USD".to_string(), Operation::Multiply),
65///             ],
66///         ),
67///         SourceQuery::new(
68///             "coinbase".to_string(),
69///             "BTC-USD".to_string(),
70///             vec![],
71///         ),
72///     ],
73///     // Processor (median with at least 1 source required)
74///     Processor::Median(MedianProcessor { min_source_count: 1 }),
75///     // Post-processors (convert to tick value)
76///     vec![PostProcessor::TickConvertor(TickPostProcessor {})],
77/// );
78/// ```
79#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Encode, Decode)]
80pub struct Signal {
81    /// Source queries that define where to obtain input data.
82    ///
83    /// Each query specifies a data source, an identifier within that source,
84    /// and optionally a series of routes to apply to the data before processing.
85    #[serde(rename = "sources")]
86    pub source_queries: Vec<SourceQuery>,
87
88    /// The processing strategy to apply to the source data.
89    ///
90    /// This defines how data from different sources will be combined into a
91    /// single output value, such as by taking the median or weighted median.
92    pub processor: Processor,
93
94    /// Optional post-processing transformations to apply to the processed value.
95    ///
96    /// These transformations are applied in sequence after the main processing
97    /// step, allowing for additional adjustments like tick conversion.
98    #[serde(default)]
99    #[serde(skip_serializing_if = "Vec::is_empty")]
100    pub post_processors: Vec<PostProcessor>,
101}
102
103impl Signal {
104    /// Creates a new Signal with the specified components.
105    ///
106    /// This method constructs a complete signal definition from its constituent parts:
107    /// the source queries for input data, the processor for combining data, and any
108    /// post-processors for transforming the result.
109    ///
110    /// # Examples
111    ///
112    /// ```
113    /// use bothan_lib::registry::signal::Signal;
114    /// use bothan_lib::registry::source::SourceQuery;
115    /// use bothan_lib::registry::processor::{Processor, median::MedianProcessor};
116    /// use bothan_lib::registry::post_processor::PostProcessor;
117    ///
118    /// // Create a simple signal with a median processor
119    /// let signal = Signal::new(
120    ///     vec![
121    ///         SourceQuery::new("exchange1".to_string(), "btc-usd".to_string(), vec![]),
122    ///         SourceQuery::new("exchange2".to_string(), "btc-usd".to_string(), vec![]),
123    ///     ],
124    ///     Processor::Median(MedianProcessor { min_source_count: 2 }),
125    ///     vec![], // No post-processors
126    /// );
127    /// ```
128    pub fn new(
129        source_queries: Vec<SourceQuery>,
130        processor: Processor,
131        post_processors: Vec<PostProcessor>,
132    ) -> Self {
133        Signal {
134            source_queries,
135            processor,
136            post_processors,
137        }
138    }
139}