From 3f66e6c66a4e6674e6e6bcebb5fbcf15ba8ed2e9 Mon Sep 17 00:00:00 2001 From: warren-oneill Date: Fri, 30 Oct 2015 11:53:53 +0100 Subject: [PATCH 1/3] TST: adds future flip test --- tests/test_algorithm.py | 50 ++++++++++++++++++++++++++++++++++++++ zipline/test_algorithms.py | 10 ++++++++ 2 files changed, 60 insertions(+) diff --git a/tests/test_algorithm.py b/tests/test_algorithm.py index e8d67335..85715f50 100644 --- a/tests/test_algorithm.py +++ b/tests/test_algorithm.py @@ -48,6 +48,7 @@ from zipline.test_algorithms import ( EmptyPositionsAlgorithm, InvalidOrderAlgorithm, RecordAlgorithm, + FutureFlipAlgo, TestAlgorithm, TestOrderAlgorithm, TestOrderInstantAlgorithm, @@ -1842,3 +1843,52 @@ class TestClosePosAlgo(TestCase): actual_position, expected_positions[i], "position for day={0} not equal, actual={1}, expected={2}". format(i, actual_position, expected_positions[i])) + + +class TestFutureFlip(TestCase): + def setUp(self): + self.env = TradingEnvironment() + self.days = self.env.trading_days[:4] + self.trades_panel = pd.Panel({1: pd.DataFrame({ + 'price': [1, 2, 4], 'volume': [1e9, 1e9, 1e9], + 'type': [DATASOURCE_TYPE.TRADE, + DATASOURCE_TYPE.TRADE, + DATASOURCE_TYPE.TRADE]}, + index=self.days[:3]) + }) + + def test_flip_algo(self): + metadata = {1: {'symbol': 'TEST', + 'asset_type': 'equity', + 'end_date': self.days[3], + 'contract_multiplier': 5}} + self.env.write_data(futures_data=metadata) + + algo = FutureFlipAlgo(sid=1, amount=1, env=self.env, + commission=PerShare(0), + order_count=0, # not applicable but required + instant_fill=True) + data = DataPanelSource(self.trades_panel) + + results = algo.run(data) + + expected_positions = [1, -1, 0] + self.check_algo_positions(results, expected_positions) + + expected_pnl = [0, 5, -10] + self.check_algo_pnl(results, expected_pnl) + + def check_algo_pnl(self, results, expected_pnl): + np.testing.assert_array_almost_equal(results.pnl, expected_pnl) + + def check_algo_positions(self, results, expected_positions): + for i, amount in enumerate(results.positions): + if amount: + actual_position = amount[0]['amount'] + else: + actual_position = 0 + + self.assertEqual( + actual_position, expected_positions[i], + "position for day={0} not equal, actual={1}, expected={2}". + format(i, actual_position, expected_positions[i])) diff --git a/zipline/test_algorithms.py b/zipline/test_algorithms.py index 496c3739..a56d001f 100644 --- a/zipline/test_algorithms.py +++ b/zipline/test_algorithms.py @@ -442,6 +442,16 @@ class TestTargetValueAlgorithm(TradingAlgorithm): 20 / (data[0].price * self.sid(0).contract_multiplier)) +class FutureFlipAlgo(TestAlgorithm): + def handle_data(self, data): + if len(self.portfolio.positions) > 0: + if self.portfolio.positions[self.asset.sid]["amount"] > 0: + self.order_target(self.asset, -self.amount) + else: + self.order_target(self.asset, 0) + else: + self.order_target(self.asset, self.amount) + ############################ # AccountControl Test Algos# ############################ From 7d29bb6a6720b08f3a253a08952d6a61d8eb8f85 Mon Sep 17 00:00:00 2001 From: jfkirk Date: Fri, 30 Oct 2015 12:04:38 -0400 Subject: [PATCH 2/3] BUG: Fixes failure to account for Futures transaction prices --- tests/test_algorithm.py | 1 - zipline/finance/performance/tracker.py | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_algorithm.py b/tests/test_algorithm.py index 85715f50..2ca03482 100644 --- a/tests/test_algorithm.py +++ b/tests/test_algorithm.py @@ -1859,7 +1859,6 @@ class TestFutureFlip(TestCase): def test_flip_algo(self): metadata = {1: {'symbol': 'TEST', - 'asset_type': 'equity', 'end_date': self.days[3], 'contract_multiplier': 5}} self.env.write_data(futures_data=metadata) diff --git a/zipline/finance/performance/tracker.py b/zipline/finance/performance/tracker.py index f954ef56..ac3e00a7 100644 --- a/zipline/finance/performance/tracker.py +++ b/zipline/finance/performance/tracker.py @@ -296,6 +296,8 @@ class PerformanceTracker(object): perf_period.handle_cash_payment(cash_adjustment) def process_transaction(self, event): + # Transaction events also count as trade events for the trackers + self.process_trade(event) self.txn_count += 1 self.position_tracker.execute_transaction(event) for perf_period in self.perf_periods: From a1584cebe7bdc479fa94c5597687261d29ee75e5 Mon Sep 17 00:00:00 2001 From: jfkirk Date: Mon, 2 Nov 2015 10:02:58 -0500 Subject: [PATCH 3/3] STY: Factors-out event price handling --- zipline/finance/performance/tracker.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/zipline/finance/performance/tracker.py b/zipline/finance/performance/tracker.py index ac3e00a7..83553416 100644 --- a/zipline/finance/performance/tracker.py +++ b/zipline/finance/performance/tracker.py @@ -288,16 +288,18 @@ class PerformanceTracker(object): return _dict - def process_trade(self, event): - # update last sale, and pay out a cash adjustment + def _handle_event_price(self, event): + # updates last sale, and pays out a cash adjustment if applicable cash_adjustment = self.position_tracker.update_last_sale(event) if cash_adjustment != 0: for perf_period in self.perf_periods: perf_period.handle_cash_payment(cash_adjustment) + def process_trade(self, event): + self._handle_event_price(event) + def process_transaction(self, event): - # Transaction events also count as trade events for the trackers - self.process_trade(event) + self._handle_event_price(event) self.txn_count += 1 self.position_tracker.execute_transaction(event) for perf_period in self.perf_periods: