mirror of
https://github.com/wassname/catalyst.git
synced 2026-06-27 16:29:48 +08:00
Merge pull request #1408 from quantopian/really-can-i-trade-how-about-now
ENH: Update can_trade to check exchange time
This commit is contained in:
@@ -112,7 +112,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(data_portal,
|
||||
lambda: self.minutes[0],
|
||||
'minute')
|
||||
'minute',
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
@@ -153,7 +154,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
# Volume share slippage should not execute when there is no trade.
|
||||
bar_data = BarData(data_portal,
|
||||
lambda: self.minutes[1],
|
||||
'minute')
|
||||
'minute',
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
@@ -179,7 +181,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: self.minutes[3],
|
||||
self.sim_params.data_frequency)
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
@@ -201,7 +204,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: self.minutes[3],
|
||||
self.sim_params.data_frequency)
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
@@ -223,7 +227,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: self.minutes[3],
|
||||
self.sim_params.data_frequency)
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
@@ -262,7 +267,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: self.minutes[0],
|
||||
self.sim_params.data_frequency)
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
@@ -284,7 +290,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: self.minutes[0],
|
||||
self.sim_params.data_frequency)
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
@@ -306,7 +313,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: self.minutes[1],
|
||||
self.sim_params.data_frequency)
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
@@ -495,7 +503,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
dt = pd.Timestamp('2006-01-05 14:31', tz='UTC')
|
||||
bar_data = BarData(data_portal,
|
||||
lambda: dt,
|
||||
'minute')
|
||||
'minute',
|
||||
self.trading_calendar)
|
||||
_, txn = next(slippage_model.simulate(
|
||||
bar_data,
|
||||
self.ASSET133,
|
||||
@@ -529,7 +538,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: self.minutes[2],
|
||||
self.sim_params.data_frequency)
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
@@ -541,7 +551,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: self.minutes[3],
|
||||
self.sim_params.data_frequency)
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
@@ -564,7 +575,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: self.minutes[2],
|
||||
self.sim_params.data_frequency)
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
@@ -576,7 +588,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: self.minutes[3],
|
||||
self.sim_params.data_frequency)
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
@@ -599,7 +612,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: self.minutes[2],
|
||||
self.sim_params.data_frequency)
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
@@ -611,7 +625,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: self.minutes[3],
|
||||
self.sim_params.data_frequency)
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
@@ -647,7 +662,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: self.minutes[0],
|
||||
self.sim_params.data_frequency)
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
@@ -659,7 +675,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: self.minutes[1],
|
||||
self.sim_params.data_frequency)
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
@@ -682,7 +699,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: self.minutes[0],
|
||||
self.sim_params.data_frequency)
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
@@ -694,7 +712,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: self.minutes[1],
|
||||
self.sim_params.data_frequency)
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
@@ -717,7 +736,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: self.minutes[0],
|
||||
self.sim_params.data_frequency)
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
@@ -729,7 +749,8 @@ class SlippageTestCase(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: self.minutes[1],
|
||||
self.sim_params.data_frequency)
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar)
|
||||
|
||||
orders_txns = list(slippage_model.simulate(
|
||||
bar_data,
|
||||
|
||||
@@ -188,7 +188,9 @@ class TestAPIShim(WithDataPortal, WithSimParams, ZiplineTestCase):
|
||||
)[-1]
|
||||
bar_data = BarData(
|
||||
self.data_portal,
|
||||
lambda: test_end_minute, "minute"
|
||||
lambda: test_end_minute,
|
||||
"minute",
|
||||
self.trading_calendar
|
||||
)
|
||||
ohlcvp_fields = [
|
||||
"open",
|
||||
|
||||
+175
-91
@@ -13,6 +13,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from datetime import timedelta
|
||||
from itertools import chain
|
||||
|
||||
from nose_parameterized import parameterized
|
||||
import numpy as np
|
||||
from numpy import nan
|
||||
@@ -31,6 +33,7 @@ from zipline.testing.fixtures import (
|
||||
WithDataPortal,
|
||||
ZiplineTestCase,
|
||||
)
|
||||
from zipline.utils.calendars import get_calendar
|
||||
|
||||
OHLC = ["open", "high", "low", "close"]
|
||||
OHLCP = OHLC + ["price"]
|
||||
@@ -200,7 +203,8 @@ class TestMinuteBarData(WithBarDataChecks,
|
||||
|
||||
# this entire day is before either asset has started trading
|
||||
for idx, minute in enumerate(minutes):
|
||||
bar_data = BarData(self.data_portal, lambda: minute, "minute")
|
||||
bar_data = BarData(self.data_portal, lambda: minute, "minute",
|
||||
self.trading_calendar)
|
||||
self.check_internal_consistency(bar_data)
|
||||
|
||||
self.assertFalse(bar_data.can_trade(self.ASSET1))
|
||||
@@ -242,7 +246,8 @@ class TestMinuteBarData(WithBarDataChecks,
|
||||
# this test covers the "IPO morning" case, because asset2 only
|
||||
# has data starting on the 10th minute.
|
||||
|
||||
bar_data = BarData(self.data_portal, lambda: minute, "minute")
|
||||
bar_data = BarData(self.data_portal, lambda: minute, "minute",
|
||||
self.trading_calendar)
|
||||
self.check_internal_consistency(bar_data)
|
||||
asset2_has_data = (((idx + 1) % 10) == 0)
|
||||
|
||||
@@ -321,7 +326,8 @@ class TestMinuteBarData(WithBarDataChecks,
|
||||
|
||||
# this is the last day the assets exist
|
||||
for idx, minute in enumerate(minutes):
|
||||
bar_data = BarData(self.data_portal, lambda: minute, "minute")
|
||||
bar_data = BarData(self.data_portal, lambda: minute, "minute",
|
||||
self.trading_calendar)
|
||||
|
||||
self.assertTrue(bar_data.can_trade(self.ASSET1))
|
||||
self.assertTrue(bar_data.can_trade(self.ASSET2))
|
||||
@@ -339,7 +345,8 @@ class TestMinuteBarData(WithBarDataChecks,
|
||||
|
||||
# this entire day is after both assets have stopped trading
|
||||
for idx, minute in enumerate(minutes):
|
||||
bar_data = BarData(self.data_portal, lambda: minute, "minute")
|
||||
bar_data = BarData(self.data_portal, lambda: minute, "minute",
|
||||
self.trading_calendar)
|
||||
|
||||
self.assertFalse(bar_data.can_trade(self.ASSET1))
|
||||
self.assertFalse(bar_data.can_trade(self.ASSET2))
|
||||
@@ -381,7 +388,8 @@ class TestMinuteBarData(WithBarDataChecks,
|
||||
)
|
||||
|
||||
for idx, minute in enumerate(minutes):
|
||||
bar_data = BarData(self.data_portal, lambda: minute, "minute")
|
||||
bar_data = BarData(self.data_portal, lambda: minute, "minute",
|
||||
self.trading_calendar)
|
||||
self.assertEqual(
|
||||
idx + 1,
|
||||
bar_data.current(self.SPLIT_ASSET, "price")
|
||||
@@ -398,14 +406,16 @@ class TestMinuteBarData(WithBarDataChecks,
|
||||
)
|
||||
|
||||
for idx, minute in enumerate(day0_minutes[-10:-1]):
|
||||
bar_data = BarData(self.data_portal, lambda: minute, "minute")
|
||||
bar_data = BarData(self.data_portal, lambda: minute, "minute",
|
||||
self.trading_calendar)
|
||||
self.assertEqual(
|
||||
380,
|
||||
bar_data.current(self.ILLIQUID_SPLIT_ASSET, "price")
|
||||
)
|
||||
|
||||
bar_data = BarData(
|
||||
self.data_portal, lambda: day0_minutes[-1], "minute"
|
||||
self.data_portal, lambda: day0_minutes[-1], "minute",
|
||||
self.trading_calendar
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
@@ -414,7 +424,8 @@ class TestMinuteBarData(WithBarDataChecks,
|
||||
)
|
||||
|
||||
for idx, minute in enumerate(day1_minutes[0:9]):
|
||||
bar_data = BarData(self.data_portal, lambda: minute, "minute")
|
||||
bar_data = BarData(self.data_portal, lambda: minute, "minute",
|
||||
self.trading_calendar)
|
||||
|
||||
# should be half of 390, due to the split
|
||||
self.assertEqual(
|
||||
@@ -433,10 +444,12 @@ class TestMinuteBarData(WithBarDataChecks,
|
||||
tz='US/Eastern'
|
||||
)
|
||||
|
||||
bar_data = BarData(self.data_portal, lambda: day, "minute")
|
||||
bar_data = BarData(self.data_portal, lambda: day, "minute",
|
||||
self.trading_calendar)
|
||||
bar_data2 = BarData(self.data_portal,
|
||||
lambda: eight_fortyfive_am_eastern,
|
||||
"minute")
|
||||
"minute",
|
||||
self.trading_calendar)
|
||||
|
||||
with handle_non_market_minutes(bar_data), \
|
||||
handle_non_market_minutes(bar_data2):
|
||||
@@ -464,91 +477,152 @@ class TestMinuteBarData(WithBarDataChecks,
|
||||
bd.current(self.HILARIOUSLY_ILLIQUID_ASSET, "volume")
|
||||
)
|
||||
|
||||
# FIXME temporarily commenting out until we restore the new can_trade
|
||||
# functionality that checks exchange status
|
||||
# def test_can_trade_during_non_market_hours(self):
|
||||
# # make sure that if we use `can_trade` at midnight, we don't pretend
|
||||
# # we're in the previous day's last minute
|
||||
# the_day_after = self.trading_calendar.next_session_label(
|
||||
# self.equity_minute_bar_days[-1]
|
||||
# )
|
||||
#
|
||||
# bar_data = BarData(self.data_portal, lambda: the_day_after, "minute")
|
||||
#
|
||||
# for asset in [self.ASSET1, self.HILARIOUSLY_ILLIQUID_ASSET]:
|
||||
# self.assertFalse(bar_data.can_trade(asset))
|
||||
#
|
||||
# with handle_non_market_minutes(bar_data):
|
||||
# self.assertFalse(bar_data.can_trade(asset))
|
||||
#
|
||||
# # NYSE is closed at midnight, so even if the asset is alive,
|
||||
# # can_trade should return False
|
||||
# bar_data2 = BarData(
|
||||
# self.data_portal,
|
||||
# lambda: self.equity_minute_bar_days[1],
|
||||
# "minute",
|
||||
# )
|
||||
# for asset in [self.ASSET1, self.HILARIOUSLY_ILLIQUID_ASSET]:
|
||||
# self.assertFalse(bar_data2.can_trade(asset))
|
||||
#
|
||||
# with handle_non_market_minutes(bar_data2):
|
||||
# self.assertFalse(bar_data2.can_trade(asset))
|
||||
def test_can_trade_equity_same_cal_outside_lifetime(self):
|
||||
cal = get_calendar(self.ASSET1.exchange)
|
||||
|
||||
# FIXME temporarily commenting out until we restore the new can_trade
|
||||
# functionality that checks exchange status
|
||||
# def test_can_trade_exchange_closed(self):
|
||||
# nyse_asset = self.asset_finder.retrieve_asset(1)
|
||||
# ice_asset = self.asset_finder.retrieve_asset(6)
|
||||
#
|
||||
# # minutes we're going to check (to verify that that the same bardata
|
||||
# # can check multiple exchange calendars, all times Eastern):
|
||||
# # 2016-01-05:
|
||||
# # 20:00 (minute before ICE opens)
|
||||
# # 20:01 (first minute of ICE session)
|
||||
# # 20:02 (second minute of ICE session)
|
||||
# # 00:00 (Cinderella's ride becomes a pumpkin)
|
||||
# # 2016-01-06:
|
||||
# # 9:30 (minute before NYSE opens)
|
||||
# # 9:31 (first minute of NYSE session)
|
||||
# # 9:32 (second minute of NYSE session)
|
||||
# # 15:59 (second-to-last minute of NYSE session)
|
||||
# # 16:00 (last minute of NYSE session)
|
||||
# # 16:01 (minute after NYSE closed)
|
||||
# # 17:59 (second-to-last minute of ICE session)
|
||||
# # 18:00 (last minute of ICE session)
|
||||
# # 18:01 (minute after ICE closed)
|
||||
#
|
||||
# # each row is dt, whether-nyse-is-open, whether-ice-is-open
|
||||
# minutes_to_check = [
|
||||
# (pd.Timestamp("2016-01-05 20:00", tz="US/Eastern"), False,
|
||||
# False),
|
||||
# (pd.Timestamp("2016-01-05 20:01", tz="US/Eastern"), False, True),
|
||||
# (pd.Timestamp("2016-01-05 20:02", tz="US/Eastern"), False, True),
|
||||
# (pd.Timestamp("2016-01-06 00:00", tz="US/Eastern"), False, True),
|
||||
# (pd.Timestamp("2016-01-06 9:30", tz="US/Eastern"), False, True),
|
||||
# (pd.Timestamp("2016-01-06 9:31", tz="US/Eastern"), True, True),
|
||||
# (pd.Timestamp("2016-01-06 9:32", tz="US/Eastern"), True, True),
|
||||
# (pd.Timestamp("2016-01-06 15:59", tz="US/Eastern"), True, True),
|
||||
# (pd.Timestamp("2016-01-06 16:00", tz="US/Eastern"), True, True),
|
||||
# (pd.Timestamp("2016-01-06 16:01", tz="US/Eastern"), False, True),
|
||||
# (pd.Timestamp("2016-01-06 17:59", tz="US/Eastern"), False, True),
|
||||
# (pd.Timestamp("2016-01-06 18:00", tz="US/Eastern"), False, True),
|
||||
# (pd.Timestamp("2016-01-06 18:01", tz="US/Eastern"), False,
|
||||
# False),
|
||||
# ]
|
||||
#
|
||||
# for info in minutes_to_check:
|
||||
# bar_data = BarData(self.data_portal, lambda: info[0], "minute")
|
||||
# series = bar_data.can_trade([nyse_asset, ice_asset])
|
||||
#
|
||||
# self.assertEqual(info[1], series.loc[nyse_asset])
|
||||
# self.assertEqual(info[2], series.loc[ice_asset])
|
||||
# verify that can_trade returns False for the session before the
|
||||
# asset's first session
|
||||
session_before_asset1_start = cal.previous_session_label(
|
||||
self.ASSET1.start_date
|
||||
)
|
||||
minutes_for_session = cal.minutes_for_session(
|
||||
session_before_asset1_start
|
||||
)
|
||||
|
||||
# for good measure, check the minute before the session too
|
||||
minutes_to_check = chain(
|
||||
[minutes_for_session[0] - pd.Timedelta(minutes=1)],
|
||||
minutes_for_session
|
||||
)
|
||||
|
||||
for minute in minutes_to_check:
|
||||
bar_data = BarData(
|
||||
self.data_portal, lambda: minute, "minute", cal
|
||||
)
|
||||
|
||||
self.assertFalse(bar_data.can_trade(self.ASSET1))
|
||||
|
||||
# after asset lifetime
|
||||
session_after_asset1_end = cal.next_session_label(
|
||||
self.ASSET1.end_date
|
||||
)
|
||||
bts_after_asset1_end = session_after_asset1_end.replace(
|
||||
hour=8, minute=45
|
||||
).tz_convert(None).tz_localize("US/Eastern")
|
||||
|
||||
minutes_to_check = chain(
|
||||
cal.minutes_for_session(session_after_asset1_end),
|
||||
[bts_after_asset1_end]
|
||||
)
|
||||
|
||||
for minute in minutes_to_check:
|
||||
bar_data = BarData(
|
||||
self.data_portal, lambda: minute, "minute", cal
|
||||
)
|
||||
|
||||
self.assertFalse(bar_data.can_trade(self.ASSET1))
|
||||
|
||||
def test_can_trade_equity_same_cal_exchange_closed(self):
|
||||
cal = get_calendar(self.ASSET1.exchange)
|
||||
|
||||
# verify that can_trade returns true for minutes that are
|
||||
# outside the asset's calendar (assuming the asset is alive and
|
||||
# there is a last price), because the asset is alive on the
|
||||
# next market minute.
|
||||
minutes = cal.minutes_for_sessions_in_range(
|
||||
self.ASSET1.start_date,
|
||||
self.ASSET1.end_date
|
||||
)
|
||||
|
||||
for minute in minutes:
|
||||
bar_data = BarData(
|
||||
self.data_portal, lambda: minute, "minute", cal
|
||||
)
|
||||
|
||||
self.assertTrue(bar_data.can_trade(self.ASSET1))
|
||||
|
||||
def test_can_trade_equity_same_cal_no_last_price(self):
|
||||
# self.HILARIOUSLY_ILLIQUID_ASSET's first trade is at
|
||||
# 2016-01-05 15:20:00+00:00. Make sure that can_trade returns false
|
||||
# for all minutes in that session before the first trade, and true
|
||||
# for all minutes afterwards.
|
||||
cal = get_calendar(self.ASSET1.exchange)
|
||||
|
||||
minutes_in_session = cal.minutes_for_session(self.ASSET1.start_date)
|
||||
|
||||
for minute in minutes_in_session[0:49]:
|
||||
bar_data = BarData(
|
||||
self.data_portal, lambda: minute, "minute", cal
|
||||
)
|
||||
|
||||
self.assertFalse(bar_data.can_trade(
|
||||
self.HILARIOUSLY_ILLIQUID_ASSET)
|
||||
)
|
||||
|
||||
for minute in minutes_in_session[50:]:
|
||||
bar_data = BarData(
|
||||
self.data_portal, lambda: minute, "minute", cal
|
||||
)
|
||||
|
||||
self.assertTrue(bar_data.can_trade(
|
||||
self.HILARIOUSLY_ILLIQUID_ASSET)
|
||||
)
|
||||
|
||||
def test_can_trade_multiple_exchange_closed(self):
|
||||
nyse_asset = self.asset_finder.retrieve_asset(1)
|
||||
ice_asset = self.asset_finder.retrieve_asset(6)
|
||||
|
||||
# minutes we're going to check (to verify that that the same bardata
|
||||
# can check multiple exchange calendars, all times Eastern):
|
||||
# 2016-01-05:
|
||||
# 20:00 (minute before ICE opens)
|
||||
# 20:01 (first minute of ICE session)
|
||||
# 20:02 (second minute of ICE session)
|
||||
# 00:00 (Cinderella's ride becomes a pumpkin)
|
||||
# 2016-01-06:
|
||||
# 9:30 (minute before NYSE opens)
|
||||
# 9:31 (first minute of NYSE session)
|
||||
# 9:32 (second minute of NYSE session)
|
||||
# 15:59 (second-to-last minute of NYSE session)
|
||||
# 16:00 (last minute of NYSE session)
|
||||
# 16:01 (minute after NYSE closed)
|
||||
# 17:59 (second-to-last minute of ICE session)
|
||||
# 18:00 (last minute of ICE session)
|
||||
# 18:01 (minute after ICE closed)
|
||||
|
||||
# each row is dt, whether-nyse-is-open, whether-ice-is-open
|
||||
minutes_to_check = [
|
||||
(pd.Timestamp("2016-01-05 20:00", tz="US/Eastern"), False, False),
|
||||
(pd.Timestamp("2016-01-05 20:01", tz="US/Eastern"), False, True),
|
||||
(pd.Timestamp("2016-01-05 20:02", tz="US/Eastern"), False, True),
|
||||
(pd.Timestamp("2016-01-06 00:00", tz="US/Eastern"), False, True),
|
||||
(pd.Timestamp("2016-01-06 9:30", tz="US/Eastern"), False, True),
|
||||
(pd.Timestamp("2016-01-06 9:31", tz="US/Eastern"), True, True),
|
||||
(pd.Timestamp("2016-01-06 9:32", tz="US/Eastern"), True, True),
|
||||
(pd.Timestamp("2016-01-06 15:59", tz="US/Eastern"), True, True),
|
||||
(pd.Timestamp("2016-01-06 16:00", tz="US/Eastern"), True, True),
|
||||
(pd.Timestamp("2016-01-06 16:01", tz="US/Eastern"), False, True),
|
||||
(pd.Timestamp("2016-01-06 17:59", tz="US/Eastern"), False, True),
|
||||
(pd.Timestamp("2016-01-06 18:00", tz="US/Eastern"), False, True),
|
||||
(pd.Timestamp("2016-01-06 18:01", tz="US/Eastern"), False, False),
|
||||
]
|
||||
|
||||
for info in minutes_to_check:
|
||||
# use the CME calendar, which covers 24 hours
|
||||
bar_data = BarData(self.data_portal, lambda: info[0], "minute",
|
||||
trading_calendar=get_calendar("CME"))
|
||||
|
||||
series = bar_data.can_trade([nyse_asset, ice_asset])
|
||||
|
||||
self.assertEqual(info[1], series.loc[nyse_asset])
|
||||
self.assertEqual(info[2], series.loc[ice_asset])
|
||||
|
||||
def test_is_stale_during_non_market_hours(self):
|
||||
bar_data = BarData(
|
||||
self.data_portal,
|
||||
lambda: self.equity_minute_bar_days[1],
|
||||
"minute",
|
||||
self.trading_calendar
|
||||
)
|
||||
|
||||
with handle_non_market_minutes(bar_data):
|
||||
@@ -578,7 +652,8 @@ class TestMinuteBarData(WithBarDataChecks,
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: eight_fortyfive_am_eastern,
|
||||
"minute")
|
||||
"minute",
|
||||
self.trading_calendar)
|
||||
|
||||
expected = {
|
||||
'open': 391 / 2.0,
|
||||
@@ -743,7 +818,8 @@ class TestDailyBarData(WithBarDataChecks,
|
||||
)
|
||||
)
|
||||
|
||||
bar_data = BarData(self.data_portal, lambda: minute, "daily")
|
||||
bar_data = BarData(self.data_portal, lambda: minute, "daily",
|
||||
self.trading_calendar)
|
||||
self.check_internal_consistency(bar_data)
|
||||
|
||||
self.assertFalse(bar_data.can_trade(self.ASSET1))
|
||||
@@ -771,6 +847,7 @@ class TestDailyBarData(WithBarDataChecks,
|
||||
self.equity_daily_bar_days[0]
|
||||
),
|
||||
"daily",
|
||||
self.trading_calendar
|
||||
)
|
||||
self.check_internal_consistency(bar_data)
|
||||
|
||||
@@ -808,6 +885,7 @@ class TestDailyBarData(WithBarDataChecks,
|
||||
self.equity_daily_bar_days[1]
|
||||
),
|
||||
"daily",
|
||||
self.trading_calendar
|
||||
)
|
||||
self.check_internal_consistency(bar_data)
|
||||
|
||||
@@ -834,6 +912,7 @@ class TestDailyBarData(WithBarDataChecks,
|
||||
self.equity_daily_bar_days[-1]
|
||||
),
|
||||
"daily",
|
||||
self.trading_calendar
|
||||
)
|
||||
self.check_internal_consistency(bar_data)
|
||||
|
||||
@@ -862,7 +941,8 @@ class TestDailyBarData(WithBarDataChecks,
|
||||
def test_after_assets_dead(self):
|
||||
session = self.END_DATE
|
||||
|
||||
bar_data = BarData(self.data_portal, lambda: session, "daily")
|
||||
bar_data = BarData(self.data_portal, lambda: session, "daily",
|
||||
self.trading_calendar)
|
||||
self.check_internal_consistency(bar_data)
|
||||
|
||||
for asset in self.ASSETS:
|
||||
@@ -916,6 +996,7 @@ class TestDailyBarData(WithBarDataChecks,
|
||||
self.data_portal,
|
||||
lambda: self.equity_daily_bar_days[0],
|
||||
"daily",
|
||||
self.trading_calendar
|
||||
)
|
||||
self.assertEqual(
|
||||
liquid_day_0_price,
|
||||
@@ -925,6 +1006,7 @@ class TestDailyBarData(WithBarDataChecks,
|
||||
self.data_portal,
|
||||
lambda: self.equity_daily_bar_days[1],
|
||||
"daily",
|
||||
self.trading_calendar
|
||||
)
|
||||
self.assertEqual(
|
||||
liquid_day_1_price,
|
||||
@@ -937,6 +1019,7 @@ class TestDailyBarData(WithBarDataChecks,
|
||||
self.data_portal,
|
||||
lambda: self.equity_daily_bar_days[1],
|
||||
"daily",
|
||||
self.trading_calendar
|
||||
)
|
||||
self.assertEqual(
|
||||
illiquid_day_0_price, bar_data.current(illiquid_asset, "price")
|
||||
@@ -946,6 +1029,7 @@ class TestDailyBarData(WithBarDataChecks,
|
||||
self.data_portal,
|
||||
lambda: self.equity_daily_bar_days[2],
|
||||
"daily",
|
||||
self.trading_calendar
|
||||
)
|
||||
|
||||
# 3 (price from previous day) * 0.5 (split ratio)
|
||||
|
||||
@@ -223,6 +223,7 @@ class BlotterTestCase(WithLogger,
|
||||
self.data_portal,
|
||||
lambda: self.sim_params.sessions[-1],
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar
|
||||
)
|
||||
txns, _, closed_orders = blotter.get_transactions(bar_data)
|
||||
for txn in txns:
|
||||
@@ -298,6 +299,7 @@ class BlotterTestCase(WithLogger,
|
||||
self.data_portal,
|
||||
lambda: dt,
|
||||
self.sim_params.data_frequency,
|
||||
self.trading_calendar
|
||||
)
|
||||
txns, _, _ = blotter.get_transactions(bar_data)
|
||||
for txn in txns:
|
||||
|
||||
@@ -319,7 +319,8 @@ class FinanceTestCase(WithLogger,
|
||||
bar_data = BarData(
|
||||
data_portal,
|
||||
lambda: tick,
|
||||
sim_params.data_frequency
|
||||
sim_params.data_frequency,
|
||||
self.trading_calendar
|
||||
)
|
||||
txns, _, closed_orders = blotter.get_transactions(bar_data)
|
||||
for txn in txns:
|
||||
|
||||
+26
-13
@@ -251,7 +251,8 @@ class WithHistory(WithDataPortal):
|
||||
fields = fields if fields is not None else ALL_FIELDS
|
||||
assets = assets if assets is not None else [self.ASSET2, self.ASSET3]
|
||||
|
||||
bar_data = BarData(self.data_portal, lambda: dt, mode)
|
||||
bar_data = BarData(self.data_portal, lambda: dt, mode,
|
||||
self.trading_calendar)
|
||||
check_internal_consistency(
|
||||
bar_data, assets, fields, 10, freq
|
||||
)
|
||||
@@ -703,7 +704,8 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase):
|
||||
)[0:60]
|
||||
|
||||
for idx, minute in enumerate(minutes):
|
||||
bar_data = BarData(self.data_portal, lambda: minute, 'minute')
|
||||
bar_data = BarData(self.data_portal, lambda: minute, 'minute',
|
||||
self.trading_calendar)
|
||||
check_internal_consistency(
|
||||
bar_data, [self.ASSET2, self.ASSET3], ALL_FIELDS, 10, '1m'
|
||||
)
|
||||
@@ -765,10 +767,12 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase):
|
||||
)[1]
|
||||
|
||||
midnight_bar_data = \
|
||||
BarData(self.data_portal, lambda: midnight, 'minute')
|
||||
BarData(self.data_portal, lambda: midnight, 'minute',
|
||||
self.trading_calendar)
|
||||
|
||||
yesterday_bar_data = \
|
||||
BarData(self.data_portal, lambda: last_minute, 'minute')
|
||||
BarData(self.data_portal, lambda: last_minute, 'minute',
|
||||
self.trading_calendar)
|
||||
|
||||
with handle_non_market_minutes(midnight_bar_data):
|
||||
for field in ALL_FIELDS:
|
||||
@@ -785,7 +789,8 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase):
|
||||
)[0:60]
|
||||
|
||||
for idx, minute in enumerate(minutes):
|
||||
bar_data = BarData(self.data_portal, lambda: minute, 'minute')
|
||||
bar_data = BarData(self.data_portal, lambda: minute, 'minute',
|
||||
self.trading_calendar)
|
||||
check_internal_consistency(
|
||||
bar_data, self.SHORT_ASSET, ALL_FIELDS, 30, '1m'
|
||||
)
|
||||
@@ -794,7 +799,8 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase):
|
||||
data_portal = self.make_data_portal()
|
||||
|
||||
# choose a window that contains the last minute of the asset
|
||||
bar_data = BarData(data_portal, lambda: minutes[15], 'minute')
|
||||
bar_data = BarData(data_portal, lambda: minutes[15], 'minute',
|
||||
self.trading_calendar)
|
||||
|
||||
# close high low open price volume
|
||||
# 2015-01-06 20:47:00+00:00 768 770 767 769 768 76800
|
||||
@@ -1006,7 +1012,8 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase):
|
||||
def test_passing_iterable_to_history_regular_hours(self):
|
||||
# regular hours
|
||||
current_dt = pd.Timestamp("2015-01-06 9:45", tz='US/Eastern')
|
||||
bar_data = BarData(self.data_portal, lambda: current_dt, "minute")
|
||||
bar_data = BarData(self.data_portal, lambda: current_dt, "minute",
|
||||
self.trading_calendar)
|
||||
|
||||
bar_data.history(pd.Index([self.ASSET1, self.ASSET2]),
|
||||
"high", 5, "1m")
|
||||
@@ -1014,7 +1021,8 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase):
|
||||
def test_passing_iterable_to_history_bts(self):
|
||||
# before market hours
|
||||
current_dt = pd.Timestamp("2015-01-07 8:45", tz='US/Eastern')
|
||||
bar_data = BarData(self.data_portal, lambda: current_dt, "minute")
|
||||
bar_data = BarData(self.data_portal, lambda: current_dt, "minute",
|
||||
self.trading_calendar)
|
||||
|
||||
with handle_non_market_minutes(bar_data):
|
||||
bar_data.history(pd.Index([self.ASSET1, self.ASSET2]),
|
||||
@@ -1023,7 +1031,8 @@ class MinuteEquityHistoryTestCase(WithHistory, ZiplineTestCase):
|
||||
def test_overnight_adjustments(self):
|
||||
# Should incorporate adjustments on midnight 01/06
|
||||
current_dt = pd.Timestamp('2015-01-06 8:45', tz='US/Eastern')
|
||||
bar_data = BarData(self.data_portal, lambda: current_dt, 'minute')
|
||||
bar_data = BarData(self.data_portal, lambda: current_dt, 'minute',
|
||||
self.trading_calendar)
|
||||
|
||||
adj_expected = {
|
||||
'open': np.arange(8381, 8391) / 4.0,
|
||||
@@ -1394,7 +1403,8 @@ class DailyEquityHistoryTestCase(WithHistory, ZiplineTestCase):
|
||||
)
|
||||
|
||||
for idx, day in enumerate(days):
|
||||
bar_data = BarData(self.data_portal, lambda: day, 'daily')
|
||||
bar_data = BarData(self.data_portal, lambda: day, 'daily',
|
||||
self.trading_calendar)
|
||||
check_internal_consistency(
|
||||
bar_data, [self.ASSET2, self.ASSET3], ALL_FIELDS, 10, '1d'
|
||||
)
|
||||
@@ -1437,7 +1447,8 @@ class DailyEquityHistoryTestCase(WithHistory, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: pd.Timestamp('2016-01-06', tz='UTC'),
|
||||
'daily')
|
||||
'daily',
|
||||
self.trading_calendar)
|
||||
|
||||
for field in OHLCP:
|
||||
window = bar_data.history(
|
||||
@@ -1475,7 +1486,8 @@ class DailyEquityHistoryTestCase(WithHistory, ZiplineTestCase):
|
||||
|
||||
# days has 1/7, 1/8
|
||||
for idx, day in enumerate(days):
|
||||
bar_data = BarData(self.data_portal, lambda: day, 'daily')
|
||||
bar_data = BarData(self.data_portal, lambda: day, 'daily',
|
||||
self.trading_calendar)
|
||||
check_internal_consistency(
|
||||
bar_data, self.SHORT_ASSET, ALL_FIELDS, 2, '1d'
|
||||
)
|
||||
@@ -1629,7 +1641,8 @@ class DailyEquityHistoryTestCase(WithHistory, ZiplineTestCase):
|
||||
|
||||
bar_data = BarData(self.data_portal,
|
||||
lambda: pd.Timestamp('2016-01-06 16:00', tz='UTC'),
|
||||
'daily')
|
||||
'daily',
|
||||
self.trading_calendar)
|
||||
|
||||
for field in OHLCP:
|
||||
window = bar_data.history(
|
||||
|
||||
+38
-16
@@ -165,14 +165,12 @@ cdef class BarData:
|
||||
cdef object _last_calculated_universe
|
||||
cdef object _universe_last_updated_at
|
||||
cdef bool _daily_mode
|
||||
cdef object _trading_calendar
|
||||
|
||||
cdef bool _adjust_minutes
|
||||
|
||||
def __init__(self, data_portal, simulation_dt_func, data_frequency,
|
||||
universe_func=None):
|
||||
"""
|
||||
|
||||
"""
|
||||
trading_calendar, universe_func=None):
|
||||
self.data_portal = data_portal
|
||||
self.simulation_dt_func = simulation_dt_func
|
||||
self.data_frequency = data_frequency
|
||||
@@ -186,6 +184,8 @@ cdef class BarData:
|
||||
|
||||
self._adjust_minutes = False
|
||||
|
||||
self._trading_calendar = trading_calendar
|
||||
|
||||
cdef _get_equity_price_view(self, asset):
|
||||
"""
|
||||
Returns a DataPortalSidView for the given asset. Used to support the
|
||||
@@ -428,9 +428,25 @@ cdef class BarData:
|
||||
"""
|
||||
For the given asset or iterable of assets, returns true if all of the
|
||||
following are true:
|
||||
- the asset is alive at the current simulation time
|
||||
- the asset's exchange is open at the current simulation time
|
||||
- there is a known last price for the asset.
|
||||
1) the asset is alive for the session of the current simulation time
|
||||
(if current simulation time is not a market minute, we use the next
|
||||
session)
|
||||
2) (if we are in minute mode) the asset's exchange is open at the
|
||||
current simulation time or at the simulation calendar's next market
|
||||
minute
|
||||
3) there is a known last price for the asset.
|
||||
|
||||
Notes
|
||||
-----
|
||||
The second condition above warrants some further explanation.
|
||||
- If the asset's exchange calendar is identical to the simulation
|
||||
calendar, then this condition always returns True.
|
||||
- If there are market minutes in the simulation calendar outside of
|
||||
this asset's exchange's trading hours (for example, if the simulation
|
||||
is running on the CME calendar but the asset is MSFT, which trades on
|
||||
the NYSE), during those minutes, this condition will return false
|
||||
(for example, 3:15 am Eastern on a weekday, during which the CME is
|
||||
open but the NYSE is closed).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
@@ -462,20 +478,26 @@ cdef class BarData:
|
||||
})
|
||||
|
||||
cdef bool _can_trade_for_asset(self, asset, dt, adjusted_dt, data_portal):
|
||||
session_label = normalize_date(dt) # FIXME
|
||||
cdef object session_label
|
||||
cdef object dt_to_use_for_exchange_check,
|
||||
|
||||
session_label = self._trading_calendar.minute_to_session_label(dt)
|
||||
|
||||
if not asset.is_alive_for_session(session_label):
|
||||
# asset isn't alive
|
||||
return False
|
||||
|
||||
# FIXME temporarily commenting out while we sort out some downstream
|
||||
# dependencies
|
||||
# if not asset.is_exchange_open(dt):
|
||||
# # exchange isn't open
|
||||
# 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.
|
||||
if self._trading_calendar.is_open_on_minute(dt):
|
||||
dt_to_use_for_exchange_check = dt
|
||||
else:
|
||||
dt_to_use_for_exchange_check = \
|
||||
self._trading_calendar.next_open(dt)
|
||||
|
||||
if isinstance(asset, Future):
|
||||
# FIXME: this will get removed once we can get prices for futures
|
||||
return True
|
||||
if not asset.is_exchange_open(dt_to_use_for_exchange_check):
|
||||
return False
|
||||
|
||||
# is there a last price?
|
||||
return not np.isnan(
|
||||
|
||||
@@ -88,6 +88,7 @@ class AlgorithmSimulator(object):
|
||||
data_portal=self.data_portal,
|
||||
simulation_dt_func=self.get_simulation_dt,
|
||||
data_frequency=self.sim_params.data_frequency,
|
||||
trading_calendar=self.algo.trading_calendar,
|
||||
universe_func=universe_func
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user