From a5d1f79a3768e862d4cbffd15e4cc7c30921ca02 Mon Sep 17 00:00:00 2001 From: jfkirk Date: Mon, 8 Jun 2015 16:30:16 -0400 Subject: [PATCH] TST: Reconciles tests with asset management system --- docs/release-notes/zipline-0.8.0.md | 3 +- tests/test_algorithm.py | 189 +++++++-- tests/test_algorithm_gen.py | 11 +- tests/test_batchtransform.py | 5 +- tests/test_blotter.py | 5 +- tests/test_cli.py | 6 +- tests/test_events_through_risk.py | 4 +- tests/test_history.py | 3 +- tests/test_perf_tracking.py | 631 +++++++++++++++------------- tests/test_security_list.py | 91 ++-- tests/test_sources.py | 51 ++- 11 files changed, 608 insertions(+), 391 deletions(-) diff --git a/docs/release-notes/zipline-0.8.0.md b/docs/release-notes/zipline-0.8.0.md index 222e4355..bcab3732 100644 --- a/docs/release-notes/zipline-0.8.0.md +++ b/docs/release-notes/zipline-0.8.0.md @@ -44,11 +44,10 @@ > running with minute data, then this will calculate the number of minutes in > those days, accounting for early closes and the current time and apply the > transform over the set of minutes. `returns` takes no parameters and will - > return the daily returns of the given security. + > return the daily returns of the given asset. > Example: ```python -# The standard deviation of the price in the last 3 days. data[security].stddev(3) ``` diff --git a/tests/test_algorithm.py b/tests/test_algorithm.py index 5adcdd85..c1a7acce 100644 --- a/tests/test_algorithm.py +++ b/tests/test_algorithm.py @@ -36,6 +36,7 @@ from zipline.errors import ( RegisterTradingControlPostInit, TradingControlViolation, AccountControlViolation, + SymbolNotFound, ) from zipline.test_algorithms import ( access_account_in_init, @@ -55,6 +56,7 @@ from zipline.test_algorithms import ( TestTargetPercentAlgorithm, TestTargetValueAlgorithm, SetLongOnlyAlgorithm, + SetAssetDateBoundsAlgorithm, SetMaxPositionSizeAlgorithm, SetMaxOrderCountAlgorithm, SetMaxOrderSizeAlgorithm, @@ -85,6 +87,9 @@ from zipline.sources import (SpecificEquityTrades, DataFrameSource, DataPanelSource, RandomWalkSource) +from zipline.assets import ( + Equity, Future +) from zipline.finance.execution import LimitOrder from zipline.finance.trading import SimulationParameters @@ -178,12 +183,12 @@ class TestMiscellaneousAPI(TestCase): if algo.minute == 0: # Should be filled by the next minute - algo.order(1, 1) + algo.order(algo.sid(1), 1) # Won't be filled because the price is too low. - algo.order(2, 1, style=LimitOrder(0.01)) - algo.order(2, 1, style=LimitOrder(0.01)) - algo.order(2, 1, style=LimitOrder(0.01)) + algo.order(algo.sid(2), 1, style=LimitOrder(0.01)) + algo.order(algo.sid(2), 1, style=LimitOrder(0.01)) + algo.order(algo.sid(2), 1, style=LimitOrder(0.01)) all_orders = algo.get_open_orders() self.assertEqual(list(all_orders.keys()), [1, 2]) @@ -293,6 +298,58 @@ class TestMiscellaneousAPI(TestCase): self.assertIs(composer, zipline.utils.events.ComposedRule.lazy_and) + def test_asset_lookup(self): + metadata = {0: {'symbol': 'PLAY', + 'asset_type': 'equity', + 'start_date': '2002-01-01', + 'end_date': '2004-01-01'}, + 1: {'symbol': 'PLAY', + 'asset_type': 'equity', + 'start_date': '2005-01-01', + 'end_date': '2006-01-01'}, + 2: {'symbol': 'OMG15', + 'asset_type': 'future'}} + algo = TradingAlgorithm(asset_metadata=metadata) + + # Test before either PLAY existed + algo.datetime = pd.Timestamp('2001-12-01', tz='UTC') + self.assertEqual(2, algo.symbol('OMG15')) + with self.assertRaises(SymbolNotFound): + algo.symbol('PLAY') + with self.assertRaises(SymbolNotFound): + algo.symbols('PLAY', 'OMG15') + + # Test when first PLAY exists + algo.datetime = pd.Timestamp('2002-12-01', tz='UTC') + self.assertEqual(2, algo.symbol('OMG15')) + self.assertEqual(0, algo.symbol('PLAY')) + list_result = algo.symbols('PLAY', 'OMG15') + self.assertEqual(0, list_result[0]) + self.assertEqual(2, list_result[1]) + + # Test after first PLAY ends + algo.datetime = pd.Timestamp('2004-12-01', tz='UTC') + self.assertEqual(2, algo.symbol('OMG15')) + self.assertEqual(0, algo.symbol('PLAY')) + + # Test after second PLAY begins + algo.datetime = pd.Timestamp('2005-12-01', tz='UTC') + self.assertEqual(2, algo.symbol('OMG15')) + self.assertEqual(1, algo.symbol('PLAY')) + + # Test after second PLAY ends + algo.datetime = pd.Timestamp('2006-12-01', tz='UTC') + self.assertEqual(2, algo.symbol('OMG15')) + self.assertEqual(1, algo.symbol('PLAY')) + list_result = algo.symbols('PLAY', 'OMG15') + self.assertEqual(1, list_result[0]) + self.assertEqual(2, list_result[1]) + + # Test lookup SID + self.assertIsInstance(algo.sid(0), Equity) + self.assertIsInstance(algo.sid(1), Equity) + self.assertIsInstance(algo.sid(2), Future) + class TestTransformAlgorithm(TestCase): def setUp(self): @@ -384,19 +441,36 @@ class TestTransformAlgorithm(TestCase): ) self.assertEqual(algo.sim_params.data_frequency, 'minute') - def test_order_methods(self): - AlgoClasses = [TestOrderAlgorithm, - TestOrderValueAlgorithm, - TestTargetAlgorithm, - TestOrderPercentAlgorithm, - TestTargetPercentAlgorithm, - TestTargetValueAlgorithm] + @parameterized.expand([ + (TestOrderAlgorithm,), + (TestOrderValueAlgorithm,), + (TestTargetAlgorithm,), + (TestOrderPercentAlgorithm,), + (TestTargetPercentAlgorithm,), + (TestTargetValueAlgorithm,), + ]) + def test_order_methods(self, algo_class): + algo = algo_class( + sim_params=self.sim_params, + ) + algo.run(self.df) - for AlgoClass in AlgoClasses: - algo = AlgoClass( - sim_params=self.sim_params, - ) - algo.run(self.df) + @parameterized.expand([ + (TestOrderAlgorithm,), + (TestOrderValueAlgorithm,), + (TestTargetAlgorithm,), + (TestOrderPercentAlgorithm,), + (TestTargetValueAlgorithm,), + ]) + def test_order_methods_for_future(self, algo_class): + metadata = {0: {'asset_type': 'future', + 'contract_multiplier': 10}} + + algo = algo_class( + sim_params=self.sim_params, + asset_metadata=metadata + ) + algo.run(self.df) def test_order_method_style_forwarding(self): @@ -493,7 +567,8 @@ class TestAlgoScript(TestCase): self.sim_params ) - self.source = SpecificEquityTrades(event_list=trade_history) + self.source = SpecificEquityTrades(sids=[133], + event_list=trade_history) self.df_source, self.df = \ factory.create_test_df_source(self.sim_params) @@ -543,7 +618,8 @@ from zipline.api import (slippage, set_slippage, set_commission, order, - record) + record, + sid) def initialize(context): model = slippage.FixedSlippage(spread=0.10) @@ -554,7 +630,7 @@ def initialize(context): def handle_data(context, data): if context.incr < context.count: - order(0, -1000) + order(sid(0), -1000) record(price=data[0].price) context.incr += 1""", @@ -607,7 +683,7 @@ def handle_data(context, data): if context.incr < context.count: # order small lots to be sure the # order will fill in a single transaction - order(0, 5000) + order(sid(0), 5000) record(price=data[0].price) record(volume=data[0].volume) record(incr=context.incr) @@ -877,6 +953,7 @@ class TestGetDatetime(TestCase): algo = TradingAlgorithm( script=algo, sim_params=sim_params, + identifiers=[1] ) algo.run(source) self.assertFalse(algo.first_bar) @@ -923,7 +1000,7 @@ class TestTradingControls(TestCase): # Buy one share four times. Should be fine. def handle_data(algo, data): - algo.order(self.sid, 1) + algo.order(algo.sid(self.sid), 1) algo.order_count += 1 algo = SetMaxPositionSizeAlgorithm(sid=self.sid, max_shares=10, @@ -933,7 +1010,7 @@ class TestTradingControls(TestCase): # Buy three shares four times. Should bail on the fourth before it's # placed. def handle_data(algo, data): - algo.order(self.sid, 3) + algo.order(algo.sid(self.sid), 3) algo.order_count += 1 algo = SetMaxPositionSizeAlgorithm(sid=self.sid, @@ -944,7 +1021,7 @@ class TestTradingControls(TestCase): # Buy two shares four times. Should bail due to max_notional on the # third attempt. def handle_data(algo, data): - algo.order(self.sid, 3) + algo.order(algo.sid(self.sid), 3) algo.order_count += 1 algo = SetMaxPositionSizeAlgorithm(sid=self.sid, @@ -955,7 +1032,7 @@ class TestTradingControls(TestCase): # Set the trading control to a different sid, then BUY ALL THE THINGS!. # Should continue normally. def handle_data(algo, data): - algo.order(self.sid, 10000) + algo.order(algo.sid(self.sid), 10000) algo.order_count += 1 algo = SetMaxPositionSizeAlgorithm(sid=self.sid + 1, max_shares=10, @@ -965,7 +1042,7 @@ class TestTradingControls(TestCase): # Set the trading control sid to None, then BUY ALL THE THINGS!. Should # fail because setting sid to None makes the control apply to all sids. def handle_data(algo, data): - algo.order(self.sid, 10000) + algo.order(algo.sid(self.sid), 10000) algo.order_count += 1 algo = SetMaxPositionSizeAlgorithm(max_shares=10, max_notional=61.0) self.check_algo_fails(algo, handle_data, 0) @@ -977,7 +1054,7 @@ class TestTradingControls(TestCase): restricted_list=[self.sid]) def handle_data(algo, data): - algo.order(self.sid, 100) + algo.order(algo.sid(self.sid), 100) algo.order_count += 1 self.check_algo_fails(algo, handle_data, 0) @@ -988,7 +1065,7 @@ class TestTradingControls(TestCase): restricted_list=[134, 135, 136]) def handle_data(algo, data): - algo.order(self.sid, 100) + algo.order(algo.sid(self.sid), 100) algo.order_count += 1 self.check_algo_succeeds(algo, handle_data) @@ -997,7 +1074,7 @@ class TestTradingControls(TestCase): # Buy one share. def handle_data(algo, data): - algo.order(self.sid, 1) + algo.order(algo.sid(self.sid), 1) algo.order_count += 1 algo = SetMaxOrderSizeAlgorithm(sid=self.sid, max_shares=10, @@ -1007,7 +1084,7 @@ class TestTradingControls(TestCase): # Buy 1, then 2, then 3, then 4 shares. Bail on the last attempt # because we exceed shares. def handle_data(algo, data): - algo.order(self.sid, algo.order_count + 1) + algo.order(algo.sid(self.sid), algo.order_count + 1) algo.order_count += 1 algo = SetMaxOrderSizeAlgorithm(sid=self.sid, @@ -1018,7 +1095,7 @@ class TestTradingControls(TestCase): # Buy 1, then 2, then 3, then 4 shares. Bail on the last attempt # because we exceed notional. def handle_data(algo, data): - algo.order(self.sid, algo.order_count + 1) + algo.order(algo.sid(self.sid), algo.order_count + 1) algo.order_count += 1 algo = SetMaxOrderSizeAlgorithm(sid=self.sid, @@ -1029,7 +1106,7 @@ class TestTradingControls(TestCase): # Set the trading control to a different sid, then BUY ALL THE THINGS!. # Should continue normally. def handle_data(algo, data): - algo.order(self.sid, 10000) + algo.order(algo.sid(self.sid), 10000) algo.order_count += 1 algo = SetMaxOrderSizeAlgorithm(sid=self.sid + 1, max_shares=1, @@ -1040,7 +1117,7 @@ class TestTradingControls(TestCase): # Should fail because not specifying a sid makes the trading control # apply to all sids. def handle_data(algo, data): - algo.order(self.sid, 10000) + algo.order(algo.sid(self.sid), 10000) algo.order_count += 1 algo = SetMaxOrderSizeAlgorithm(max_shares=1, max_notional=1.0) @@ -1061,7 +1138,7 @@ class TestTradingControls(TestCase): def handle_data(algo, data): for i in range(5): - algo.order(self.sid, 1) + algo.order(algo.sid(self.sid), 1) algo.order_count += 1 algo = SetMaxOrderCountAlgorithm(3) @@ -1080,7 +1157,7 @@ class TestTradingControls(TestCase): def test_long_only(self): # Sell immediately -> fail immediately. def handle_data(algo, data): - algo.order(self.sid, -1) + algo.order(algo.sid(self.sid), -1) algo.order_count += 1 algo = SetLongOnlyAlgorithm() self.check_algo_fails(algo, handle_data, 0) @@ -1089,9 +1166,9 @@ class TestTradingControls(TestCase): # should succeed. def handle_data(algo, data): if (algo.order_count % 2) == 0: - algo.order(self.sid, 1) + algo.order(algo.sid(self.sid), 1) else: - algo.order(self.sid, -1) + algo.order(algo.sid(self.sid), -1) algo.order_count += 1 algo = SetLongOnlyAlgorithm() self.check_algo_succeeds(algo, handle_data) @@ -1099,7 +1176,7 @@ class TestTradingControls(TestCase): # Buy on first three days, then sell off holdings. Should succeed. def handle_data(algo, data): amounts = [1, 1, 1, -3] - algo.order(self.sid, amounts[algo.order_count]) + algo.order(algo.sid(self.sid), amounts[algo.order_count]) algo.order_count += 1 algo = SetLongOnlyAlgorithm() self.check_algo_succeeds(algo, handle_data) @@ -1108,7 +1185,7 @@ class TestTradingControls(TestCase): # Should fail on the last sale. def handle_data(algo, data): amounts = [1, 1, 1, -4] - algo.order(self.sid, amounts[algo.order_count]) + algo.order(algo.sid(self.sid), amounts[algo.order_count]) algo.order_count += 1 algo = SetLongOnlyAlgorithm() self.check_algo_fails(algo, handle_data, 3) @@ -1134,14 +1211,42 @@ class TestTradingControls(TestCase): algo.run(self.source) self.source.rewind() + def test_asset_date_bounds(self): + + # Run the algorithm with a sid that ends far in the future + df_source, _ = factory.create_test_df_source(self.sim_params) + metadata = {0: {'start_date': '1990-01-01', + 'end_date': '2020-01-01'}} + algo = SetAssetDateBoundsAlgorithm(asset_metadata=metadata, + sim_params=self.sim_params,) + algo.run(df_source) + + # Run the algorithm with a sid that has already ended + df_source, _ = factory.create_test_df_source(self.sim_params) + metadata = {0: {'start_date': '1989-01-01', + 'end_date': '1990-01-01'}} + algo = SetAssetDateBoundsAlgorithm(asset_metadata=metadata, + sim_params=self.sim_params,) + with self.assertRaises(TradingControlViolation): + algo.run(df_source) + + # Run the algorithm with a sid that has not started + df_source, _ = factory.create_test_df_source(self.sim_params) + metadata = {0: {'start_date': '2020-01-01', + 'end_date': '2021-01-01'}} + algo = SetAssetDateBoundsAlgorithm(asset_metadata=metadata, + sim_params=self.sim_params,) + with self.assertRaises(TradingControlViolation): + algo.run(df_source) + class TestAccountControls(TestCase): def setUp(self): self.sim_params = factory.create_simulation_parameters(num_days=4) - self.sid = 133 + self.sidint = 133 self.trade_history = factory.create_trade_history( - self.sid, + self.sidint, [10.0, 10.0, 11.0, 11.0], [100, 100, 100, 300], timedelta(days=1), @@ -1173,14 +1278,14 @@ class TestAccountControls(TestCase): # Set max leverage to 0 so buying one share fails. def handle_data(algo, data): - algo.order(self.sid, 1) + algo.order(algo.sid(self.sidint), 1) algo = SetMaxLeverageAlgorithm(0) self.check_algo_fails(algo, handle_data) # Set max leverage to 1 so buying one share passes def handle_data(algo, data): - algo.order(self.sid, 1) + algo.order(algo.sid(self.sidint), 1) algo = SetMaxLeverageAlgorithm(1) self.check_algo_succeeds(algo, handle_data) diff --git a/tests/test_algorithm_gen.py b/tests/test_algorithm_gen.py index 03d3c625..d33abfd3 100644 --- a/tests/test_algorithm_gen.py +++ b/tests/test_algorithm_gen.py @@ -62,7 +62,7 @@ class TestAlgo(TradingAlgorithm): self.latest_date = None self.set_slippage(RecordDateSlippage(spread=0.05)) - self.stocks = [8229] + self.stocks = [self.sid(8229)] self.ordered = False self.num_bars = 0 @@ -105,7 +105,7 @@ class AlgorithmGeneratorTestCase(TestCase): start=datetime(2012, 5, 1, tzinfo=pytz.utc), end=datetime(2012, 6, 30, tzinfo=pytz.utc) ) - algo = TestAlgo(self, sim_params=sim_params) + algo = TestAlgo(self, identifiers=[8229], sim_params=sim_params) trade_source = factory.create_daily_trade_source( [8229], 200, @@ -131,7 +131,7 @@ class AlgorithmGeneratorTestCase(TestCase): start=datetime(2011, 7, 30, tzinfo=pytz.utc), end=datetime(2012, 7, 30, tzinfo=pytz.utc) ) - algo = TestAlgo(self, sim_params=sim_params) + algo = TestAlgo(self, identifiers=[8229], sim_params=sim_params) trade_source = factory.create_daily_trade_source( [8229], sim_params @@ -158,8 +158,7 @@ class AlgorithmGeneratorTestCase(TestCase): period_end=datetime(2012, 7, 30, tzinfo=pytz.utc), data_frequency='minute' ) - algo = TestAlgo(self, - sim_params=sim_params) + algo = TestAlgo(self, identifiers=[8229], sim_params=sim_params) midnight_custom_source = [Event({ 'custom_field': 42.0, @@ -223,6 +222,6 @@ class AlgorithmGeneratorTestCase(TestCase): """ sim_params = create_simulation_parameters(num_days=1, data_frequency='minute') - algo = TestAlgo(self, sim_params=sim_params) + algo = TestAlgo(self, sim_params=sim_params, identifiers=[8229]) algo.run(source=[], overwrite_sim_params=False) self.assertEqual(algo.datetime, sim_params.last_close) diff --git a/tests/test_batchtransform.py b/tests/test_batchtransform.py index 62c2fb54..4ef711e3 100644 --- a/tests/test_batchtransform.py +++ b/tests/test_batchtransform.py @@ -112,7 +112,10 @@ class TestChangeOfSids(TestCase): ) def test_all_sids_passed(self): - algo = BatchTransformAlgorithmSetSid(sim_params=self.sim_params) + algo = BatchTransformAlgorithmSetSid( + sim_params=self.sim_params, + identifiers=[i for i in range(0, 90)] + ) source = DifferentSidSource() algo.run(source) for i, (df, date) in enumerate(zip(algo.history, source.trading_days)): diff --git a/tests/test_blotter.py b/tests/test_blotter.py index cc3223dc..71c3d144 100644 --- a/tests/test_blotter.py +++ b/tests/test_blotter.py @@ -18,6 +18,7 @@ from nose_parameterized import parameterized from unittest import TestCase from zipline.finance.blotter import Blotter, ORDER_STATUS +from zipline.finance.trading import with_environment from zipline.finance.execution import ( LimitOrder, MarketOrder, @@ -34,8 +35,10 @@ from zipline.utils.test_utils import( class BlotterTestCase(TestCase): - def setUp(self): + @with_environment() + def setUp(self, env=None): setup_logger(self) + env.update_asset_finder(identifiers=[24]) def tearDown(self): teardown_logger(self) diff --git a/tests/test_cli.py b/tests/test_cli.py index 35a136e5..d7c187b2 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -46,7 +46,8 @@ start=1990-1-1 self.assertEqual(args['algofile'], 'test.py') self.assertEqual(args['symbols'], 'test_symbols') self.assertEqual(args['start'], '1990-1-1') - self.assertEqual(args['end'], cli.DEFAULTS['end']) + self.assertEqual(args['data_frequency'], + cli.DEFAULTS['data_frequency']) finally: os.remove('test.conf') @@ -63,6 +64,7 @@ start=1990-1-1 # Non-overwritten values self.assertEqual(args['symbols'], 'test_symbols') # Default values - self.assertEqual(args['end'], cli.DEFAULTS['end']) + self.assertEqual(args['data_frequency'], + cli.DEFAULTS['data_frequency']) finally: os.remove('test.conf') diff --git a/tests/test_events_through_risk.py b/tests/test_events_through_risk.py index e951e26b..c365c479 100644 --- a/tests/test_events_through_risk.py +++ b/tests/test_events_through_risk.py @@ -37,7 +37,7 @@ class BuyAndHoldAlgorithm(TradingAlgorithm): def handle_data(self, data): if not self.holding: - self.order(self.SID_TO_BUY_AND_HOLD, 100) + self.order(self.sid(self.SID_TO_BUY_AND_HOLD), 100) self.holding = True @@ -68,6 +68,7 @@ class TestEventsThroughRisk(unittest.TestCase): ) algo = BuyAndHoldAlgorithm( + identifiers=[1], sim_params=sim_params) first_date = datetime.datetime(2006, 1, 3, tzinfo=pytz.utc) @@ -188,6 +189,7 @@ class TestEventsThroughRisk(unittest.TestCase): data_frequency='minute') algo = BuyAndHoldAlgorithm( + identifiers=[1], sim_params=sim_params) first_date = datetime.datetime(2006, 1, 3, tzinfo=pytz.utc) diff --git a/tests/test_history.py b/tests/test_history.py index ef2191b5..a29ef469 100644 --- a/tests/test_history.py +++ b/tests/test_history.py @@ -509,7 +509,8 @@ def handle_data(context, data): test_algo = TradingAlgorithm( script=algo_text, data_frequency='minute', - sim_params=sim_params + sim_params=sim_params, + identifiers=[0] ) source = RandomWalkSource(start=start, diff --git a/tests/test_perf_tracking.py b/tests/test_perf_tracking.py index 1bb90802..916dfa39 100644 --- a/tests/test_perf_tracking.py +++ b/tests/test_perf_tracking.py @@ -43,7 +43,7 @@ from zipline.gens.composites import date_sorted_sources from zipline.finance.trading import SimulationParameters from zipline.finance.blotter import Order from zipline.finance.commission import PerShare, PerTrade, PerDollar -from zipline.finance import trading +from zipline.finance.trading import with_environment from zipline.utils.factory import create_random_simulation_parameters import zipline.protocol as zp from zipline.protocol import Event, DATASOURCE_TYPE @@ -127,7 +127,8 @@ def create_txn(trade_event, price, amount): return create_transaction(trade_event, mock_order, price, amount) -def benchmark_events_in_range(sim_params): +@with_environment() +def benchmark_events_in_range(sim_params, env=None): return [ Event({'dt': dt, 'returns': ret, @@ -135,7 +136,7 @@ def benchmark_events_in_range(sim_params): # We explicitly rely on the behavior that benchmarks sort before # any other events. 'source_id': '1Abenchmarks'}) - for dt, ret in trading.environment.benchmark_returns.iteritems() + for dt, ret in env.benchmark_returns.iteritems() if dt.date() >= sim_params.period_start.date() and dt.date() <= sim_params.period_end.date() ] @@ -368,79 +369,78 @@ class TestCommissionEvents(unittest.TestCase): self.benchmark_events = benchmark_events_in_range(self.sim_params) def test_commission_event(self): - with trading.TradingEnvironment(): - events = factory.create_trade_history( - 1, - [10, 10, 10, 10, 10], - [100, 100, 100, 100, 100], - oneday, - self.sim_params - ) + events = factory.create_trade_history( + 1, + [10, 10, 10, 10, 10], + [100, 100, 100, 100, 100], + oneday, + self.sim_params + ) - # Test commission models and validate result - # Expected commission amounts: - # PerShare commission: 1.00, 1.00, 1.50 = $3.50 - # PerTrade commission: 5.00, 5.00, 5.00 = $15.00 - # PerDollar commission: 1.50, 3.00, 4.50 = $9.00 - # Total commission = $3.50 + $15.00 + $9.00 = $27.50 + # Test commission models and validate result + # Expected commission amounts: + # PerShare commission: 1.00, 1.00, 1.50 = $3.50 + # PerTrade commission: 5.00, 5.00, 5.00 = $15.00 + # PerDollar commission: 1.50, 3.00, 4.50 = $9.00 + # Total commission = $3.50 + $15.00 + $9.00 = $27.50 - # Create 3 transactions: 50, 100, 150 shares traded @ $20 - transactions = [create_txn(events[0], 20, i) - for i in [50, 100, 150]] + # Create 3 transactions: 50, 100, 150 shares traded @ $20 + transactions = [create_txn(events[0], 20, i) + for i in [50, 100, 150]] - # Create commission models and validate that produce expected - # commissions. - models = [PerShare(cost=0.01, min_trade_cost=1.00), - PerTrade(cost=5.00), - PerDollar(cost=0.0015)] - expected_results = [3.50, 15.0, 9.0] + # Create commission models and validate that produce expected + # commissions. + models = [PerShare(cost=0.01, min_trade_cost=1.00), + PerTrade(cost=5.00), + PerDollar(cost=0.0015)] + expected_results = [3.50, 15.0, 9.0] - for model, expected in zip(models, expected_results): - total_commission = 0 - for trade in transactions: - total_commission += model.calculate(trade)[1] - self.assertEqual(total_commission, expected) + for model, expected in zip(models, expected_results): + total_commission = 0 + for trade in transactions: + total_commission += model.calculate(trade)[1] + self.assertEqual(total_commission, expected) - # Verify that commission events are handled correctly by - # PerformanceTracker. - cash_adj_dt = events[0].dt - cash_adjustment = factory.create_commission(1, 300.0, cash_adj_dt) - events.append(cash_adjustment) + # Verify that commission events are handled correctly by + # PerformanceTracker. + cash_adj_dt = events[0].dt + cash_adjustment = factory.create_commission(1, 300.0, cash_adj_dt) + events.append(cash_adjustment) - # Insert a purchase order. - txns = [create_txn(events[0], 20, 1)] - results = calculate_results(self, events, txns=txns) + # Insert a purchase order. + txns = [create_txn(events[0], 20, 1)] + results = calculate_results(self, events, txns=txns) - # Validate that we lost 320 dollars from our cash pool. - self.assertEqual(results[-1]['cumulative_perf']['ending_cash'], - 9680) - # Validate that the cost basis of our position changed. - self.assertEqual(results[-1]['daily_perf']['positions'] - [0]['cost_basis'], 320.0) - # Validate that the account attributes were updated. - account = results[1]['account'] - self.assertEqual(float('inf'), account['day_trades_remaining']) - np.testing.assert_allclose(0.001, account['leverage'], rtol=1e-3, - atol=1e-4) - np.testing.assert_allclose(9680, account['regt_equity'], rtol=1e-3) - self.assertEqual(float('inf'), account['regt_margin']) - np.testing.assert_allclose(9680, account['available_funds'], - rtol=1e-3) - self.assertEqual(0, account['maintenance_margin_requirement']) - np.testing.assert_allclose(9690, - account['equity_with_loan'], rtol=1e-3) - self.assertEqual(float('inf'), account['buying_power']) - self.assertEqual(0, account['initial_margin_requirement']) - np.testing.assert_allclose(9680, account['excess_liquidity'], - rtol=1e-3) - np.testing.assert_allclose(9680, account['settled_cash'], - rtol=1e-3) - np.testing.assert_allclose(9690, account['net_liquidation'], - rtol=1e-3) - np.testing.assert_allclose(0.999, account['cushion'], rtol=1e-3) - np.testing.assert_allclose(10, account['total_positions_value'], - rtol=1e-3) - self.assertEqual(0, account['accrued_interest']) + # Validate that we lost 320 dollars from our cash pool. + self.assertEqual(results[-1]['cumulative_perf']['ending_cash'], + 9680) + # Validate that the cost basis of our position changed. + self.assertEqual(results[-1]['daily_perf']['positions'] + [0]['cost_basis'], 320.0) + # Validate that the account attributes were updated. + account = results[1]['account'] + self.assertEqual(float('inf'), account['day_trades_remaining']) + np.testing.assert_allclose(0.001, account['leverage'], rtol=1e-3, + atol=1e-4) + np.testing.assert_allclose(9680, account['regt_equity'], rtol=1e-3) + self.assertEqual(float('inf'), account['regt_margin']) + np.testing.assert_allclose(9680, account['available_funds'], + rtol=1e-3) + self.assertEqual(0, account['maintenance_margin_requirement']) + np.testing.assert_allclose(9690, + account['equity_with_loan'], rtol=1e-3) + self.assertEqual(float('inf'), account['buying_power']) + self.assertEqual(0, account['initial_margin_requirement']) + np.testing.assert_allclose(9680, account['excess_liquidity'], + rtol=1e-3) + np.testing.assert_allclose(9680, account['settled_cash'], + rtol=1e-3) + np.testing.assert_allclose(9690, account['net_liquidation'], + rtol=1e-3) + np.testing.assert_allclose(0.999, account['cushion'], rtol=1e-3) + np.testing.assert_allclose(10, account['total_positions_value'], + rtol=1e-3) + self.assertEqual(0, account['accrued_interest']) def test_commission_zero_position(self): """ @@ -476,24 +476,23 @@ class TestCommissionEvents(unittest.TestCase): """ Ensure no position-not-found or sid-not-found errors. """ - with trading.TradingEnvironment(): - events = factory.create_trade_history( - 1, - [10, 10, 10, 10, 10], - [100, 100, 100, 100, 100], - oneday, - self.sim_params - ) + events = factory.create_trade_history( + 1, + [10, 10, 10, 10, 10], + [100, 100, 100, 100, 100], + oneday, + self.sim_params + ) - # Add a cash adjustment at the time of event[3]. - cash_adj_dt = events[3].dt - cash_adjustment = factory.create_commission(1, 300.0, cash_adj_dt) - events.append(cash_adjustment) + # Add a cash adjustment at the time of event[3]. + cash_adj_dt = events[3].dt + cash_adjustment = factory.create_commission(1, 300.0, cash_adj_dt) + events.append(cash_adjustment) - results = calculate_results(self, events) - # Validate that we lost 300 dollars from our cash pool. - self.assertEqual(results[-1]['cumulative_perf']['ending_cash'], - 9700) + results = calculate_results(self, events) + # Validate that we lost 300 dollars from our cash pool. + self.assertEqual(results[-1]['cumulative_perf']['ending_cash'], + 9700) class TestDividendPerformance(unittest.TestCase): @@ -508,117 +507,114 @@ class TestDividendPerformance(unittest.TestCase): self.benchmark_events = benchmark_events_in_range(self.sim_params) def test_market_hours_calculations(self): - with trading.TradingEnvironment(): - # DST in US/Eastern began on Sunday March 14, 2010 - before = datetime(2010, 3, 12, 14, 31, tzinfo=pytz.utc) - after = factory.get_next_trading_dt( - before, - timedelta(days=1) - ) - self.assertEqual(after.hour, 13) + # DST in US/Eastern began on Sunday March 14, 2010 + before = datetime(2010, 3, 12, 14, 31, tzinfo=pytz.utc) + after = factory.get_next_trading_dt( + before, + timedelta(days=1) + ) + self.assertEqual(after.hour, 13) def test_long_position_receives_dividend(self): - with trading.TradingEnvironment(): - # post some trades in the market - events = factory.create_trade_history( - 1, - [10, 10, 10, 10, 10], - [100, 100, 100, 100, 100], - oneday, - self.sim_params - ) - dividend = factory.create_dividend( - 1, - 10.00, - # declared date, when the algorithm finds out about - # the dividend - events[0].dt, - # ex_date, the date before which the algorithm must hold stock - # to receive the dividend - events[1].dt, - # pay date, when the algorithm receives the dividend. - events[2].dt - ) + # post some trades in the market + events = factory.create_trade_history( + 1, + [10, 10, 10, 10, 10], + [100, 100, 100, 100, 100], + oneday, + self.sim_params + ) + dividend = factory.create_dividend( + 1, + 10.00, + # declared date, when the algorithm finds out about + # the dividend + events[0].dt, + # ex_date, the date before which the algorithm must hold stock + # to receive the dividend + events[1].dt, + # pay date, when the algorithm receives the dividend. + events[2].dt + ) - # Simulate a transaction being filled prior to the ex_date. - txns = [create_txn(events[0], 10.0, 100)] - results = calculate_results( - self, - events, - dividend_events=[dividend], - txns=txns, - ) + # Simulate a transaction being filled prior to the ex_date. + txns = [create_txn(events[0], 10.0, 100)] + results = calculate_results( + self, + events, + dividend_events=[dividend], + txns=txns, + ) - self.assertEqual(len(results), 5) - cumulative_returns = \ - [event['cumulative_perf']['returns'] for event in results] - self.assertEqual(cumulative_returns, [0.0, 0.0, 0.1, 0.1, 0.1]) - daily_returns = [event['daily_perf']['returns'] - for event in results] - self.assertEqual(daily_returns, [0.0, 0.0, 0.10, 0.0, 0.0]) - cash_flows = [event['daily_perf']['capital_used'] - for event in results] - self.assertEqual(cash_flows, [-1000, 0, 1000, 0, 0]) - cumulative_cash_flows = \ - [event['cumulative_perf']['capital_used'] for event in results] - self.assertEqual(cumulative_cash_flows, [-1000, -1000, 0, 0, 0]) - cash_pos = \ - [event['cumulative_perf']['ending_cash'] for event in results] - self.assertEqual(cash_pos, [9000, 9000, 10000, 10000, 10000]) + self.assertEqual(len(results), 5) + cumulative_returns = \ + [event['cumulative_perf']['returns'] for event in results] + self.assertEqual(cumulative_returns, [0.0, 0.0, 0.1, 0.1, 0.1]) + daily_returns = [event['daily_perf']['returns'] + for event in results] + self.assertEqual(daily_returns, [0.0, 0.0, 0.10, 0.0, 0.0]) + cash_flows = [event['daily_perf']['capital_used'] + for event in results] + self.assertEqual(cash_flows, [-1000, 0, 1000, 0, 0]) + cumulative_cash_flows = \ + [event['cumulative_perf']['capital_used'] for event in results] + self.assertEqual(cumulative_cash_flows, [-1000, -1000, 0, 0, 0]) + cash_pos = \ + [event['cumulative_perf']['ending_cash'] for event in results] + self.assertEqual(cash_pos, [9000, 9000, 10000, 10000, 10000]) def test_long_position_receives_stock_dividend(self): - with trading.TradingEnvironment(): - # post some trades in the market - events = [] - for sid in (1, 2): - events.extend( - factory.create_trade_history( - sid, - [10, 10, 10, 10, 10], - [100, 100, 100, 100, 100], - oneday, - self.sim_params) - ) - - dividend = factory.create_stock_dividend( - 1, - payment_sid=2, - ratio=2, - # declared date, when the algorithm finds out about - # the dividend - declared_date=events[0].dt, - # ex_date, the date before which the algorithm must hold stock - # to receive the dividend - ex_date=events[1].dt, - # pay date, when the algorithm receives the dividend. - pay_date=events[2].dt + # post some trades in the market + events = [] + for sid in (1, 2): + events.extend( + factory.create_trade_history( + sid, + [10, 10, 10, 10, 10], + [100, 100, 100, 100, 100], + oneday, + self.sim_params) ) - txns = [create_txn(events[0], 10.0, 100)] + dividend = factory.create_stock_dividend( + 1, + payment_sid=2, + ratio=2, + # declared date, when the algorithm finds out about + # the dividend + declared_date=events[0].dt, + # ex_date, the date before which the algorithm must hold stock + # to receive the dividend + ex_date=events[1].dt, + # pay date, when the algorithm receives the dividend. + pay_date=events[2].dt + ) - results = calculate_results( - self, - events, - dividend_events=[dividend], - txns=txns, - ) + txns = [create_txn(events[0], 10.0, 100)] - self.assertEqual(len(results), 5) - cumulative_returns = \ - [event['cumulative_perf']['returns'] for event in results] - self.assertEqual(cumulative_returns, [0.0, 0.0, 0.2, 0.2, 0.2]) - daily_returns = [event['daily_perf']['returns'] - for event in results] - self.assertEqual(daily_returns, [0.0, 0.0, 0.2, 0.0, 0.0]) - cash_flows = [event['daily_perf']['capital_used'] - for event in results] - self.assertEqual(cash_flows, [-1000, 0, 0, 0, 0]) - cumulative_cash_flows = \ - [event['cumulative_perf']['capital_used'] for event in results] - self.assertEqual(cumulative_cash_flows, [-1000] * 5) - cash_pos = \ - [event['cumulative_perf']['ending_cash'] for event in results] - self.assertEqual(cash_pos, [9000] * 5) + results = calculate_results( + self, + events, + dividend_events=[dividend], + txns=txns, + ) + + self.assertEqual(len(results), 5) + cumulative_returns = \ + [event['cumulative_perf']['returns'] for event in results] + self.assertEqual(cumulative_returns, [0.0, 0.0, 0.2, 0.2, 0.2]) + daily_returns = [event['daily_perf']['returns'] + for event in results] + self.assertEqual(daily_returns, [0.0, 0.0, 0.2, 0.0, 0.0]) + cash_flows = [event['daily_perf']['capital_used'] + for event in results] + self.assertEqual(cash_flows, [-1000, 0, 0, 0, 0]) + cumulative_cash_flows = \ + [event['cumulative_perf']['capital_used'] for event in results] + self.assertEqual(cumulative_cash_flows, [-1000] * 5) + cash_pos = \ + [event['cumulative_perf']['ending_cash'] for event in results] + self.assertEqual(cash_pos, [9000] * 5) def test_long_position_purchased_on_ex_date_receives_no_dividend(self): # post some trades in the market @@ -1831,112 +1827,115 @@ class TestPerformanceTracker(unittest.TestCase): else: yield event - def test_minute_tracker(self): + @with_environment() + def test_minute_tracker(self, env=None): """ Tests minute performance tracking.""" - with trading.TradingEnvironment(): - start_dt = trading.environment.exchange_dt_in_utc( - datetime(2013, 3, 1, 9, 31)) - end_dt = trading.environment.exchange_dt_in_utc( - datetime(2013, 3, 1, 16, 0)) + start_dt = env.exchange_dt_in_utc(datetime(2013, 3, 1, 9, 31)) + end_dt = env.exchange_dt_in_utc(datetime(2013, 3, 1, 16, 0)) - sim_params = SimulationParameters( - period_start=start_dt, - period_end=end_dt, - emission_rate='minute' - ) - tracker = perf.PerformanceTracker(sim_params) + sim_params = SimulationParameters( + period_start=start_dt, + period_end=end_dt, + emission_rate='minute' + ) + tracker = perf.PerformanceTracker(sim_params) - foo_event_1 = factory.create_trade('foo', 10.0, 20, start_dt) - order_event_1 = Order(sid=foo_event_1.sid, + foosid = 1 + barsid = 2 + + env.update_asset_finder(identifiers=[foosid, barsid]) + + foo_event_1 = factory.create_trade(foosid, 10.0, 20, start_dt) + order_event_1 = Order(sid=foo_event_1.sid, + amount=-25, + dt=foo_event_1.dt) + bar_event_1 = factory.create_trade(barsid, 100.0, 200, start_dt) + txn_event_1 = Transaction(sid=foo_event_1.sid, amount=-25, - dt=foo_event_1.dt) - bar_event_1 = factory.create_trade('bar', 100.0, 200, start_dt) - txn_event_1 = Transaction(sid=foo_event_1.sid, - amount=-25, - dt=foo_event_1.dt, - price=10.0, - commission=0.50, - order_id=order_event_1.id) - benchmark_event_1 = Event({ - 'dt': start_dt, - 'returns': 0.01, - 'type': zp.DATASOURCE_TYPE.BENCHMARK - }) + dt=foo_event_1.dt, + price=10.0, + commission=0.50, + order_id=order_event_1.id) + benchmark_event_1 = Event({ + 'dt': start_dt, + 'returns': 0.01, + 'type': zp.DATASOURCE_TYPE.BENCHMARK + }) - foo_event_2 = factory.create_trade( - 'foo', 11.0, 20, start_dt + timedelta(minutes=1)) - bar_event_2 = factory.create_trade( - 'bar', 11.0, 20, start_dt + timedelta(minutes=1)) - benchmark_event_2 = Event({ - 'dt': start_dt + timedelta(minutes=1), - 'returns': 0.02, - 'type': zp.DATASOURCE_TYPE.BENCHMARK - }) + foo_event_2 = factory.create_trade( + foosid, 11.0, 20, start_dt + timedelta(minutes=1)) + bar_event_2 = factory.create_trade( + barsid, 11.0, 20, start_dt + timedelta(minutes=1)) + benchmark_event_2 = Event({ + 'dt': start_dt + timedelta(minutes=1), + 'returns': 0.02, + 'type': zp.DATASOURCE_TYPE.BENCHMARK + }) - events = [ - foo_event_1, - order_event_1, - benchmark_event_1, - txn_event_1, - bar_event_1, - foo_event_2, - benchmark_event_2, - bar_event_2, - ] + events = [ + foo_event_1, + order_event_1, + benchmark_event_1, + txn_event_1, + bar_event_1, + foo_event_2, + benchmark_event_2, + bar_event_2, + ] - grouped_events = itertools.groupby( - events, operator.attrgetter('dt')) + grouped_events = itertools.groupby( + events, operator.attrgetter('dt')) - messages = {} - for date, group in grouped_events: - tracker.set_date(date) - for event in group: - if event.type == zp.DATASOURCE_TYPE.TRADE: - tracker.process_trade(event) - elif event.type == zp.DATASOURCE_TYPE.BENCHMARK: - tracker.process_benchmark(event) - elif event.type == zp.DATASOURCE_TYPE.ORDER: - tracker.process_order(event) - elif event.type == zp.DATASOURCE_TYPE.TRANSACTION: - tracker.process_transaction(event) - tracker.handle_minute_close(date) - msg = tracker.to_dict() - messages[date] = msg + messages = {} + for date, group in grouped_events: + tracker.set_date(date) + for event in group: + if event.type == zp.DATASOURCE_TYPE.TRADE: + tracker.process_trade(event) + elif event.type == zp.DATASOURCE_TYPE.BENCHMARK: + tracker.process_benchmark(event) + elif event.type == zp.DATASOURCE_TYPE.ORDER: + tracker.process_order(event) + elif event.type == zp.DATASOURCE_TYPE.TRANSACTION: + tracker.process_transaction(event) + tracker.handle_minute_close(date) + msg = tracker.to_dict() + messages[date] = msg - self.assertEquals(2, len(messages)) + self.assertEquals(2, len(messages)) - msg_1 = messages[foo_event_1.dt] - msg_2 = messages[foo_event_2.dt] + msg_1 = messages[foo_event_1.dt] + msg_2 = messages[foo_event_2.dt] - self.assertEquals(1, len(msg_1['minute_perf']['transactions']), - "The first message should contain one " - "transaction.") - # Check that transactions aren't emitted for previous events. - self.assertEquals(0, len(msg_2['minute_perf']['transactions']), - "The second message should have no " - "transactions.") + self.assertEquals(1, len(msg_1['minute_perf']['transactions']), + "The first message should contain one " + "transaction.") + # Check that transactions aren't emitted for previous events. + self.assertEquals(0, len(msg_2['minute_perf']['transactions']), + "The second message should have no " + "transactions.") - self.assertEquals(1, len(msg_1['minute_perf']['orders']), - "The first message should contain one orders.") - # Check that orders aren't emitted for previous events. - self.assertEquals(0, len(msg_2['minute_perf']['orders']), - "The second message should have no orders.") + self.assertEquals(1, len(msg_1['minute_perf']['orders']), + "The first message should contain one orders.") + # Check that orders aren't emitted for previous events. + self.assertEquals(0, len(msg_2['minute_perf']['orders']), + "The second message should have no orders.") - # Ensure that period_close moves through time. - # Also, ensure that the period_closes are the expected dts. - self.assertEquals(foo_event_1.dt, - msg_1['minute_perf']['period_close']) - self.assertEquals(foo_event_2.dt, - msg_2['minute_perf']['period_close']) + # Ensure that period_close moves through time. + # Also, ensure that the period_closes are the expected dts. + self.assertEquals(foo_event_1.dt, + msg_1['minute_perf']['period_close']) + self.assertEquals(foo_event_2.dt, + msg_2['minute_perf']['period_close']) - # In this test event1 transactions arrive on the first bar. - # This leads to no returns as the price is constant. - # Sharpe ratio cannot be computed and is None. - # In the second bar we can start establishing a sharpe ratio. - self.assertIsNone(msg_1['cumulative_risk_metrics']['sharpe']) - self.assertIsNotNone(msg_2['cumulative_risk_metrics']['sharpe']) + # In this test event1 transactions arrive on the first bar. + # This leads to no returns as the price is constant. + # Sharpe ratio cannot be computed and is None. + # In the second bar we can start establishing a sharpe ratio. + self.assertIsNone(msg_1['cumulative_risk_metrics']['sharpe']) + self.assertIsNotNone(msg_2['cumulative_risk_metrics']['sharpe']) - check_perf_tracker_serialization(tracker) + check_perf_tracker_serialization(tracker) def test_close_position_event(self): pt = perf.PositionTracker() @@ -2021,9 +2020,12 @@ class TestPositionTracker(unittest.TestCase): stats = [ 'calculate_positions_value', '_net_exposure', + '_gross_value', '_gross_exposure', + '_short_value', '_short_exposure', '_shorts_count', + '_long_value', '_long_exposure', '_longs_count', ] @@ -2033,15 +2035,82 @@ class TestPositionTracker(unittest.TestCase): self.assertEquals(val, 0) self.assertNotIsInstance(val, (bool, np.bool_)) - def test_serializaition(self): + @with_environment() + def test_update_last_sale(self, env=None): + metadata = {1: {'asset_type': 'equity'}, + 2: {'asset_type': 'future', + 'contract_multiplier': 1000}} + env.update_asset_finder(asset_metadata=metadata) pt = perf.PositionTracker() dt = pd.Timestamp("1984/03/06 3:00PM") - pos1 = perf.Position('AAPL', amount=np.float64(120.0), + pos1 = perf.Position(1, amount=np.float64(100.0), + last_sale_date=dt, last_sale_price=10) + pos2 = perf.Position(2, amount=np.float64(100.0), + last_sale_date=dt, last_sale_price=10) + pt.update_positions({1: pos1, 2: pos2}) + + event1 = Event({'sid': 1, + 'price': 11, + 'dt': dt}) + event2 = Event({'sid': 2, + 'price': 11, + 'dt': dt}) + + # Check cash-adjustment return value + self.assertEqual(0, pt.update_last_sale(event1)) + self.assertEqual(100000, pt.update_last_sale(event2)) + + @with_environment() + def test_position_values_and_exposures(self, env=None): + metadata = {1: {'asset_type': 'equity'}, + 2: {'asset_type': 'equity'}, + 3: {'asset_type': 'future', + 'contract_multiplier': 1000}, + 4: {'asset_type': 'future', + 'contract_multiplier': 1000}} + env.update_asset_finder(asset_metadata=metadata) + pt = perf.PositionTracker() + dt = pd.Timestamp("1984/03/06 3:00PM") + pos1 = perf.Position(1, amount=np.float64(10.0), + last_sale_date=dt, last_sale_price=10) + pos2 = perf.Position(2, amount=np.float64(-20.0), + last_sale_date=dt, last_sale_price=10) + pos3 = perf.Position(3, amount=np.float64(30.0), + last_sale_date=dt, last_sale_price=10) + pos4 = perf.Position(4, amount=np.float64(-40.0), + last_sale_date=dt, last_sale_price=10) + pt.update_positions({1: pos1, 2: pos2, 3: pos3, 4: pos4}) + + # Test long-only methods + self.assertEqual(100, pt._long_value()) + self.assertEqual(100 + 300000, pt._long_exposure()) + + # Test short-only methods + self.assertEqual(-200, pt._short_value()) + self.assertEqual(-200 - 400000, pt._short_exposure()) + + # Test gross and net values + self.assertEqual(100 + 200, pt._gross_value()) + self.assertEqual(100 - 200, pt._net_value()) + + # Test gross and net exposures + self.assertEqual(100 + 200 + 300000 + 400000, pt._gross_exposure()) + self.assertEqual(100 - 200 + 300000 - 400000, pt._net_exposure()) + + @with_environment() + def test_serialization(self, env=None): + metadata = {1: {'asset_type': 'equity'}, + 2: {'asset_type': 'future', + 'contract_multiplier': 1000}} + env.update_asset_finder(asset_metadata=metadata) + pt = perf.PositionTracker() + dt = pd.Timestamp("1984/03/06 3:00PM") + pos1 = perf.Position(1, amount=np.float64(120.0), last_sale_date=dt, last_sale_price=3.4) - pos2 = perf.Position('IBM', amount=np.float64(100.0), + pos2 = perf.Position(2, amount=np.float64(100.0), last_sale_date=dt, last_sale_price=3.4) - pt.update_positions({'AAPL': pos1, 'IBM': pos2}) + pt.update_positions({1: pos1, 2: pos2}) p_string = pickle.dumps(pt) test = pickle.loads(p_string) nt.assert_dict_equal(test._position_amounts, pt._position_amounts) diff --git a/tests/test_security_list.py b/tests/test_security_list.py index cec5f010..a72ebff3 100644 --- a/tests/test_security_list.py +++ b/tests/test_security_list.py @@ -6,6 +6,7 @@ from unittest import TestCase from zipline.algorithm import TradingAlgorithm from zipline.errors import TradingControlViolation from zipline.sources import SpecificEquityTrades +from zipline.finance.trading import with_environment from zipline.utils.test_utils import ( setup_logger, teardown_logger, security_list_copy, add_security_data) from zipline.utils import factory @@ -16,11 +17,11 @@ LEVERAGED_ETFS = load_from_directory('leveraged_etf_list') class RestrictedAlgoWithCheck(TradingAlgorithm): - def initialize(self, sid): + def initialize(self, symbol): self.rl = SecurityListSet(self.get_datetime) self.set_do_not_order_list(self.rl.leveraged_etf_list) self.order_count = 0 - self.sid = sid + self.sid = self.symbol(symbol) def handle_data(self, data): if not self.order_count: @@ -31,11 +32,11 @@ class RestrictedAlgoWithCheck(TradingAlgorithm): class RestrictedAlgoWithoutCheck(TradingAlgorithm): - def initialize(self, sid): + def initialize(self, symbol): self.rl = SecurityListSet(self.get_datetime) self.set_do_not_order_list(self.rl.leveraged_etf_list) self.order_count = 0 - self.sid = sid + self.sid = self.symbol(symbol) def handle_data(self, data): self.order(self.sid, 100) @@ -43,11 +44,11 @@ class RestrictedAlgoWithoutCheck(TradingAlgorithm): class IterateRLAlgo(TradingAlgorithm): - def initialize(self, sid): + def initialize(self, symbol): self.rl = SecurityListSet(self.get_datetime) self.set_do_not_order_list(self.rl.leveraged_etf_list) self.order_count = 0 - self.sid = sid + self.sid = self.symbol(symbol) self.found = False def handle_data(self, data): @@ -58,12 +59,18 @@ class IterateRLAlgo(TradingAlgorithm): class SecurityListTestCase(TestCase): - def setUp(self): + @with_environment() + def setUp(self, env=None): self.extra_knowledge_date = \ datetime(2015, 1, 27, 0, 0, tzinfo=pytz.utc) self.trading_day_before_first_kd = datetime( 2015, 1, 23, 0, 0, tzinfo=pytz.utc) + env.update_asset_finder( + clear_metadata=True, + identifiers=["BZQ", "URTY", "JFT", "AAPL", "GOOG"] + ) + setup_logger(self) def tearDown(self): @@ -81,11 +88,12 @@ class SecurityListTestCase(TestCase): sim_params ) self.source = SpecificEquityTrades(event_list=trade_history) - algo = IterateRLAlgo(sid='BZQ', sim_params=sim_params) + algo = IterateRLAlgo(symbol='BZQ', sim_params=sim_params) algo.run(self.source) self.assertTrue(algo.found) - def test_security_list(self): + @with_environment() + def test_security_list(self, env=None): # set the knowledge date to the first day of the # leveraged etf knowledge date. @@ -94,27 +102,43 @@ class SecurityListTestCase(TestCase): rl = SecurityListSet(get_datetime) # assert that a sample from the leveraged list are in restricted - - self.assertIn("BZQ", rl.leveraged_etf_list) - self.assertIn("URTY", rl.leveraged_etf_list) - self.assertIn("JFT", rl.leveraged_etf_list) + should_exist = [ + asset.sid for asset in + [env.asset_finder.lookup_symbol( + symbol, + as_of_date=self.extra_knowledge_date) + for symbol in ["BZQ", "URTY", "JFT"]] + ] + for sid in should_exist: + self.assertIn(sid, rl.leveraged_etf_list) # assert that a sample of allowed stocks are not in restricted - # AAPL - self.assertNotIn("AAPL", rl.leveraged_etf_list) - # GOOG - self.assertNotIn("GOOG", rl.leveraged_etf_list) + shouldnt_exist = [ + asset.sid for asset in + [env.asset_finder.lookup_symbol( + symbol, + as_of_date=self.extra_knowledge_date) + for symbol in ["AAPL", "GOOG"]] + ] + for sid in shouldnt_exist: + self.assertNotIn(sid, rl.leveraged_etf_list) - def test_security_add(self): + @with_environment() + def test_security_add(self, env=None): def get_datetime(): return datetime(2015, 1, 27, tzinfo=pytz.utc) with security_list_copy(): add_security_data(['AAPL', 'GOOG'], []) rl = SecurityListSet(get_datetime) - self.assertIn("AAPL", rl.leveraged_etf_list) - self.assertIn("GOOG", rl.leveraged_etf_list) - self.assertIn("BZQ", rl.leveraged_etf_list) - self.assertIn("URTY", rl.leveraged_etf_list) + should_exist = [ + asset.sid for asset in + [env.asset_finder.lookup_symbol( + symbol, + as_of_date=self.extra_knowledge_date + ) for symbol in ["AAPL", "GOOG", "BZQ", "URTY"]] + ] + for sid in should_exist: + self.assertIn(sid, rl.leveraged_etf_list) def test_security_add_delete(self): with security_list_copy(): @@ -138,7 +162,7 @@ class SecurityListTestCase(TestCase): ) self.source = SpecificEquityTrades(event_list=trade_history) - algo = RestrictedAlgoWithCheck(sid='BZQ', sim_params=sim_params) + algo = RestrictedAlgoWithCheck(symbol='BZQ', sim_params=sim_params) algo.run(self.source) def test_algo_without_rl_violation(self): @@ -153,7 +177,7 @@ class SecurityListTestCase(TestCase): sim_params ) self.source = SpecificEquityTrades(event_list=trade_history) - algo = RestrictedAlgoWithoutCheck(sid='AAPL', sim_params=sim_params) + algo = RestrictedAlgoWithoutCheck(symbol='AAPL', sim_params=sim_params) algo.run(self.source) def test_algo_with_rl_violation(self): @@ -169,10 +193,7 @@ class SecurityListTestCase(TestCase): ) self.source = SpecificEquityTrades(event_list=trade_history) - self.df_source, self.df = \ - factory.create_test_df_source(sim_params) - - algo = RestrictedAlgoWithoutCheck(sid='BZQ', sim_params=sim_params) + algo = RestrictedAlgoWithoutCheck(symbol='BZQ', sim_params=sim_params) with self.assertRaises(TradingControlViolation) as ctx: algo.run(self.source) @@ -189,10 +210,7 @@ class SecurityListTestCase(TestCase): ) self.source = SpecificEquityTrades(event_list=trade_history) - self.df_source, self.df = \ - factory.create_test_df_source(sim_params) - - algo = RestrictedAlgoWithoutCheck(sid='JFT', sim_params=sim_params) + algo = RestrictedAlgoWithoutCheck(symbol='JFT', sim_params=sim_params) with self.assertRaises(TradingControlViolation) as ctx: algo.run(self.source) @@ -211,7 +229,7 @@ class SecurityListTestCase(TestCase): sim_params ) self.source = SpecificEquityTrades(event_list=trade_history) - algo = RestrictedAlgoWithoutCheck(sid='BZQ', sim_params=sim_params) + algo = RestrictedAlgoWithoutCheck(symbol='BZQ', sim_params=sim_params) with self.assertRaises(TradingControlViolation) as ctx: algo.run(self.source) @@ -238,7 +256,7 @@ class SecurityListTestCase(TestCase): ) self.source = SpecificEquityTrades(event_list=trade_history) algo = RestrictedAlgoWithoutCheck( - sid='BZQ', sim_params=sim_params) + symbol='BZQ', sim_params=sim_params) with self.assertRaises(TradingControlViolation) as ctx: algo.run(self.source) @@ -261,7 +279,8 @@ class SecurityListTestCase(TestCase): ) self.source = SpecificEquityTrades(event_list=trade_history) algo = RestrictedAlgoWithoutCheck( - sid='BZQ', sim_params=sim_params) + symbol='BZQ', sim_params=sim_params + ) algo.run(self.source) def test_algo_with_rl_violation_after_add(self): @@ -278,7 +297,7 @@ class SecurityListTestCase(TestCase): ) self.source = SpecificEquityTrades(event_list=trade_history) algo = RestrictedAlgoWithoutCheck( - sid='AAPL', sim_params=sim_params) + symbol='AAPL', sim_params=sim_params) with self.assertRaises(TradingControlViolation) as ctx: algo.run(self.source) diff --git a/tests/test_sources.py b/tests/test_sources.py index 89ce89a7..3664acdd 100644 --- a/tests/test_sources.py +++ b/tests/test_sources.py @@ -26,6 +26,7 @@ from zipline.sources import (DataFrameSource, DataPanelSource, RandomWalkSource) from zipline.utils import tradingcalendar as calendar_nyse +from zipline.finance.trading import with_environment class TestDataFrameSource(TestCase): @@ -62,7 +63,8 @@ class TestDataFrameSource(TestCase): self.assertTrue(isinstance(event['volume'], int)) self.assertTrue(isinstance(event['arbitrary'], float)) - def test_yahoo_bars_to_panel_source(self): + @with_environment() + def test_yahoo_bars_to_panel_source(self, env=None): stocks = ['AAPL', 'GE'] start = pd.datetime(1993, 1, 1, 0, 0, 0, 0, pytz.utc) end = pd.datetime(2002, 1, 1, 0, 0, 0, 0, pytz.utc) @@ -74,45 +76,58 @@ class TestDataFrameSource(TestCase): check_fields = ['sid', 'open', 'high', 'low', 'close', 'volume', 'price'] source = DataPanelSource(data) - stocks_iter = cycle(stocks) + sids = [ + asset.sid for asset in + [env.asset_finder.lookup_symbol(symbol, as_of_date=end) + for symbol in stocks] + ] + stocks_iter = cycle(sids) for event in source: for check_field in check_fields: self.assertIn(check_field, event) self.assertTrue(isinstance(event['volume'], (integer_types))) self.assertEqual(next(stocks_iter), event['sid']) - def test_nan_filter_dataframe(self): + @with_environment() + def test_nan_filter_dataframe(self, env=None): + env.update_asset_finder(identifiers=[4, 5]) dates = pd.date_range('1/1/2000', periods=2, freq='B', tz='UTC') df = pd.DataFrame(np.random.randn(2, 2), index=dates, - columns=['A', 'B']) - df.loc[dates[0], 'A'] = np.nan # should be filtered - df.loc[dates[1], 'B'] = np.nan # should not be filtered + columns=[4, 5]) + # should be filtered + df.loc[dates[0], 4] = np.nan + # should not be filtered, should have been ffilled + df.loc[dates[1], 5] = np.nan source = DataFrameSource(df) event = next(source) - self.assertEqual('B', event.sid) + self.assertEqual(5, event.sid) event = next(source) - self.assertEqual('A', event.sid) + self.assertEqual(4, event.sid) event = next(source) - self.assertEqual('B', event.sid) - self.assertTrue(np.isnan(event.price)) + self.assertEqual(5, event.sid) + self.assertFalse(np.isnan(event.price)) - def test_nan_filter_panel(self): + @with_environment() + def test_nan_filter_panel(self, env=None): + env.update_asset_finder(identifiers=[4, 5]) dates = pd.date_range('1/1/2000', periods=2, freq='B', tz='UTC') df = pd.Panel(np.random.randn(2, 2, 2), major_axis=dates, - items=['A', 'B'], + items=[4, 5], minor_axis=['price', 'volume']) - df.loc['A', dates[0], 'price'] = np.nan # should be filtered - df.loc['B', dates[1], 'price'] = np.nan # should not be filtered + # should be filtered + df.loc[4, dates[0], 'price'] = np.nan + # should not be filtered, should have been ffilled + df.loc[5, dates[1], 'price'] = np.nan source = DataPanelSource(df) event = next(source) - self.assertEqual('B', event.sid) + self.assertEqual(5, event.sid) event = next(source) - self.assertEqual('A', event.sid) + self.assertEqual(4, event.sid) event = next(source) - self.assertEqual('B', event.sid) - self.assertTrue(np.isnan(event.price)) + self.assertEqual(5, event.sid) + self.assertFalse(np.isnan(event.price)) class TestRandomWalkSource(TestCase):