nautilus_architect_ax/common/
parse.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//! Conversion functions that translate AX API schemas into Nautilus types.
17
18pub use nautilus_core::serialization::{
19    deserialize_decimal_or_zero, deserialize_optional_decimal,
20    deserialize_optional_decimal_from_str, deserialize_optional_decimal_or_zero, parse_decimal,
21    parse_optional_decimal,
22};
23use nautilus_model::{data::BarSpecification, enums::BarAggregation};
24
25use super::enums::AxCandleWidth;
26
27/// Maps a Nautilus [`BarSpecification`] to an [`AxCandleWidth`].
28///
29/// # Errors
30///
31/// Returns an error if the bar specification is not supported by Ax.
32pub fn map_bar_spec_to_candle_width(spec: &BarSpecification) -> anyhow::Result<AxCandleWidth> {
33    match spec.step.get() {
34        1 => match spec.aggregation {
35            BarAggregation::Second => Ok(AxCandleWidth::Seconds1),
36            BarAggregation::Minute => Ok(AxCandleWidth::Minutes1),
37            BarAggregation::Hour => Ok(AxCandleWidth::Hours1),
38            BarAggregation::Day => Ok(AxCandleWidth::Days1),
39            _ => anyhow::bail!("Unsupported bar aggregation: {:?}", spec.aggregation),
40        },
41        5 => match spec.aggregation {
42            BarAggregation::Second => Ok(AxCandleWidth::Seconds5),
43            BarAggregation::Minute => Ok(AxCandleWidth::Minutes5),
44            _ => anyhow::bail!(
45                "Unsupported bar step 5 with aggregation {:?}",
46                spec.aggregation
47            ),
48        },
49        15 if spec.aggregation == BarAggregation::Minute => Ok(AxCandleWidth::Minutes15),
50        step => anyhow::bail!(
51            "Unsupported bar step: {step} with aggregation {:?}",
52            spec.aggregation
53        ),
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use nautilus_model::enums::PriceType;
60    use rstest::rstest;
61
62    use super::*;
63
64    #[rstest]
65    fn test_map_bar_spec_1_second() {
66        let spec = BarSpecification::new(1, BarAggregation::Second, PriceType::Last);
67        let result = map_bar_spec_to_candle_width(&spec);
68        assert!(result.is_ok());
69        assert!(matches!(result.unwrap(), AxCandleWidth::Seconds1));
70    }
71
72    #[rstest]
73    fn test_map_bar_spec_5_second() {
74        let spec = BarSpecification::new(5, BarAggregation::Second, PriceType::Last);
75        let result = map_bar_spec_to_candle_width(&spec);
76        assert!(result.is_ok());
77        assert!(matches!(result.unwrap(), AxCandleWidth::Seconds5));
78    }
79
80    #[rstest]
81    fn test_map_bar_spec_1_minute() {
82        let spec = BarSpecification::new(1, BarAggregation::Minute, PriceType::Last);
83        let result = map_bar_spec_to_candle_width(&spec);
84        assert!(result.is_ok());
85        assert!(matches!(result.unwrap(), AxCandleWidth::Minutes1));
86    }
87
88    #[rstest]
89    fn test_map_bar_spec_5_minute() {
90        let spec = BarSpecification::new(5, BarAggregation::Minute, PriceType::Last);
91        let result = map_bar_spec_to_candle_width(&spec);
92        assert!(result.is_ok());
93        assert!(matches!(result.unwrap(), AxCandleWidth::Minutes5));
94    }
95
96    #[rstest]
97    fn test_map_bar_spec_15_minute() {
98        let spec = BarSpecification::new(15, BarAggregation::Minute, PriceType::Last);
99        let result = map_bar_spec_to_candle_width(&spec);
100        assert!(result.is_ok());
101        assert!(matches!(result.unwrap(), AxCandleWidth::Minutes15));
102    }
103
104    #[rstest]
105    fn test_map_bar_spec_1_hour() {
106        let spec = BarSpecification::new(1, BarAggregation::Hour, PriceType::Last);
107        let result = map_bar_spec_to_candle_width(&spec);
108        assert!(result.is_ok());
109        assert!(matches!(result.unwrap(), AxCandleWidth::Hours1));
110    }
111
112    #[rstest]
113    fn test_map_bar_spec_1_day() {
114        let spec = BarSpecification::new(1, BarAggregation::Day, PriceType::Last);
115        let result = map_bar_spec_to_candle_width(&spec);
116        assert!(result.is_ok());
117        assert!(matches!(result.unwrap(), AxCandleWidth::Days1));
118    }
119
120    #[rstest]
121    fn test_map_bar_spec_unsupported_step() {
122        let spec = BarSpecification::new(3, BarAggregation::Minute, PriceType::Last);
123        let result = map_bar_spec_to_candle_width(&spec);
124        assert!(result.is_err());
125    }
126
127    #[rstest]
128    fn test_map_bar_spec_unsupported_aggregation() {
129        let spec = BarSpecification::new(1, BarAggregation::Tick, PriceType::Last);
130        let result = map_bar_spec_to_candle_width(&spec);
131        assert!(result.is_err());
132    }
133}