Merge pull request #1644 from quantopian/closed-means-closed

Don't allow ordering assets after their auto close date
This commit is contained in:
David Michalowicz
2017-01-20 10:19:27 -05:00
committed by GitHub
4 changed files with 77 additions and 25 deletions
+29 -13
View File
@@ -4294,6 +4294,7 @@ class TestOrderAfterDelist(WithTradingEnvironment, ZiplineTestCase):
def make_equity_info(cls):
return pd.DataFrame.from_dict(
{
# Asset whose auto close date is after its end date.
1: {
'start_date': cls.start,
'end_date': cls.day_1,
@@ -4301,6 +4302,14 @@ class TestOrderAfterDelist(WithTradingEnvironment, ZiplineTestCase):
'symbol': "ASSET1",
'exchange': "TEST",
},
# Asset whose auto close date is before its end date.
2: {
'start_date': cls.start,
'end_date': cls.day_4,
'auto_close_date': cls.day_1,
'symbol': 'ASSET2',
'exchange': 'TEST',
},
},
orient='index',
)
@@ -4310,7 +4319,13 @@ class TestOrderAfterDelist(WithTradingEnvironment, ZiplineTestCase):
super(TestOrderAfterDelist, cls).init_class_fixtures()
cls.data_portal = FakeDataPortal(cls.env)
def test_order_in_quiet_period(self):
@parameterized.expand([
('auto_close_after_end_date', 1),
('auto_close_before_end_date', 2),
])
def test_order_in_quiet_period(self, name, sid):
asset = self.asset_finder.retrieve_asset(sid)
algo_code = dedent("""
from zipline.api import (
sid,
@@ -4326,13 +4341,13 @@ class TestOrderAfterDelist(WithTradingEnvironment, ZiplineTestCase):
pass
def handle_data(context, data):
order(sid(1), 1)
order_value(sid(1), 100)
order_percent(sid(1), 0.5)
order_target(sid(1), 50)
order_target_percent(sid(1), 0.5)
order_target_value(sid(1), 50)
""")
order(sid({sid}), 1)
order_value(sid({sid}), 100)
order_percent(sid({sid}), 0.5)
order_target(sid({sid}), 50)
order_target_percent(sid({sid}), 0.5)
order_target_value(sid({sid}), 50)
""").format(sid=sid)
# run algo from 1/6 to 1/7
algo = TradingAlgorithm(
@@ -4356,11 +4371,12 @@ class TestOrderAfterDelist(WithTradingEnvironment, ZiplineTestCase):
self.assertEqual(6 * 390, len(warnings))
for w in warnings:
self.assertEqual("Cannot place order for ASSET1, as it has "
"de-listed. Any existing positions for this "
"asset will be liquidated on "
"2016-01-11 00:00:00+00:00.",
w.message)
expected_message = (
'Cannot place order for ASSET{sid}, as it has de-listed. '
'Any existing positions for this asset will be liquidated '
'on {date}.'.format(sid=sid, date=asset.auto_close_date)
)
self.assertEqual(expected_message, w.message)
class AlgoInputValidationTestCase(ZiplineTestCase):
+41 -8
View File
@@ -716,9 +716,9 @@ class TestMinuteBarData(WithCreateBarData,
self.assertEqual(bar_data.can_trade(self.ASSET1), info[1])
class TestMinuteBarDataMultipleExchanges(WithCreateBarData,
WithBarDataChecks,
ZiplineTestCase):
class TestMinuteBarDataFuturesCalendar(WithCreateBarData,
WithBarDataChecks,
ZiplineTestCase):
START_DATE = pd.Timestamp('2016-01-05', tz='UTC')
END_DATE = ASSET_FINDER_EQUITY_END_DATE = pd.Timestamp(
@@ -742,20 +742,29 @@ class TestMinuteBarDataMultipleExchanges(WithCreateBarData,
return pd.DataFrame.from_dict(
{
6: {
'symbol': 'CLG06',
'symbol': 'CLH16',
'root_symbol': 'CL',
'start_date': pd.Timestamp('2005-12-01', tz='UTC'),
'notice_date': pd.Timestamp('2005-12-20', tz='UTC'),
'expiration_date': pd.Timestamp('2006-01-20', tz='UTC'),
'start_date': pd.Timestamp('2016-01-04', tz='UTC'),
'notice_date': pd.Timestamp('2016-01-19', tz='UTC'),
'expiration_date': pd.Timestamp('2016-02-19', tz='UTC'),
'exchange': 'ICEUS',
},
7: {
'symbol': 'FVH16',
'root_symbol': 'FV',
'start_date': pd.Timestamp('2016-01-04', tz='UTC'),
'notice_date': pd.Timestamp('2016-01-22', tz='UTC'),
'expiration_date': pd.Timestamp('2016-02-22', tz='UTC'),
'auto_close_date': pd.Timestamp('2016-01-20', tz='UTC'),
'exchange': 'CME',
},
},
orient='index',
)
@classmethod
def init_class_fixtures(cls):
super(TestMinuteBarDataMultipleExchanges, cls).init_class_fixtures()
super(TestMinuteBarDataFuturesCalendar, cls).init_class_fixtures()
cls.trading_calendar = get_calendar('CME')
def test_can_trade_multiple_exchange_closed(self):
@@ -808,6 +817,30 @@ class TestMinuteBarDataMultipleExchanges(WithCreateBarData,
self.assertEqual(info[1], series.loc[nyse_asset])
self.assertEqual(info[2], series.loc[ice_asset])
def test_can_trade_delisted(self):
"""
Test that can_trade returns False for an asset on or after its auto
close date.
"""
auto_closing_asset = self.asset_finder.retrieve_asset(7)
# Our asset's auto close date is 2016-01-20, which means that as of the
# market open for the 2016-01-20 session, `can_trade` should return
# False.
minutes_to_check = [
(pd.Timestamp('2016-01-19 00:00:00', tz='UTC'), True),
(pd.Timestamp('2016-01-19 23:00:00', tz='UTC'), True),
(pd.Timestamp('2016-01-19 23:01:00', tz='UTC'), False),
(pd.Timestamp('2016-01-19 23:59:00', tz='UTC'), False),
(pd.Timestamp('2016-01-20 00:00:00', tz='UTC'), False),
(pd.Timestamp('2016-01-20 00:01:00', tz='UTC'), False),
(pd.Timestamp('2016-01-21 00:00:00', tz='UTC'), False),
]
for info in minutes_to_check:
bar_data = self.create_bardata(simulation_dt_func=lambda: info[0])
self.assertEqual(bar_data.can_trade(auto_closing_asset), info[1])
class TestDailyBarData(WithCreateBarData,
WithBarDataChecks,
+3
View File
@@ -508,6 +508,9 @@ cdef class BarData:
# asset isn't alive
return False
if asset.auto_close_date and session_label >= asset.auto_close_date:
return False
if not self._daily_mode:
# Find the next market minute for this calendar, and check if this
# asset's exchange is open at that minute.
+4 -4
View File
@@ -1348,10 +1348,10 @@ class TradingAlgorithm(object):
if asset.auto_close_date:
day = normalize_date(self.get_datetime())
if asset.end_date < day < asset.auto_close_date:
# we are between the asset's end date and auto close date,
# so warn the user that they can't place an order for this
# asset, and return None.
if day > min(asset.end_date, asset.auto_close_date):
# If we are after the asset's end date or auto close date, warn
# the user that they can't place an order for this asset, and
# return None.
log.warn("Cannot place order for {0}, as it has de-listed. "
"Any existing positions for this asset will be "
"liquidated on "