mirror of
https://github.com/wassname/catalyst.git
synced 2026-06-30 13:27:48 +08:00
Merge pull request #1346 from quantopian/check-exchange-time
ENH: time/calendar business logic improvements
This commit is contained in:
@@ -56,13 +56,13 @@ class CalendarRegistrationTestCase(TestCase):
|
||||
dummy_cal = self.dummy_cal_type('DMY')
|
||||
|
||||
# Try to register and retrieve the calendar
|
||||
register_calendar(dummy_cal)
|
||||
register_calendar('DMY', dummy_cal)
|
||||
retr_cal = get_calendar('DMY')
|
||||
self.assertEqual(dummy_cal, retr_cal)
|
||||
|
||||
# Try to register again, expecting a name collision
|
||||
with self.assertRaises(CalendarNameCollision):
|
||||
register_calendar(dummy_cal)
|
||||
register_calendar('DMY', dummy_cal)
|
||||
|
||||
# Deregister the calendar and ensure that it is removed
|
||||
deregister_calendar('DMY')
|
||||
@@ -76,7 +76,7 @@ class CalendarRegistrationTestCase(TestCase):
|
||||
real_nyse = get_calendar('NYSE')
|
||||
|
||||
# Force a registration of the dummy NYSE
|
||||
register_calendar(dummy_nyse, force=True)
|
||||
register_calendar("NYSE", dummy_nyse, force=True)
|
||||
|
||||
# Ensure that the dummy overwrote the real calendar
|
||||
retr_cal = get_calendar('NYSE')
|
||||
|
||||
@@ -17,7 +17,7 @@ from unittest import TestCase
|
||||
from zipline.finance.cancel_policy import NeverCancel, EODCancel
|
||||
from zipline.gens.sim_engine import (
|
||||
BAR,
|
||||
DAY_END
|
||||
SESSION_END
|
||||
)
|
||||
|
||||
|
||||
@@ -25,10 +25,10 @@ class CancelPolicyTestCase(TestCase):
|
||||
|
||||
def test_eod_cancel(self):
|
||||
cancel_policy = EODCancel()
|
||||
self.assertTrue(cancel_policy.should_cancel(DAY_END))
|
||||
self.assertTrue(cancel_policy.should_cancel(SESSION_END))
|
||||
self.assertFalse(cancel_policy.should_cancel(BAR))
|
||||
|
||||
def test_never_cancel(self):
|
||||
cancel_policy = NeverCancel()
|
||||
self.assertFalse(cancel_policy.should_cancel(DAY_END))
|
||||
self.assertFalse(cancel_policy.should_cancel(SESSION_END))
|
||||
self.assertFalse(cancel_policy.should_cancel(BAR))
|
||||
|
||||
@@ -165,7 +165,7 @@ class SliceTestCase(WithSeededRandomPipelineEngine, ZiplineTestCase):
|
||||
Test that indexing into a term with a non-existent asset raises the
|
||||
proper exception.
|
||||
"""
|
||||
my_asset = Asset(0)
|
||||
my_asset = Asset(0, exchange="TEST")
|
||||
returns = Returns(window_length=2, inputs=[self.col])
|
||||
returns_slice = returns[my_asset]
|
||||
|
||||
|
||||
@@ -314,7 +314,7 @@ class StatisticalBuiltInsTestCase(WithTradingEnvironment, ZiplineTestCase):
|
||||
`RollingLinearRegressionOfReturns` raise the proper exception when
|
||||
given a nonexistent target asset.
|
||||
"""
|
||||
my_asset = Equity(0)
|
||||
my_asset = Equity(0, exchange="TEST")
|
||||
start_date = self.pipeline_start_date
|
||||
end_date = self.pipeline_end_date
|
||||
run_pipeline = self.run_pipeline
|
||||
|
||||
@@ -290,7 +290,7 @@ class ObjectIdentityTestCase(TestCase):
|
||||
self.assertIs(beta, multiple_outputs.beta)
|
||||
|
||||
def test_instance_caching_of_slices(self):
|
||||
my_asset = Asset(1)
|
||||
my_asset = Asset(1, exchange="TEST")
|
||||
|
||||
f = GenericCustomFactor()
|
||||
f_slice = f[my_asset]
|
||||
|
||||
Binary file not shown.
+62
-34
@@ -12,11 +12,11 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import warnings
|
||||
from collections import namedtuple
|
||||
import datetime
|
||||
from datetime import timedelta
|
||||
from textwrap import dedent
|
||||
import warnings
|
||||
from unittest import skip
|
||||
from copy import deepcopy
|
||||
|
||||
@@ -32,11 +32,10 @@ from testfixtures import TempDirectory
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import pytz
|
||||
from pandas.io.common import PerformanceWarning
|
||||
|
||||
from zipline import (
|
||||
run_algorithm,
|
||||
TradingAlgorithm,
|
||||
)
|
||||
from zipline import run_algorithm
|
||||
from zipline import TradingAlgorithm
|
||||
from zipline.api import FixedSlippage
|
||||
from zipline.assets import Equity, Future
|
||||
from zipline.assets.synthetic import (
|
||||
@@ -164,7 +163,7 @@ from zipline.test_algorithms import (
|
||||
no_handle_data,
|
||||
)
|
||||
from zipline.utils.api_support import ZiplineAPI, set_algo_instance
|
||||
from zipline.utils.calendars import get_calendar
|
||||
from zipline.utils.calendars import get_calendar, register_calendar
|
||||
from zipline.utils.context_tricks import CallbackManager
|
||||
from zipline.utils.control_flow import nullctx
|
||||
import zipline.utils.events
|
||||
@@ -211,10 +210,12 @@ class TestMiscellaneousAPI(WithLogger,
|
||||
pd.DataFrame.from_dict(
|
||||
{3: {'symbol': 'PLAY',
|
||||
'start_date': '2002-01-01',
|
||||
'end_date': '2004-01-01'},
|
||||
'end_date': '2004-01-01',
|
||||
'exchange': 'TEST'},
|
||||
4: {'symbol': 'PLAY',
|
||||
'start_date': '2005-01-01',
|
||||
'end_date': '2006-01-01'}},
|
||||
'end_date': '2006-01-01',
|
||||
'exchange': 'TEST'}},
|
||||
orient='index',
|
||||
),
|
||||
))
|
||||
@@ -228,25 +229,33 @@ class TestMiscellaneousAPI(WithLogger,
|
||||
'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')},
|
||||
'expiration_date': pd.Timestamp('2006-01-20', tz='UTC'),
|
||||
'exchange': 'TEST'
|
||||
},
|
||||
6: {
|
||||
'root_symbol': 'CL',
|
||||
'symbol': 'CLK06',
|
||||
'start_date': pd.Timestamp('2005-12-01', tz='UTC'),
|
||||
'notice_date': pd.Timestamp('2006-03-20', tz='UTC'),
|
||||
'expiration_date': pd.Timestamp('2006-04-20', tz='UTC')},
|
||||
'expiration_date': pd.Timestamp('2006-04-20', tz='UTC'),
|
||||
'exchange': 'TEST',
|
||||
},
|
||||
7: {
|
||||
'symbol': 'CLQ06',
|
||||
'root_symbol': 'CL',
|
||||
'start_date': pd.Timestamp('2005-12-01', tz='UTC'),
|
||||
'notice_date': pd.Timestamp('2006-06-20', tz='UTC'),
|
||||
'expiration_date': pd.Timestamp('2006-07-20', tz='UTC')},
|
||||
'expiration_date': pd.Timestamp('2006-07-20', tz='UTC'),
|
||||
'exchange': 'TEST',
|
||||
},
|
||||
8: {
|
||||
'symbol': 'CLX06',
|
||||
'root_symbol': 'CL',
|
||||
'start_date': pd.Timestamp('2006-02-01', tz='UTC'),
|
||||
'notice_date': pd.Timestamp('2006-09-20', tz='UTC'),
|
||||
'expiration_date': pd.Timestamp('2006-10-20', tz='UTC')}
|
||||
'expiration_date': pd.Timestamp('2006-10-20', tz='UTC'),
|
||||
'exchange': 'TEST',
|
||||
}
|
||||
},
|
||||
orient='index',
|
||||
)
|
||||
@@ -738,6 +747,7 @@ def handle_data(context, data):
|
||||
'symbol': 'DUP',
|
||||
'start_date': date.value,
|
||||
'end_date': (date + timedelta(days=1)).value,
|
||||
'exchange': 'TEST',
|
||||
}
|
||||
for i, date in enumerate(dates)
|
||||
]
|
||||
@@ -781,10 +791,13 @@ class TestTransformAlgorithm(WithLogger,
|
||||
|
||||
@classmethod
|
||||
def make_futures_info(cls):
|
||||
return pd.DataFrame.from_dict(
|
||||
{3: {'multiplier': 10, 'symbol': 'F'}},
|
||||
orient='index',
|
||||
)
|
||||
return pd.DataFrame.from_dict({
|
||||
3: {
|
||||
'multiplier': 10,
|
||||
'symbol': 'F',
|
||||
'exchange': 'TEST'
|
||||
}
|
||||
}, orient='index')
|
||||
|
||||
@classmethod
|
||||
def make_equity_daily_bar_data(cls):
|
||||
@@ -990,7 +1003,8 @@ def before_trading_start(context, data):
|
||||
period_end = pd.Timestamp('2002-1-4', tz='UTC')
|
||||
equities = pd.DataFrame([{
|
||||
'start_date': start_session,
|
||||
'end_date': period_end + timedelta(days=1)
|
||||
'end_date': period_end + timedelta(days=1),
|
||||
'exchange': "TEST",
|
||||
}] * 2)
|
||||
equities['symbol'] = ['A', 'B']
|
||||
with TempDirectory() as tempdir, \
|
||||
@@ -1439,6 +1453,8 @@ class TestAlgoScript(WithLogger,
|
||||
|
||||
@classmethod
|
||||
def make_equity_info(cls):
|
||||
register_calendar("TEST", get_calendar("NYSE"), force=True)
|
||||
|
||||
data = make_simple_equity_info(
|
||||
cls.sids,
|
||||
cls.START_DATE,
|
||||
@@ -1773,6 +1789,7 @@ def handle_data(context, data):
|
||||
Test that api methods on the data object can be called with positional
|
||||
arguments.
|
||||
"""
|
||||
|
||||
params = SimulationParameters(
|
||||
start_session=pd.Timestamp("2006-01-10", tz='UTC'),
|
||||
end_session=pd.Timestamp("2006-01-11", tz='UTC'),
|
||||
@@ -1961,6 +1978,8 @@ def handle_data(context, data):
|
||||
""")
|
||||
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("ignore", PerformanceWarning)
|
||||
|
||||
algo = TradingAlgorithm(
|
||||
script=algocode,
|
||||
sim_params=sim_params,
|
||||
@@ -1968,18 +1987,19 @@ def handle_data(context, data):
|
||||
)
|
||||
algo.run(self.data_portal)
|
||||
|
||||
self.assertEqual(len(w), 2)
|
||||
for i, warning in enumerate(w):
|
||||
self.assertIsInstance(warning.message, UserWarning)
|
||||
self.assertEqual(
|
||||
warning.message.args[0],
|
||||
'Got a time rule for the second positional argument '
|
||||
'date_rule. You should use keyword argument '
|
||||
'time_rule= when calling schedule_function without '
|
||||
'specifying a date_rule'
|
||||
)
|
||||
# The warnings come from line 13 and 14 in the algocode
|
||||
self.assertEqual(warning.lineno, 13 + i)
|
||||
self.assertEqual(len(w), 2)
|
||||
|
||||
for i, warning in enumerate(w):
|
||||
self.assertIsInstance(warning.message, UserWarning)
|
||||
self.assertEqual(
|
||||
warning.message.args[0],
|
||||
'Got a time rule for the second positional argument '
|
||||
'date_rule. You should use keyword argument '
|
||||
'time_rule= when calling schedule_function without '
|
||||
'specifying a date_rule'
|
||||
)
|
||||
# The warnings come from line 13 and 14 in the algocode
|
||||
self.assertEqual(warning.lineno, 13 + i)
|
||||
|
||||
self.assertEqual(
|
||||
algo.done_at_open,
|
||||
@@ -2855,7 +2875,8 @@ class TestTradingControls(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
1: {
|
||||
'symbol': 'SYM',
|
||||
'start_date': start,
|
||||
'end_date': start + timedelta(days=6)
|
||||
'end_date': start + timedelta(days=6),
|
||||
'exchange': "TEST",
|
||||
},
|
||||
},
|
||||
orient='index',
|
||||
@@ -2984,6 +3005,8 @@ class TestTradingControls(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
'symbol': 'SYM',
|
||||
'start_date': self.sim_params.start_session,
|
||||
'end_date': '2020-01-01',
|
||||
'exchange': "TEST",
|
||||
'sid': 999,
|
||||
}])
|
||||
with TempDirectory() as tempdir, \
|
||||
tmp_trading_env(equities=metadata) as env:
|
||||
@@ -2995,7 +3018,7 @@ class TestTradingControls(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
env.asset_finder,
|
||||
tempdir,
|
||||
self.sim_params,
|
||||
[0],
|
||||
[999],
|
||||
self.trading_calendar,
|
||||
)
|
||||
algo.run(data_portal)
|
||||
@@ -3004,6 +3027,8 @@ class TestTradingControls(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
'symbol': 'SYM',
|
||||
'start_date': '1989-01-01',
|
||||
'end_date': '1990-01-01',
|
||||
'exchange': "TEST",
|
||||
'sid': 999,
|
||||
}])
|
||||
with TempDirectory() as tempdir, \
|
||||
tmp_trading_env(equities=metadata) as env:
|
||||
@@ -3011,7 +3036,7 @@ class TestTradingControls(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
env.asset_finder,
|
||||
tempdir,
|
||||
self.sim_params,
|
||||
[0],
|
||||
[999],
|
||||
self.trading_calendar,
|
||||
)
|
||||
algo = SetAssetDateBoundsAlgorithm(
|
||||
@@ -3025,6 +3050,8 @@ class TestTradingControls(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
'symbol': 'SYM',
|
||||
'start_date': '2020-01-01',
|
||||
'end_date': '2021-01-01',
|
||||
'exchange': "TEST",
|
||||
'sid': 999,
|
||||
}])
|
||||
with TempDirectory() as tempdir, \
|
||||
tmp_trading_env(equities=metadata) as env:
|
||||
@@ -3032,7 +3059,7 @@ class TestTradingControls(WithSimParams, WithDataPortal, ZiplineTestCase):
|
||||
env.asset_finder,
|
||||
tempdir,
|
||||
self.sim_params,
|
||||
[0],
|
||||
[999],
|
||||
self.trading_calendar,
|
||||
)
|
||||
algo = SetAssetDateBoundsAlgorithm(
|
||||
@@ -4054,7 +4081,8 @@ class TestOrderAfterDelist(WithTradingEnvironment, ZiplineTestCase):
|
||||
'start_date': cls.start,
|
||||
'end_date': cls.day_1,
|
||||
'auto_close_date': cls.day_4,
|
||||
'symbol': "ASSET1"
|
||||
'symbol': "ASSET1",
|
||||
'exchange': "TEST",
|
||||
},
|
||||
},
|
||||
orient='index',
|
||||
|
||||
@@ -3,6 +3,7 @@ import warnings
|
||||
from mock import patch
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from pandas.io.common import PerformanceWarning
|
||||
|
||||
from zipline import TradingAlgorithm
|
||||
from zipline.finance.trading import SimulationParameters
|
||||
@@ -291,6 +292,7 @@ class TestAPIShim(WithDataPortal, WithSimParams, ZiplineTestCase):
|
||||
deprecation warning.
|
||||
"""
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("ignore", PerformanceWarning)
|
||||
warnings.simplefilter("default", ZiplineDeprecationWarning)
|
||||
algo = self.create_algo(sid_accessor_algo)
|
||||
algo.run(self.data_portal)
|
||||
@@ -319,6 +321,7 @@ class TestAPIShim(WithDataPortal, WithSimParams, ZiplineTestCase):
|
||||
in `data` is deprecated.
|
||||
"""
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("ignore", PerformanceWarning)
|
||||
warnings.simplefilter("default", ZiplineDeprecationWarning)
|
||||
algo = self.create_algo(data_items_algo)
|
||||
algo.run(self.data_portal)
|
||||
@@ -343,6 +346,7 @@ class TestAPIShim(WithDataPortal, WithSimParams, ZiplineTestCase):
|
||||
|
||||
def test_iterate_data(self):
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("ignore", PerformanceWarning)
|
||||
warnings.simplefilter("default", ZiplineDeprecationWarning)
|
||||
|
||||
algo = self.create_algo(simple_algo)
|
||||
@@ -373,6 +377,7 @@ class TestAPIShim(WithDataPortal, WithSimParams, ZiplineTestCase):
|
||||
|
||||
def test_history(self):
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("ignore", PerformanceWarning)
|
||||
warnings.simplefilter("default", ZiplineDeprecationWarning)
|
||||
|
||||
sim_params = self.sim_params.create_new(
|
||||
@@ -414,6 +419,7 @@ class TestAPIShim(WithDataPortal, WithSimParams, ZiplineTestCase):
|
||||
|
||||
def test_simple_transforms(self):
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("ignore", PerformanceWarning)
|
||||
warnings.simplefilter("default", ZiplineDeprecationWarning)
|
||||
|
||||
sim_params = SimulationParameters(
|
||||
@@ -484,6 +490,7 @@ class TestAPIShim(WithDataPortal, WithSimParams, ZiplineTestCase):
|
||||
|
||||
def test_manipulation(self):
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
warnings.simplefilter("ignore", PerformanceWarning)
|
||||
warnings.simplefilter("default", ZiplineDeprecationWarning)
|
||||
|
||||
algo = self.create_algo(simple_algo)
|
||||
|
||||
+71
-47
@@ -110,21 +110,21 @@ def build_lookup_generic_cases(asset_finder_type):
|
||||
'symbol': 'duplicated',
|
||||
'start_date': dupe_0_start.value,
|
||||
'end_date': dupe_0_end.value,
|
||||
'exchange': '',
|
||||
'exchange': 'TEST',
|
||||
},
|
||||
{
|
||||
'sid': 1,
|
||||
'symbol': 'duplicated',
|
||||
'start_date': dupe_1_start.value,
|
||||
'end_date': dupe_1_end.value,
|
||||
'exchange': '',
|
||||
'exchange': 'TEST',
|
||||
},
|
||||
{
|
||||
'sid': 2,
|
||||
'symbol': 'unique',
|
||||
'start_date': unique_start.value,
|
||||
'end_date': unique_end.value,
|
||||
'exchange': '',
|
||||
'exchange': 'TEST',
|
||||
},
|
||||
],
|
||||
index='sid')
|
||||
@@ -194,15 +194,21 @@ class AssetTestCase(TestCase):
|
||||
exchange='THE MOON',
|
||||
)
|
||||
|
||||
asset3 = Asset(3, exchange="test")
|
||||
asset4 = Asset(4, exchange="test")
|
||||
asset5 = Asset(5, exchange="still testing")
|
||||
|
||||
def test_asset_object(self):
|
||||
self.assertEquals({5061: 'foo'}[Asset(5061)], 'foo')
|
||||
self.assertEquals(Asset(5061), 5061)
|
||||
self.assertEquals(5061, Asset(5061))
|
||||
the_asset = Asset(5061, exchange="bar")
|
||||
|
||||
self.assertEquals(Asset(5061), Asset(5061))
|
||||
self.assertEquals(int(Asset(5061)), 5061)
|
||||
self.assertEquals({5061: 'foo'}[the_asset], 'foo')
|
||||
self.assertEquals(the_asset, 5061)
|
||||
self.assertEquals(5061, the_asset)
|
||||
|
||||
self.assertEquals(str(Asset(5061)), 'Asset(5061)')
|
||||
self.assertEquals(the_asset, the_asset)
|
||||
self.assertEquals(int(the_asset), 5061)
|
||||
|
||||
self.assertEquals(str(the_asset), 'Asset(5061)')
|
||||
|
||||
def test_to_and_from_dict(self):
|
||||
asset_from_dict = Asset.from_dict(self.asset.to_dict())
|
||||
@@ -220,8 +226,8 @@ class AssetTestCase(TestCase):
|
||||
|
||||
def test_asset_comparisons(self):
|
||||
|
||||
s_23 = Asset(23)
|
||||
s_24 = Asset(24)
|
||||
s_23 = Asset(23, exchange="test")
|
||||
s_24 = Asset(24, exchange="test")
|
||||
|
||||
self.assertEqual(s_23, s_23)
|
||||
self.assertEqual(s_23, 23)
|
||||
@@ -250,39 +256,39 @@ class AssetTestCase(TestCase):
|
||||
self.assertGreater(s_24, s_23)
|
||||
|
||||
def test_lt(self):
|
||||
self.assertTrue(Asset(3) < Asset(4))
|
||||
self.assertFalse(Asset(4) < Asset(4))
|
||||
self.assertFalse(Asset(5) < Asset(4))
|
||||
self.assertTrue(self.asset3 < self.asset4)
|
||||
self.assertFalse(self.asset4 < self.asset4)
|
||||
self.assertFalse(self.asset5 < self.asset4)
|
||||
|
||||
def test_le(self):
|
||||
self.assertTrue(Asset(3) <= Asset(4))
|
||||
self.assertTrue(Asset(4) <= Asset(4))
|
||||
self.assertFalse(Asset(5) <= Asset(4))
|
||||
self.assertTrue(self.asset3 <= self.asset4)
|
||||
self.assertTrue(self.asset4 <= self.asset4)
|
||||
self.assertFalse(self.asset5 <= self.asset4)
|
||||
|
||||
def test_eq(self):
|
||||
self.assertFalse(Asset(3) == Asset(4))
|
||||
self.assertTrue(Asset(4) == Asset(4))
|
||||
self.assertFalse(Asset(5) == Asset(4))
|
||||
self.assertFalse(self.asset3 == self.asset4)
|
||||
self.assertTrue(self.asset4 == self.asset4)
|
||||
self.assertFalse(self.asset5 == self.asset4)
|
||||
|
||||
def test_ge(self):
|
||||
self.assertFalse(Asset(3) >= Asset(4))
|
||||
self.assertTrue(Asset(4) >= Asset(4))
|
||||
self.assertTrue(Asset(5) >= Asset(4))
|
||||
self.assertFalse(self.asset3 >= self.asset4)
|
||||
self.assertTrue(self.asset4 >= self.asset4)
|
||||
self.assertTrue(self.asset5 >= self.asset4)
|
||||
|
||||
def test_gt(self):
|
||||
self.assertFalse(Asset(3) > Asset(4))
|
||||
self.assertFalse(Asset(4) > Asset(4))
|
||||
self.assertTrue(Asset(5) > Asset(4))
|
||||
self.assertFalse(self.asset3 > self.asset4)
|
||||
self.assertFalse(self.asset4 > self.asset4)
|
||||
self.assertTrue(self.asset5 > self.asset4)
|
||||
|
||||
def test_type_mismatch(self):
|
||||
if sys.version_info.major < 3:
|
||||
self.assertIsNotNone(Asset(3) < 'a')
|
||||
self.assertIsNotNone('a' < Asset(3))
|
||||
self.assertIsNotNone(self.asset3 < 'a')
|
||||
self.assertIsNotNone('a' < self.asset3)
|
||||
else:
|
||||
with self.assertRaises(TypeError):
|
||||
Asset(3) < 'a'
|
||||
self.asset3 < 'a'
|
||||
with self.assertRaises(TypeError):
|
||||
'a' < Asset(3)
|
||||
'a' < self.asset3
|
||||
|
||||
|
||||
class TestFuture(WithAssetFinder, ZiplineTestCase):
|
||||
@@ -298,6 +304,7 @@ class TestFuture(WithAssetFinder, ZiplineTestCase):
|
||||
'auto_close_date': pd.Timestamp('2014-01-18', tz='UTC'),
|
||||
'tick_size': .01,
|
||||
'multiplier': 500.0,
|
||||
'exchange': "TEST",
|
||||
},
|
||||
0: {
|
||||
'symbol': 'CLG06',
|
||||
@@ -306,6 +313,7 @@ class TestFuture(WithAssetFinder, ZiplineTestCase):
|
||||
'notice_date': pd.Timestamp('2005-12-20', tz='UTC'),
|
||||
'expiration_date': pd.Timestamp('2006-01-20', tz='UTC'),
|
||||
'multiplier': 1.0,
|
||||
'exchange': 'TEST',
|
||||
},
|
||||
},
|
||||
orient='index',
|
||||
@@ -423,6 +431,7 @@ class AssetFinderTestCase(WithTradingCalendar, ZiplineTestCase):
|
||||
'symbol': 'TEST.%d' % sid,
|
||||
'start_date': as_of.value,
|
||||
'end_date': as_of.value,
|
||||
'exchange': uuid.uuid4().hex
|
||||
}
|
||||
for sid in sids
|
||||
]
|
||||
@@ -471,9 +480,9 @@ class AssetFinderTestCase(WithTradingCalendar, ZiplineTestCase):
|
||||
|
||||
def test_lookup_symbol_fuzzy(self):
|
||||
metadata = pd.DataFrame.from_records([
|
||||
{'symbol': 'PRTY_HRD'},
|
||||
{'symbol': 'BRKA'},
|
||||
{'symbol': 'BRK_A'},
|
||||
{'symbol': 'PRTY_HRD', 'exchange': "TEST"},
|
||||
{'symbol': 'BRKA', 'exchange': "TEST"},
|
||||
{'symbol': 'BRK_A', 'exchange': "TEST"},
|
||||
])
|
||||
self.write_assets(equities=metadata)
|
||||
finder = self.asset_finder
|
||||
@@ -516,11 +525,13 @@ class AssetFinderTestCase(WithTradingCalendar, ZiplineTestCase):
|
||||
'symbol': 'A',
|
||||
'start_date': T('2014-01-01'),
|
||||
'end_date': T('2014-01-05'),
|
||||
'exchange': "TEST",
|
||||
},
|
||||
{
|
||||
'symbol': 'B',
|
||||
'start_date': T('2014-01-06'),
|
||||
'end_date': T('2014-01-10'),
|
||||
'exchange': "TEST",
|
||||
},
|
||||
|
||||
# sid 1
|
||||
@@ -528,11 +539,13 @@ class AssetFinderTestCase(WithTradingCalendar, ZiplineTestCase):
|
||||
'symbol': 'C',
|
||||
'start_date': T('2014-01-01'),
|
||||
'end_date': T('2014-01-05'),
|
||||
'exchange': "TEST",
|
||||
},
|
||||
{
|
||||
'symbol': 'A', # claiming the unused symbol 'A'
|
||||
'start_date': T('2014-01-06'),
|
||||
'end_date': T('2014-01-10'),
|
||||
'exchange': "TEST",
|
||||
},
|
||||
],
|
||||
index=[0, 0, 1, 1],
|
||||
@@ -697,14 +710,14 @@ class AssetFinderTestCase(WithTradingCalendar, ZiplineTestCase):
|
||||
'symbol': 'real',
|
||||
'start_date': pd.Timestamp('2013-1-1', tz='UTC'),
|
||||
'end_date': pd.Timestamp('2014-1-1', tz='UTC'),
|
||||
'exchange': '',
|
||||
'exchange': 'TEST',
|
||||
},
|
||||
{
|
||||
'sid': 1,
|
||||
'symbol': 'also_real',
|
||||
'start_date': pd.Timestamp('2013-1-1', tz='UTC'),
|
||||
'end_date': pd.Timestamp('2014-1-1', tz='UTC'),
|
||||
'exchange': '',
|
||||
'exchange': 'TEST',
|
||||
},
|
||||
# Sid whose end date is before our query date. We should
|
||||
# still correctly find it.
|
||||
@@ -713,7 +726,7 @@ class AssetFinderTestCase(WithTradingCalendar, ZiplineTestCase):
|
||||
'symbol': 'real_but_old',
|
||||
'start_date': pd.Timestamp('2002-1-1', tz='UTC'),
|
||||
'end_date': pd.Timestamp('2003-1-1', tz='UTC'),
|
||||
'exchange': '',
|
||||
'exchange': 'TEST',
|
||||
},
|
||||
# Sid whose start_date is **after** our query date. We should
|
||||
# **not** find it.
|
||||
@@ -749,7 +762,8 @@ class AssetFinderTestCase(WithTradingCalendar, ZiplineTestCase):
|
||||
|
||||
# Build an asset with an end_date
|
||||
eq_end = pd.Timestamp('2012-01-01', tz='UTC')
|
||||
equity_asset = Equity(1, symbol="TESTEQ", end_date=eq_end)
|
||||
equity_asset = Equity(1, symbol="TESTEQ", end_date=eq_end,
|
||||
exchange="TEST")
|
||||
|
||||
# Catch all warnings
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
@@ -772,14 +786,16 @@ class AssetFinderTestCase(WithTradingCalendar, ZiplineTestCase):
|
||||
'root_symbol': 'AD',
|
||||
'notice_date': pd.Timestamp('2015-06-14', tz='UTC'),
|
||||
'expiration_date': pd.Timestamp('2015-08-14', tz='UTC'),
|
||||
'start_date': pd.Timestamp('2015-01-01', tz='UTC')
|
||||
'start_date': pd.Timestamp('2015-01-01', tz='UTC'),
|
||||
'exchange': "TEST",
|
||||
},
|
||||
{
|
||||
'symbol': 'ADV15',
|
||||
'root_symbol': 'AD',
|
||||
'notice_date': pd.Timestamp('2015-05-14', tz='UTC'),
|
||||
'expiration_date': pd.Timestamp('2015-09-14', tz='UTC'),
|
||||
'start_date': pd.Timestamp('2015-01-01', tz='UTC')
|
||||
'start_date': pd.Timestamp('2015-01-01', tz='UTC'),
|
||||
'exchange': "TEST",
|
||||
},
|
||||
# Starts trading today, so should be valid.
|
||||
{
|
||||
@@ -787,7 +803,8 @@ class AssetFinderTestCase(WithTradingCalendar, ZiplineTestCase):
|
||||
'root_symbol': 'AD',
|
||||
'notice_date': pd.Timestamp('2015-11-16', tz='UTC'),
|
||||
'expiration_date': pd.Timestamp('2015-12-16', tz='UTC'),
|
||||
'start_date': pd.Timestamp('2015-05-14', tz='UTC')
|
||||
'start_date': pd.Timestamp('2015-05-14', tz='UTC'),
|
||||
'exchange': "TEST",
|
||||
},
|
||||
# Starts trading in August, so not valid.
|
||||
{
|
||||
@@ -795,7 +812,8 @@ class AssetFinderTestCase(WithTradingCalendar, ZiplineTestCase):
|
||||
'root_symbol': 'AD',
|
||||
'notice_date': pd.Timestamp('2015-11-16', tz='UTC'),
|
||||
'expiration_date': pd.Timestamp('2015-12-16', tz='UTC'),
|
||||
'start_date': pd.Timestamp('2015-08-01', tz='UTC')
|
||||
'start_date': pd.Timestamp('2015-08-01', tz='UTC'),
|
||||
'exchange': "TEST",
|
||||
},
|
||||
# Notice date comes after expiration
|
||||
{
|
||||
@@ -803,7 +821,8 @@ class AssetFinderTestCase(WithTradingCalendar, ZiplineTestCase):
|
||||
'root_symbol': 'AD',
|
||||
'notice_date': pd.Timestamp('2016-11-25', tz='UTC'),
|
||||
'expiration_date': pd.Timestamp('2016-11-16', tz='UTC'),
|
||||
'start_date': pd.Timestamp('2015-08-01', tz='UTC')
|
||||
'start_date': pd.Timestamp('2015-08-01', tz='UTC'),
|
||||
'exchange': "TEST",
|
||||
},
|
||||
# This contract has no start date and also this contract should be
|
||||
# last in all chains
|
||||
@@ -811,7 +830,8 @@ class AssetFinderTestCase(WithTradingCalendar, ZiplineTestCase):
|
||||
'symbol': 'ADZ20',
|
||||
'root_symbol': 'AD',
|
||||
'notice_date': pd.Timestamp('2020-11-25', tz='UTC'),
|
||||
'expiration_date': pd.Timestamp('2020-11-16', tz='UTC')
|
||||
'expiration_date': pd.Timestamp('2020-11-16', tz='UTC'),
|
||||
'exchange': "TEST",
|
||||
},
|
||||
])
|
||||
self.write_assets(futures=metadata)
|
||||
@@ -850,10 +870,10 @@ class AssetFinderTestCase(WithTradingCalendar, ZiplineTestCase):
|
||||
# Build an empty finder and some Assets
|
||||
dt = pd.Timestamp('2014-01-01', tz='UTC')
|
||||
finder = self.asset_finder
|
||||
asset1 = Equity(1, symbol="AAPL")
|
||||
asset2 = Equity(2, symbol="GOOG")
|
||||
asset200 = Future(200, symbol="CLK15")
|
||||
asset201 = Future(201, symbol="CLM15")
|
||||
asset1 = Equity(1, symbol="AAPL", exchange="TEST")
|
||||
asset2 = Equity(2, symbol="GOOG", exchange="TEST")
|
||||
asset200 = Future(200, symbol="CLK15", exchange="TEST")
|
||||
asset201 = Future(201, symbol="CLM15", exchange="TEST")
|
||||
|
||||
# Check for correct mapping and types
|
||||
pre_map = [asset1, asset2, asset200, asset201]
|
||||
@@ -1103,6 +1123,7 @@ class TestFutureChain(WithAssetFinder, ZiplineTestCase):
|
||||
'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'),
|
||||
'exchange': "TEST",
|
||||
},
|
||||
{
|
||||
'root_symbol': 'CL',
|
||||
@@ -1110,6 +1131,7 @@ class TestFutureChain(WithAssetFinder, ZiplineTestCase):
|
||||
'start_date': pd.Timestamp('2005-12-01', tz='UTC'),
|
||||
'notice_date': pd.Timestamp('2006-03-20', tz='UTC'),
|
||||
'expiration_date': pd.Timestamp('2006-04-20', tz='UTC'),
|
||||
'exchange': "TEST",
|
||||
},
|
||||
{
|
||||
'symbol': 'CLQ06',
|
||||
@@ -1117,6 +1139,7 @@ class TestFutureChain(WithAssetFinder, ZiplineTestCase):
|
||||
'start_date': pd.Timestamp('2005-12-01', tz='UTC'),
|
||||
'notice_date': pd.Timestamp('2006-06-20', tz='UTC'),
|
||||
'expiration_date': pd.Timestamp('2006-07-20', tz='UTC'),
|
||||
'exchange': "TEST",
|
||||
},
|
||||
{
|
||||
'symbol': 'CLX06',
|
||||
@@ -1124,6 +1147,7 @@ class TestFutureChain(WithAssetFinder, ZiplineTestCase):
|
||||
'start_date': pd.Timestamp('2006-02-01', tz='UTC'),
|
||||
'notice_date': pd.Timestamp('2006-09-20', tz='UTC'),
|
||||
'expiration_date': pd.Timestamp('2006-10-20', tz='UTC'),
|
||||
'exchange': "TEST",
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
+100
-14
@@ -131,6 +131,30 @@ class TestMinuteBarData(WithBarDataChecks,
|
||||
50,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def make_futures_info(cls):
|
||||
return pd.DataFrame.from_dict(
|
||||
{
|
||||
6: {
|
||||
'symbol': 'CLG06',
|
||||
'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'),
|
||||
'exchange': 'ICEUS',
|
||||
},
|
||||
7: {
|
||||
'symbol': 'CLK06',
|
||||
'root_symbol': 'CL',
|
||||
'start_date': pd.Timestamp('2005-12-01', tz='UTC'),
|
||||
'notice_date': pd.Timestamp('2006-03-20', tz='UTC'),
|
||||
'expiration_date': pd.Timestamp('2006-04-20', tz='UTC'),
|
||||
'exchange': 'ICEUS',
|
||||
},
|
||||
},
|
||||
orient='index',
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def make_splits_data(cls):
|
||||
return pd.DataFrame([
|
||||
@@ -438,7 +462,7 @@ class TestMinuteBarData(WithBarDataChecks,
|
||||
bd.current(self.HILARIOUSLY_ILLIQUID_ASSET, "volume")
|
||||
)
|
||||
|
||||
def test_can_trade_at_midnight(self):
|
||||
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(
|
||||
@@ -453,19 +477,66 @@ class TestMinuteBarData(WithBarDataChecks,
|
||||
with handle_non_market_minutes(bar_data):
|
||||
self.assertFalse(bar_data.can_trade(asset))
|
||||
|
||||
# but make sure it works when the assets are alive
|
||||
# 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.assertTrue(bar_data2.can_trade(asset))
|
||||
self.assertFalse(bar_data2.can_trade(asset))
|
||||
|
||||
with handle_non_market_minutes(bar_data2):
|
||||
self.assertTrue(bar_data2.can_trade(asset))
|
||||
self.assertFalse(bar_data2.can_trade(asset))
|
||||
|
||||
def test_is_stale_at_midnight(self):
|
||||
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])
|
||||
|
||||
def test_is_stale_during_non_market_hours(self):
|
||||
bar_data = BarData(
|
||||
self.data_portal,
|
||||
lambda: self.equity_minute_bar_days[1],
|
||||
@@ -644,13 +715,20 @@ class TestDailyBarData(WithBarDataChecks,
|
||||
)
|
||||
cls.ASSETS = [cls.ASSET1, cls.ASSET2]
|
||||
|
||||
def get_last_minute_of_session(self, session_label):
|
||||
return self.trading_calendar.open_and_close_for_session(
|
||||
session_label
|
||||
)[1]
|
||||
|
||||
def test_day_before_assets_trading(self):
|
||||
# use the day before self.bcolz_daily_bar_days[0]
|
||||
day = self.trading_calendar.previous_session_label(
|
||||
self.equity_daily_bar_days[0]
|
||||
minute = self.get_last_minute_of_session(
|
||||
self.trading_calendar.previous_session_label(
|
||||
self.equity_daily_bar_days[0]
|
||||
)
|
||||
)
|
||||
|
||||
bar_data = BarData(self.data_portal, lambda: day, "daily")
|
||||
bar_data = BarData(self.data_portal, lambda: minute, "daily")
|
||||
self.check_internal_consistency(bar_data)
|
||||
|
||||
self.assertFalse(bar_data.can_trade(self.ASSET1))
|
||||
@@ -674,7 +752,9 @@ class TestDailyBarData(WithBarDataChecks,
|
||||
# on self.equity_daily_bar_days[0], only asset1 has data
|
||||
bar_data = BarData(
|
||||
self.data_portal,
|
||||
lambda: self.equity_daily_bar_days[0],
|
||||
lambda: self.get_last_minute_of_session(
|
||||
self.equity_daily_bar_days[0]
|
||||
),
|
||||
"daily",
|
||||
)
|
||||
self.check_internal_consistency(bar_data)
|
||||
@@ -709,7 +789,9 @@ class TestDailyBarData(WithBarDataChecks,
|
||||
def test_fully_active_day(self):
|
||||
bar_data = BarData(
|
||||
self.data_portal,
|
||||
lambda: self.equity_daily_bar_days[1],
|
||||
lambda: self.get_last_minute_of_session(
|
||||
self.equity_daily_bar_days[1]
|
||||
),
|
||||
"daily",
|
||||
)
|
||||
self.check_internal_consistency(bar_data)
|
||||
@@ -733,7 +815,9 @@ class TestDailyBarData(WithBarDataChecks,
|
||||
def test_last_active_day(self):
|
||||
bar_data = BarData(
|
||||
self.data_portal,
|
||||
lambda: self.equity_daily_bar_days[-1],
|
||||
lambda: self.get_last_minute_of_session(
|
||||
self.equity_daily_bar_days[-1]
|
||||
),
|
||||
"daily",
|
||||
)
|
||||
self.check_internal_consistency(bar_data)
|
||||
@@ -751,11 +835,13 @@ class TestDailyBarData(WithBarDataChecks,
|
||||
|
||||
def test_after_assets_dead(self):
|
||||
# both assets end on self.day[-1], so let's try the next day
|
||||
next_day = self.trading_calendar.next_session_label(
|
||||
self.equity_daily_bar_days[-1]
|
||||
minute = self.get_last_minute_of_session(
|
||||
self.trading_calendar.next_session_label(
|
||||
self.equity_daily_bar_days[-1]
|
||||
)
|
||||
)
|
||||
|
||||
bar_data = BarData(self.data_portal, lambda: next_day, "daily")
|
||||
bar_data = BarData(self.data_portal, lambda: minute, "daily")
|
||||
self.check_internal_consistency(bar_data)
|
||||
|
||||
for asset in self.ASSETS:
|
||||
|
||||
@@ -47,22 +47,26 @@ class TestBenchmark(WithDataPortal, WithSimParams, WithTradingCalendar,
|
||||
1: {
|
||||
'symbol': 'A',
|
||||
'start_date': cls.START_DATE,
|
||||
'end_date': cls.END_DATE + pd.Timedelta(days=1)
|
||||
'end_date': cls.END_DATE + pd.Timedelta(days=1),
|
||||
"exchange": "TEST",
|
||||
},
|
||||
2: {
|
||||
'symbol': 'B',
|
||||
'start_date': cls.START_DATE,
|
||||
'end_date': cls.END_DATE + pd.Timedelta(days=1)
|
||||
'end_date': cls.END_DATE + pd.Timedelta(days=1),
|
||||
"exchange": "TEST",
|
||||
},
|
||||
3: {
|
||||
'symbol': 'C',
|
||||
'start_date': pd.Timestamp('2006-05-26', tz='utc'),
|
||||
'end_date': pd.Timestamp('2006-08-09', tz='utc')
|
||||
'end_date': pd.Timestamp('2006-08-09', tz='utc'),
|
||||
"exchange": "TEST",
|
||||
},
|
||||
4: {
|
||||
'symbol': 'D',
|
||||
'start_date': cls.START_DATE,
|
||||
'end_date': cls.END_DATE + pd.Timedelta(days=1)
|
||||
'end_date': cls.END_DATE + pd.Timedelta(days=1),
|
||||
"exchange": "TEST",
|
||||
},
|
||||
},
|
||||
orient='index',
|
||||
|
||||
@@ -25,7 +25,7 @@ from zipline.finance.execution import (
|
||||
StopOrder,
|
||||
)
|
||||
|
||||
from zipline.gens.sim_engine import DAY_END, BAR
|
||||
from zipline.gens.sim_engine import SESSION_END, BAR
|
||||
from zipline.finance.cancel_policy import EODCancel, NeverCancel
|
||||
from zipline.finance.slippage import (
|
||||
DEFAULT_VOLUME_SLIPPAGE_BAR_LIMIT,
|
||||
@@ -143,7 +143,7 @@ class BlotterTestCase(WithLogger,
|
||||
self.assertEqual(blotter.new_orders[0].status, ORDER_STATUS.OPEN)
|
||||
self.assertEqual(blotter.new_orders[1].status, ORDER_STATUS.OPEN)
|
||||
|
||||
blotter.execute_cancel_policy(DAY_END)
|
||||
blotter.execute_cancel_policy(SESSION_END)
|
||||
for order_id in order_ids:
|
||||
order = blotter.orders[order_id]
|
||||
self.assertEqual(order.status, ORDER_STATUS.CANCELLED)
|
||||
@@ -161,7 +161,7 @@ class BlotterTestCase(WithLogger,
|
||||
blotter.execute_cancel_policy(BAR)
|
||||
self.assertEqual(blotter.new_orders[0].status, ORDER_STATUS.OPEN)
|
||||
|
||||
blotter.execute_cancel_policy(DAY_END)
|
||||
blotter.execute_cancel_policy(SESSION_END)
|
||||
self.assertEqual(blotter.new_orders[0].status, ORDER_STATUS.OPEN)
|
||||
|
||||
def test_order_rejection(self):
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
from datetime import time
|
||||
from unittest import TestCase
|
||||
import pandas as pd
|
||||
from zipline.gens.sim_engine import (
|
||||
MinuteSimulationClock,
|
||||
SESSION_START,
|
||||
BEFORE_TRADING_START_BAR,
|
||||
BAR,
|
||||
SESSION_END
|
||||
)
|
||||
|
||||
from zipline.utils.calendars import get_calendar
|
||||
from zipline.utils.calendars.trading_calendar import days_at_time
|
||||
|
||||
|
||||
class TestClock(TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.nyse_calendar = get_calendar("NYSE")
|
||||
|
||||
# july 15 is friday, so there are 3 sessions in this range (15, 18, 19)
|
||||
cls.sessions = cls.nyse_calendar.sessions_in_range(
|
||||
pd.Timestamp("2016-07-15"),
|
||||
pd.Timestamp("2016-07-19")
|
||||
)
|
||||
|
||||
trading_o_and_c = cls.nyse_calendar.schedule.ix[cls.sessions]
|
||||
cls.opens = trading_o_and_c['market_open']
|
||||
cls.closes = trading_o_and_c['market_close']
|
||||
|
||||
def test_bts_before_session(self):
|
||||
clock = MinuteSimulationClock(
|
||||
self.sessions,
|
||||
self.opens,
|
||||
self.closes,
|
||||
days_at_time(self.sessions, time(6, 17), "US/Eastern"),
|
||||
False
|
||||
)
|
||||
|
||||
all_events = list(clock)
|
||||
|
||||
def _check_session_bts_first(session_label, events, bts_dt):
|
||||
minutes = self.nyse_calendar.minutes_for_session(session_label)
|
||||
|
||||
self.assertEqual(393, len(events))
|
||||
|
||||
self.assertEqual(events[0], (session_label, SESSION_START))
|
||||
self.assertEqual(events[1], (bts_dt, BEFORE_TRADING_START_BAR))
|
||||
for i in range(2, 392):
|
||||
self.assertEqual(events[i], (minutes[i - 2], BAR))
|
||||
self.assertEqual(events[392], (minutes[-1], SESSION_END))
|
||||
|
||||
_check_session_bts_first(
|
||||
self.sessions[0],
|
||||
all_events[0:393],
|
||||
pd.Timestamp("2016-07-15 6:17", tz='US/Eastern')
|
||||
)
|
||||
|
||||
_check_session_bts_first(
|
||||
self.sessions[1],
|
||||
all_events[393:786],
|
||||
pd.Timestamp("2016-07-18 6:17", tz='US/Eastern')
|
||||
)
|
||||
|
||||
_check_session_bts_first(
|
||||
self.sessions[2],
|
||||
all_events[786:],
|
||||
pd.Timestamp("2016-07-19 6:17", tz='US/Eastern')
|
||||
)
|
||||
|
||||
def test_bts_during_session(self):
|
||||
self.verify_bts_during_session(
|
||||
time(11, 45), [
|
||||
pd.Timestamp("2016-07-15 11:45", tz='US/Eastern'),
|
||||
pd.Timestamp("2016-07-18 11:45", tz='US/Eastern'),
|
||||
pd.Timestamp("2016-07-19 11:45", tz='US/Eastern')
|
||||
],
|
||||
135
|
||||
)
|
||||
|
||||
def test_bts_on_first_minute(self):
|
||||
self.verify_bts_during_session(
|
||||
time(9, 30), [
|
||||
pd.Timestamp("2016-07-15 9:30", tz='US/Eastern'),
|
||||
pd.Timestamp("2016-07-18 9:30", tz='US/Eastern'),
|
||||
pd.Timestamp("2016-07-19 9:30", tz='US/Eastern')
|
||||
],
|
||||
1
|
||||
)
|
||||
|
||||
def test_bts_on_last_minute(self):
|
||||
self.verify_bts_during_session(
|
||||
time(16, 00), [
|
||||
pd.Timestamp("2016-07-15 16:00", tz='US/Eastern'),
|
||||
pd.Timestamp("2016-07-18 16:00", tz='US/Eastern'),
|
||||
pd.Timestamp("2016-07-19 16:00", tz='US/Eastern')
|
||||
],
|
||||
390
|
||||
)
|
||||
|
||||
def verify_bts_during_session(self, bts_time, bts_session_times, bts_idx):
|
||||
def _check_session_bts_during(session_label, events, bts_dt):
|
||||
minutes = self.nyse_calendar.minutes_for_session(session_label)
|
||||
|
||||
self.assertEqual(393, len(events))
|
||||
|
||||
self.assertEqual(events[0], (session_label, SESSION_START))
|
||||
|
||||
for i in range(1, bts_idx):
|
||||
self.assertEqual(events[i], (minutes[i - 1], BAR))
|
||||
|
||||
self.assertEqual(
|
||||
events[bts_idx],
|
||||
(bts_dt, BEFORE_TRADING_START_BAR)
|
||||
)
|
||||
|
||||
for i in range(bts_idx + 1, 391):
|
||||
self.assertEqual(events[i], (minutes[i - 2], BAR))
|
||||
|
||||
self.assertEqual(events[392], (minutes[-1], SESSION_END))
|
||||
|
||||
clock = MinuteSimulationClock(
|
||||
self.sessions,
|
||||
self.opens,
|
||||
self.closes,
|
||||
days_at_time(self.sessions, bts_time, "US/Eastern"),
|
||||
False
|
||||
)
|
||||
|
||||
all_events = list(clock)
|
||||
|
||||
_check_session_bts_during(
|
||||
self.sessions[0],
|
||||
all_events[0:393],
|
||||
bts_session_times[0]
|
||||
)
|
||||
|
||||
_check_session_bts_during(
|
||||
self.sessions[1],
|
||||
all_events[393:786],
|
||||
bts_session_times[1]
|
||||
)
|
||||
|
||||
_check_session_bts_during(
|
||||
self.sessions[2],
|
||||
all_events[786:],
|
||||
bts_session_times[2]
|
||||
)
|
||||
|
||||
def test_bts_after_session(self):
|
||||
clock = MinuteSimulationClock(
|
||||
self.sessions,
|
||||
self.opens,
|
||||
self.closes,
|
||||
days_at_time(self.sessions, time(19, 5), "US/Eastern"),
|
||||
False
|
||||
)
|
||||
|
||||
all_events = list(clock)
|
||||
|
||||
# since 19:05 Eastern is after the NYSE is closed, we don't emit
|
||||
# BEFORE_TRADING_START. therefore, each day has SESSION_START,
|
||||
# 390 BARs, and then SESSION_END
|
||||
|
||||
def _check_session_bts_after(session_label, events):
|
||||
minutes = self.nyse_calendar.minutes_for_session(session_label)
|
||||
|
||||
self.assertEqual(392, len(events))
|
||||
self.assertEqual(events[0], (session_label, SESSION_START))
|
||||
|
||||
for i in range(1, 391):
|
||||
self.assertEqual(events[i], (minutes[i - 1], BAR))
|
||||
|
||||
self.assertEqual(events[-1], (minutes[389], SESSION_END))
|
||||
|
||||
for i in range(0, 2):
|
||||
_check_session_bts_after(
|
||||
self.sessions[i],
|
||||
all_events[(i * 392): ((i + 1) * 392)]
|
||||
)
|
||||
@@ -120,6 +120,7 @@ class MinuteToDailyAggregationTestCase(WithBcolzEquityMinuteBarReader,
|
||||
self.equity_daily_aggregator = DailyHistoryAggregator(
|
||||
self.trading_calendar.schedule.market_open,
|
||||
self.bcolz_equity_minute_bar_reader,
|
||||
self.trading_calendar
|
||||
)
|
||||
|
||||
@parameterized.expand([
|
||||
|
||||
+16
-8
@@ -121,42 +121,50 @@ class WithHistory(WithDataPortal):
|
||||
1: {
|
||||
'start_date': pd.Timestamp('2014-01-03', tz='UTC'),
|
||||
'end_date': cls.TRADING_END_DT,
|
||||
'symbol': 'ASSET1'
|
||||
'symbol': 'ASSET1',
|
||||
'exchange': "TEST",
|
||||
},
|
||||
2: {
|
||||
'start_date': jan_5_2015,
|
||||
'end_date': day_after_12312015,
|
||||
'symbol': 'ASSET2'
|
||||
'symbol': 'ASSET2',
|
||||
'exchange': "TEST",
|
||||
},
|
||||
3: {
|
||||
'start_date': jan_5_2015,
|
||||
'end_date': day_after_12312015,
|
||||
'symbol': 'ASSET3'
|
||||
'symbol': 'ASSET3',
|
||||
'exchange': "TEST",
|
||||
},
|
||||
cls.SPLIT_ASSET_SID: {
|
||||
'start_date': jan_5_2015,
|
||||
'end_date': day_after_12312015,
|
||||
'symbol': 'SPLIT_ASSET'
|
||||
'symbol': 'SPLIT_ASSET',
|
||||
'exchange': "TEST",
|
||||
},
|
||||
cls.DIVIDEND_ASSET_SID: {
|
||||
'start_date': jan_5_2015,
|
||||
'end_date': day_after_12312015,
|
||||
'symbol': 'DIVIDEND_ASSET'
|
||||
'symbol': 'DIVIDEND_ASSET',
|
||||
'exchange': "TEST",
|
||||
},
|
||||
cls.MERGER_ASSET_SID: {
|
||||
'start_date': jan_5_2015,
|
||||
'end_date': day_after_12312015,
|
||||
'symbol': 'MERGER_ASSET'
|
||||
'symbol': 'MERGER_ASSET',
|
||||
'exchange': "TEST",
|
||||
},
|
||||
cls.HALF_DAY_TEST_ASSET_SID: {
|
||||
'start_date': pd.Timestamp('2014-07-02', tz='UTC'),
|
||||
'end_date': day_after_12312015,
|
||||
'symbol': 'HALF_DAY_TEST_ASSET'
|
||||
'symbol': 'HALF_DAY_TEST_ASSET',
|
||||
'exchange': "TEST",
|
||||
},
|
||||
cls.SHORT_ASSET_SID: {
|
||||
'start_date': pd.Timestamp('2015-01-05', tz='UTC'),
|
||||
'end_date': pd.Timestamp('2015-01-06', tz='UTC'),
|
||||
'symbol': 'SHORT_ASSET'
|
||||
'symbol': 'SHORT_ASSET',
|
||||
'exchange': "TEST",
|
||||
}
|
||||
},
|
||||
orient='index',
|
||||
|
||||
@@ -1046,6 +1046,7 @@ class TestPositionPerformance(WithInstanceTmpDir, WithTradingCalendar,
|
||||
'start_date': start,
|
||||
'end_date': end,
|
||||
'multiplier': 100,
|
||||
'exchange': "TEST",
|
||||
}
|
||||
for sid in futures_sids
|
||||
},
|
||||
@@ -2345,9 +2346,9 @@ class TestPositionTracker(WithTradingEnvironment,
|
||||
def make_futures_info(cls):
|
||||
return pd.DataFrame.from_dict(
|
||||
{
|
||||
3: {'multiplier': 1000},
|
||||
4: {'multiplier': 1000},
|
||||
1032201401: {'multiplier': 50},
|
||||
3: {'multiplier': 1000, 'exchange': 'TEST'},
|
||||
4: {'multiplier': 1000, 'exchange': 'TEST'},
|
||||
1032201401: {'multiplier': 50, 'exchange': 'TEST'},
|
||||
},
|
||||
orient='index',
|
||||
)
|
||||
|
||||
@@ -85,7 +85,8 @@ class SecurityListTestCase(WithLogger, WithTradingCalendar, ZiplineTestCase):
|
||||
equities=pd.DataFrame.from_records([{
|
||||
'start_date': cls.start,
|
||||
'end_date': end,
|
||||
'symbol': symbol
|
||||
'symbol': symbol,
|
||||
'exchange': "TEST",
|
||||
} for symbol in symbols]),
|
||||
))
|
||||
|
||||
@@ -103,7 +104,8 @@ class SecurityListTestCase(WithLogger, WithTradingCalendar, ZiplineTestCase):
|
||||
equities=pd.DataFrame.from_records([{
|
||||
'start_date': sp2.start_session,
|
||||
'end_date': sp2.end_session,
|
||||
'symbol': symbol
|
||||
'symbol': symbol,
|
||||
'exchange': "TEST",
|
||||
} for symbol in symbols]),
|
||||
))
|
||||
|
||||
@@ -267,6 +269,7 @@ class SecurityListTestCase(WithLogger, WithTradingCalendar, ZiplineTestCase):
|
||||
'symbol': 'BZQ',
|
||||
'start_date': sim_params.start_session,
|
||||
'end_date': sim_params.end_session,
|
||||
'exchange': "TEST",
|
||||
}])
|
||||
with TempDirectory() as new_tempdir, \
|
||||
security_list_copy(), \
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
from datetime import time
|
||||
|
||||
import pandas as pd
|
||||
from mock import patch
|
||||
|
||||
@@ -23,6 +25,7 @@ from zipline.sources.benchmark_source import BenchmarkSource
|
||||
from zipline.test_algorithms import NoopAlgorithm
|
||||
from zipline.utils import factory
|
||||
from zipline.testing.core import FakeDataPortal
|
||||
from zipline.utils.calendars.trading_calendar import days_at_time
|
||||
|
||||
|
||||
class BeforeTradingAlgorithm(TradingAlgorithm):
|
||||
@@ -75,10 +78,18 @@ class TestTradeSimulation(TestCase):
|
||||
algo = BeforeTradingAlgorithm(sim_params=params)
|
||||
algo.run(FakeDataPortal())
|
||||
|
||||
self.assertEqual(len(algo.perf_tracker.sim_params.sessions),
|
||||
num_days)
|
||||
self.assertEqual(
|
||||
len(algo.perf_tracker.sim_params.sessions),
|
||||
num_days
|
||||
)
|
||||
|
||||
self.assertTrue(params.sessions.equals(
|
||||
pd.DatetimeIndex(algo.before_trading_at)),
|
||||
"Expected %s but was %s."
|
||||
% (params.sessions, algo.before_trading_at))
|
||||
bts_minutes = days_at_time(
|
||||
params.sessions, time(8, 45), "US/Eastern"
|
||||
)
|
||||
|
||||
self.assertTrue(
|
||||
bts_minutes.equals(
|
||||
pd.DatetimeIndex(algo.before_trading_at)
|
||||
),
|
||||
"Expected %s but was %s." % (params.sessions,
|
||||
algo.before_trading_at))
|
||||
|
||||
+27
-13
@@ -24,7 +24,7 @@ from six import iteritems, PY2
|
||||
from cpython cimport bool
|
||||
from collections import Iterable
|
||||
|
||||
from zipline.assets import Asset
|
||||
from zipline.assets import Asset, Future
|
||||
from zipline.zipline_warnings import ZiplineDeprecationWarning
|
||||
|
||||
|
||||
@@ -426,9 +426,11 @@ cdef class BarData:
|
||||
@check_parameters(('assets',), (Asset,))
|
||||
def can_trade(self, assets):
|
||||
"""
|
||||
For the given asset or iterable of assets, returns true if the asset
|
||||
is alive at the current simulation time and there is a known last
|
||||
price.
|
||||
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.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
@@ -460,15 +462,25 @@ cdef class BarData:
|
||||
})
|
||||
|
||||
cdef bool _can_trade_for_asset(self, asset, dt, adjusted_dt, data_portal):
|
||||
if asset._is_alive(dt, False):
|
||||
# is there a last price?
|
||||
return not np.isnan(
|
||||
data_portal.get_spot_value(
|
||||
asset, "price", adjusted_dt, self.data_frequency
|
||||
)
|
||||
)
|
||||
session_label = normalize_date(dt) # FIXME
|
||||
if not asset.is_alive_for_session(session_label):
|
||||
# asset isn't alive
|
||||
return False
|
||||
|
||||
return False
|
||||
if not asset.is_exchange_open(dt):
|
||||
# exchange isn't open
|
||||
return False
|
||||
|
||||
if isinstance(asset, Future):
|
||||
# FIXME: this will get removed once we can get prices for futures
|
||||
return True
|
||||
|
||||
# is there a last price?
|
||||
return not np.isnan(
|
||||
data_portal.get_spot_value(
|
||||
asset, "price", adjusted_dt, self.data_frequency
|
||||
)
|
||||
)
|
||||
|
||||
@check_parameters(('assets',), (Asset,))
|
||||
def is_stale(self, assets):
|
||||
@@ -511,7 +523,9 @@ cdef class BarData:
|
||||
})
|
||||
|
||||
cdef bool _is_stale_for_asset(self, asset, dt, adjusted_dt, data_portal):
|
||||
if not asset._is_alive(dt, False):
|
||||
session_label = normalize_date(dt) # FIXME
|
||||
|
||||
if not asset.is_alive_for_session(session_label):
|
||||
return False
|
||||
|
||||
current_volume = data_portal.get_spot_value(
|
||||
|
||||
+22
-18
@@ -15,7 +15,7 @@
|
||||
from copy import copy
|
||||
import operator as op
|
||||
import warnings
|
||||
from datetime import tzinfo
|
||||
from datetime import tzinfo, time
|
||||
import logbook
|
||||
import pytz
|
||||
import pandas as pd
|
||||
@@ -94,9 +94,9 @@ from zipline.utils.api_support import (
|
||||
require_not_initialized,
|
||||
ZiplineAPI,
|
||||
disallowed_in_before_trading_start)
|
||||
|
||||
from zipline.utils.input_validation import ensure_upper_case, error_keywords, \
|
||||
expect_types, optional, coerce_string
|
||||
from zipline.utils.calendars.trading_calendar import days_at_time
|
||||
from zipline.utils.cache import CachedObject, Expired
|
||||
from zipline.utils.calendars import get_calendar
|
||||
|
||||
@@ -496,29 +496,32 @@ class TradingAlgorithm(object):
|
||||
"""
|
||||
trading_o_and_c = self.trading_calendar.schedule.ix[
|
||||
self.sim_params.sessions]
|
||||
market_closes = trading_o_and_c['market_close'].values.astype(np.int64)
|
||||
market_closes = trading_o_and_c['market_close']
|
||||
minutely_emission = False
|
||||
|
||||
if self.sim_params.data_frequency == 'minute':
|
||||
market_opens = trading_o_and_c['market_open'].values.astype(
|
||||
np.int64)
|
||||
market_opens = trading_o_and_c['market_open']
|
||||
|
||||
minutely_emission = self.sim_params.emission_rate == "minute"
|
||||
|
||||
return MinuteSimulationClock(
|
||||
self.sim_params.sessions,
|
||||
market_opens,
|
||||
market_closes,
|
||||
minutely_emission
|
||||
)
|
||||
else:
|
||||
# in daily mode, we want to have one bar per session, timestamped
|
||||
# as the last minute of the session.
|
||||
return MinuteSimulationClock(
|
||||
self.sim_params.sessions,
|
||||
market_closes,
|
||||
market_closes,
|
||||
False
|
||||
)
|
||||
market_opens = market_closes
|
||||
|
||||
# FIXME generalize these values
|
||||
before_trading_start_minutes = days_at_time(
|
||||
self.sim_params.sessions,
|
||||
time(8, 45),
|
||||
"US/Eastern"
|
||||
)
|
||||
|
||||
return MinuteSimulationClock(
|
||||
self.sim_params.sessions,
|
||||
market_opens,
|
||||
market_closes,
|
||||
before_trading_start_minutes,
|
||||
minute_emission=minutely_emission,
|
||||
)
|
||||
|
||||
def _create_benchmark_source(self):
|
||||
return BenchmarkSource(
|
||||
@@ -1545,6 +1548,7 @@ class TradingAlgorithm(object):
|
||||
self.datetime, self._in_before_trading_start, self.data_portal)
|
||||
self._account = \
|
||||
self.perf_tracker.get_account(self.performance_needs_update)
|
||||
|
||||
self.account_needs_update = False
|
||||
self.performance_needs_update = False
|
||||
return self._account
|
||||
|
||||
+28
-24
@@ -34,14 +34,15 @@ from numpy cimport int64_t
|
||||
import warnings
|
||||
cimport numpy as np
|
||||
|
||||
from zipline.utils.calendars import get_calendar
|
||||
|
||||
|
||||
# IMPORTANT NOTE: You must change this template if you change
|
||||
# Asset.__reduce__, or else we'll attempt to unpickle an old version of this
|
||||
# class
|
||||
from pandas.tslib import normalize_date
|
||||
|
||||
CACHE_FILE_TEMPLATE = '/tmp/.%s-%s.v6.cache'
|
||||
|
||||
|
||||
cdef class Asset:
|
||||
|
||||
cdef readonly int sid
|
||||
@@ -71,13 +72,13 @@ cdef class Asset:
|
||||
|
||||
def __init__(self,
|
||||
int sid, # sid is required
|
||||
object exchange, # exchange is required
|
||||
object symbol="",
|
||||
object asset_name="",
|
||||
object start_date=None,
|
||||
object end_date=None,
|
||||
object first_traded=None,
|
||||
object auto_close_date=None,
|
||||
object exchange=""):
|
||||
object auto_close_date=None):
|
||||
|
||||
self.sid = sid
|
||||
self.sid_hash = hash(sid)
|
||||
@@ -157,13 +158,13 @@ cdef class Asset:
|
||||
be serialized/deserialized during pickling.
|
||||
"""
|
||||
return (self.__class__, (self.sid,
|
||||
self.exchange,
|
||||
self.symbol,
|
||||
self.asset_name,
|
||||
self.start_date,
|
||||
self.end_date,
|
||||
self.first_traded,
|
||||
self.auto_close_date,
|
||||
self.exchange,))
|
||||
self.auto_close_date,))
|
||||
|
||||
cpdef to_dict(self):
|
||||
"""
|
||||
@@ -187,37 +188,40 @@ cdef class Asset:
|
||||
"""
|
||||
return cls(**dict_)
|
||||
|
||||
def _is_alive(self, dt, bool normalized):
|
||||
def is_alive_for_session(self, session_label):
|
||||
"""
|
||||
Returns whether the asset is alive at the given dt.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
dt: pd.Timestamp
|
||||
The desired timestamp.
|
||||
|
||||
normalized: boolean
|
||||
Whether the date has already been normalized. If not, we need
|
||||
to first normalize the date before doing the alive check. If the
|
||||
date is already normalized, this method runs up to 80% faster.
|
||||
session_label: pd.Timestamp
|
||||
The desired session label to check. (midnight UTC)
|
||||
|
||||
Returns
|
||||
-------
|
||||
boolean: whether the asset is alive at the given dt.
|
||||
"""
|
||||
cdef int64_t dt_value
|
||||
cdef int64_t ref_start
|
||||
cdef int64_t ref_end
|
||||
|
||||
if not normalized:
|
||||
dt_value = normalize_date(dt).value
|
||||
else:
|
||||
dt_value = dt.value
|
||||
|
||||
ref_start = self.start_date.value
|
||||
ref_end = self.end_date.value
|
||||
|
||||
return ref_start <= dt_value <= ref_end
|
||||
return ref_start <= session_label.value <= ref_end
|
||||
|
||||
def is_exchange_open(self, dt_minute):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
dt_minute: pd.Timestamp (UTC, tz-aware)
|
||||
The minute to check.
|
||||
|
||||
Returns
|
||||
-------
|
||||
boolean: whether the asset's exchange is open at the given minute.
|
||||
"""
|
||||
calendar = get_calendar(self.exchange)
|
||||
return calendar.is_open_on_minute(dt_minute)
|
||||
|
||||
|
||||
cdef class Equity(Asset):
|
||||
@@ -291,6 +295,7 @@ cdef class Future(Asset):
|
||||
|
||||
def __init__(self,
|
||||
int sid, # sid is required
|
||||
object exchange, # exchange is required
|
||||
object symbol="",
|
||||
object root_symbol="",
|
||||
object asset_name="",
|
||||
@@ -300,19 +305,18 @@ cdef class Future(Asset):
|
||||
object expiration_date=None,
|
||||
object auto_close_date=None,
|
||||
object first_traded=None,
|
||||
object exchange="",
|
||||
object tick_size="",
|
||||
float multiplier=1.0):
|
||||
|
||||
super().__init__(
|
||||
sid,
|
||||
exchange,
|
||||
symbol=symbol,
|
||||
asset_name=asset_name,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
first_traded=first_traded,
|
||||
auto_close_date=auto_close_date,
|
||||
exchange=exchange,
|
||||
)
|
||||
self.root_symbol = root_symbol
|
||||
self.notice_date = notice_date
|
||||
@@ -347,6 +351,7 @@ cdef class Future(Asset):
|
||||
be serialized/deserialized during pickling.
|
||||
"""
|
||||
return (self.__class__, (self.sid,
|
||||
self.exchange,
|
||||
self.symbol,
|
||||
self.root_symbol,
|
||||
self.asset_name,
|
||||
@@ -356,7 +361,6 @@ cdef class Future(Asset):
|
||||
self.expiration_date,
|
||||
self.auto_close_date,
|
||||
self.first_traded,
|
||||
self.exchange,
|
||||
self.tick_size,
|
||||
self.multiplier,))
|
||||
|
||||
|
||||
@@ -217,6 +217,7 @@ def make_future_info(first_sid,
|
||||
'notice_date': notice_date_func(month_begin),
|
||||
'expiration_date': notice_date_func(month_begin),
|
||||
'multiplier': 500,
|
||||
'exchange': "TEST",
|
||||
})
|
||||
return pd.DataFrame.from_records(contracts, index='sid').convert_objects()
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ from zipline.utils.compat import mappingproxy
|
||||
from zipline.utils.input_validation import ensure_timestamp, optionally
|
||||
import zipline.utils.paths as pth
|
||||
from zipline.utils.preprocess import preprocess
|
||||
from zipline.utils.calendars import get_calendar
|
||||
from zipline.utils.calendars import get_calendar, register_calendar
|
||||
|
||||
nyse_cal = get_calendar('NYSE')
|
||||
trading_days = nyse_cal.all_sessions
|
||||
@@ -564,3 +564,6 @@ def _make_bundle_core():
|
||||
return BundleCore(bundles, register, unregister, ingest, load, clean)
|
||||
|
||||
bundles, register, unregister, ingest, load, clean = _make_bundle_core()
|
||||
|
||||
register_calendar("YAHOO", get_calendar("NYSE"))
|
||||
register_calendar("QUANDL", get_calendar("NYSE"))
|
||||
|
||||
@@ -123,7 +123,7 @@ def fetch_symbol_metadata_frame(api_key,
|
||||
# cut out all the other stuff in the name column
|
||||
# we need to escape the paren because it is actually splitting on a regex
|
||||
data.asset_name = data.asset_name.str.split(r' \(', 1).str.get(0)
|
||||
data['exchange'] = 'quandl'
|
||||
data['exchange'] = 'QUANDL'
|
||||
data['auto_close_date'] = data['end_date'] + pd.Timedelta(days=1)
|
||||
return data
|
||||
|
||||
|
||||
@@ -123,6 +123,11 @@ def yahoo_equities(symbols, start=None, end=None):
|
||||
daily_bar_writer.write(_pricing_iter(), show_progress=show_progress)
|
||||
|
||||
symbol_map = pd.Series(metadata.symbol.index, metadata.symbol)
|
||||
|
||||
# Hardcode the exchange to "YAHOO" for all assets and (elsewhere)
|
||||
# register "YAHOO" to resolve to the NYSE calendar, because these are
|
||||
# all equities and thus can use the NYSE calendar.
|
||||
metadata['exchange'] = "YAHOO"
|
||||
asset_db_writer.write(equities=metadata)
|
||||
|
||||
adjustments = []
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
from pandas.tslib import normalize_date
|
||||
|
||||
|
||||
class DailyHistoryAggregator(object):
|
||||
"""
|
||||
@@ -32,9 +30,10 @@ class DailyHistoryAggregator(object):
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, market_opens, minute_reader):
|
||||
def __init__(self, market_opens, minute_reader, trading_calendar):
|
||||
self._market_opens = market_opens
|
||||
self._minute_reader = minute_reader
|
||||
self._trading_calendar = trading_calendar
|
||||
|
||||
# The caches are structured as (date, market_open, entries), where
|
||||
# entries is a dict of asset -> (last_visited_dt, value)
|
||||
@@ -97,10 +96,10 @@ class DailyHistoryAggregator(object):
|
||||
market_open, prev_dt, dt_value, entries = self._prelude(dt, 'open')
|
||||
|
||||
opens = []
|
||||
normalized_date = normalize_date(dt)
|
||||
session_label = self._trading_calendar.minute_to_session_label(dt)
|
||||
|
||||
for asset in assets:
|
||||
if not asset._is_alive(normalized_date, True):
|
||||
if not asset.is_alive_for_session(session_label):
|
||||
opens.append(np.NaN)
|
||||
continue
|
||||
|
||||
@@ -166,10 +165,10 @@ class DailyHistoryAggregator(object):
|
||||
market_open, prev_dt, dt_value, entries = self._prelude(dt, 'high')
|
||||
|
||||
highs = []
|
||||
normalized_date = normalize_date(dt)
|
||||
session_label = self._trading_calendar.minute_to_session_label(dt)
|
||||
|
||||
for asset in assets:
|
||||
if not asset._is_alive(normalized_date, True):
|
||||
if not asset.is_alive_for_session(session_label):
|
||||
highs.append(np.NaN)
|
||||
continue
|
||||
|
||||
@@ -235,10 +234,10 @@ class DailyHistoryAggregator(object):
|
||||
market_open, prev_dt, dt_value, entries = self._prelude(dt, 'low')
|
||||
|
||||
lows = []
|
||||
normalized_date = normalize_date(dt)
|
||||
session_label = self._trading_calendar.minute_to_session_label(dt)
|
||||
|
||||
for asset in assets:
|
||||
if not asset._is_alive(normalized_date, True):
|
||||
if not asset.is_alive_for_session(session_label):
|
||||
lows.append(np.NaN)
|
||||
continue
|
||||
|
||||
@@ -305,10 +304,10 @@ class DailyHistoryAggregator(object):
|
||||
market_open, prev_dt, dt_value, entries = self._prelude(dt, 'close')
|
||||
|
||||
closes = []
|
||||
normalized_dt = normalize_date(dt)
|
||||
session_label = self._trading_calendar.minute_to_session_label(dt)
|
||||
|
||||
for asset in assets:
|
||||
if not asset._is_alive(normalized_dt, True):
|
||||
if not asset.is_alive_for_session(session_label):
|
||||
closes.append(np.NaN)
|
||||
continue
|
||||
|
||||
@@ -365,10 +364,10 @@ class DailyHistoryAggregator(object):
|
||||
market_open, prev_dt, dt_value, entries = self._prelude(dt, 'volume')
|
||||
|
||||
volumes = []
|
||||
normalized_date = normalize_date(dt)
|
||||
session_label = self._trading_calendar.minute_to_session_label(dt)
|
||||
|
||||
for asset in assets:
|
||||
if not asset._is_alive(normalized_date, True):
|
||||
if not asset.is_alive_for_session(session_label):
|
||||
volumes.append(0)
|
||||
continue
|
||||
|
||||
|
||||
@@ -150,7 +150,9 @@ class DataPortal(object):
|
||||
if self._equity_minute_reader is not None:
|
||||
self._equity_daily_aggregator = DailyHistoryAggregator(
|
||||
self.trading_calendar.schedule.market_open,
|
||||
self._equity_minute_reader)
|
||||
self._equity_minute_reader,
|
||||
self.trading_calendar
|
||||
)
|
||||
self._equity_minute_history_loader = USEquityMinuteHistoryLoader(
|
||||
self.trading_calendar,
|
||||
self._equity_minute_reader,
|
||||
|
||||
@@ -7,6 +7,8 @@ from zipline import run_algorithm
|
||||
|
||||
|
||||
# These are used by test_examples.py to discover the examples to run.
|
||||
from zipline.utils.calendars import register_calendar, get_calendar
|
||||
|
||||
EXAMPLE_MODULES = {}
|
||||
for f in os.listdir(os.path.dirname(__file__)):
|
||||
if not f.endswith('.py') or f == '__init__.py':
|
||||
@@ -65,6 +67,9 @@ def run_example(example_name, environ):
|
||||
Run an example module from zipline.examples.
|
||||
"""
|
||||
mod = EXAMPLE_MODULES[example_name]
|
||||
|
||||
register_calendar("YAHOO", get_calendar("NYSE"), force=True)
|
||||
|
||||
return run_algorithm(
|
||||
initialize=getattr(mod, 'initialize', None),
|
||||
handle_data=getattr(mod, 'handle_data', None),
|
||||
|
||||
@@ -17,7 +17,7 @@ import abc
|
||||
from abc import abstractmethod
|
||||
from six import with_metaclass
|
||||
|
||||
from zipline.gens.sim_engine import DAY_END
|
||||
from zipline.gens.sim_engine import SESSION_END
|
||||
|
||||
|
||||
class CancelPolicy(with_metaclass(abc.ABCMeta)):
|
||||
@@ -58,7 +58,7 @@ class EODCancel(CancelPolicy):
|
||||
self.warn_on_cancel = warn_on_cancel
|
||||
|
||||
def should_cancel(self, event):
|
||||
return event == DAY_END
|
||||
return event == SESSION_END
|
||||
|
||||
|
||||
class NeverCancel(CancelPolicy):
|
||||
|
||||
+70
-39
@@ -24,63 +24,94 @@ NANOS_IN_MINUTE = _nanos_in_minute
|
||||
|
||||
cpdef enum:
|
||||
BAR = 0
|
||||
DAY_START = 1
|
||||
DAY_END = 2
|
||||
SESSION_START = 1
|
||||
SESSION_END = 2
|
||||
MINUTE_END = 3
|
||||
BEFORE_TRADING_START_BAR = 4
|
||||
|
||||
cdef class MinuteSimulationClock:
|
||||
cdef object trading_days
|
||||
cdef bool minute_emission
|
||||
cdef np.int64_t[:] market_opens, market_closes
|
||||
cdef public dict minutes_by_day, minutes_to_day
|
||||
cdef np.int64_t[:] market_opens_nanos, market_closes_nanos, bts_nanos, \
|
||||
sessions_nanos
|
||||
cdef dict minutes_by_session
|
||||
|
||||
def __init__(self,
|
||||
trading_days,
|
||||
sessions,
|
||||
market_opens,
|
||||
market_closes,
|
||||
before_trading_start_minutes,
|
||||
minute_emission=False):
|
||||
self.minute_emission = minute_emission
|
||||
self.market_opens = market_opens
|
||||
self.market_closes = market_closes
|
||||
self.trading_days = trading_days
|
||||
self.minutes_by_day = self.calc_minutes_by_day()
|
||||
|
||||
self.market_opens_nanos = market_opens.values.astype(np.int64)
|
||||
self.market_closes_nanos = market_closes.values.astype(np.int64)
|
||||
self.sessions_nanos = sessions.values.astype(np.int64)
|
||||
self.bts_nanos = before_trading_start_minutes.values.astype(np.int64)
|
||||
|
||||
self.minutes_by_session = self.calc_minutes_by_session()
|
||||
|
||||
@cython.boundscheck(False)
|
||||
@cython.wraparound(False)
|
||||
cdef np.ndarray[np.int64_t, ndim=1] market_minutes(self, np.intp_t i):
|
||||
cdef np.int64_t[:] market_opens, market_closes
|
||||
cdef dict calc_minutes_by_session(self):
|
||||
cdef dict minutes_by_session
|
||||
cdef int session_idx
|
||||
cdef np.int64_t session_nano
|
||||
cdef np.ndarray[np.int64_t, ndim=1] minutes_nanos
|
||||
|
||||
market_opens = self.market_opens
|
||||
market_closes = self.market_closes
|
||||
|
||||
return np.arange(market_opens[i],
|
||||
market_closes[i] + _nanos_in_minute,
|
||||
_nanos_in_minute)
|
||||
|
||||
@cython.boundscheck(False)
|
||||
@cython.wraparound(False)
|
||||
cdef dict calc_minutes_by_day(self):
|
||||
cdef dict minutes_by_day
|
||||
cdef int day_idx
|
||||
cdef object day
|
||||
|
||||
minutes_by_day = {}
|
||||
for day_idx, day in enumerate(self.trading_days):
|
||||
minutes_by_day[day] = pd.to_datetime(
|
||||
self.market_minutes(day_idx), utc=True, box=True)
|
||||
return minutes_by_day
|
||||
minutes_by_session = {}
|
||||
for session_idx, session_nano in enumerate(self.sessions_nanos):
|
||||
minutes_nanos = np.arange(
|
||||
self.market_opens_nanos[session_idx],
|
||||
self.market_closes_nanos[session_idx] + _nanos_in_minute,
|
||||
_nanos_in_minute
|
||||
)
|
||||
minutes_by_session[session_nano] = pd.to_datetime(
|
||||
minutes_nanos, utc=True, box=True
|
||||
)
|
||||
return minutes_by_session
|
||||
|
||||
def __iter__(self):
|
||||
minute_emission = self.minute_emission
|
||||
|
||||
for day in self.trading_days:
|
||||
yield day, DAY_START
|
||||
for idx, session_nano in enumerate(self.sessions_nanos):
|
||||
yield pd.Timestamp(session_nano, tz='UTC'), SESSION_START
|
||||
|
||||
minutes = self.minutes_by_day[day]
|
||||
bts_minute = pd.Timestamp(self.bts_nanos[idx], tz='UTC')
|
||||
regular_minutes = self.minutes_by_session[session_nano]
|
||||
|
||||
for minute in minutes:
|
||||
yield minute, BAR
|
||||
if minute_emission:
|
||||
yield minute, MINUTE_END
|
||||
if bts_minute > regular_minutes[-1]:
|
||||
# before_trading_start is after the last close,
|
||||
# so don't emit it
|
||||
for minute, evt in self._get_minutes_for_list(
|
||||
regular_minutes,
|
||||
minute_emission
|
||||
):
|
||||
yield minute, evt
|
||||
else:
|
||||
# we have to search anew every session, because there is no
|
||||
# guarantee that any two session start on the same minute
|
||||
bts_idx = regular_minutes.searchsorted(bts_minute)
|
||||
|
||||
yield minutes[-1], DAY_END
|
||||
# emit all the minutes before bts_minute
|
||||
for minute, evt in self._get_minutes_for_list(
|
||||
regular_minutes[0:bts_idx],
|
||||
minute_emission
|
||||
):
|
||||
yield minute, evt
|
||||
|
||||
yield bts_minute, BEFORE_TRADING_START_BAR
|
||||
|
||||
# emit all the minutes after bts_minute
|
||||
for minute, evt in self._get_minutes_for_list(
|
||||
regular_minutes[bts_idx:],
|
||||
minute_emission
|
||||
):
|
||||
yield minute, evt
|
||||
|
||||
yield regular_minutes[-1], SESSION_END
|
||||
|
||||
def _get_minutes_for_list(self, minutes, minute_emission):
|
||||
for minute in minutes:
|
||||
yield minute, BAR
|
||||
if minute_emission:
|
||||
yield minute, MINUTE_END
|
||||
|
||||
@@ -21,9 +21,10 @@ from six import viewkeys
|
||||
|
||||
from zipline.gens.sim_engine import (
|
||||
BAR,
|
||||
DAY_START,
|
||||
DAY_END,
|
||||
MINUTE_END
|
||||
SESSION_START,
|
||||
SESSION_END,
|
||||
MINUTE_END,
|
||||
BEFORE_TRADING_START_BAR
|
||||
)
|
||||
|
||||
log = Logger('Trade Simulation')
|
||||
@@ -181,9 +182,6 @@ class AlgorithmSimulator(object):
|
||||
algo.blotter.process_splits(splits)
|
||||
perf_tracker.position_tracker.handle_splits(splits)
|
||||
|
||||
# call before trading start
|
||||
algo.before_trading_start(current_data)
|
||||
|
||||
def handle_benchmark(date, benchmark_source=self.benchmark_source):
|
||||
algo.perf_tracker.all_benchmark_returns[date] = \
|
||||
benchmark_source.get_value(date)
|
||||
@@ -202,7 +200,7 @@ class AlgorithmSimulator(object):
|
||||
|
||||
if algo.data_frequency == 'minute':
|
||||
def execute_order_cancellation_policy():
|
||||
algo.blotter.execute_cancel_policy(DAY_END)
|
||||
algo.blotter.execute_cancel_policy(SESSION_END)
|
||||
|
||||
def calculate_minute_capital_changes(dt):
|
||||
# process any capital changes that came between the last
|
||||
@@ -220,16 +218,20 @@ class AlgorithmSimulator(object):
|
||||
if action == BAR:
|
||||
for capital_change_packet in every_bar(dt):
|
||||
yield capital_change_packet
|
||||
elif action == DAY_START:
|
||||
elif action == SESSION_START:
|
||||
for capital_change_packet in once_a_day(dt):
|
||||
yield capital_change_packet
|
||||
elif action == DAY_END:
|
||||
# End of the day.
|
||||
elif action == SESSION_END:
|
||||
# End of the session.
|
||||
if emission_rate == 'daily':
|
||||
handle_benchmark(normalize_date(dt))
|
||||
execute_order_cancellation_policy()
|
||||
|
||||
yield self._get_daily_message(dt, algo, algo.perf_tracker)
|
||||
elif action == BEFORE_TRADING_START_BAR:
|
||||
# call before trading start
|
||||
algo.on_dt_changed(dt)
|
||||
algo.before_trading_start(self.current_data)
|
||||
elif action == MINUTE_END:
|
||||
handle_benchmark(dt)
|
||||
minute_msg = \
|
||||
|
||||
@@ -525,14 +525,14 @@ class SetLongOnlyAlgorithm(TradingAlgorithm):
|
||||
|
||||
class SetAssetDateBoundsAlgorithm(TradingAlgorithm):
|
||||
"""
|
||||
Algorithm that tries to order 1 share of sid 0 on every bar and has an
|
||||
Algorithm that tries to order 1 share of sid 999 on every bar and has an
|
||||
AssetDateBounds() trading control in place.
|
||||
"""
|
||||
def initialize(self):
|
||||
self.register_trading_control(AssetDateBounds())
|
||||
|
||||
def handle_data(algo, data):
|
||||
algo.order(algo.sid(0), 1)
|
||||
algo.order(algo.sid(999), 1)
|
||||
|
||||
|
||||
class TestRegisterTransformAlgorithm(TradingAlgorithm):
|
||||
|
||||
@@ -37,7 +37,7 @@ from zipline.pipeline import SimplePipelineEngine
|
||||
from zipline.pipeline.loaders.testing import make_seeded_random_loader
|
||||
from zipline.utils.calendars import (
|
||||
get_calendar,
|
||||
)
|
||||
register_calendar)
|
||||
|
||||
|
||||
class ZiplineTestCase(with_metaclass(FinalMeta, TestCase)):
|
||||
@@ -336,6 +336,8 @@ class WithAssetFinder(WithDefaultDateBounds):
|
||||
|
||||
@classmethod
|
||||
def make_equity_info(cls):
|
||||
register_calendar("TEST", get_calendar("NYSE"), force=True)
|
||||
|
||||
return make_simple_equity_info(
|
||||
cls.ASSET_FINDER_EQUITY_SIDS,
|
||||
cls.ASSET_FINDER_EQUITY_START_DATE,
|
||||
|
||||
@@ -28,24 +28,25 @@ def get_calendar(name):
|
||||
The desired calendar.
|
||||
"""
|
||||
if name not in _static_calendars:
|
||||
if name == 'NYSE':
|
||||
if name in ["NYSE", "NASDAQ", "BATS"]:
|
||||
cal = NYSEExchangeCalendar()
|
||||
elif name == 'CME':
|
||||
elif name in ["CME", "CBOT", "COMEX", "NYMEX"]:
|
||||
cal = CMEExchangeCalendar()
|
||||
elif name in ["ICEUS", "NYFE"]:
|
||||
cal = ICEExchangeCalendar()
|
||||
elif name == "CFE":
|
||||
cal = CFEExchangeCalendar()
|
||||
elif name == 'BMF':
|
||||
cal = BMFExchangeCalendar()
|
||||
elif name == 'LSE':
|
||||
cal = LSEExchangeCalendar()
|
||||
elif name == 'TSX':
|
||||
cal = TSXExchangeCalendar()
|
||||
elif name == "ICE":
|
||||
cal = ICEExchangeCalendar()
|
||||
elif name == "CFE":
|
||||
cal = CFEExchangeCalendar()
|
||||
|
||||
else:
|
||||
raise InvalidCalendarName(calendar_name=name)
|
||||
|
||||
register_calendar(cal)
|
||||
register_calendar(name, cal)
|
||||
|
||||
return _static_calendars[name]
|
||||
|
||||
@@ -72,13 +73,15 @@ def clear_calendars():
|
||||
_static_calendars.clear()
|
||||
|
||||
|
||||
def register_calendar(calendar, force=False):
|
||||
def register_calendar(name, calendar, force=False):
|
||||
"""
|
||||
Registers a calendar for retrieval by the get_calendar method.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
calendar : TradingCalendar
|
||||
name: str
|
||||
The key with which to register this calendar.
|
||||
calendar: TradingCalendar
|
||||
The calendar to be registered for retrieval.
|
||||
force : bool, optional
|
||||
If True, old calendars will be overwritten on a name collision.
|
||||
@@ -92,10 +95,10 @@ def register_calendar(calendar, force=False):
|
||||
# If we are forcing the registration, remove an existing calendar with the
|
||||
# same name.
|
||||
if force:
|
||||
deregister_calendar(calendar.name)
|
||||
deregister_calendar(name)
|
||||
|
||||
# Check if we are already holding a calendar with the same name
|
||||
if calendar.name in _static_calendars:
|
||||
raise CalendarNameCollision(calendar_name=calendar.name)
|
||||
if name in _static_calendars:
|
||||
raise CalendarNameCollision(calendar_name=name)
|
||||
|
||||
_static_calendars[calendar.name] = calendar
|
||||
_static_calendars[name] = calendar
|
||||
|
||||
@@ -757,12 +757,18 @@ def days_at_time(days, t, tz, day_offset=0):
|
||||
day_offset : int
|
||||
The number of days we want to offset @days by
|
||||
"""
|
||||
if len(days) == 0:
|
||||
return days
|
||||
|
||||
# Offset days without tz to avoid timezone issues.
|
||||
days = DatetimeIndex(days).tz_localize(None)
|
||||
days_offset = days + DateOffset(days=day_offset)
|
||||
|
||||
# Shift all days to the target time in the local timezone, then
|
||||
# convert to UTC.
|
||||
|
||||
# FIXME: Once we're off Pandas 16, see if we can replace DateOffset with
|
||||
# TimeDelta.
|
||||
return days_offset.shift(
|
||||
1, freq=DateOffset(hour=t.hour, minute=t.minute, second=t.second)
|
||||
).tz_localize(tz).tz_convert('UTC')
|
||||
|
||||
Reference in New Issue
Block a user