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}