diff --git a/tests/test_algorithm.py b/tests/test_algorithm.py index e6016f21..bca840b9 100644 --- a/tests/test_algorithm.py +++ b/tests/test_algorithm.py @@ -42,7 +42,8 @@ from zipline.test_algorithms import (TestRegisterTransformAlgorithm, api_symbol_algo, call_all_order_methods, record_variables, - record_float_magic + record_float_magic, + AmbitiousStopLimitAlgorithm ) from zipline.utils.test_utils import drain_zipline, assert_single_position @@ -264,6 +265,15 @@ class TestPositions(TestCase): self.assertEqual(daily_stats.ix[i]['num_positions'], expected) + def test_noop_orders(self): + + algo = AmbitiousStopLimitAlgorithm(sid=1) + daily_stats = algo.run(self.source) + + # Verify that possitions are empty for all dates. + empty_positions = daily_stats.positions.map(lambda x: len(x) == 0) + self.assertTrue(empty_positions.all()) + class TestAlgoScript(TestCase): def setUp(self): diff --git a/zipline/finance/blotter.py b/zipline/finance/blotter.py index 46e33cd3..227d95cf 100644 --- a/zipline/finance/blotter.py +++ b/zipline/finance/blotter.py @@ -335,3 +335,12 @@ class Order(object): @property def open_amount(self): return self.amount - self.filled + + def __repr__(self): + """ + String representation for this object. + """ + return "Order(%s)" % self.to_dict().__repr__() + + def __unicode__(self): + return unicode(self.__repr__) diff --git a/zipline/test_algorithms.py b/zipline/test_algorithms.py index 4046f9d4..ca624594 100644 --- a/zipline/test_algorithms.py +++ b/zipline/test_algorithms.py @@ -376,6 +376,55 @@ class TestRegisterTransformAlgorithm(TradingAlgorithm): pass +class AmbitiousStopLimitAlgorithm(TradingAlgorithm): + """ + Algorithm that tries to buy with extremely low stops/limits and tries to + sell with extremely high versions of same. Should not end up with any + positions for reasonable data. + """ + + def initialize(self, *args, **kwargs): + self.sid = kwargs.pop('sid') + + def handle_data(self, data): + + ######## + # Buys # + ######## + + # Buy with low limit, shouldn't trigger. + self.order(self.sid, 100, limit_price=1) + + # But with high stop, shouldn't trigger + self.order(self.sid, 100, stop_price=10000000) + + # Buy with high limit (should trigger) but also high stop (should + # prevent trigger). + self.order(self.sid, 100, limit_price=10000000, stop_price=10000000) + + # Buy with low stop (should trigger), but also low limit (should + # prevent trigger). + self.order(self.sid, 100, limit_price=1, stop_price=1) + + ######### + # Sells # + ######### + + # Sell with high limit, shouldn't trigger. + self.order(self.sid, -100, limit_price=1000000) + + # Sell with low stop, shouldn't trigger. + self.order(self.sid, -100, stop_price=1) + + # Sell with low limit (should trigger), but also high stop (should + # prevent trigger). + self.order(self.sid, -100, limit_price=1000000, stop_price=1000000) + + # Sell with low limit (should trigger), but also low stop (should + # prevent trigger). + self.order(self.sid, -100, limit_price=1, stop_price=1) + + ########################################## # Algorithm using simple batch transforms