1- from backtest_lib .market .polars_impl ._axis import SecurityAxis
1+ import datetime
2+
3+ import numpy as np
4+ import polars as pl
5+ import pytest
6+ from polars .exceptions import InvalidOperationError
7+
8+ from backtest_lib .market import Closed
9+ from backtest_lib .market .polars_impl ._axis import PeriodAxis , SecurityAxis
210
311
412def test_static_constructor_security_axis ():
@@ -17,3 +25,144 @@ def test_returning_length_of_names_security_axis():
1725def test_ability_to_handle_empty_names_security_axis ():
1826 security_axis = SecurityAxis .from_names ([])
1927 assert len (security_axis .names ) == 0
28+
29+
30+ def test_casting_incompatible_data_to_date_should_throw_error_period_axis ():
31+ s_string = pl .Series ("string" , ["a" , "b" , "c" ])
32+ with pytest .raises (InvalidOperationError ) as e_info :
33+ PeriodAxis .from_series (s_string )
34+ assert "conversion from `str` to `datetime[μs]` failed in column 'string'" in str (
35+ e_info .value
36+ )
37+
38+
39+ def test_constructor_casting_required_period_axis ():
40+ series_date = pl .Series (
41+ "dates" ,
42+ [
43+ datetime .date (2023 , 1 , 1 ),
44+ datetime .date (2023 , 1 , 2 ),
45+ datetime .date (2023 , 1 , 3 ),
46+ ],
47+ )
48+ period_axis = PeriodAxis .from_series (series_date )
49+ assert period_axis .labels == ("2023-01-01" , "2023-01-02" , "2023-01-03" )
50+ assert period_axis .pos == {"2023-01-01" : 0 , "2023-01-02" : 1 , "2023-01-03" : 2 }
51+ expected_dt64_array = np .array (
52+ ["2023-01-01" , "2023-01-02" , "2023-01-03" ], dtype = "datetime64[us]"
53+ )
54+ np .testing .assert_array_equal (period_axis .dt64 , expected_dt64_array )
55+
56+
57+ def test_len_period_axis ():
58+ series_date = pl .Series (
59+ "dates" ,
60+ [
61+ datetime .date (2023 , 1 , 1 ),
62+ datetime .date (2023 , 1 , 2 ),
63+ datetime .date (2023 , 1 , 3 ),
64+ ],
65+ )
66+ period_axis = PeriodAxis .from_series (series_date )
67+ assert len (period_axis ) == 3
68+
69+
70+ def test_slicing_incontiguous_sequence ():
71+ series_date = pl .Series (
72+ "dates" ,
73+ [
74+ datetime .date (2023 , 1 , 1 ),
75+ datetime .date (2023 , 1 , 2 ),
76+ datetime .date (2023 , 1 , 3 ),
77+ ],
78+ )
79+ period_axis = PeriodAxis .from_series (series_date )
80+ sliced_period_axis = period_axis .slice (slice (None , None , 2 ))
81+ assert sliced_period_axis .labels == ("2023-01-01" , "2023-01-03" )
82+ assert sliced_period_axis .pos == {"2023-01-01" : 0 , "2023-01-03" : 1 }
83+ expected_dt64_array = np .array (["2023-01-01" , "2023-01-03" ], dtype = "datetime64[us]" )
84+ np .testing .assert_array_equal (sliced_period_axis .dt64 , expected_dt64_array )
85+
86+
87+ def test_slicing_contiguous_sequence ():
88+ series_date = pl .Series (
89+ "dates" ,
90+ [
91+ datetime .date (2023 , 1 , 1 ),
92+ datetime .date (2023 , 1 , 2 ),
93+ datetime .date (2023 , 1 , 3 ),
94+ ],
95+ )
96+ period_axis = PeriodAxis .from_series (series_date )
97+ sliced_period_axis = period_axis .slice (slice (1 , 3 ))
98+ assert sliced_period_axis .labels == ("2023-01-02" , "2023-01-03" )
99+ assert sliced_period_axis .pos == {"2023-01-02" : 0 , "2023-01-03" : 1 }
100+ expected_dt64_array = np .array (["2023-01-02" , "2023-01-03" ], dtype = "datetime64[us]" )
101+ np .testing .assert_array_equal (sliced_period_axis .dt64 , expected_dt64_array )
102+
103+
104+ def test_bounds_after ():
105+ series_date = pl .Series (
106+ "dates" ,
107+ [
108+ datetime .date (2023 , 1 , 1 ),
109+ datetime .date (2023 , 1 , 2 ),
110+ datetime .date (2023 , 1 , 3 ),
111+ ],
112+ )
113+ period_axis = PeriodAxis .from_series (series_date )
114+ assert period_axis .bounds_after (np .datetime64 ("2023-01-02" ), inclusive = True ) == (
115+ 1 ,
116+ 3 ,
117+ )
118+ assert period_axis .bounds_after (np .datetime64 ("2023-01-02" ), inclusive = False ) == (
119+ 2 ,
120+ 3 ,
121+ )
122+
123+
124+ def test_bounds_before ():
125+ series_date = pl .Series (
126+ "dates" ,
127+ [
128+ datetime .date (2023 , 1 , 1 ),
129+ datetime .date (2023 , 1 , 2 ),
130+ datetime .date (2023 , 1 , 3 ),
131+ ],
132+ )
133+ period_axis = PeriodAxis .from_series (series_date )
134+ assert period_axis .bounds_before (np .datetime64 ("2023-01-02" ), inclusive = True ) == (
135+ 0 ,
136+ 2 ,
137+ )
138+ assert period_axis .bounds_before (np .datetime64 ("2023-01-02" ), inclusive = False ) == (
139+ 0 ,
140+ 1 ,
141+ )
142+
143+
144+ def test_bounds_between ():
145+ series_date = pl .Series (
146+ "dates" ,
147+ [
148+ datetime .date (2023 , 1 , 1 ),
149+ datetime .date (2023 , 1 , 2 ),
150+ datetime .date (2023 , 1 , 3 ),
151+ ],
152+ )
153+ period_axis = PeriodAxis .from_series (series_date )
154+ assert period_axis .bounds_between (
155+ np .datetime64 ("2023-01-01" ), np .datetime64 ("2023-01-03" ), closed = Closed .LEFT
156+ ) == (0 , 2 )
157+ assert period_axis .bounds_between (
158+ np .datetime64 ("2023-01-01" ), np .datetime64 ("2023-01-03" ), closed = "left"
159+ ) == (0 , 2 )
160+ assert period_axis .bounds_between (
161+ np .datetime64 ("2023-01-01" ), np .datetime64 ("2023-01-03" ), closed = Closed .RIGHT
162+ ) == (1 , 3 )
163+ assert period_axis .bounds_between (
164+ np .datetime64 ("2023-01-01" ), np .datetime64 ("2023-01-03" ), closed = Closed .BOTH
165+ ) == (0 , 3 )
166+ assert period_axis .bounds_between (
167+ np .datetime64 ("2023-01-02" ), np .datetime64 ("2023-01-03" ), closed = Closed .BOTH
168+ ) == (1 , 3 )
0 commit comments