mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-01 20:21:50 +08:00
Merge pull request #1134 from quantopian/week_start
BUG: Recalculate trigger for week rule if we miss the first one
This commit is contained in:
+31
-10
@@ -369,15 +369,29 @@ class TestStatelessRules(RuleTestCase):
|
||||
self.assertEqual(m, first_of_week + timedelta(days=4-n))
|
||||
self.assertEqual(n_triggered, 1)
|
||||
|
||||
@parameter_space(n=(0, 1, 2, 3, 4), type=('week_start', 'week_end'))
|
||||
def test_edge_cases_for_TradingDayOfWeek(self, n, type):
|
||||
@parameter_space(
|
||||
rule_offset=(0, 1, 2, 3, 4),
|
||||
start_offset=(0, 1, 2, 3, 4),
|
||||
type=('week_start', 'week_end')
|
||||
)
|
||||
def test_edge_cases_for_TradingDayOfWeek(self,
|
||||
rule_offset,
|
||||
start_offset,
|
||||
type):
|
||||
"""
|
||||
Test that we account for midweek holidays. Monday 01/20 is a holiday.
|
||||
Ensure that the trigger date for that week is adjustmented
|
||||
appropriately, or thrown out if not enough trading days.
|
||||
Ensure that the trigger date for that week is adjusted
|
||||
appropriately, or thrown out if not enough trading days. Also, test
|
||||
that if we start the simulation on a day where we miss the trigger
|
||||
for that week, that the trigger is recalculated for next week.
|
||||
"""
|
||||
|
||||
sim_start = pd.Timestamp('01-06-2014 14:31:00', tz='UTC') + \
|
||||
timedelta(days=start_offset)
|
||||
|
||||
jan_minutes = self.env.minutes_for_days_in_range(
|
||||
datetime.date(year=2014, month=1, day=6),
|
||||
datetime.date(year=2014, month=1, day=6) +
|
||||
timedelta(days=start_offset),
|
||||
datetime.date(year=2014, month=1, day=31)
|
||||
)
|
||||
|
||||
@@ -391,7 +405,8 @@ class TestStatelessRules(RuleTestCase):
|
||||
pd.Timestamp('2014-01-21 14:31:00', tz='UTC'),
|
||||
pd.Timestamp('2014-01-27 14:31:00', tz='UTC'),
|
||||
]
|
||||
trigger_dates = [x + timedelta(days=n) for x in trigger_dates]
|
||||
trigger_dates = \
|
||||
[x + timedelta(days=rule_offset) for x in trigger_dates]
|
||||
else:
|
||||
rule = NDaysBeforeLastTradingDayOfWeek
|
||||
# Expect to trigger on the last trading day of the week, minus the
|
||||
@@ -402,16 +417,22 @@ class TestStatelessRules(RuleTestCase):
|
||||
pd.Timestamp('2014-01-24 14:31:00', tz='UTC'),
|
||||
pd.Timestamp('2014-01-31 14:31:00', tz='UTC'),
|
||||
]
|
||||
trigger_dates = [x - timedelta(days=n) for x in trigger_dates]
|
||||
trigger_dates = \
|
||||
[x - timedelta(days=rule_offset) for x in trigger_dates]
|
||||
|
||||
should_trigger = partial(
|
||||
rule(n).should_trigger, env=self.env
|
||||
rule(rule_offset).should_trigger, env=self.env
|
||||
)
|
||||
|
||||
# If offset is 4, there is not enough trading days in the short week,
|
||||
# and so it should not trigger
|
||||
if n == 4:
|
||||
if rule_offset == 4:
|
||||
del trigger_dates[2]
|
||||
|
||||
# Filter out trigger dates that happen before the simulation starts
|
||||
trigger_dates = [x for x in trigger_dates if x >= sim_start]
|
||||
|
||||
expected_n_triggered = len(trigger_dates)
|
||||
trigger_dates = iter(trigger_dates)
|
||||
|
||||
n_triggered = 0
|
||||
@@ -420,7 +441,7 @@ class TestStatelessRules(RuleTestCase):
|
||||
self.assertEqual(m, next(trigger_dates))
|
||||
n_triggered += 1
|
||||
|
||||
self.assertEqual(n_triggered, 4 if n != 4 else 3)
|
||||
self.assertEqual(n_triggered, expected_n_triggered)
|
||||
|
||||
@subtest(param_range(MAX_MONTH_RANGE), 'n')
|
||||
def test_NthTradingDayOfMonth(self, n):
|
||||
|
||||
+11
-6
@@ -445,8 +445,7 @@ class TradingDayOfWeekRule(six.with_metaclass(ABCMeta, StatelessRule)):
|
||||
|
||||
# If after applying the offset to the start/end day of the week, we get
|
||||
# day in a different week, skip this week and go on to the next
|
||||
while next_trading_day.date().isocalendar()[1] != \
|
||||
dt.date().isocalendar()[1]:
|
||||
while next_trading_day.isocalendar()[1] != dt.isocalendar()[1]:
|
||||
dt += datetime.timedelta(days=7)
|
||||
next_trading_day = _coerce_datetime(
|
||||
env.add_trading_days(
|
||||
@@ -458,15 +457,21 @@ class TradingDayOfWeekRule(six.with_metaclass(ABCMeta, StatelessRule)):
|
||||
next_open, next_close = env.get_open_and_close(next_trading_day)
|
||||
self.next_date_start = next_open
|
||||
self.next_date_end = next_close
|
||||
self.next_midnight_timestamp = \
|
||||
pd.Timestamp(next_trading_day.date(), tz='UTC')
|
||||
self.next_midnight_timestamp = next_trading_day
|
||||
|
||||
def should_trigger(self, dt, env):
|
||||
if self.next_date_start is None:
|
||||
# first time this method has been called. calculate the midnight,
|
||||
# open, and close of the next matching day.
|
||||
# First time this method has been called. Calculate the midnight,
|
||||
# open, and close for the first trigger, which occurs on the week
|
||||
# of the simulation start
|
||||
self.calculate_start_and_end(dt, env)
|
||||
|
||||
# If we've missed the first trigger because it occurs before the
|
||||
# simulation starts, recalculate for the next week
|
||||
if dt > self.next_date_end:
|
||||
self.calculate_start_and_end(dt + datetime.timedelta(days=7),
|
||||
env)
|
||||
|
||||
# if the given dt is within the next matching day, return true. Also
|
||||
# calculate the start and end dates for the next trigger
|
||||
if self.next_date_start <= dt <= self.next_date_end or \
|
||||
|
||||
Reference in New Issue
Block a user