From 086c12ddf8c9be06e770b536a6d04cc99d26783b Mon Sep 17 00:00:00 2001 From: Eddie Hebert Date: Fri, 2 Nov 2012 16:32:50 -0400 Subject: [PATCH] Locks down the ability to easily override the algo's portfolio. Starting down the path of making the portfolio completely read-only with respect to the handle_data in algo. The portfolio should only be changed during the course of running the algorithm by the simulator. This doesn't do a 100% protection, i.e. an algo could use _portfolio, or the set_attr property, but hoping this helps guides algo writing to treat the portfolio as read-only. --- tests/test_exception_handling.py | 23 +++++++++++++++++++++++ zipline/algorithm.py | 8 ++++++-- zipline/finance/performance.py | 1 + zipline/test_algorithms.py | 15 +++++++++++++++ 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/tests/test_exception_handling.py b/tests/test_exception_handling.py index 32a4f339..cfd9e6b3 100644 --- a/tests/test_exception_handling.py +++ b/tests/test_exception_handling.py @@ -19,6 +19,7 @@ import zipline.utils.simfactory as simfactory from zipline.test_algorithms import ( ExceptionAlgorithm, DivByZeroAlgorithm, + SetPortfolioAlgorithm, ) from zipline.finance.slippage import FixedSlippage from zipline.transforms.utils import StatefulTransform @@ -113,3 +114,25 @@ class ExceptionTestCase(TestCase): self.assertEqual(ctx.exception.message, 'integer division or modulo by zero') + + def test_set_portfolio(self): + """ + Are we protected against overwriting an algo's portfolio? + """ + + # Simulation + # ---------- + self.zipline_test_config['algorithm'] = \ + SetPortfolioAlgorithm( + self.zipline_test_config['sid'] + ) + + zipline = simfactory.create_test_zipline( + **self.zipline_test_config + ) + + with self.assertRaises(AttributeError) as ctx: + output, _ = drain_zipline(self, zipline) + + self.assertEqual(ctx.exception.message, + "can't set attribute") diff --git a/zipline/algorithm.py b/zipline/algorithm.py index 3d6b26b6..47386f0c 100644 --- a/zipline/algorithm.py +++ b/zipline/algorithm.py @@ -66,7 +66,7 @@ class TradingAlgorithm(object): self.done = False self.order = None self.frame_count = 0 - self.portfolio = None + self._portfolio = None self.datetime = None self.registered_transforms = {} @@ -217,8 +217,12 @@ class TradingAlgorithm(object): 'args': args, 'kwargs': kwargs} + @property + def portfolio(self): + return self._portfolio + def set_portfolio(self, portfolio): - self.portfolio = portfolio + self._portfolio = portfolio def set_order(self, order_callable): self.order = order_callable diff --git a/zipline/finance/performance.py b/zipline/finance/performance.py index 7e90e106..67ad400e 100644 --- a/zipline/finance/performance.py +++ b/zipline/finance/performance.py @@ -544,6 +544,7 @@ class PerformancePeriod(object): del(portfolio['max_capital_used']) portfolio['positions'] = self.get_positions() + return ndict(portfolio) def get_positions(self): diff --git a/zipline/test_algorithms.py b/zipline/test_algorithms.py index fb711ff0..f6fcaee9 100644 --- a/zipline/test_algorithms.py +++ b/zipline/test_algorithms.py @@ -283,3 +283,18 @@ class BatchTransformAlgorithm(TradingAlgorithm): self.history_return_args.append( self.return_args_batch.handle_data( data, *self.args, **self.kwargs)) + + +class SetPortfolioAlgorithm(TradingAlgorithm): + """ + An algorithm that tries to set the portfolio directly. + + The portfolio should be treated as a read-only object + within the algorithm. + """ + + def initialize(self, *args, **kwargs): + pass + + def handle_data(self, data): + self.portfolio = 3