Coverage for tests / test_datetools.py: 99%

504 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-27 14:36 +0000

1from unittest import main, TestCase 

2 

3import holidays 

4from matplotlib.dates import relativedelta 

5from rivapy.tools.datetools import calc_end_day, is_business_day, roll_day, Period, Schedule, DayCounter 

6from rivapy.tools.enums import RollConvention, DayCounterType, RollRule 

7import calendar 

8from rivapy.tools.datetools import ( 

9 _date_to_datetime, 

10 _is_ambiguous_date, 

11 calc_end_day, 

12 _is_IMM_date, 

13 calc_start_day, 

14 last_day_of_month, 

15 is_last_day_of_month, 

16 next_IMM_date, 

17 last_business_day_of_month, 

18 is_last_business_day_of_month, 

19 nearest_business_day, 

20 nearest_last_business_day_of_month, 

21 next_or_previous_business_day, 

22) 

23from datetime import date, datetime 

24from rivapy.tools.holidays_compat import DE, ECB 

25 

26 

27class DayCounterTests(TestCase): 

28 

29 def test_yf(self): 

30 d1 = datetime(2023, 1, 1) 

31 d2 = datetime(2024, 1, 1) 

32 self.assertAlmostEqual(DayCounter.yf_Act365Fixed(d1, d2), 1.0, delta=1e-5) 

33 dc = DayCounter(DayCounterType.Act365Fixed) 

34 self.assertAlmostEqual(DayCounter.yf_Act365Fixed(d1, d2), dc.yf(d1, d2), delta=1e-5) 

35 

36 d1 = datetime(2024, 12, 1) 

37 d2 = datetime(2025, 2, 1) 

38 self.assertAlmostEqual(DayCounter.yf_ActAct(d1, d2), 0.16963096040122763, delta=1e-5) 

39 d1 = date(2024, 1, 1) 

40 d2 = date(2025, 1, 1) 

41 self.assertAlmostEqual(DayCounter.yf_Act360(d1, d2), 1.0166666666666666, delta=1e-5) 

42 self.assertAlmostEqual(DayCounter.yf_Act365Fixed(d1, d2), 1.0027397260273974, delta=1e-5) 

43 coupon_schedule = [date(2024, 5, 1), date(2024, 11, 1)] 

44 self.assertEqual(DayCounter.yf_ActActICMA(date(2024, 5, 1), date(2024, 5, 31), coupon_schedule, coupon_frequency=2), 30 / 368) 

45 

46 self.assertAlmostEqual(DayCounter.yf_30360ISDA(date(2025, 1, 1), date(2025, 2, 1)), 0.08333333333333333, delta=1e-5) 

47 self.assertAlmostEqual(DayCounter.yf_30360ISDA(date(2024, 12, 31), date(2025, 1, 31)), 0.08333333333333333, delta=1e-5) 

48 self.assertAlmostEqual(DayCounter.yf_30360ISDA(date(2025, 4, 29), date(2025, 5, 30)), 0.08611111111111111, delta=1e-5) 

49 self.assertAlmostEqual(DayCounter.yf_30360ISDA(date(2025, 4, 30), date(2025, 5, 31)), 0.08333333333333333, delta=1e-5) 

50 

51 self.assertAlmostEqual(DayCounter.yf_30E360(date(2024, 12, 31), date(2025, 1, 31)), 0.08333333333333333, delta=1e-5) 

52 self.assertAlmostEqual(DayCounter.yf_30E360(date(2024, 12, 31), date(2025, 1, 30)), 0.08333333333333333, delta=1e-5) 

53 self.assertAlmostEqual(DayCounter.yf_30E360(date(2024, 12, 30), date(2025, 1, 31)), 0.08333333333333333, delta=1e-5) 

54 self.assertAlmostEqual(DayCounter.yf_30E360(date(2024, 12, 30), date(2025, 1, 30)), 0.08333333333333333, delta=1e-5) 

55 

56 self.assertAlmostEqual(DayCounter.yf_30U360(date(2024, 2, 29), date(2025, 2, 28)), 1.0, delta=1e-5) 

57 self.assertAlmostEqual(DayCounter.yf_30U360(date(2024, 2, 28), date(2025, 2, 28)), 1.0, delta=1e-5) 

58 self.assertAlmostEqual(DayCounter.yf_30U360(date(2023, 2, 28), date(2024, 2, 28)), 0.9944444444444445, delta=1e-5) 

59 

60 

61class PeriodTests(TestCase): 

62 

63 def test_period(self): 

64 p = Period(1975, 8, 22) 

65 self.assertEqual(p.years, 1975) 

66 self.assertEqual(p.months, 8) 

67 self.assertEqual(p.days, 22) 

68 self.assertTrue(p == Period(1975, 8, 22)) 

69 p = Period.from_string("T/N") 

70 self.assertEqual(p.years, 0) 

71 self.assertEqual(p.months, 0) 

72 self.assertEqual(p.days, 1) 

73 p = Period.from_string("O/N") 

74 self.assertEqual(p.years, 0) 

75 self.assertEqual(p.months, 0) 

76 self.assertEqual(p.days, 1) 

77 p = Period.from_string("5D") 

78 self.assertEqual(p.years, 0) 

79 self.assertEqual(p.months, 0) 

80 self.assertEqual(p.days, 5) 

81 P = Period.from_string("3M") 

82 self.assertEqual(P.years, 0) 

83 self.assertEqual(P.months, 3) 

84 self.assertEqual(P.days, 0) 

85 p = Period.from_string("2Y") 

86 self.assertEqual(p.years, 2) 

87 self.assertEqual(p.months, 0) 

88 self.assertEqual(p.days, 0) 

89 with self.assertRaises(Exception): 

90 Period.from_string("1W") 

91 

92 

93class OGandOwnTests(TestCase): 

94 # These tests are shall reproduce the results given in [Chapter 4](https://usermanual.wiki/Document/interestrateinstrumentsandmarketconventionsguide.1425400940/view) 

95 # of Interest Rate Instruments and Market Conventions Guide by OpenGamma 

96 holidays_target2 = holidays.ECB(years=[2011, 2012]) 

97 holidays_de = holidays.DE(years=1997) 

98 

99 # OpenGamma Tests 

100 def test_rolls(self): 

101 start_date = date(2011, 8, 18) 

102 end_date = start_date + relativedelta(months=1) 

103 self.assertEqual(roll_day(end_date, self.holidays_target2, RollConvention.FOLLOWING), datetime(2011, 9, 19)) 

104 self.assertEqual(roll_day(end_date, self.holidays_target2, RollConvention.PRECEDING), datetime(2011, 9, 16)) 

105 start_date = date(2011, 6, 30) 

106 end_date = start_date + relativedelta(months=1) 

107 self.assertEqual(roll_day(end_date, self.holidays_target2, RollConvention.MODIFIED_FOLLOWING), datetime(2011, 7, 29)) 

108 self.assertEqual(roll_day(end_date, self.holidays_target2, RollConvention.FOLLOWING), datetime(2011, 8, 1)) 

109 self.assertEqual(roll_day(end_date, self.holidays_target2, RollConvention.MODIFIED_FOLLOWING), datetime(2011, 7, 29)) 

110 start_date = date(2011, 9, 15) 

111 end_date = start_date + relativedelta(months=1) 

112 self.assertEqual(roll_day(end_date, self.holidays_target2, RollConvention.MODIFIED_FOLLOWING_BIMONTHLY), datetime(2011, 10, 14)) 

113 self.assertEqual(roll_day(end_date, self.holidays_target2, RollConvention.FOLLOWING), datetime(2011, 10, 17)) 

114 start_date = date(2011, 2, 28) 

115 end_date = start_date + relativedelta(months=1) 

116 self.assertEqual(roll_day(end_date, self.holidays_target2, RollConvention.MODIFIED_FOLLOWING_EOM, start_date), datetime(2011, 3, 31)) 

117 start_date = date(2011, 4, 29) 

118 end_date = start_date + relativedelta(months=1) 

119 self.assertEqual(roll_day(end_date, self.holidays_target2, RollConvention.MODIFIED_FOLLOWING_EOM, start_date), datetime(2011, 5, 31)) 

120 start_date = date(2012, 2, 28) 

121 end_date = start_date + relativedelta(months=1) 

122 self.assertEqual(roll_day(end_date, self.holidays_target2, RollConvention.MODIFIED_FOLLOWING_EOM, start_date), datetime(2012, 3, 28)) 

123 

124 # Own Tests 

125 

126 @staticmethod 

127 def weekday(n): 

128 name = {1: "Monday", 2: "Tuesday", 3: "Wednesday", 4: "Thursday", 5: "Friday", 6: "Saturday", 7: "Sunday"} 

129 return name.get(n, "weekday(" + str(n) + ") is an invalid day of the week!") 

130 

131 def test_holidays(self): 

132 for holiday_date, holiday_name in sorted(self.holidays_de.items()): 

133 # self.assertTrue( 

134 # self.weekday(holiday_date.isoweekday()) in [6, 7] 

135 # or holiday_name 

136 # in [ 

137 # "Neujahr", 

138 # "Karfreitag", 

139 # "Ostermontag", 

140 # "Erster Mai", 

141 # "Christi Himmelfahrt", 

142 # "Pfingstmontag", 

143 # "Tag der Deutschen Einheit", 

144 # "Erster Weihnachtstag", 

145 # "Zweiter Weihnachtstag", 

146 # ], 

147 # f"Unexpected holiday {holiday_name} on {holiday_date} ({self.weekday(holiday_date.isoweekday())})", 

148 # ) 

149 self.assertTrue( 

150 holiday_date 

151 in [ 

152 date(1997, 1, 1), 

153 date(1997, 3, 28), 

154 date(1997, 3, 31), 

155 date(1997, 5, 1), 

156 date(1997, 5, 8), 

157 date(1997, 5, 19), 

158 date(1997, 10, 3), 

159 date(1997, 12, 25), 

160 date(1997, 12, 26), 

161 ] 

162 ) 

163 

164 

165class RollingSchedulingTests(TestCase): 

166 def test_roll_day(self): 

167 holidays_de = DE() 

168 

169 # business days are unchanged for all roll conventions 

170 roll_conventions = [roll_convention.value for roll_convention in RollConvention] 

171 roll_conventions.pop(2) # remove 'ModifiedFollowingEOM' as it needs a start date 

172 for roll_convention in roll_conventions: 

173 self.assertEqual(roll_day(date(1997, 1, 2), holidays_de, roll_convention), datetime(1997, 1, 2)) 

174 

175 # test UNADJUSTED 

176 roll_convention = RollConvention.UNADJUSTED 

177 self.assertEqual(roll_day(datetime(1997, 5, 1), holidays_de, roll_convention), datetime(1997, 5, 1)) 

178 self.assertEqual(roll_day(datetime(1997, 7, 6), holidays_de, roll_convention), datetime(1997, 7, 6)) 

179 

180 # test FOLLOWING 

181 roll_convention = RollConvention.FOLLOWING 

182 self.assertEqual(roll_day(datetime(1997, 5, 1), holidays_de, roll_convention), datetime(1997, 5, 2)) 

183 self.assertEqual(roll_day(datetime(1997, 7, 5), holidays_de, roll_convention), datetime(1997, 7, 7)) 

184 self.assertEqual(roll_day(datetime(1997, 5, 17), holidays_de, roll_convention), datetime(1997, 5, 20)) 

185 self.assertEqual(roll_day(datetime(1997, 12, 25), holidays_de, roll_convention), datetime(1997, 12, 29)) 

186 self.assertEqual(roll_day(datetime(1997, 3, 28), holidays_de, roll_convention), datetime(1997, 4, 1)) 

187 

188 # test MODIFIED_FOLLOWING 

189 roll_convention = RollConvention.MODIFIED_FOLLOWING 

190 self.assertEqual(roll_day(datetime(1997, 5, 1), holidays_de, roll_convention), datetime(1997, 5, 2)) 

191 self.assertEqual(roll_day(datetime(1997, 12, 25), holidays_de, roll_convention), datetime(1997, 12, 29)) 

192 self.assertEqual(roll_day(datetime(1997, 8, 30), holidays_de, roll_convention), datetime(1997, 8, 29)) 

193 self.assertEqual(roll_day(datetime(1997, 8, 31), holidays_de, roll_convention), datetime(1997, 8, 29)) 

194 self.assertEqual(roll_day(datetime(1997, 3, 28), holidays_de, roll_convention), datetime(1997, 3, 27)) 

195 

196 # test MODIFIED_FOLLOWING_BIMONTHLY 

197 roll_convention = RollConvention.MODIFIED_FOLLOWING_BIMONTHLY 

198 self.assertEqual(roll_day(datetime(1997, 5, 1), holidays_de, roll_convention), datetime(1997, 5, 2)) 

199 self.assertEqual(roll_day(datetime(1997, 12, 25), holidays_de, roll_convention), datetime(1997, 12, 29)) 

200 self.assertEqual(roll_day(datetime(1997, 8, 30), holidays_de, roll_convention), datetime(1997, 8, 29)) 

201 self.assertEqual(roll_day(datetime(1997, 8, 31), holidays_de, roll_convention), datetime(1997, 8, 29)) 

202 self.assertEqual(roll_day(datetime(1997, 3, 28), holidays_de, roll_convention), datetime(1997, 3, 27)) 

203 self.assertEqual(roll_day(datetime(1997, 2, 15), holidays_de, roll_convention), datetime(1997, 2, 14)) 

204 self.assertEqual(roll_day(datetime(1997, 2, 16), holidays_de, roll_convention), datetime(1997, 2, 17)) 

205 

206 # test NEAREST 

207 roll_convention = RollConvention.NEAREST 

208 self.assertEqual(roll_day(datetime(1997, 5, 1), holidays_de, roll_convention), datetime(1997, 5, 2)) 

209 self.assertEqual(roll_day(datetime(1997, 7, 5), holidays_de, roll_convention), datetime(1997, 7, 4)) 

210 self.assertEqual(roll_day(datetime(1997, 7, 6), holidays_de, roll_convention), datetime(1997, 7, 7)) 

211 self.assertEqual(roll_day(datetime(1997, 5, 18), holidays_de, roll_convention), datetime(1997, 5, 20)) 

212 self.assertEqual(roll_day(datetime(1997, 3, 29), holidays_de, roll_convention), datetime(1997, 3, 27)) 

213 self.assertEqual(roll_day(datetime(1997, 3, 30), holidays_de, roll_convention), datetime(1997, 4, 1)) 

214 

215 # test PRECEDING 

216 roll_convention = RollConvention.PRECEDING 

217 self.assertEqual(roll_day(datetime(1997, 5, 1), holidays_de, roll_convention), datetime(1997, 4, 30)) 

218 self.assertEqual(roll_day(datetime(1997, 7, 6), holidays_de, roll_convention), datetime(1997, 7, 4)) 

219 self.assertEqual(roll_day(datetime(1997, 5, 19), holidays_de, roll_convention), datetime(1997, 5, 16)) 

220 self.assertEqual(roll_day(datetime(1997, 12, 28), holidays_de, roll_convention), datetime(1997, 12, 24)) 

221 self.assertEqual(roll_day(datetime(1997, 3, 31), holidays_de, roll_convention), datetime(1997, 3, 27)) 

222 

223 # test MODIFIED_PRECEDING 

224 roll_convention = RollConvention.MODIFIED_PRECEDING 

225 self.assertEqual(roll_day(datetime(1997, 5, 1), holidays_de, roll_convention), datetime(1997, 5, 2)) 

226 self.assertEqual(roll_day(datetime(1997, 7, 5), holidays_de, roll_convention), datetime(1997, 7, 4)) 

227 self.assertEqual(roll_day(datetime(1997, 7, 6), holidays_de, roll_convention), datetime(1997, 7, 4)) 

228 self.assertEqual(roll_day(datetime(1997, 3, 2), holidays_de, roll_convention), datetime(1997, 3, 3)) 

229 

230 # test MODIFIED_FOLLOWING_EOM 

231 roll_convention = RollConvention.MODIFIED_FOLLOWING_EOM 

232 self.assertEqual(roll_day(datetime(1997, 3, 28), holidays_de, roll_convention, datetime(1997, 2, 28)), datetime(1997, 3, 27)) 

233 self.assertEqual(roll_day(datetime(1997, 4, 26), holidays_de, roll_convention, datetime(1997, 3, 26)), datetime(1997, 4, 28)) 

234 self.assertEqual(roll_day(datetime(1997, 4, 27), holidays_de, roll_convention, datetime(1997, 3, 27)), datetime(1997, 4, 30)) 

235 self.assertEqual(roll_day(datetime(1997, 6, 29), holidays_de, roll_convention, datetime(1997, 5, 29)), datetime(1997, 6, 30)) 

236 self.assertEqual(roll_day(datetime(1997, 7, 30), holidays_de, roll_convention, datetime(1997, 6, 30)), datetime(1997, 7, 31)) 

237 self.assertEqual(roll_day(datetime(1997, 8, 31), holidays_de, roll_convention, datetime(1997, 7, 31)), datetime(1997, 8, 29)) 

238 self.assertEqual(roll_day(datetime(1997, 9, 28), holidays_de, roll_convention, datetime(1997, 8, 28)), datetime(1997, 9, 29)) 

239 self.assertEqual(roll_day(datetime(1997, 9, 29), holidays_de, roll_convention, datetime(1997, 8, 29)), datetime(1997, 9, 30)) 

240 self.assertEqual(roll_day(datetime(1997, 11, 30), holidays_de, roll_convention, datetime(1997, 10, 30)), datetime(1997, 11, 28)) 

241 self.assertEqual(roll_day(datetime(1997, 12, 28), holidays_de, roll_convention, datetime(1997, 11, 28)), datetime(1997, 12, 31)) 

242 

243 def test_schedule_generation(self): 

244 holidays_de = ECB() 

245 # test roll_out using differnt roll conventions 

246 self.assertEqual( 

247 Schedule._roll_out(datetime(2024, 1, 30), datetime(2024, 7, 31), Period(0, 1, 0), False, False, RollRule.NONE), 

248 [ 

249 datetime(2024, 1, 30), 

250 datetime(2024, 2, 29), 

251 datetime(2024, 3, 29), 

252 datetime(2024, 4, 29), 

253 datetime(2024, 5, 29), 

254 datetime(2024, 6, 29), 

255 datetime(2024, 7, 29), 

256 datetime(2024, 7, 31), 

257 ], 

258 ), 

259 self.assertEqual( 

260 Schedule._roll_out(datetime(2024, 1, 30), datetime(2024, 7, 31), Period(0, 1, 0), False, True, RollRule.NONE), 

261 [ 

262 datetime(2024, 1, 30), 

263 datetime(2024, 2, 29), 

264 datetime(2024, 3, 29), 

265 datetime(2024, 4, 29), 

266 datetime(2024, 5, 29), 

267 datetime(2024, 6, 29), 

268 datetime(2024, 7, 31), 

269 ], 

270 ), 

271 dates = Schedule._roll_out(datetime(2024, 7, 31), datetime(2024, 1, 30), Period(0, 1, 0), True, False, RollRule.NONE) 

272 dates.reverse() 

273 self.assertEqual( 

274 dates, 

275 [ 

276 datetime(2024, 7, 31), 

277 datetime(2024, 6, 30), 

278 datetime(2024, 5, 30), 

279 datetime(2024, 4, 30), 

280 datetime(2024, 3, 30), 

281 datetime(2024, 2, 29), 

282 datetime(2024, 1, 30), 

283 ], 

284 ), 

285 dates = Schedule._roll_out(datetime(2024, 7, 31), datetime(2024, 1, 30), Period(0, 1, 0), True, True, RollRule.NONE) 

286 dates.reverse() 

287 self.assertEqual( 

288 dates, 

289 [ 

290 datetime(2024, 7, 31), 

291 datetime(2024, 6, 30), 

292 datetime(2024, 5, 30), 

293 datetime(2024, 4, 30), 

294 datetime(2024, 3, 30), 

295 datetime(2024, 1, 30), 

296 ], 

297 ), 

298 self.assertEqual( 

299 Schedule._roll_out(datetime(2024, 1, 30), datetime(2024, 7, 31), Period(0, 1, 0), False, False, RollRule.EOM), 

300 [ 

301 datetime(2024, 1, 31), 

302 datetime(2024, 2, 29), 

303 datetime(2024, 3, 31), 

304 datetime(2024, 4, 30), 

305 datetime(2024, 5, 31), 

306 datetime(2024, 6, 30), 

307 datetime(2024, 7, 31), 

308 ], 

309 ), 

310 self.assertEqual( 

311 Schedule._roll_out(datetime(2024, 1, 30), datetime(2024, 7, 31), Period(0, 1, 0), False, True, RollRule.EOM), 

312 [ 

313 datetime(2024, 1, 31), 

314 datetime(2024, 2, 29), 

315 datetime(2024, 3, 31), 

316 datetime(2024, 4, 30), 

317 datetime(2024, 5, 31), 

318 datetime(2024, 6, 30), 

319 datetime(2024, 7, 31), 

320 ], 

321 ), 

322 dates = Schedule._roll_out(datetime(2024, 7, 31), datetime(2024, 1, 30), Period(0, 1, 0), True, False, RollRule.EOM) 

323 dates.reverse() 

324 self.assertEqual( 

325 dates, 

326 [ 

327 datetime(2024, 7, 31), 

328 datetime(2024, 6, 30), 

329 datetime(2024, 5, 31), 

330 datetime(2024, 4, 30), 

331 datetime(2024, 3, 31), 

332 datetime(2024, 2, 29), 

333 datetime(2024, 1, 31), 

334 ], 

335 ), 

336 dates = Schedule._roll_out(datetime(2024, 7, 31), datetime(2024, 1, 30), Period(0, 1, 0), True, True, RollRule.EOM) 

337 dates.reverse() 

338 self.assertEqual( 

339 dates, 

340 [ 

341 datetime(2024, 7, 31), 

342 datetime(2024, 6, 30), 

343 datetime(2024, 5, 31), 

344 datetime(2024, 4, 30), 

345 datetime(2024, 3, 31), 

346 datetime(2024, 2, 29), 

347 datetime(2024, 1, 31), 

348 ], 

349 ), 

350 self.assertEqual( 

351 Schedule._roll_out(datetime(2023, 11, 30), datetime(2024, 9, 30), Period(0, 3, 0), False, True, RollRule.EOM), 

352 [ 

353 datetime(2023, 11, 30), 

354 datetime(2024, 2, 29), 

355 datetime(2024, 5, 31), 

356 datetime(2024, 9, 30), 

357 ], 

358 ), 

359 self.assertEqual( 

360 Schedule._roll_out(datetime(2024, 1, 30), datetime(2024, 7, 31), Period(0, 1, 0), False, False, RollRule.DOM), 

361 [ 

362 datetime(2024, 1, 30), 

363 datetime(2024, 2, 29), 

364 datetime(2024, 3, 30), 

365 datetime(2024, 4, 30), 

366 datetime(2024, 5, 30), 

367 datetime(2024, 6, 30), 

368 datetime(2024, 7, 30), 

369 datetime(2024, 7, 31), 

370 ], 

371 ), 

372 self.assertEqual( 

373 Schedule._roll_out(datetime(2024, 1, 30), datetime(2024, 7, 31), Period(0, 1, 0), False, True, RollRule.DOM), 

374 [ 

375 datetime(2024, 1, 30), 

376 datetime(2024, 2, 29), 

377 datetime(2024, 3, 30), 

378 datetime(2024, 4, 30), 

379 datetime(2024, 5, 30), 

380 datetime(2024, 6, 30), 

381 datetime(2024, 7, 31), 

382 ], 

383 ), 

384 self.assertEqual( 

385 Schedule._roll_out(datetime(2024, 1, 29), datetime(2024, 7, 31), Period(0, 1, 0), False, True, RollRule.DOM), 

386 [ 

387 datetime(2024, 1, 29), 

388 datetime(2024, 2, 29), 

389 datetime(2024, 3, 29), 

390 datetime(2024, 4, 29), 

391 datetime(2024, 5, 29), 

392 datetime(2024, 6, 29), 

393 datetime(2024, 7, 31), 

394 ], 

395 ), 

396 dates = Schedule._roll_out(datetime(2024, 7, 31), datetime(2024, 1, 30), Period(0, 1, 0), True, False, RollRule.DOM) 

397 dates.reverse() 

398 self.assertEqual( 

399 dates, 

400 [ 

401 datetime(2024, 7, 31), 

402 datetime(2024, 6, 30), 

403 datetime(2024, 5, 31), 

404 datetime(2024, 4, 30), 

405 datetime(2024, 3, 31), 

406 datetime(2024, 2, 29), 

407 datetime(2024, 1, 31), 

408 datetime(2024, 1, 30), 

409 ], 

410 ), 

411 dates = Schedule._roll_out(datetime(2024, 7, 31), datetime(2024, 1, 30), Period(0, 1, 0), True, True, RollRule.DOM) 

412 dates.reverse() 

413 self.assertEqual( 

414 dates, 

415 [ 

416 datetime(2024, 7, 31), 

417 datetime(2024, 6, 30), 

418 datetime(2024, 5, 31), 

419 datetime(2024, 4, 30), 

420 datetime(2024, 3, 31), 

421 datetime(2024, 2, 29), 

422 datetime(2024, 1, 30), 

423 ], 

424 ), 

425 self.assertEqual( 

426 Schedule._roll_out(datetime(2023, 12, 18), datetime(2024, 7, 31), Period(0, 3, 0), False, False, RollRule.IMM), 

427 [ 

428 datetime(2023, 12, 20), 

429 datetime(2024, 3, 20), 

430 datetime(2024, 6, 19), 

431 datetime(2024, 7, 31), 

432 ], 

433 ), 

434 self.assertEqual( 

435 Schedule._roll_out(datetime(2023, 12, 18), datetime(2024, 7, 31), Period(0, 3, 0), False, True, RollRule.IMM), 

436 [ 

437 datetime(2023, 12, 20), 

438 datetime(2024, 3, 20), 

439 datetime(2024, 7, 31), 

440 ], 

441 ), 

442 self.assertEqual( 

443 Schedule._roll_out(datetime(2023, 12, 18), datetime(2024, 7, 31), Period(0, 1, 0), False, True, RollRule.IMM), 

444 [ 

445 datetime(2023, 12, 20), 

446 datetime(2024, 3, 20), 

447 datetime(2024, 7, 31), 

448 ], 

449 ), 

450 # self.assertEqual( 

451 # Schedule._roll_out(datetime(2024, 7, 31), datetime(2023, 12, 18), Period(0, 3, 0), True, False, RollRule.IMM), 

452 # [ 

453 # datetime(2024, 6, 19), 

454 # datetime(2024, 3, 20), 

455 # datetime(2023, 12, 20), 

456 # datetime(2023, 12, 18), 

457 # ], 

458 # ), 

459 # self.assertEqual( 

460 # Schedule._roll_out(datetime(2024, 7, 31), datetime(2023, 12, 18), Period(0, 3, 0), True, True, RollRule.IMM), 

461 # [ 

462 # datetime(2024, 6, 19), 

463 # datetime(2024, 3, 20), 

464 # datetime(2023, 12, 18), 

465 # ], 

466 # ), 

467 self.assertEqual( 

468 Schedule._roll_out(datetime(2023, 12, 21), datetime(2024, 2, 1), Period(0, 1, 0), False, True, RollRule.IMM), 

469 [], 

470 ), 

471 # generate_dates with different periods and business day conventions 

472 # sufficient to test with two different business day conventions? should be ok, if application of bdc is tested properly 

473 self.assertEqual( 

474 Schedule( 

475 datetime(2020, 8, 21), datetime(2021, 8, 21), Period(0, 3, 0), True, True, RollConvention.UNADJUSTED, holidays_de 

476 ).generate_dates(False), 

477 [datetime(2020, 8, 21), datetime(2020, 11, 21), datetime(2021, 2, 21), datetime(2021, 5, 21), datetime(2021, 8, 21)], 

478 ) 

479 self.assertEqual( 

480 Schedule( 

481 datetime(2020, 8, 21), datetime(2021, 8, 21), Period(0, 3, 0), True, False, RollConvention.UNADJUSTED, holidays_de 

482 ).generate_dates(False), 

483 [datetime(2020, 8, 21), datetime(2020, 11, 21), datetime(2021, 2, 21), datetime(2021, 5, 21), datetime(2021, 8, 21)], 

484 ) 

485 self.assertEqual( 

486 Schedule( 

487 datetime(2020, 8, 21), datetime(2021, 8, 21), Period(0, 3, 0), False, True, RollConvention.UNADJUSTED, holidays_de 

488 ).generate_dates(False), 

489 [datetime(2020, 8, 21), datetime(2020, 11, 21), datetime(2021, 2, 21), datetime(2021, 5, 21), datetime(2021, 8, 21)], 

490 ) 

491 self.assertEqual( 

492 Schedule( 

493 datetime(2020, 8, 21), datetime(2021, 8, 21), Period(0, 3, 0), False, False, RollConvention.UNADJUSTED, holidays_de 

494 ).generate_dates(False), 

495 [datetime(2020, 8, 21), datetime(2020, 11, 21), datetime(2021, 2, 21), datetime(2021, 5, 21), datetime(2021, 8, 21)], 

496 ) 

497 

498 self.assertEqual( 

499 Schedule( 

500 datetime(2020, 8, 21), datetime(2021, 8, 21), Period(0, 5, 0), True, True, RollConvention.UNADJUSTED, holidays_de 

501 ).generate_dates(False), 

502 [datetime(2020, 8, 21), datetime(2021, 3, 21), datetime(2021, 8, 21)], 

503 ) 

504 self.assertEqual( 

505 Schedule( 

506 datetime(2020, 8, 21), datetime(2021, 8, 21), Period(0, 5, 0), True, False, RollConvention.UNADJUSTED, holidays_de 

507 ).generate_dates(False), 

508 [datetime(2020, 8, 21), datetime(2020, 10, 21), datetime(2021, 3, 21), datetime(2021, 8, 21)], 

509 ) 

510 self.assertEqual( 

511 Schedule( 

512 datetime(2020, 8, 21), datetime(2021, 8, 21), Period(0, 5, 0), False, True, RollConvention.UNADJUSTED, holidays_de 

513 ).generate_dates(False), 

514 [datetime(2020, 8, 21), datetime(2021, 1, 21), datetime(2021, 8, 21)], 

515 ) 

516 self.assertEqual( 

517 Schedule( 

518 datetime(2020, 8, 21), datetime(2021, 8, 21), Period(0, 5, 0), False, False, RollConvention.UNADJUSTED, holidays_de 

519 ).generate_dates(False), 

520 [datetime(2020, 8, 21), datetime(2021, 1, 21), datetime(2021, 6, 21), datetime(2021, 8, 21)], 

521 ) 

522 

523 self.assertEqual( 

524 Schedule( 

525 datetime(2020, 8, 21), datetime(2021, 8, 21), Period(0, 3, 0), True, True, RollConvention.MODIFIED_FOLLOWING, holidays_de 

526 ).generate_dates(False), 

527 [datetime(2020, 8, 21), datetime(2020, 11, 23), datetime(2021, 2, 22), datetime(2021, 5, 21), datetime(2021, 8, 23)], 

528 ) 

529 self.assertEqual( 

530 Schedule( 

531 datetime(2020, 8, 21), datetime(2021, 8, 21), Period(0, 3, 0), True, False, RollConvention.MODIFIED_FOLLOWING, holidays_de 

532 ).generate_dates(False), 

533 [datetime(2020, 8, 21), datetime(2020, 11, 23), datetime(2021, 2, 22), datetime(2021, 5, 21), datetime(2021, 8, 23)], 

534 ) 

535 self.assertEqual( 

536 Schedule( 

537 datetime(2020, 8, 21), datetime(2021, 8, 21), Period(0, 3, 0), False, True, RollConvention.MODIFIED_FOLLOWING, holidays_de 

538 ).generate_dates(False), 

539 [datetime(2020, 8, 21), datetime(2020, 11, 23), datetime(2021, 2, 22), datetime(2021, 5, 21), datetime(2021, 8, 23)], 

540 ) 

541 self.assertEqual( 

542 Schedule( 

543 datetime(2020, 8, 21), datetime(2021, 8, 21), Period(0, 3, 0), False, False, RollConvention.MODIFIED_FOLLOWING, holidays_de 

544 ).generate_dates(False), 

545 [datetime(2020, 8, 21), datetime(2020, 11, 23), datetime(2021, 2, 22), datetime(2021, 5, 21), datetime(2021, 8, 23)], 

546 ) 

547 

548 self.assertEqual( 

549 Schedule( 

550 datetime(2020, 8, 21), datetime(2021, 8, 21), Period(0, 5, 0), True, True, RollConvention.MODIFIED_FOLLOWING, holidays_de 

551 ).generate_dates(False), 

552 [datetime(2020, 8, 21), datetime(2021, 3, 22), datetime(2021, 8, 23)], 

553 ) 

554 self.assertEqual( 

555 Schedule( 

556 datetime(2020, 8, 21), datetime(2021, 8, 21), Period(0, 5, 0), True, False, RollConvention.MODIFIED_FOLLOWING, holidays_de 

557 ).generate_dates(False), 

558 [datetime(2020, 8, 21), datetime(2020, 10, 21), datetime(2021, 3, 22), datetime(2021, 8, 23)], 

559 ) 

560 self.assertEqual( 

561 Schedule( 

562 datetime(2020, 8, 21), datetime(2021, 8, 21), Period(0, 5, 0), False, True, RollConvention.MODIFIED_FOLLOWING, holidays_de 

563 ).generate_dates(False), 

564 [datetime(2020, 8, 21), datetime(2021, 1, 21), datetime(2021, 8, 23)], 

565 ) 

566 self.assertEqual( 

567 Schedule( 

568 datetime(2020, 8, 21), datetime(2021, 8, 21), Period(0, 5, 0), False, False, RollConvention.MODIFIED_FOLLOWING, holidays_de 

569 ).generate_dates(False), 

570 [datetime(2020, 8, 21), datetime(2021, 1, 21), datetime(2021, 6, 21), datetime(2021, 8, 23)], 

571 ) 

572 

573 def test_calc_end_day(self): 

574 self.assertEqual(calc_end_day(datetime(2023, 1, 31), Period(0, 1, 0)), datetime(2023, 2, 28)) 

575 self.assertEqual(calc_end_day(datetime(2023, 1, 30), Period(0, 1, 0)), datetime(2023, 2, 28)) 

576 self.assertEqual(calc_end_day(datetime(2023, 1, 29), Period(0, 1, 0)), datetime(2023, 2, 28)) 

577 self.assertEqual(calc_end_day(datetime(2023, 1, 28), Period(0, 1, 0)), datetime(2023, 2, 28)) 

578 self.assertEqual(calc_end_day(datetime(2024, 1, 31), Period(0, 1, 0)), datetime(2024, 2, 29)) 

579 self.assertEqual(calc_end_day(datetime(2024, 1, 30), Period(0, 1, 0)), datetime(2024, 2, 29)) 

580 self.assertEqual(calc_end_day(datetime(2024, 1, 29), Period(0, 1, 0)), datetime(2024, 2, 29)) 

581 self.assertEqual(calc_end_day(datetime(2024, 1, 28), Period(0, 1, 0)), datetime(2024, 2, 28)) 

582 self.assertEqual(calc_end_day(datetime(2023, 2, 28), Period(0, 1, 0)), datetime(2023, 3, 28)) 

583 self.assertEqual(calc_end_day(datetime(2023, 2, 27), Period(0, 1, 0)), datetime(2023, 3, 27)) 

584 self.assertEqual(calc_end_day(datetime(2024, 2, 29), Period(0, 1, 0)), datetime(2024, 3, 29)) 

585 self.assertEqual(calc_end_day(datetime(2024, 2, 28), Period(0, 1, 0)), datetime(2024, 3, 28)) 

586 self.assertEqual(calc_end_day(datetime(2024, 2, 27), Period(0, 1, 0)), datetime(2024, 3, 27)) 

587 roll = RollRule(RollRule.EOM) 

588 self.assertEqual(calc_end_day(datetime(2023, 1, 31), Period(0, 1, 0), roll_convention=roll), datetime(2023, 2, 28)) 

589 self.assertEqual(calc_end_day(datetime(2023, 1, 30), Period(0, 1, 0), roll_convention=roll), datetime(2023, 2, 28)) 

590 self.assertEqual(calc_end_day(datetime(2023, 1, 29), Period(0, 1, 0), roll_convention=roll), datetime(2023, 2, 28)) 

591 self.assertEqual(calc_end_day(datetime(2023, 1, 28), Period(0, 1, 0), roll_convention=roll), datetime(2023, 2, 28)) 

592 self.assertEqual(calc_end_day(datetime(2024, 1, 31), Period(0, 1, 0), roll_convention=roll), datetime(2024, 2, 29)) 

593 self.assertEqual(calc_end_day(datetime(2024, 1, 30), Period(0, 1, 0), roll_convention=roll), datetime(2024, 2, 29)) 

594 self.assertEqual(calc_end_day(datetime(2024, 1, 29), Period(0, 1, 0), roll_convention=roll), datetime(2024, 2, 29)) 

595 self.assertEqual(calc_end_day(datetime(2024, 1, 28), Period(0, 1, 0), roll_convention=roll), datetime(2024, 2, 28)) 

596 self.assertEqual(calc_end_day(datetime(2023, 2, 28), Period(0, 1, 0), roll_convention=roll), datetime(2023, 3, 31)) 

597 self.assertEqual(calc_end_day(datetime(2023, 2, 27), Period(0, 1, 0), roll_convention=roll), datetime(2023, 3, 27)) 

598 self.assertEqual(calc_end_day(datetime(2024, 2, 29), Period(0, 1, 0), roll_convention=roll), datetime(2024, 3, 31)) 

599 self.assertEqual(calc_end_day(datetime(2024, 2, 28), Period(0, 1, 0), roll_convention=roll), datetime(2024, 3, 31)) 

600 self.assertEqual(calc_end_day(datetime(2024, 2, 27), Period(0, 1, 0), roll_convention=roll), datetime(2024, 3, 27)) 

601 self.assertEqual(calc_end_day(datetime(2023, 3, 31), Period(0, 1, 0), roll_convention=roll), datetime(2023, 4, 30)) 

602 self.assertEqual(calc_end_day(datetime(2023, 3, 30), Period(0, 1, 0), roll_convention=roll), datetime(2023, 4, 30)) 

603 self.assertEqual(calc_end_day(datetime(2023, 3, 29), Period(0, 1, 0), roll_convention=roll), datetime(2023, 4, 29)) 

604 self.assertEqual(calc_end_day(datetime(2023, 3, 28), Period(0, 1, 0), roll_convention=roll), datetime(2023, 4, 28)) 

605 roll = RollRule(RollRule.DOM) 

606 self.assertEqual(calc_end_day(datetime(2023, 1, 31), Period(0, 1, 0), roll_convention=roll), datetime(2023, 2, 28)) 

607 self.assertEqual(calc_end_day(datetime(2023, 1, 30), Period(0, 1, 0), roll_convention=roll), datetime(2023, 2, 28)) 

608 self.assertEqual(calc_end_day(datetime(2023, 1, 29), Period(0, 1, 0), roll_convention=roll), datetime(2023, 2, 28)) 

609 self.assertEqual(calc_end_day(datetime(2023, 1, 28), Period(0, 1, 0), roll_convention=roll), datetime(2023, 2, 28)) 

610 self.assertEqual(calc_end_day(datetime(2024, 1, 31), Period(0, 1, 0), roll_convention=roll), datetime(2024, 2, 29)) 

611 self.assertEqual(calc_end_day(datetime(2024, 1, 30), Period(0, 1, 0), roll_convention=roll), datetime(2024, 2, 29)) 

612 self.assertEqual(calc_end_day(datetime(2024, 1, 29), Period(0, 1, 0), roll_convention=roll), datetime(2024, 2, 29)) 

613 self.assertEqual(calc_end_day(datetime(2024, 1, 28), Period(0, 1, 0), roll_convention=roll), datetime(2024, 2, 28)) 

614 self.assertEqual(calc_end_day(datetime(2023, 2, 28), Period(0, 1, 0), roll_convention=roll), datetime(2023, 3, 28)) 

615 self.assertEqual(calc_end_day(datetime(2023, 2, 27), Period(0, 1, 0), roll_convention=roll), datetime(2023, 3, 27)) 

616 self.assertEqual(calc_end_day(datetime(2024, 2, 29), Period(0, 1, 0), roll_convention=roll), datetime(2024, 3, 29)) 

617 self.assertEqual(calc_end_day(datetime(2024, 2, 28), Period(0, 1, 0), roll_convention=roll), datetime(2024, 3, 28)) 

618 self.assertEqual(calc_end_day(datetime(2024, 2, 27), Period(0, 1, 0), roll_convention=roll), datetime(2024, 3, 27)) 

619 self.assertEqual(calc_end_day(datetime(2023, 3, 31), Period(0, 1, 0), roll_convention=roll), datetime(2023, 4, 30)) 

620 self.assertEqual(calc_end_day(datetime(2023, 3, 30), Period(0, 1, 0), roll_convention=roll), datetime(2023, 4, 30)) 

621 self.assertEqual(calc_end_day(datetime(2023, 3, 29), Period(0, 1, 0), roll_convention=roll), datetime(2023, 4, 29)) 

622 self.assertEqual(calc_end_day(datetime(2023, 3, 28), Period(0, 1, 0), roll_convention=roll), datetime(2023, 4, 28)) 

623 roll = RollRule(RollRule.IMM) 

624 self.assertEqual(calc_end_day(datetime(2023, 1, 31), Period(0, 1, 0), roll_convention=roll), datetime(2023, 6, 21)) 

625 self.assertEqual(calc_end_day(datetime(2023, 2, 28), Period(0, 1, 0), roll_convention=roll), datetime(2023, 6, 21)) 

626 self.assertEqual(calc_end_day(datetime(2023, 3, 29), Period(0, 1, 0), roll_convention=roll), datetime(2023, 9, 20)) 

627 self.assertEqual(calc_end_day(datetime(2023, 1, 28), Period(0, 2, 0), roll_convention=roll), datetime(2023, 6, 21)) 

628 self.assertEqual(calc_end_day(datetime(2023, 1, 28), Period(0, 3, 0), roll_convention=roll), datetime(2023, 6, 21)) 

629 self.assertEqual(calc_end_day(datetime(2023, 3, 15), Period(0, 3, 0), roll_convention=roll), datetime(2023, 6, 21)) 

630 self.assertEqual(calc_end_day(datetime(2023, 3, 15), Period(0, 6, 0), roll_convention=roll), datetime(2023, 9, 20)) 

631 self.assertEqual(calc_end_day(datetime(2023, 1, 28), Period(0, 4, 0), roll_convention=roll), datetime(2023, 9, 20)) 

632 self.assertEqual(calc_end_day(datetime(2023, 3, 28), Period(0, 4, 0), roll_convention=roll), datetime(2023, 12, 20)) 

633 self.assertEqual(calc_end_day(datetime(2023, 3, 28), Period(0, 6, 0), roll_convention=roll), datetime(2023, 12, 20)) 

634 

635 def test_is_ambiguous_date(self): 

636 self.assertEqual(_is_ambiguous_date(datetime(2023, 1, 31)), False) 

637 self.assertEqual(_is_ambiguous_date(datetime(2023, 2, 28)), True) 

638 self.assertEqual(_is_ambiguous_date(datetime(2024, 1, 31)), False) 

639 self.assertEqual(_is_ambiguous_date(datetime(2024, 2, 27)), False) 

640 self.assertEqual(_is_ambiguous_date(datetime(2024, 1, 30)), True) 

641 self.assertEqual(_is_ambiguous_date(datetime(2024, 2, 29)), True) 

642 self.assertEqual(_is_ambiguous_date(datetime(2024, 3, 30)), True) 

643 self.assertEqual(_is_ambiguous_date(datetime(2024, 3, 29)), False) 

644 self.assertEqual(_is_ambiguous_date(datetime(2024, 3, 31)), False) 

645 

646 def test_is_IMM_date(self): 

647 self.assertEqual(_is_IMM_date(datetime(2023, 1, 18)), False) 

648 self.assertEqual(_is_IMM_date(datetime(2023, 1, 19)), False) 

649 self.assertEqual(_is_IMM_date(datetime(2023, 1, 20)), False) 

650 self.assertEqual(_is_IMM_date(datetime(2023, 1, 21)), False) 

651 self.assertEqual(_is_IMM_date(datetime(2023, 2, 15)), False) 

652 self.assertEqual(_is_IMM_date(datetime(2023, 2, 16)), False) 

653 self.assertEqual(_is_IMM_date(datetime(2023, 2, 17)), False) 

654 self.assertEqual(_is_IMM_date(datetime(2023, 2, 18)), False) 

655 self.assertEqual(_is_IMM_date(datetime(2023, 3, 15)), True) 

656 self.assertEqual(_is_IMM_date(datetime(2023, 3, 16)), False) 

657 self.assertEqual(_is_IMM_date(datetime(2023, 3, 17)), False) 

658 self.assertEqual(_is_IMM_date(datetime(2023, 3, 22)), False) 

659 self.assertEqual(_is_IMM_date(datetime(2023, 4, 19)), False) 

660 self.assertEqual(_is_IMM_date(datetime(2023, 4, 20)), False) 

661 self.assertEqual(_is_IMM_date(datetime(2023, 4, 21)), False) 

662 self.assertEqual(_is_IMM_date(datetime(2023, 5, 17)), False) 

663 self.assertEqual(_is_IMM_date(datetime(2023, 5, 18)), False) 

664 self.assertEqual(_is_IMM_date(datetime(2023, 5, 19)), False) 

665 self.assertEqual(_is_IMM_date(datetime(2023, 6, 21)), True) 

666 self.assertEqual(_is_IMM_date(datetime(2023, 6, 20)), False) 

667 self.assertEqual(_is_IMM_date(datetime(2023, 6, 23)), False) 

668 self.assertEqual(_is_IMM_date(datetime(2023, 7, 19)), False) 

669 self.assertEqual(_is_IMM_date(datetime(2023, 7, 20)), False) 

670 self.assertEqual(_is_IMM_date(datetime(2023, 7, 21)), False) 

671 

672 def test_calc_start_day(self): 

673 self.assertEqual(calc_start_day(datetime(2023, 1, 31), Period(0, 1, 0)), datetime(2022, 12, 31)) 

674 self.assertEqual(calc_start_day(datetime(2023, 1, 30), Period(0, 1, 0)), datetime(2022, 12, 30)) 

675 self.assertEqual(calc_start_day(datetime(2023, 1, 29), Period(0, 1, 0)), datetime(2022, 12, 29)) 

676 self.assertEqual(calc_start_day(datetime(2023, 1, 28), Period(0, 1, 0)), datetime(2022, 12, 28)) 

677 self.assertEqual(calc_start_day(datetime(2023, 1, 27), Period(0, 1, 0)), datetime(2022, 12, 27)) 

678 self.assertEqual(calc_start_day(datetime(2023, 1, 26), Period(0, 1, 0)), datetime(2022, 12, 26)) 

679 self.assertEqual(calc_start_day(datetime(2023, 1, 1), Period(0, 0, 1)), datetime(2022, 12, 31)) 

680 self.assertEqual(calc_start_day(datetime(2023, 1, 2), Period(0, 0, 1)), datetime(2023, 1, 1)) 

681 bdc = RollConvention.MODIFIED_FOLLOWING 

682 self.assertEqual(calc_start_day(datetime(2023, 1, 31), Period(0, 1, 0), bdc), datetime(2022, 12, 31)) 

683 self.assertEqual(calc_start_day(datetime(2023, 1, 30), Period(0, 1, 0), bdc), datetime(2022, 12, 30)) 

684 self.assertEqual(calc_start_day(datetime(2023, 1, 29), Period(0, 1, 0), bdc), None) 

685 self.assertEqual(calc_start_day(datetime(2023, 1, 28), Period(0, 1, 0), bdc), None) 

686 self.assertEqual(calc_start_day(datetime(2023, 1, 27), Period(0, 1, 0), bdc), datetime(2022, 12, 27)) 

687 self.assertEqual(calc_start_day(datetime(2023, 1, 26), Period(0, 1, 0), bdc), datetime(2022, 12, 26)) 

688 self.assertEqual(calc_start_day(datetime(2023, 1, 1), Period(0, 0, 1), bdc), None) 

689 self.assertEqual(calc_start_day(datetime(2023, 1, 2), Period(0, 0, 1), bdc), datetime(2023, 1, 1)) 

690 bdc = RollConvention.FOLLOWING 

691 self.assertEqual(calc_start_day(datetime(2023, 1, 31), Period(0, 1, 0), bdc), datetime(2022, 12, 31)) 

692 self.assertEqual(calc_start_day(datetime(2023, 1, 30), Period(0, 1, 0), bdc), datetime(2022, 12, 30)) 

693 self.assertEqual(calc_start_day(datetime(2023, 1, 29), Period(0, 1, 0), bdc), None) 

694 self.assertEqual(calc_start_day(datetime(2023, 1, 28), Period(0, 1, 0), bdc), None) 

695 self.assertEqual(calc_start_day(datetime(2023, 1, 27), Period(0, 1, 0), bdc), datetime(2022, 12, 27)) 

696 self.assertEqual(calc_start_day(datetime(2023, 1, 26), Period(0, 1, 0), bdc), datetime(2022, 12, 26)) 

697 self.assertEqual(calc_start_day(datetime(2023, 1, 1), Period(0, 0, 1), bdc), None) 

698 self.assertEqual(calc_start_day(datetime(2023, 1, 2), Period(0, 0, 1), bdc), datetime(2022, 12, 30)) 

699 bdc = RollConvention.PRECEDING 

700 self.assertEqual(calc_start_day(datetime(2023, 1, 31), Period(0, 1, 0), bdc), datetime(2022, 12, 31)) 

701 self.assertEqual(calc_start_day(datetime(2023, 1, 30), Period(0, 1, 0), bdc), datetime(2022, 12, 30)) 

702 self.assertEqual(calc_start_day(datetime(2023, 1, 29), Period(0, 1, 0), bdc), None) 

703 self.assertEqual(calc_start_day(datetime(2023, 1, 28), Period(0, 1, 0), bdc), None) 

704 self.assertEqual(calc_start_day(datetime(2023, 1, 27), Period(0, 1, 0), bdc), datetime(2022, 12, 27)) 

705 self.assertEqual(calc_start_day(datetime(2023, 1, 26), Period(0, 1, 0), bdc), datetime(2022, 12, 26)) 

706 self.assertEqual(calc_start_day(datetime(2023, 1, 1), Period(0, 0, 1), bdc), None) 

707 self.assertEqual(calc_start_day(datetime(2023, 1, 2), Period(0, 0, 1), bdc), datetime(2023, 1, 1)) 

708 

709 def test_last_day_of_month(self): 

710 self.assertEqual(last_day_of_month(datetime(2023, 1, 15)), date(2023, 1, 31)) 

711 self.assertEqual(last_day_of_month(datetime(2023, 2, 15)), date(2023, 2, 28)) 

712 self.assertEqual(last_day_of_month(datetime(2023, 3, 15)), date(2023, 3, 31)) 

713 self.assertEqual(last_day_of_month(datetime(2023, 4, 15)), date(2023, 4, 30)) 

714 self.assertEqual(last_day_of_month(datetime(2023, 5, 15)), date(2023, 5, 31)) 

715 self.assertEqual(last_day_of_month(datetime(2023, 6, 15)), date(2023, 6, 30)) 

716 self.assertEqual(last_day_of_month(datetime(2023, 7, 15)), date(2023, 7, 31)) 

717 self.assertEqual(last_day_of_month(datetime(2023, 8, 15)), date(2023, 8, 31)) 

718 self.assertEqual(last_day_of_month(datetime(2023, 9, 15)), date(2023, 9, 30)) 

719 self.assertEqual(last_day_of_month(datetime(2023, 10, 15)), date(2023, 10, 31)) 

720 self.assertEqual(last_day_of_month(datetime(2023, 11, 15)), date(2023, 11, 30)) 

721 self.assertEqual(last_day_of_month(datetime(2023, 12, 15)), date(2023, 12, 31)) 

722 self.assertEqual(last_day_of_month(datetime(2024, 1, 15)), date(2024, 1, 31)) 

723 self.assertEqual(last_day_of_month(datetime(2024, 2, 15)), date(2024, 2, 29)) 

724 

725 def test_is_last_day_of_month(self): 

726 self.assertEqual(is_last_day_of_month(datetime(2023, 1, 31)), True) 

727 self.assertEqual(is_last_day_of_month(datetime(2023, 2, 28)), True) 

728 self.assertEqual(is_last_day_of_month(datetime(2024, 2, 28)), False) 

729 self.assertEqual(is_last_day_of_month(datetime(2024, 2, 29)), True) 

730 self.assertEqual(is_last_day_of_month(datetime(2023, 3, 31)), True) 

731 self.assertEqual(is_last_day_of_month(datetime(2023, 4, 30)), True) 

732 self.assertEqual(is_last_day_of_month(datetime(2023, 4, 29)), False) 

733 

734 def test_is_business_day(self): 

735 holidays_de = ECB() 

736 self.assertEqual(is_business_day(datetime(2023, 1, 2), holidays_de), True) # New Year's Day observed 

737 self.assertEqual(is_business_day(datetime(2023, 1, 3), holidays_de), True) 

738 self.assertEqual(is_business_day(datetime(2023, 1, 7), holidays_de), False) 

739 self.assertEqual(is_business_day(datetime(2023, 1, 8), holidays_de), False) # Sunday 

740 self.assertEqual(is_business_day(datetime(2023, 1, 9), holidays_de), True) # Monday after New Year's Day 

741 self.assertEqual(is_business_day(datetime(2023, 4, 7), holidays_de), False) # Good Friday 

742 self.assertEqual(is_business_day(datetime(2023, 4, 10), holidays_de), False) # Easter Monday 

743 self.assertEqual(is_business_day(datetime(2023, 5, 1), holidays_de), False) # Labour Day 

744 self.assertEqual(is_business_day(datetime(2023, 5, 18), holidays_de), True) # Ascension Day 

745 self.assertEqual(is_business_day(datetime(2023, 5, 29), holidays_de), True) # Whit Monday 

746 self.assertEqual(is_business_day(datetime(2023, 6, 8), holidays_de), True) # Corpus Christi 

747 self.assertEqual(is_business_day(datetime(2023, 10, 3), holidays_de), True) # German Unity Day 

748 self.assertEqual(is_business_day(datetime(2023, 12, 25), holidays_de), False) # Christmas Day 

749 self.assertEqual(is_business_day(datetime(2023, 12, 26), holidays_de), False) # Second Day of Christmas 

750 self.assertEqual(is_business_day(datetime(2023, 12, 27), holidays_de), True) 

751 self.assertEqual(is_business_day(datetime(2025, 4, 18), holidays_de), False) # Good Friday 

752 holidays_de = DE() 

753 self.assertEqual(is_business_day(datetime(2023, 1, 2), holidays_de), True) # New Year's Day observed 

754 self.assertEqual(is_business_day(datetime(2023, 1, 3), holidays_de), True) 

755 self.assertEqual(is_business_day(datetime(2023, 1, 7), holidays_de), False) 

756 self.assertEqual(is_business_day(datetime(2023, 1, 8), holidays_de), False) # Sunday 

757 self.assertEqual(is_business_day(datetime(2023, 1, 9), holidays_de), True) # Monday after New Year's Day 

758 self.assertEqual(is_business_day(datetime(2023, 4, 7), holidays_de), False) # Good Friday 

759 self.assertEqual(is_business_day(datetime(2023, 4, 10), holidays_de), False) # Easter Monday 

760 self.assertEqual(is_business_day(datetime(2023, 5, 1), holidays_de), False) # Labour Day 

761 self.assertEqual(is_business_day(datetime(2023, 5, 18), holidays_de), False) # Ascension Day 

762 self.assertEqual(is_business_day(datetime(2023, 5, 29), holidays_de), False) # Whit Monday 

763 self.assertEqual(is_business_day(datetime(2023, 6, 8), holidays_de), True) # Corpus Christi 

764 self.assertEqual(is_business_day(datetime(2023, 10, 3), holidays_de), False) # German Unity Day 

765 self.assertEqual(is_business_day(datetime(2023, 12, 25), holidays_de), False) # Christmas Day 

766 self.assertEqual(is_business_day(datetime(2023, 12, 26), holidays_de), False) # Second Day of Christmas 

767 self.assertEqual(is_business_day(datetime(2023, 12, 27), holidays_de), True) 

768 self.assertEqual(is_business_day(datetime(2025, 4, 18), holidays_de), False) # Good Friday 

769 

770 def test_last_business_day_of_month(self): 

771 self.assertEqual(last_business_day_of_month(datetime(2023, 1, 15), ECB()), date(2023, 1, 31)) 

772 self.assertEqual(last_business_day_of_month(datetime(2023, 2, 15), ECB()), date(2023, 2, 28)) 

773 self.assertEqual(last_business_day_of_month(datetime(2023, 3, 15), ECB()), date(2023, 3, 31)) 

774 self.assertEqual(last_business_day_of_month(datetime(2023, 4, 15), ECB()), date(2023, 4, 28)) 

775 self.assertEqual(last_business_day_of_month(datetime(2023, 5, 15), ECB()), date(2023, 5, 31)) 

776 self.assertEqual(last_business_day_of_month(datetime(2023, 6, 15), ECB()), date(2023, 6, 30)) 

777 self.assertEqual(last_business_day_of_month(datetime(2023, 7, 15), ECB()), date(2023, 7, 31)) 

778 self.assertEqual(last_business_day_of_month(datetime(2023, 8, 15), ECB()), date(2023, 8, 31)) 

779 self.assertEqual(last_business_day_of_month(datetime(2023, 9, 15), ECB()), date(2023, 9, 29)) 

780 self.assertEqual(last_business_day_of_month(datetime(2023, 10, 15), ECB()), date(2023, 10, 31)) 

781 self.assertEqual(last_business_day_of_month(datetime(2023, 11, 15), ECB()), date(2023, 11, 30)) 

782 self.assertEqual(last_business_day_of_month(datetime(2023, 12, 15), ECB()), date(2023, 12, 29)) 

783 self.assertEqual(last_business_day_of_month(datetime(2024, 1, 15), ECB()), date(2024, 1, 31)) 

784 self.assertEqual(last_business_day_of_month(datetime(2024, 2, 15), ECB()), date(2024, 2, 29)) 

785 

786 def test_is_last_business_day_of_month(self): 

787 self.assertEqual(is_last_business_day_of_month(datetime(2023, 1, 31), ECB()), True) 

788 self.assertEqual(is_last_business_day_of_month(datetime(2023, 2, 27), ECB()), False) 

789 self.assertEqual(is_last_business_day_of_month(datetime(2023, 3, 31), ECB()), True) 

790 self.assertEqual(is_last_business_day_of_month(datetime(2023, 4, 28), ECB()), True) 

791 self.assertEqual(is_last_business_day_of_month(datetime(2023, 5, 31), ECB()), True) 

792 self.assertEqual(is_last_business_day_of_month(datetime(2023, 6, 30), ECB()), True) 

793 self.assertEqual(is_last_business_day_of_month(datetime(2023, 7, 31), ECB()), True) 

794 self.assertEqual(is_last_business_day_of_month(datetime(2023, 8, 31), ECB()), True) 

795 self.assertEqual(is_last_business_day_of_month(datetime(2023, 9, 29), ECB()), True) 

796 self.assertEqual(is_last_business_day_of_month(datetime(2023, 10, 30), ECB()), False) 

797 self.assertEqual(is_last_business_day_of_month(datetime(2023, 11, 30), ECB()), True) 

798 self.assertEqual(is_last_business_day_of_month(datetime(2023, 12, 30), ECB()), False) 

799 self.assertEqual(is_last_business_day_of_month(datetime(2024, 1, 31), ECB()), True) 

800 self.assertEqual(is_last_business_day_of_month(datetime(2024, 2, 28), ECB()), False) 

801 

802 def test_nearest_business_day(self): 

803 self.assertEqual(nearest_business_day(datetime(2023, 1, 1), ECB()), datetime(2023, 1, 2)) 

804 self.assertEqual(nearest_business_day(datetime(2023, 1, 2), ECB()), datetime(2023, 1, 2)) 

805 self.assertEqual(nearest_business_day(datetime(2023, 1, 3), ECB()), datetime(2023, 1, 3)) 

806 self.assertEqual(nearest_business_day(datetime(2023, 1, 7), ECB()), datetime(2023, 1, 6)) 

807 self.assertEqual(nearest_business_day(datetime(2023, 1, 8), ECB()), datetime(2023, 1, 9)) 

808 self.assertEqual(nearest_business_day(datetime(2023, 1, 9), ECB()), datetime(2023, 1, 9)) 

809 self.assertEqual(nearest_business_day(datetime(2023, 4, 7), ECB()), datetime(2023, 4, 6)) # Good Friday 

810 self.assertEqual(nearest_business_day(datetime(2023, 4, 8), ECB()), datetime(2023, 4, 6)) # Saturday 

811 self.assertEqual(nearest_business_day(datetime(2023, 4, 9), ECB()), datetime(2023, 4, 11)) # Easter Sunday 

812 self.assertEqual(nearest_business_day(datetime(2023, 4, 10), ECB()), datetime(2023, 4, 11)) # Easter Monday 

813 self.assertEqual(nearest_business_day(datetime(2023, 12, 23), ECB()), datetime(2023, 12, 22)) 

814 self.assertEqual(nearest_business_day(datetime(2023, 12, 24), ECB()), datetime(2023, 12, 22)) # Sunday 

815 self.assertEqual(nearest_business_day(datetime(2023, 12, 25), ECB()), datetime(2023, 12, 27)) # Christmas Day 

816 self.assertEqual(nearest_business_day(datetime(2023, 12, 26), ECB()), datetime(2023, 12, 27)) # Second Day of Christmas 

817 self.assertEqual(nearest_business_day(datetime(2023, 12, 27), ECB()), datetime(2023, 12, 27)) 

818 self.assertEqual(nearest_business_day(datetime(2023, 12, 30), ECB()), datetime(2023, 12, 29)) 

819 self.assertEqual(nearest_business_day(datetime(2023, 12, 31), ECB()), datetime(2024, 1, 2)) # New Year's Day observed 

820 

821 def test_nearest_last_business_day_of_month(self): 

822 self.assertEqual(nearest_last_business_day_of_month(datetime(2023, 1, 15), ECB()), datetime(2023, 1, 31)) 

823 self.assertEqual(nearest_last_business_day_of_month(datetime(2023, 1, 30), ECB()), datetime(2023, 1, 31)) 

824 self.assertEqual(nearest_last_business_day_of_month(datetime(2023, 1, 31), ECB()), datetime(2023, 1, 31)) 

825 self.assertEqual(nearest_last_business_day_of_month(datetime(2023, 2, 1), ECB()), datetime(2023, 1, 31)) 

826 self.assertEqual(nearest_last_business_day_of_month(datetime(2023, 2, 14), ECB()), datetime(2023, 2, 28)) 

827 self.assertEqual(nearest_last_business_day_of_month(datetime(2023, 2, 15), ECB()), datetime(2023, 2, 28)) 

828 self.assertEqual(nearest_last_business_day_of_month(datetime(2023, 2, 27), ECB()), datetime(2023, 2, 28)) 

829 self.assertEqual(nearest_last_business_day_of_month(datetime(2023, 2, 28), ECB()), datetime(2023, 2, 28)) 

830 self.assertEqual(nearest_last_business_day_of_month(datetime(2023, 3, 1), ECB()), datetime(2023, 2, 28)) 

831 self.assertEqual(nearest_last_business_day_of_month(datetime(2023, 3, 15), ECB()), datetime(2023, 2, 28)) 

832 self.assertEqual(nearest_last_business_day_of_month(datetime(2023, 3, 15), ECB(), False), datetime(2023, 2, 28)) 

833 self.assertEqual(nearest_last_business_day_of_month(datetime(2023, 3, 16), ECB()), datetime(2023, 3, 31)) 

834 self.assertEqual(nearest_last_business_day_of_month(datetime(2023, 3, 30), ECB()), datetime(2023, 3, 31)) 

835 self.assertEqual(nearest_last_business_day_of_month(datetime(2023, 3, 31), ECB()), datetime(2023, 3, 31)) 

836 self.assertEqual(nearest_last_business_day_of_month(datetime(2023, 4, 1), ECB()), datetime(2023, 3, 31)) 

837 self.assertEqual(nearest_last_business_day_of_month(datetime(2023, 4, 13), ECB()), datetime(2023, 3, 31)) 

838 self.assertEqual(nearest_last_business_day_of_month(datetime(2023, 4, 14), ECB()), datetime(2023, 4, 28)) 

839 

840 def test_next_or_previous_business_day(self): 

841 self.assertEqual(next_or_previous_business_day(datetime(2023, 1, 1), ECB(), True), datetime(2023, 1, 2)) 

842 self.assertEqual(next_or_previous_business_day(datetime(2023, 1, 2), ECB(), True), datetime(2023, 1, 2)) 

843 self.assertEqual(next_or_previous_business_day(datetime(2023, 1, 3), ECB(), True), datetime(2023, 1, 3)) 

844 self.assertEqual(next_or_previous_business_day(datetime(2023, 1, 7), ECB(), True), datetime(2023, 1, 9)) 

845 self.assertEqual(next_or_previous_business_day(datetime(2023, 1, 8), ECB(), True), datetime(2023, 1, 9)) 

846 self.assertEqual(next_or_previous_business_day(datetime(2023, 1, 7), ECB(), False), datetime(2023, 1, 6)) 

847 self.assertEqual(next_or_previous_business_day(datetime(2023, 1, 8), ECB(), False), datetime(2023, 1, 6)) 

848 self.assertEqual(next_or_previous_business_day(datetime(2023, 1, 9), ECB(), True), datetime(2023, 1, 9)) 

849 self.assertEqual(next_or_previous_business_day(datetime(2023, 4, 7), ECB(), True), datetime(2023, 4, 11)) # Good Friday 

850 self.assertEqual(next_or_previous_business_day(datetime(2023, 4, 8), ECB(), True), datetime(2023, 4, 11)) # Saturday 

851 self.assertEqual(next_or_previous_business_day(datetime(2023, 4, 9), ECB(), True), datetime(2023, 4, 11)) # Easter Sunday 

852 self.assertEqual(next_or_previous_business_day(datetime(2023, 4, 10), ECB(), True), datetime(2023, 4, 11)) # Easter Monday 

853 self.assertEqual(next_or_previous_business_day(datetime(2023, 4, 7), ECB(), False), datetime(2023, 4, 6)) # Good Friday 

854 self.assertEqual(next_or_previous_business_day(datetime(2023, 4, 8), ECB(), False), datetime(2023, 4, 6)) # Saturday 

855 self.assertEqual(next_or_previous_business_day(datetime(2023, 4, 9), ECB(), False), datetime(2023, 4, 6)) # Easter Sunday 

856 self.assertEqual(next_or_previous_business_day(datetime(2023, 4, 10), ECB(), False), datetime(2023, 4, 6)) # Easter Monday 

857 self.assertEqual(next_or_previous_business_day(datetime(2023, 12, 23), ECB(), True), datetime(2023, 12, 27)) 

858 self.assertEqual(next_or_previous_business_day(datetime(2023, 12, 24), ECB(), True), datetime(2023, 12, 27)) # Sunday 

859 self.assertEqual(next_or_previous_business_day(datetime(2023, 12, 25), ECB(), True), datetime(2023, 12, 27)) # Christmas Day 

860 self.assertEqual(next_or_previous_business_day(datetime(2023, 12, 26), ECB(), False), datetime(2023, 12, 22)) # Second Day of Christmas 

861 

862 def test_next_IMM_date(self): 

863 self.assertEqual(next_IMM_date(datetime(2023, 1, 15)), date(2023, 3, 15)) 

864 self.assertEqual(next_IMM_date(datetime(2023, 1, 16)), date(2023, 3, 15)) 

865 self.assertEqual(next_IMM_date(datetime(2023, 1, 17)), date(2023, 3, 15)) 

866 self.assertEqual(next_IMM_date(datetime(2023, 1, 18)), date(2023, 3, 15)) 

867 self.assertEqual(next_IMM_date(datetime(2023, 1, 19)), date(2023, 3, 15)) 

868 self.assertEqual(next_IMM_date(datetime(2023, 1, 20)), date(2023, 3, 15)) 

869 self.assertEqual(next_IMM_date(datetime(2023, 1, 21)), date(2023, 3, 15)) 

870 self.assertEqual(next_IMM_date(datetime(2023, 2, 15)), date(2023, 3, 15)) 

871 self.assertEqual(next_IMM_date(datetime(2023, 2, 16)), date(2023, 3, 15)) 

872 self.assertEqual(next_IMM_date(datetime(2023, 2, 17)), date(2023, 3, 15)) 

873 self.assertEqual(next_IMM_date(datetime(2023, 2, 18)), date(2023, 3, 15)) 

874 self.assertEqual(next_IMM_date(datetime(2023, 2, 19)), date(2023, 3, 15)) 

875 self.assertEqual(next_IMM_date(datetime(2023, 2, 20)), date(2023, 3, 15)) 

876 self.assertEqual(next_IMM_date(datetime(2023, 2, 21)), date(2023, 3, 15)) 

877 self.assertEqual(next_IMM_date(datetime(2023, 3, 14)), date(2023, 3, 15)) 

878 self.assertEqual(next_IMM_date(datetime(2023, 3, 15)), date(2023, 6, 21)) 

879 self.assertEqual(next_IMM_date(datetime(2023, 3, 16)), date(2023, 6, 21)) 

880 self.assertEqual(next_IMM_date(datetime(2023, 12, 19)), date(2023, 12, 20)) 

881 self.assertEqual(next_IMM_date(datetime(2023, 12, 20)), date(2024, 3, 20)) 

882 

883 

884if __name__ == "__main__": 

885 main()