From 531907970b8f01de015fe6ade7cd40aac3631db5 Mon Sep 17 00:00:00 2001 From: fawce Date: Fri, 18 May 2012 13:34:20 -0400 Subject: [PATCH 1/5] small fix --- zipline/utils/date_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zipline/utils/date_utils.py b/zipline/utils/date_utils.py index 85ff98d3..9b69d371 100644 --- a/zipline/utils/date_utils.py +++ b/zipline/utils/date_utils.py @@ -57,13 +57,13 @@ def iso8061_to_epoch(datestring): return EPOCH(dt) def epoch_now(): - dt = datetime.utcnow().replace(tzinfo=pytz.utc) + dt = utcnow() return EPOCH(dt) # UTC Datetime Subclasses # ----------------------- def utcnow(): - return datetime.now(pytz.utc) + return datetime.utcnow().replace(tzinfo=pytz.utc) class utcdatetime(datetime): def __new__(cls, *args, **kwargs): From 52d75c9d31fa350680930cc01fb42b7deeca5fee Mon Sep 17 00:00:00 2001 From: fawce Date: Fri, 18 May 2012 14:42:47 -0400 Subject: [PATCH 2/5] fixed bug with BaseTransform state and name properties. --- zipline/components/merge.py | 2 -- zipline/finance/movingaverage.py | 21 ++++++++++++++------- zipline/finance/returns.py | 8 +++++++- zipline/finance/vwap.py | 12 +++++++++--- zipline/transforms/base.py | 1 - 5 files changed, 30 insertions(+), 14 deletions(-) diff --git a/zipline/components/merge.py b/zipline/components/merge.py index 689ec8cf..8a2ae7c1 100644 --- a/zipline/components/merge.py +++ b/zipline/components/merge.py @@ -1,7 +1,5 @@ -from feed import Feed import zipline.protocol as zp -from zipline.protocol import COMPONENT_TYPE from zipline.components.aggregator import Aggregate from collections import Counter diff --git a/zipline/finance/movingaverage.py b/zipline/finance/movingaverage.py index 2aee0531..f0e401fe 100644 --- a/zipline/finance/movingaverage.py +++ b/zipline/finance/movingaverage.py @@ -5,10 +5,17 @@ from zipline.transforms.base import BaseTransform class MovingAverageTransform(BaseTransform): - def init(self, name, daycount=3): - self.daycount = daycount + + def init(self, name, days=3): + self.state = {} + self.state['name'] = name + self.days = days self.by_sid = defaultdict(self._create) + @property + def get_id(self): + return self.state['name'] + def transform(self, event): cur = self.by_sid[event.sid] cur.update(event) @@ -16,12 +23,12 @@ class MovingAverageTransform(BaseTransform): return self.state def _create(self): - return MovingAverage(self.daycount) + return MovingAverage(self.days) class MovingAverage(object): - def init(self, daycount): - self.window = EventWindow(daycount) + def __init__(self, days): + self.window = EventWindow(days) self.total = 0.0 self.average = 0.0 @@ -43,10 +50,10 @@ class EventWindow(object): Tracks a window of the event history. Use an instance to track the events inside your window to efficiently calculate rolling statistics. """ - def init(self, daycount): + def __init__(self, days): self.ticks = [] self.dropped_ticks = [] - self.delta = timedelta(days=daycount) + self.delta = timedelta(days=days) def update(self, event): # add new event diff --git a/zipline/finance/returns.py b/zipline/finance/returns.py index 01dfd7fd..5585f325 100644 --- a/zipline/finance/returns.py +++ b/zipline/finance/returns.py @@ -4,8 +4,15 @@ from zipline.transforms.base import BaseTransform class ReturnsTransform(BaseTransform): def init(self, name): + self.state = {} + self.state['name'] = name self.by_sid = defaultdict(self._create) + @property + def get_id(self): + return self.state['name'] + + def transform(self, event): cur = self.by_sid[event.sid] cur.update(event) @@ -27,7 +34,6 @@ class ReturnsFromPriorClose(object): self.returns = 0.0 def update(self, event): - next_close = None if self.last_close: change = event.price - self.last_close.price self.returns = change / self.last_close.price diff --git a/zipline/finance/vwap.py b/zipline/finance/vwap.py index 3b165a71..35eb9578 100644 --- a/zipline/finance/vwap.py +++ b/zipline/finance/vwap.py @@ -7,9 +7,15 @@ from zipline.finance.movingaverage import EventWindow class VWAPTransform(BaseTransform): def init(self, name, daycount=3): + self.state = {} + self.state['name'] = name self.daycount = daycount self.by_sid = defaultdict(self.create_vwap) + @property + def get_id(self): + return self.state['name'] + def transform(self, event): cur = self.by_sid[event.sid] cur.update(event) @@ -24,12 +30,12 @@ class DailyVWAP(object): A class that tracks the volume weighted average price based on tick updates. """ - def init(self, name, daycount=3): - self.window = EventWindow(daycount) + def __init__(self, days=3): + self.window = EventWindow(days) self.flux = 0.0 self.volume = 0 self.vwap = 0.0 - self.delta = timedelta(days=daycount) + self.delta = timedelta(days=days) def update(self, event): diff --git a/zipline/transforms/base.py b/zipline/transforms/base.py index 8ab922dc..082e52b9 100644 --- a/zipline/transforms/base.py +++ b/zipline/transforms/base.py @@ -20,7 +20,6 @@ class BaseTransform(Component): Parent class for feed transforms. Subclass and override transform method to create a new derived value from the combined feed. """ - def init(self): pass From ba2783081cf992251284cfcc3367e60549f44160 Mon Sep 17 00:00:00 2001 From: fawce Date: Fri, 18 May 2012 21:46:42 -0400 Subject: [PATCH 3/5] switched SID to sid --- tests/test_finance.py | 8 ++++---- zipline/components/datasource.py | 2 +- zipline/finance/sources.py | 4 ++-- zipline/lines.py | 2 +- zipline/test_algorithms.py | 8 ++++---- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/test_finance.py b/tests/test_finance.py index ac2938c5..77a04495 100644 --- a/tests/test_finance.py +++ b/tests/test_finance.py @@ -224,11 +224,11 @@ class FinanceTestCase(TestCase): "Portfolio should have one position." ) - SID = self.zipline_test_config['sid'] + sid = self.zipline_test_config['sid'] self.assertEqual( - zipline.get_positions()[SID]['sid'], - SID, - "Portfolio should have one position in " + str(SID) + zipline.get_positions()[sid]['sid'], + sid, + "Portfolio should have one position in " + str(sid) ) self.assertEqual( diff --git a/zipline/components/datasource.py b/zipline/components/datasource.py index d9e7da96..4cafdb5f 100644 --- a/zipline/components/datasource.py +++ b/zipline/components/datasource.py @@ -17,7 +17,7 @@ class DataSource(Component): converting to a dict, and calling send(map). Every datasource has a dict property to hold filters:: - - key -- name of the filter, e.g. SID + - key -- name of the filter, e.g. sid - value -- a primitive representing the filter. e.g. a list of ints. Modify the datasource's filters via the set_filter(name, value) diff --git a/zipline/finance/sources.py b/zipline/finance/sources.py index 73fa56e0..346e4673 100644 --- a/zipline/finance/sources.py +++ b/zipline/finance/sources.py @@ -31,7 +31,7 @@ class TradeDataSource(DataSource): def send(self, event): """ - Sends the event iff it matches the internal SID filter. + Sends the event iff it matches the internal sid filter. :param dict event: is a trade event with data as per :py:func: `zipline.protocol.TRADE_FRAME` :rtype: None @@ -39,7 +39,7 @@ class TradeDataSource(DataSource): event.source_id = self.source_id - if event.sid in self.filter['SID']: + if event.sid in self.filter['sid']: message = zp.DATASOURCE_FRAME(event) else: blank = ndict({ diff --git a/zipline/lines.py b/zipline/lines.py index 5e0b2f8f..0fbd5dc4 100644 --- a/zipline/lines.py +++ b/zipline/lines.py @@ -283,7 +283,7 @@ class SimulatedTrading(object): """ assert isinstance(source, DataSource) self.check_started() - source.set_filter('SID', self.algorithm.get_sid_filter()) + source.set_filter('sid', self.algorithm.get_sid_filter()) self.sim.register_components([source]) # ``id`` is name of source_id, ``get_id`` is the class name diff --git a/zipline/test_algorithms.py b/zipline/test_algorithms.py index 1c1e71c3..83ead2e9 100644 --- a/zipline/test_algorithms.py +++ b/zipline/test_algorithms.py @@ -20,7 +20,7 @@ The algorithm must expose methods: of the current state of the simulation universe. An example data ndict:: +-----------------+--------------+----------------+--------------------+ - | | SID(133) | SID(134) | SID(135) | + | | sid(133) | sid(134) | sid(135) | +=================+==============+================+====================+ | price | $10.10 | $22.50 | $13.37 | +-----------------+--------------+----------------+--------------------+ @@ -33,16 +33,16 @@ The algorithm must expose methods: - set_order: method that accepts a callable. Will be set as the value of the order method of trading_client. An algorithm can then place orders with a - valid SID and a number of shares:: + valid sid and a number of shares:: - self.order(SID(133), share_count) + self.order(sid(133), share_count) - set_performance: property which can be set equal to the cumulative_trading_performance property of the trading_client. An algorithm can then check position information with the Portfolio object:: - self.Portfolio[SID(133)]['cost_basis'] + self.Portfolio[sid(133)]['cost_basis'] """ From ec93bdaecdca705ba8759d6128962bbc3bffcd58 Mon Sep 17 00:00:00 2001 From: fawce Date: Fri, 18 May 2012 23:40:38 -0400 Subject: [PATCH 4/5] alias for dt as datetime. --- zipline/components/tradesimulation.py | 3 ++ zipline/test_algorithms.py | 73 +++++++++++++-------------- 2 files changed, 39 insertions(+), 37 deletions(-) diff --git a/zipline/components/tradesimulation.py b/zipline/components/tradesimulation.py index b6df2922..06aa7272 100644 --- a/zipline/components/tradesimulation.py +++ b/zipline/components/tradesimulation.py @@ -160,6 +160,9 @@ class TradeSimulationClient(Component): def get_data(self): for event in self.event_queue: + #alias the dt as datetime + event.datetime = event.dt self.event_data[event['sid']] = event + self.event_queue = [] return self.event_data diff --git a/zipline/test_algorithms.py b/zipline/test_algorithms.py index 83ead2e9..116fb2b9 100644 --- a/zipline/test_algorithms.py +++ b/zipline/test_algorithms.py @@ -2,23 +2,23 @@ Algorithm Protocol =================== -For a class to be passed as a trading algorithm to the +For a class to be passed as a trading algorithm to the :py:class:`zipline.lines.SimulatedTrading` zipline it must follow an implementation protocol. Examples of this algorithm protocol are provided below. The algorithm must expose methods: - - initialize: method that takes no args, no returns. Simply called to + - initialize: method that takes no args, no returns. Simply called to enable the algorithm to set any internal state needed. - get_sid_filter: method that takes no args, and returns a list of valid sids. List must have a length between 1 and 10. If None is returned the filter will block all events. - - - handle_data: method that accepts a :py:class:`zipline.protocol_utils.ndict` + + - handle_data: method that accepts a :py:class:`zipline.protocol_utils.ndict` of the current state of the simulation universe. An example data ndict:: - + +-----------------+--------------+----------------+--------------------+ | | sid(133) | sid(134) | sid(135) | +=================+==============+================+====================+ @@ -30,23 +30,22 @@ The algorithm must expose methods: +-----------------+--------------+----------------+--------------------+ | dt | 6/30/2012 | 6/30/2011 | 6/29/2012 | +-----------------+--------------+----------------+--------------------+ - - - set_order: method that accepts a callable. Will be set as the value of the - order method of trading_client. An algorithm can then place orders with a + + - set_order: method that accepts a callable. Will be set as the value of the + order method of trading_client. An algorithm can then place orders with a valid sid and a number of shares:: - + self.order(sid(133), share_count) - - - set_performance: property which can be set equal to the - cumulative_trading_performance property of the trading_client. An + + - set_performance: property which can be set equal to the + cumulative_trading_performance property of the trading_client. An algorithm can then check position information with the Portfolio object:: - + self.Portfolio[sid(133)]['cost_basis'] """ -import zipline.protocol as zp class TestAlgorithm(): """ @@ -54,7 +53,7 @@ class TestAlgorithm(): to verify the orders sent/received, transactions created, and positions at the close of a simulation. """ - + def __init__(self, sid, amount, order_count): self.count = order_count self.sid = sid @@ -64,26 +63,26 @@ class TestAlgorithm(): self.order = None self.frame_count = 0 self.portfolio = None - + def initialize(self): pass - + def set_order(self, order_callable): self.order = order_callable - + def set_portfolio(self, portfolio): self.portfolio = portfolio - + def handle_data(self, data): self.frame_count += 1 #place an order for 100 shares of sid if self.incr < self.count: self.order(self.sid, self.amount) self.incr += 1 - + def get_sid_filter(self): - return [self.sid] - + return [self.sid] + # class HeavyBuyAlgorithm(): """ @@ -91,7 +90,7 @@ class HeavyBuyAlgorithm(): to verify the orders sent/received, transactions created, and positions at the close of a simulation. """ - + def __init__(self, sid, amount): self.sid = sid self.amount = amount @@ -100,41 +99,41 @@ class HeavyBuyAlgorithm(): self.order = None self.frame_count = 0 self.portfolio = None - + def initialize(self): - pass - + pass + def set_order(self, order_callable): self.order = order_callable - + def set_portfolio(self, portfolio): self.portfolio = portfolio - + def handle_data(self, data): self.frame_count += 1 #place an order for 100 shares of sid self.order(self.sid, self.amount) self.incr += 1 - + def get_sid_filter(self): - return [self.sid] - + return [self.sid] + class NoopAlgorithm(object): """ Dolce fa niente. """ - + def initialize(self): pass - + def set_order(self, order_callable): pass - + def set_portfolio(self, portfolio): pass - + def handle_data(self, data): pass - + def get_sid_filter(self): - return None \ No newline at end of file + return None From 700cf64324701623396c1e5ca768f63ac3deb1b9 Mon Sep 17 00:00:00 2001 From: fawce Date: Fri, 18 May 2012 23:47:41 -0400 Subject: [PATCH 5/5] tests for transforms --- tests/test_transforms.py | 90 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 tests/test_transforms.py diff --git a/tests/test_transforms.py b/tests/test_transforms.py new file mode 100644 index 00000000..0d267e60 --- /dev/null +++ b/tests/test_transforms.py @@ -0,0 +1,90 @@ +from datetime import timedelta +from collections import defaultdict +from unittest2 import TestCase + +import zipline.utils.factory as factory +from zipline.finance.vwap import DailyVWAP, VWAPTransform +from zipline.finance.returns import ReturnsFromPriorClose +from zipline.finance.movingaverage import MovingAverage +from zipline.lines import SimulatedTrading +from zipline.core.devsimulator import AddressAllocator + +allocator = AddressAllocator(1000) + +class ZiplineWithTransformsTestCase(TestCase): + leased_sockets = defaultdict(list) + + def setUp(self): + # skip ahead 100 spots + allocator.lease(100) + self.trading_environment = factory.create_trading_environment() + self.zipline_test_config = { + 'allocator':allocator, + 'sid':133 + } + + def test_vwap_tnfm(self): + zipline = SimulatedTrading.create_test_zipline( + **self.zipline_test_config + ) + vwap = VWAPTransform("vwap_10", daycount=10) + zipline.add_transform(vwap) + + zipline.simulate(blocking=True) + + self.assertTrue(zipline.sim.ready()) + self.assertFalse(zipline.sim.exception) + +class FinanceTransformsTestCase(TestCase): + def setUp(self): + self.trading_environment = factory.create_trading_environment() + + def test_vwap(self): + + trade_history = factory.create_trade_history( + 133, + [10.0, 10.0, 10.0, 11.0], + [100, 100, 100, 300], + timedelta(days=1), + self.trading_environment + ) + + vwap = DailyVWAP(days=2) + for trade in trade_history: + vwap.update(trade) + + self.assertEqual(vwap.vwap, 10.75) + + + def test_returns(self): + trade_history = factory.create_trade_history( + 133, + [10.0, 10.0, 10.0, 11.0], + [100, 100, 100, 300], + timedelta(days=1), + self.trading_environment + ) + + returns = ReturnsFromPriorClose() + for trade in trade_history: + returns.update(trade) + + + self.assertEqual(returns.returns, .1) + + + def test_moving_average(self): + trade_history = factory.create_trade_history( + 133, + [10.0, 10.0, 10.0, 11.0], + [100, 100, 100, 300], + timedelta(days=1), + self.trading_environment + ) + + ma = MovingAverage(days=2) + for trade in trade_history: + ma.update(trade) + + + self.assertEqual(ma.average, 10.5)