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