@@ -125,3 +125,77 @@ def test_transaction_costs(self, sample_data):
125125
126126 # With costs should have lower final value (or equal)
127127 assert results_with_cost ["final_value" ] <= results_no_cost ["final_value" ]
128+
129+ def test_rebalance_frequency_daily (self , sample_data ):
130+ """Test daily rebalancing (default behavior)."""
131+ prices , signals = sample_data
132+ backtest = VectorizedBacktest (prices , signals , rebalance_freq = "D" )
133+ results = backtest .run ()
134+
135+ # Check that backtest runs successfully
136+ assert results ["final_value" ] > 0
137+ assert len (results ["portfolio_value" ]) == len (prices )
138+
139+ def test_rebalance_frequency_weekly (self , sample_data ):
140+ """Test weekly rebalancing."""
141+ prices , signals = sample_data
142+ backtest = VectorizedBacktest (prices , signals , rebalance_freq = "W" )
143+ results = backtest .run ()
144+
145+ # Check that backtest runs successfully
146+ assert results ["final_value" ] > 0
147+ assert len (results ["portfolio_value" ]) == len (prices )
148+
149+ # Weekly rebalancing should result in fewer position changes
150+ # Count the number of times weights change
151+ positions = results ["positions" ]
152+ position_changes = (positions .diff ().abs ().sum (axis = 1 ) > 0 ).sum ()
153+
154+ # Should be significantly fewer than daily (100 days)
155+ # Approximately ~14 weeks in 100 days
156+ assert position_changes < len (prices ) - 1
157+
158+ def test_rebalance_frequency_monthly (self , sample_data ):
159+ """Test monthly rebalancing."""
160+ prices , signals = sample_data
161+ backtest = VectorizedBacktest (prices , signals , rebalance_freq = "M" )
162+ results = backtest .run ()
163+
164+ # Check that backtest runs successfully
165+ assert results ["final_value" ] > 0
166+ assert len (results ["portfolio_value" ]) == len (prices )
167+
168+ # Monthly rebalancing should result in fewer position changes than weekly
169+ positions = results ["positions" ]
170+ position_changes = (positions .diff ().abs ().sum (axis = 1 ) > 0 ).sum ()
171+
172+ # Should be significantly fewer than daily
173+ # Approximately ~3 months in 100 days
174+ assert position_changes < len (prices ) - 1
175+
176+ def test_rebalance_frequency_invalid (self , sample_data ):
177+ """Test that invalid rebalance frequency raises error."""
178+ prices , signals = sample_data
179+ backtest = VectorizedBacktest (prices , signals , rebalance_freq = "X" )
180+
181+ with pytest .raises (ValueError , match = "Unsupported rebalance frequency" ):
182+ backtest .run ()
183+
184+ def test_rebalance_reduces_turnover (self , sample_data ):
185+ """Test that less frequent rebalancing reduces turnover."""
186+ prices , signals = sample_data
187+
188+ # Daily rebalancing
189+ backtest_daily = VectorizedBacktest (prices , signals , rebalance_freq = "D" , transaction_cost = 0.001 )
190+ results_daily = backtest_daily .run ()
191+
192+ # Monthly rebalancing
193+ backtest_monthly = VectorizedBacktest (prices , signals , rebalance_freq = "M" , transaction_cost = 0.001 )
194+ results_monthly = backtest_monthly .run ()
195+
196+ # Count position changes as proxy for turnover
197+ daily_changes = (results_daily ["positions" ].diff ().abs ().sum (axis = 1 ) > 0 ).sum ()
198+ monthly_changes = (results_monthly ["positions" ].diff ().abs ().sum (axis = 1 ) > 0 ).sum ()
199+
200+ # Monthly should have fewer rebalances
201+ assert monthly_changes < daily_changes
0 commit comments