From 890762bde7c527fe84965dec893952b9683a9f0e Mon Sep 17 00:00:00 2001 From: fawce Date: Mon, 18 Mar 2013 14:52:51 -0400 Subject: [PATCH] MAINT: added typed errors module - added exceptions in place of asserts for expected fields for rolling transforms. - removed assertions with Messages in favor of typed exceptions. --- zipline/MESSAGES.py | 40 ----------------- zipline/algorithm.py | 19 +++++--- zipline/errors.py | 84 +++++++++++++++++++++++++++++++++++ zipline/transforms/mavg.py | 9 ++-- zipline/transforms/returns.py | 14 +++++- zipline/transforms/stddev.py | 16 ++++--- zipline/transforms/vwap.py | 9 ++-- 7 files changed, 130 insertions(+), 61 deletions(-) delete mode 100644 zipline/MESSAGES.py create mode 100644 zipline/errors.py diff --git a/zipline/MESSAGES.py b/zipline/MESSAGES.py deleted file mode 100644 index 2c67395b..00000000 --- a/zipline/MESSAGES.py +++ /dev/null @@ -1,40 +0,0 @@ - - -# --------------------- -# Error Messages. -# User facing. -# --------------------- - -class ERRORS(object): - - # Raised if a user script calls the override_slippage magic - # with a slipage object that isn't a VolumeShareSlippage or - # FixedSlipapge - UNSUPPORTED_SLIPPAGE_MODEL = """ -You attempted to override slippage with an unsupported class. \ -Please use VolumeShareSlippage or FixedSlippage. -""".strip() - - # Raised if a users script calls override_slippage magic - # after the initialize method has returned. - OVERRIDE_SLIPPAGE_POST_INIT = """ -You attempted to override slippage after the simulation has \ -started. You may only call override_slippage in your initialize \ -method. -""".strip() - - # Raised if a user script calls the override_commission magic - # with a commission object that isn't a PerShare or - # PerTrade commission - UNSUPPORTED_COMMISSION_MODEL = """ -You attempted to override commission with an unsupported class. \ -Please use PerShare or PerTrade. -""".strip() - - # Raised if a users script calls override_commission magic - # after the initialize method has returned. - OVERRIDE_COMMISSION_POST_INIT = """ -You attempted to override commission after the simulation has \ -started. You may only call override_commission in your initialize \ -method. -""".strip() diff --git a/zipline/algorithm.py b/zipline/algorithm.py index 6ebcc3d4..0c48c7e5 100644 --- a/zipline/algorithm.py +++ b/zipline/algorithm.py @@ -23,6 +23,12 @@ from datetime import datetime from itertools import groupby from operator import attrgetter +from zipline.errors import ( + UnsupportedSlippageModel, + OverrideSlippagePostInit, + UnsupportedCommissionModel, + OverrideCommissionPostInit +) from zipline.sources import DataFrameSource, DataPanelSource from zipline.utils.factory import create_simulation_parameters from zipline.transforms.utils import StatefulTransform @@ -40,7 +46,6 @@ from zipline.gens.composites import ( alias_dt ) from zipline.gens.tradesimulation import TradeSimulationClient as tsc -from zipline import MESSAGES DEFAULT_CAPITAL_BASE = float("1.0e5") @@ -319,18 +324,18 @@ class TradingAlgorithm(object): self.trading_client.ordering_client.transact = transact def set_slippage(self, slippage): - assert isinstance(slippage, (VolumeShareSlippage, FixedSlippage)), \ - MESSAGES.ERRORS.UNSUPPORTED_SLIPPAGE_MODEL + if not isinstance(slippage, (VolumeShareSlippage, FixedSlippage)): + raise UnsupportedSlippageModel() if self.initialized: - raise Exception(MESSAGES.ERRORS.OVERRIDE_SLIPPAGE_POST_INIT) + raise OverrideSlippagePostInit() self.slippage = slippage def set_commission(self, commission): - assert isinstance(commission, (PerShare, PerTrade)), \ - MESSAGES.ERRORS.UNSUPPORTED_COMMISSION_MODEL + if not isinstance(commission, (PerShare, PerTrade)): + raise UnsupportedCommissionModel() if self.initialized: - raise Exception(MESSAGES.ERRORS.OVERRIDE_COMMISSION_POST_INIT) + raise OverrideCommissionPostInit() self.commission = commission def set_sources(self, sources): diff --git a/zipline/errors.py b/zipline/errors.py new file mode 100644 index 00000000..4095552c --- /dev/null +++ b/zipline/errors.py @@ -0,0 +1,84 @@ +# +# Copyright 2013 Quantopian, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +class ZiplineError(Exception): + msg = None + + def __init__(self, *args, **kwargs): + self.args = args + self.kwargs = kwargs + self.message = str(self) + + def __str__(self): + msg = self.msg.format(**self.kwargs) + return msg + + __unicode__ = __str__ + __repr__ = __str__ + + +class WrongDataForTransform(ZiplineError): + """ + Raised whenever a rolling transform is called on an event that + does not have the necessary properties. + """ + msg = "{transform} requires {fields}. Event cannot be processed." + + +class UnsupportedSlippageModel(ZiplineError): + """ + Raised if a user script calls the override_slippage magic + with a slipage object that isn't a VolumeShareSlippage or + FixedSlipapge + """ + msg = """ +You attempted to override slippage with an unsupported class. \ +Please use VolumeShareSlippage or FixedSlippage. +""".strip() + + +class OverrideSlippagePostInit(ZiplineError): + # Raised if a users script calls override_slippage magic + # after the initialize method has returned. + msg = """ +You attempted to override slippage after the simulation has \ +started. You may only call override_slippage in your initialize \ +method. +""".strip() + + +class UnsupportedCommissionModel(ZiplineError): + """ + Raised if a user script calls the override_commission magic + with a commission object that isn't a PerShare or + PerTrade commission + """ + msg = """ +You attempted to override commission with an unsupported class. \ +Please use PerShare or PerTrade. +""".strip() + + +class OverrideCommissionPostInit(ZiplineError): + """ + Raised if a users script calls override_commission magic + after the initialize method has returned. + """ + msg = """ +You attempted to override commission after the simulation has \ +started. You may only call override_commission in your initialize \ +method. +""".strip() diff --git a/zipline/transforms/mavg.py b/zipline/transforms/mavg.py index 3dd7bcc9..3d4a853d 100644 --- a/zipline/transforms/mavg.py +++ b/zipline/transforms/mavg.py @@ -13,10 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from numbers import Number from collections import defaultdict from zipline.transforms.utils import EventWindow, TransformMeta +from zipline.errors import WrongDataForTransform class MovingAverage(object): @@ -153,6 +153,7 @@ class MovingAverageEventWindow(EventWindow): We only allow events with all of our tracked fields. """ for field in self.fields: - assert isinstance(event[field], Number), \ - "Got %s for %s in MovingAverageEventWindow" % (event[field], - field) + if field not in event: + raise WrongDataForTransform( + transform="MovingAverageEventWindow", + fields=self.fields) diff --git a/zipline/transforms/returns.py b/zipline/transforms/returns.py index 6f0c9c67..4a5fa47b 100644 --- a/zipline/transforms/returns.py +++ b/zipline/transforms/returns.py @@ -13,6 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from numbers import Number +from zipline.errors import WrongDataForTransform from zipline.transforms.utils import TransformMeta from collections import defaultdict, deque @@ -57,7 +59,7 @@ class ReturnsFromPriorClose(object): self.window_length = window_length def update(self, event): - + self.assert_required_fields(event) if self.last_event: # Day has changed since the last event we saw. Treat @@ -88,3 +90,13 @@ class ReturnsFromPriorClose(object): # the current event is now the last_event self.last_event = event + + def assert_required_fields(self, event): + """ + We only allow events with a price field to be run through + the returns transform. + """ + if 'price' not in event: + raise WrongDataForTransform( + transform="ReturnsEventWindow", + fields='price') diff --git a/zipline/transforms/stddev.py b/zipline/transforms/stddev.py index 49648392..5d7dde14 100644 --- a/zipline/transforms/stddev.py +++ b/zipline/transforms/stddev.py @@ -13,12 +13,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -from numbers import Number from collections import defaultdict from math import sqrt import numpy as np +from zipline.errors import WrongDataForTransform from zipline.transforms.utils import EventWindow, TransformMeta @@ -73,6 +73,16 @@ class MovingStandardDev(object): window.update(event) return window.get_stddev() + def assert_required_fields(self, event): + """ + We only allow events with a price field to be run through + the returns transform. + """ + if 'price' not in event: + raise WrongDataForTransform( + transform="StdDevEventWindow", + fields='price') + class MovingStandardDevWindow(EventWindow): """ @@ -90,14 +100,10 @@ class MovingStandardDevWindow(EventWindow): self.sum_sqr = 0.0 def handle_add(self, event): - assert isinstance(event.price, Number) - self.sum += event.price self.sum_sqr += event.price ** 2 def handle_remove(self, event): - assert isinstance(event.price, Number) - self.sum -= event.price self.sum_sqr -= event.price ** 2 diff --git a/zipline/transforms/vwap.py b/zipline/transforms/vwap.py index a0a1a477..e00d4c16 100644 --- a/zipline/transforms/vwap.py +++ b/zipline/transforms/vwap.py @@ -13,10 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. - -from numbers import Number from collections import defaultdict +from zipline.errors import WrongDataForTransform from zipline.transforms.utils import EventWindow, TransformMeta @@ -100,5 +99,7 @@ class VWAPEventWindow(EventWindow): # We need numerical price and volume to calculate a vwap. def assert_required_fields(self, event): - assert isinstance(event.price, Number) - assert isinstance(event.volume, Number) + if 'price' not in event or 'volume' not in event: + raise WrongDataForTransform( + transform="VWAPEventWindow", + fields=self.fields)