diff --git a/tests/test_algorithm.py b/tests/test_algorithm.py index ea76a2f5..146ddcbc 100644 --- a/tests/test_algorithm.py +++ b/tests/test_algorithm.py @@ -22,6 +22,7 @@ import zipline.utils.factory as factory from zipline.test_algorithms import (TestRegisterTransformAlgorithm, RecordAlgorithm, TestOrderAlgorithm, + TestOrderInstantAlgorithm, TestOrderValueAlgorithm, TestTargetAlgorithm, TestOrderPercentAlgorithm, @@ -186,3 +187,10 @@ class TestTransformAlgorithm(TestCase): data_frequency='daily' ) algo.run(self.df) + + def test_order_instant(self): + algo = TestOrderInstantAlgorithm(sim_params=self.sim_params, + data_frequency='daily', + instant_fill=True) + + algo.run(self.df) diff --git a/zipline/algorithm.py b/zipline/algorithm.py index 492f8ad4..a12715ae 100644 --- a/zipline/algorithm.py +++ b/zipline/algorithm.py @@ -112,6 +112,8 @@ class TradingAlgorithm(object): else: self.data_frequency = None + self.instant_fill = kwargs.pop('instant_fill', False) + # Override annualizer if set if 'annualizer' in kwargs: self.annualizer = kwargs['annualizer'] diff --git a/zipline/gens/tradesimulation.py b/zipline/gens/tradesimulation.py index 985f363c..df48ed39 100644 --- a/zipline/gens/tradesimulation.py +++ b/zipline/gens/tradesimulation.py @@ -85,6 +85,13 @@ class AlgorithmSimulator(object): return self.EMISSION_TO_PERF_KEY_MAP[ self.algo.perf_tracker.emission_rate] + def process_event(self, event): + process_trade = self.algo.blotter.process_trade + for txn, order in process_trade(event): + self.algo.perf_tracker.process_event(txn) + self.algo.perf_tracker.process_event(order) + self.algo.perf_tracker.process_event(event) + def transform(self, stream_in): """ Main generator work loop. @@ -117,6 +124,8 @@ class AlgorithmSimulator(object): self.algo.perf_tracker.process_event(event) else: + if self.algo.instant_fill: + events = [] for event in snapshot: if event.type == DATASOURCE_TYPE.SPLIT: @@ -129,12 +138,12 @@ class AlgorithmSimulator(object): if event.type == DATASOURCE_TYPE.BENCHMARK: self.algo.set_datetime(event.dt) bm_updated = True - - process_trade = self.algo.blotter.process_trade - for txn, order in process_trade(event): - self.algo.perf_tracker.process_event(txn) - self.algo.perf_tracker.process_event(order) - self.algo.perf_tracker.process_event(event) + # If we are instantly filling orders we process + # them after handle_data(). + if not self.algo.instant_fill: + self.process_event(event) + else: + events.append(event) # Update our portfolio. self.algo.set_portfolio( @@ -155,6 +164,12 @@ class AlgorithmSimulator(object): self.algo.perf_tracker.process_event(order) self.algo.blotter.new_orders = [] + # If we are instantly filling we execute orders + # in this iteration rather than the next. + if self.algo.instant_fill: + for event in events: + self.process_event(event) + # The benchmark is our internal clock. When it # updates, we need to emit a performance message. if bm_updated: diff --git a/zipline/test_algorithms.py b/zipline/test_algorithms.py index a87924d1..88985f94 100644 --- a/zipline/test_algorithms.py +++ b/zipline/test_algorithms.py @@ -234,6 +234,24 @@ class TestOrderAlgorithm(TradingAlgorithm): self.order(0, 1) +class TestOrderInstantAlgorithm(TradingAlgorithm): + def initialize(self): + self.incr = 0 + self.last_price = None + + def handle_data(self, data): + if self.incr == 0: + assert 0 not in self.portfolio.positions + else: + assert self.portfolio.positions[0]['amount'] == \ + self.incr, "Orders not filled immediately." + assert self.portfolio.positions[0]['last_sale_price'] == \ + self.last_price, "Orders was not filled at last price." + self.incr += 2 + self.order_value(0, data[0].price * 2.) + self.last_price = data[0].price + + class TestOrderValueAlgorithm(TradingAlgorithm): def initialize(self): self.incr = 0