diff --git a/tests/test_optimize.py b/tests/test_optimize.py index 25528372..6d5ccce2 100644 --- a/tests/test_optimize.py +++ b/tests/test_optimize.py @@ -5,13 +5,8 @@ from collections import defaultdict import numpy as np -from zipline.core.devsimulator import AddressAllocator -# TODO: refactor the factory to use generators from zipline.optimize.factory import create_predictable_zipline -DEFAULT_TIMEOUT = 15 # seconds -EXTENDED_TIMEOUT = 90 - from zipline.utils.test_utils import setup_logger, teardown_logger class TestUpDown(TestCase): @@ -36,7 +31,6 @@ class TestUpDown(TestCase): teardown_logger(self) @skip - @timed(DEFAULT_TIMEOUT) def test_source_and_orders(self): """verify that UpDownSource is having the correct behavior and that BuySellAlgorithm places the buy/sell diff --git a/tests/test_transforms.py b/tests/test_transforms.py index d617ce20..0acdf004 100644 --- a/tests/test_transforms.py +++ b/tests/test_transforms.py @@ -1,5 +1,5 @@ import pytz -import numpy +import numpy as np from datetime import timedelta, datetime from unittest2 import TestCase @@ -10,13 +10,13 @@ from zipline.utils.test_utils import setup_logger from zipline.utils.date_utils import utcnow from zipline.gens.tradegens import SpecificEquityTrades -from zipline.gens.transform import StatefulTransform, EventWindow +from zipline.gens.transform import StatefulTransform, EventWindow, BatchTransform, batch_transform from zipline.gens.vwap import VWAP from zipline.gens.mavg import MovingAverage from zipline.gens.stddev import MovingStandardDev from zipline.gens.returns import Returns - import zipline.utils.factory as factory +from zipline import TradingAlgorithm def to_dt(msg): return ndict({'dt': msg}) @@ -42,26 +42,26 @@ class EventWindowTestCase(TestCase): def setUp(self): setup_logger(self) - + self.monday = datetime(2012, 7, 9, 16, tzinfo=pytz.utc) - self.eleven_normal_days = [self.monday + i*timedelta(days=1) + self.eleven_normal_days = [self.monday + i*timedelta(days=1) for i in xrange(11)] # Modify the end of the period slightly to exercise the # incomplete day logic. self.eleven_normal_days[-1] -= timedelta(minutes = 1) self.eleven_normal_days.append(self.monday+timedelta(days=11,seconds=1)) - + # Second set of dates to test holiday handling. self.jul4_monday = datetime(2012, 7, 2, 16, tzinfo=pytz.utc) self.week_of_jul4 = [self.jul4_monday + i*timedelta(days=1) for i in xrange(5)] def test_event_window_with_timedelta(self): - + # Keep all events within a 5 minute window. window = NoopEventWindow( - market_aware = False, + market_aware = False, delta = timedelta(minutes = 5), days = None ) @@ -91,7 +91,7 @@ class EventWindowTestCase(TestCase): def test_market_aware_window_normal_week(self): window = NoopEventWindow( - market_aware = True, + market_aware = True, delta = None, days = 3 ) @@ -102,7 +102,7 @@ class EventWindowTestCase(TestCase): window.update(event) # Record the length of the window after each event. lengths.append(len(window.ticks)) - + # The window stretches out during the weekend because we wait # to drop events until the weekend ends. The last window is # briefly longer because it doesn't complete a full day. The @@ -113,7 +113,7 @@ class EventWindowTestCase(TestCase): def test_market_aware_window_holiday(self): window = NoopEventWindow( - market_aware = True, + market_aware = True, delta = None, days = 2 ) @@ -125,11 +125,11 @@ class EventWindowTestCase(TestCase): window.update(event) # Record the length of the window after each event. lengths.append(len(window.ticks)) - + assert lengths == [1, 2, 3, 3, 2] assert window.added == events assert window.removed == events[:-2] - + def tearDown(self): setup_logger(self) @@ -186,7 +186,7 @@ class FinanceTransformsTestCase(TestCase): expected = [0.0, 0.0, 0.1, 0.0] assert tnfm_vals == expected - + # Two-day returns. An extra kink here is that the # factory will automatically skip a weekend for the # last event. Results shouldn't notice this blip. @@ -222,12 +222,12 @@ class FinanceTransformsTestCase(TestCase): fields = ['price', 'volume'], delta = timedelta(days = 2), ) - + transformed = list(mavg.transform(self.source)) # Output values. tnfm_prices = [message.tnfm_value.price for message in transformed] tnfm_volumes = [message.tnfm_value.volume for message in transformed] - + # "Hand-calculated" values expected_prices = [ ((10.0) / 1.0), @@ -267,16 +267,16 @@ class FinanceTransformsTestCase(TestCase): transformed = list(stddev.transform(self.source)) vals = [message.tnfm_value for message in transformed] - + expected = [ None, - numpy.std([10.0, 15.0], ddof = 1), - numpy.std([10.0, 15.0, 13.0], ddof = 1), - numpy.std([15.0, 13.0, 12.0], ddof = 1), + np.std([10.0, 15.0], ddof = 1), + np.std([10.0, 15.0, 13.0], ddof = 1), + np.std([15.0, 13.0, 12.0], ddof = 1), ] - # numpy has odd rounding behavior, cf. - # http://docs.scipy.org/doc/numpy/reference/generated/numpy.std.html + # np has odd rounding behavior, cf. + # http://docs.scipy.org/doc/np/reference/generated/np.std.html for v1, v2 in zip(vals, expected): if v1 == None: @@ -285,8 +285,68 @@ class FinanceTransformsTestCase(TestCase): assert round(v1, 5) == round(v2, 5) +############################################################ +# Test BatchTransform - - - +class NoopBatchTransform(BatchTransform): + def get_value(self, data): + return data.price + +@batch_transform +def noop_batch_decorator(data): + return data.price + +class BatchTransformAlgorithm(TradingAlgorithm): + def initialize(self, *args, **kwargs): + self.history_class = [] + self.history_decorator = [] + self.days = 3 + self.noop_class = NoopBatchTransform(sids=[0, 1], + market_aware=False, + refresh_period=2, + delta=timedelta(days=self.days)) + + self.noop_decorator = noop_batch_decorator(sids=[0, 1], + market_aware=False, + refresh_period=2, + delta=timedelta(days=self.days)) + + def handle_data(self, data): + window_class = self.noop_class.handle_data(data) + window_decorator = self.noop_decorator.handle_data(data) + self.history_class.append(window_class) + self.history_decorator.append(window_decorator) + +class BatchTransformTestCase(TestCase): + def setUp(self): + setup_logger(self) + self.source, self.df = factory.create_test_df_source() + + def test_batch_inherit(self): + algo = BatchTransformAlgorithm(sids=[0, 1]) + algo.run(self.source) + + assert algo.history_class[:2] == algo.history_decorator[:2] == [None, None] + + # test overloaded class + assert np.all(algo.history_class[2][0].values == [4, 6, 8]) + assert np.all(algo.history_class[2][1].values == [5, 7, 9]) + assert np.all(algo.history_class[3][0].values == [4, 6, 8, 10]) + assert np.all(algo.history_class[3][1].values == [5, 7, 9, 11]) + # not updated because of refresh_period=2 + assert np.all(algo.history_class[4][0].values == [4, 6, 8, 10]) + assert np.all(algo.history_class[4][1].values == [5, 7, 9, 11]) + assert np.all(algo.history_class[5][0].values == [10, 12, 14]) + assert np.all(algo.history_class[5][1].values == [11, 13, 15]) + + # test decorator + assert np.all(algo.history_decorator[2][0].values == [4, 6, 8]) + assert np.all(algo.history_decorator[2][1].values == [5, 7, 9]) + assert np.all(algo.history_decorator[3][0].values == [4, 6, 8, 10]) + assert np.all(algo.history_decorator[3][1].values == [5, 7, 9, 11]) + # not updated because of refresh_period=2 + assert np.all(algo.history_decorator[4][0].values == [4, 6, 8, 10]) + assert np.all(algo.history_decorator[4][1].values == [5, 7, 9, 11]) + assert np.all(algo.history_decorator[5][0].values == [10, 12, 14]) + assert np.all(algo.history_decorator[5][1].values == [11, 13, 15]) diff --git a/zipline/__init__.py b/zipline/__init__.py index 4151cd02..3916f8cf 100644 --- a/zipline/__init__.py +++ b/zipline/__init__.py @@ -6,7 +6,9 @@ Zipline # it is a place to expose the public interfaces. from utils.protocol_utils import ndict +from algorithm import TradingAlgorithm __all__ = [ - ndict + ndict, + TradingAlgorithm ] diff --git a/zipline/algorithm.py b/zipline/algorithm.py new file mode 100644 index 00000000..b102ea7e --- /dev/null +++ b/zipline/algorithm.py @@ -0,0 +1,163 @@ +import pandas as pd +import numpy as np + +from zipline.gens.tradegens import DataFrameSource +from zipline.utils.factory import create_trading_environment +from zipline.gens.transform import StatefulTransform +from zipline.lines import SimulatedTrading +from zipline.finance.slippage import FixedSlippage + + +class TradingAlgorithm(object): + """ + Base class for trading algorithms. Inherit and overload handle_data(data). + + A new algorithm could look like this: + ``` + class MyAlgo(TradingAlgorithm): + def initialize(amount): + self.amount = amount + + def handle_data(data): + sid = self.sids[0] + self.order(sid, amount) + ``` + To then run this algorithm: + + >>> my_algo = MyAlgo(100, sids=[0]) + >>> stats = my_algo.run(data) + + """ + def __init__(self, sids, *args, **kwargs): + """ + Initialize sids and other state variables. + + Calls user-defined initialize and forwarding *args and **kwargs. + """ + self.sids = sids + self.done = False + self.order = None + self.frame_count = 0 + self.portfolio = None + + self.registered_transforms = {} + + # call to user-defined initialize method + self.initialize(*args, **kwargs) + + def _create_simulator(self, source): + """ + Create trading environment, transforms and SimulatedTrading object. + + Gets called by self.run(). + """ + environment = create_trading_environment(start=source.data.index[0], end=source.data.index[-1]) + + # Create transforms by wrapping them into StatefulTransforms + transforms = [] + for namestring, trans_descr in self.registered_transforms.iteritems(): + sf = StatefulTransform( + trans_descr['class'], + *trans_descr['args'], + **trans_descr['kwargs'] + ) + sf.namestring = namestring + + transforms.append(sf) + + # SimulatedTrading is the main class handling data streaming, + # application of transforms and calling of the user algo. + return SimulatedTrading( + [source], + transforms, + self, + environment, + FixedSlippage() + ) + + def run(self, source): + """ + Run the algorithm. + + :Arguments: + data : zipline source or pandas.DataFrame + pandas.DataFrame must have the following structure: + * column names must consist of ints representing the different sids + * index must be TimeStamps + * array contents should be price + + :Returns: + daily_stats : pandas.DataFrame + Daily performance metrics such as returns, alpha etc. + + """ + if isinstance(source, pd.DataFrame): + assert isinstance(source.index, pd.tseries.index.DatetimeIndex) + source = DataFrameSource(source, sids=self.sids) + + # create transforms and zipline + simulated_trading = self._create_simulator(source) + + # loop through simulated_trading, each iteration returns a + # perf ndict + perfs = list(simulated_trading) + + # convert perf ndict to pandas dataframe + daily_stats = self._create_daily_stats(perfs) + + return daily_stats + + + def _create_daily_stats(self, perfs): + # create daily and cumulative stats dataframe + daily_perfs = [] + cum_perfs = [] + for perf in perfs: + if 'daily_perf' in perf: + daily_perfs.append(perf['daily_perf']) + else: + cum_perfs.append(perf) + + daily_dts = [np.datetime64(perf['period_close'], utc=True) for perf in daily_perfs] + daily_stats = pd.DataFrame(daily_perfs, index=daily_dts) + + return daily_stats + + def add_transform(self, transform_class, tag, *args, **kwargs): + """Add a single-sid, sequential transform to the model. + + :Arguments: + transform_class : class + Which transform to use. E.g. mavg. + tag : str + How to name the transform. Can later be access via: + data[sid].tag() + + Extra args and kwargs will be forwarded to the transform + instantiation. + + """ + self.registered_transforms[tag] = {'class': transform_class, + 'args': args, + 'kwargs': kwargs} + + def set_portfolio(self, portfolio): + self.portfolio = portfolio + + def set_order(self, order_callable): + self.order = order_callable + + def get_sid_filter(self): + return self.sids + + def set_logger(self, logger): + self.logger = logger + + def initialize(self, *args, **kwargs): + pass + + def set_slippage_override(self, slippage_callable): + pass + + + diff --git a/zipline/gens/cov.py b/zipline/gens/cov.py deleted file mode 100644 index 85985795..00000000 --- a/zipline/gens/cov.py +++ /dev/null @@ -1,9 +0,0 @@ -from zipline.gens.transform import BatchWindow, batch_transform - -class CovEventWindow(BatchWindow): - def get_value(self, data): - return data.cov() - -@batch_transform -def cov(data): - return data.cov() diff --git a/zipline/gens/tradegens.py b/zipline/gens/tradegens.py index 652ea135..aba3329b 100644 --- a/zipline/gens/tradegens.py +++ b/zipline/gens/tradegens.py @@ -212,6 +212,5 @@ class DataFrameSource(SpecificEquityTrades): yield ndict(event) - # Return the filtered event stream. return _generator() \ No newline at end of file diff --git a/zipline/gens/tradesimulation.py b/zipline/gens/tradesimulation.py index 61527c1a..7975fb49 100644 --- a/zipline/gens/tradesimulation.py +++ b/zipline/gens/tradesimulation.py @@ -219,61 +219,61 @@ class AlgorithmSimulator(object): # Capture any output of this generator to stdout and pipe it # to a logbook interface. Also inject the current algo # snapshot time to any log record generated. - with self.processor.threadbound(), self.stdout_capture(Logger('Print'),''): + #with self.processor.threadbound(), self.stdout_capture(Logger('Print'),''): - # Call user's initialize method with a timeout. - with Timeout(INIT_TIMEOUT, message="Call to initialize timed out"): - self.algo.initialize() + # Call user's initialize method with a timeout. + with Timeout(INIT_TIMEOUT, message="Call to initialize timed out"): + self.algo.initialize() - # Group together events with the same dt field. This depends on the - # events already being sorted. - for date, snapshot in groupby(stream_in, attrgetter('dt')): - # Set the simulation date to be the first event we see. - # This should only occur once, at the start of the test. - if self.simulation_dt == None: - self.simulation_dt = date + # Group together events with the same dt field. This depends on the + # events already being sorted. + for date, snapshot in groupby(stream_in, attrgetter('dt')): + # Set the simulation date to be the first event we see. + # This should only occur once, at the start of the test. + if self.simulation_dt == None: + self.simulation_dt = date - # Done message has the risk report, so we yield before exiting. - if date == 'DONE': - for event in snapshot: + # Done message has the risk report, so we yield before exiting. + if date == 'DONE': + for event in snapshot: + yield event.perf_message + raise StopIteration() + + # We're still in the warmup period. Use the event to + # update our universe, but don't yield any perf messages, + # and don't send a snapshot to handle_data. + elif date < self.algo_start: + for event in snapshot: + del event['perf_message'] + self.update_universe(event) + + # The algo has taken so long to process events that + # its simulated time is later than the event time. + # Update the universe and yield any perf messages + # encountered, but don't call handle_data. + elif date < self.simulation_dt: + for event in snapshot: + # Only yield if we have something interesting to say. + if event.perf_message != None: yield event.perf_message - raise StopIteration() + # Delete the message before updating so we don't send it + # to the user. + del event['perf_message'] + self.update_universe(event) - # We're still in the warmup period. Use the event to - # update our universe, but don't yield any perf messages, - # and don't send a snapshot to handle_data. - elif date < self.algo_start: - for event in snapshot: - del event['perf_message'] - self.update_universe(event) + # Regular snapshot. Update the universe and send a snapshot + # to handle data. + else: + for event in snapshot: + # Only yield if we have something interesting to say. + if event.perf_message != None: + yield event.perf_message + del event['perf_message'] - # The algo has taken so long to process events that - # its simulated time is later than the event time. - # Update the universe and yield any perf messages - # encountered, but don't call handle_data. - elif date < self.simulation_dt: - for event in snapshot: - # Only yield if we have something interesting to say. - if event.perf_message != None: - yield event.perf_message - # Delete the message before updating so we don't send it - # to the user. - del event['perf_message'] - self.update_universe(event) + self.update_universe(event) - # Regular snapshot. Update the universe and send a snapshot - # to handle data. - else: - for event in snapshot: - # Only yield if we have something interesting to say. - if event.perf_message != None: - yield event.perf_message - del event['perf_message'] - - self.update_universe(event) - - # Send the current state of the universe to the user's algo. - self.simulate_snapshot(date) + # Send the current state of the universe to the user's algo. + self.simulate_snapshot(date) def update_universe(self, event): """ diff --git a/zipline/gens/transform.py b/zipline/gens/transform.py index e4171ebc..3b253727 100644 --- a/zipline/gens/transform.py +++ b/zipline/gens/transform.py @@ -175,13 +175,13 @@ class EventWindow(object): # Mark this as an abstract base class. __metaclass__ = ABCMeta - def __init__(self, market_aware, days = None, delta = None): + def __init__(self, market_aware, days=None, delta=None): self.market_aware = market_aware self.days = days self.delta = delta - self.ticks = deque() + self.ticks = deque() # Market-aware mode only works with full-day windows. if self.market_aware: @@ -284,9 +284,46 @@ class EventWindow(object): "Events arrived out of order in EventWindow: %s -> %s" % (event, self.ticks[0]) -class BatchWindow(EventWindow): - def __init__(self, func=None, refresh_period=None, days=None, sids=None): - super(BatchWindow, self).__init__(True, days=days, delta=None) +class BatchTransform(EventWindow): + """Base class for batch transforms with a trailing window of + variable length. As opposed to pure EventWindows that get a stream + of events and are bound to a single SID, this class creates stream + of pandas DataFrames with each colum representing a sid. + + There are two ways to create a new batch window: + (i) Inherit from BatchTransform and overload get_value(data). + E.g.: + ``` + class MyBatchTransform(BatchTransform): + def get_value(self, data): + # compute difference between the means of sid 0 and sid 1 + return data[0].mean() - data[1].mean() + ``` + + (ii) Use the batch_transform decorator. + E.g.: + ``` + @batch_transform + def my_batch_transform(data): + return data[0].mean() - data[1].mean() + + ``` + + In you algorithm you would then have to instantiate this in the initialize() method: + ``` + self.my_batch_transform = MyBatchTransform() + ``` + + To then use it, inside of the algorithm handle_data(), call the + handle_data() of the BatchTransform and pass it the current event: + ``` + result = self.my_batch_transform(data) + ``` + + """ + + def __init__(self, func=None, refresh_period=None, market_aware=True, delta=None, days=None, sids=None): + super(BatchTransform, self).__init__(market_aware, days=days, delta=delta) self.func = func self.sids = sids self.refresh_period = refresh_period @@ -310,7 +347,8 @@ class BatchWindow(EventWindow): # couple of seconds shouldn't matter data.dt = max(dts) - # append data frame to window + # append data frame to window. update() will call handle_add() and + # handle_remove() appropriately self.update(data) # return newly computed or cached value @@ -323,15 +361,30 @@ class BatchWindow(EventWindow): age = event.dt - self.last_refresh if age.days >= self.refresh_period: - # create Series price object - data_sids = {} - for sid in self.sids: - dts = [tick[sid].dt for tick in self.ticks] - prices = [tick[sid].price for tick in self.ticks] - data_sids[sid] = pd.Series(prices, index=dts) + # Create a pandas.Panel (i.e. 3d DataFrame) from the + # events in the current window. + # + # The resulting panel looks like this: + # index : field_name (e.g. price) + # major axis/rows : dt + # minor axis/colums : sid + # + # This Panel data structure ultimately gets passed to the + # user-overloaded get_value() method. + fields = {} + for field_name in ['price', 'volume']: + # Skip non-existant fields + if field_name not in self.ticks[0][self.sids[0]]: + continue - # concatenate different sids into one df - self.data = pd.concat(data_sids, axis=1) + values_per_sid = {} + for sid in self.sids: + values_per_sid[sid] = pd.Series({tick[sid].dt: tick[sid][field_name] for tick in self.ticks}) + + # concatenate different sids into one df + fields[field_name] = pd.DataFrame.from_dict(values_per_sid) + + self.data = pd.Panel.from_dict(fields, orient='items') self.updated = True self.last_refresh = event.dt @@ -347,7 +400,7 @@ class BatchWindow(EventWindow): def compute(self, *args, **kwargs): if self.data is None: - return False + return None if self.updated: if self.func is not None: @@ -360,9 +413,14 @@ class BatchWindow(EventWindow): return self.cached -# decorator for BatchWindow def batch_transform(func): - def create_transform(*args, **kwargs): - return BatchWindow(*args, func=func, **kwargs) + """Decorator function to use instead of inheriting from BatchTransform. + For an example on how to use this, see the doc string of BatchTransform. + """ - return create_transform + def create_window(*args, **kwargs): + # passes the user defined function to BatchTransform which it + # will call instead of self.get_value() + return BatchTransform(*args, func=func, **kwargs) + + return create_window diff --git a/zipline/optimize/algorithms.py b/zipline/optimize/algorithms.py index 098acad8..6625012a 100644 --- a/zipline/optimize/algorithms.py +++ b/zipline/optimize/algorithms.py @@ -1,15 +1,5 @@ -import pandas as pd -import numpy as np - -from datetime import datetime -from zipline.gens.tradegens import DataFrameSource -from zipline import ndict -from zipline.utils.factory import create_trading_environment -from zipline.gens.transform import StatefulTransform -from zipline.lines import SimulatedTrading -from zipline.finance.slippage import FixedSlippage - from logbook import Logger +from zipline import TradingAlgorithm logger = Logger('Algo') @@ -64,159 +54,6 @@ class BuySellAlgorithm(object): return [self.sid] -class TradingAlgorithm(object): - """ - Base class for trading algorithms. Inherit and overload handle_data(data). - - A new algorithm could look like this: - ``` - class MyAlgo(TradingAlgorithm): - def initialize(amount): - self.amount = amount - - def handle_data(data): - sid = self.sids[0] - self.order(sid, amount) - ``` - To then run this algorithm: - - >>> my_algo = MyAlgo(100) - >>> stats = my_algo.run(data) - - """ - def __init__(self, sids, *args, **kwargs): - """ - Initialize sids and other state variables. - - Calls user-defined initialize and forwarding *args and **kwargs. - """ - self.sids = sids - self.done = False - self.order = None - self.frame_count = 0 - self.portfolio = None - - self.registered_transforms = {} - - # call to user-defined initialize method - self.initialize(*args, **kwargs) - - def _create_simulator(self, source): - """ - Create trading environment, transforms and SimulatedTrading object. - - Gets called by self.run(). - """ - environment = create_trading_environment(start=source.data.index[0], end=source.data.index[-1]) - - # Create transforms by wrapping them into StatefulTransforms - transforms = [] - for namestring, trans_descr in self.registered_transforms.iteritems(): - sf = StatefulTransform( - trans_descr['class'], - *trans_descr['args'], - **trans_descr['kwargs'] - ) - sf.namestring = namestring - - transforms.append(sf) - - # SimulatedTrading is the main class handling data streaming, - # application of transforms and calling of the user algo. - return SimulatedTrading( - [source], - transforms, - self, - environment, - FixedSlippage() - ) - - def run(self, data): - """ - Run the algorithm. - - :Arguments: - data : pandas.DataFrame - * columns must consist of ints representing the different sids - * index must be TimeStamps - * array contents should be price - - :Returns: - daily_stats : pandas.DataFrame - Daily performance metrics such as returns, alpha etc. - - """ - assert isinstance(data, pd.DataFrame) - assert isinstance(data.index, pd.tseries.index.DatetimeIndex) - - source = DataFrameSource(data, sids=self.sids) - - # create transforms and zipline - simulated_trading = self._create_simulator(source) - - # loop through simulated_trading, each iteration returns a - # perf ndict - perfs = list(self.simulated_trading) - - # convert perf ndict to pandas dataframe - daily_stats = self._create_daily_stats(perfs) - - return daily_stats - - - def _create_daily_stats(self, perfs): - # create daily and cumulative stats dataframe - daily_perfs = [] - cum_perfs = [] - for perf in perfs: - if 'daily_perf' in perf: - daily_perfs.append(perf['daily_perf']) - else: - cum_perfs.append(perf) - - daily_dts = [np.datetime64(perf['period_close'], utc=True) for perf in daily_perfs] - daily_stats = pd.DataFrame(daily_perfs, index=daily_dts) - - return daily_stats - - def add_transform(self, transform_class, tag, *args, **kwargs): - """Add a single-sid, sequential transform to the model. - - :Arguments: - transform_class : class - Which transform to use. E.g. mavg. - tag : str - How to name the transform. Can later be access via: - data[sid].tag() - - Extra args and kwargs will be forwarded to the transform - instantiation. - - """ - self.registered_transforms[tag] = {'class': transform_class, - 'args': args, - 'kwargs': kwargs} - - def set_portfolio(self, portfolio): - self.portfolio = portfolio - - def set_order(self, order_callable): - self.order = order_callable - - def get_sid_filter(self): - return self.sids - - def set_logger(self, logger): - self.logger = logger - - def initialize(self, *args, **kwargs): - pass - - def set_slippage_override(self, slippage_callable): - pass - - - class BuySellAlgorithmNew(TradingAlgorithm): """Algorithm that buys and sells alternatingly. The amount for each order can be specified. In addition, an offset that will diff --git a/zipline/optimize/example.py b/zipline/optimize/example.py index 02af8d7e..c8912558 100644 --- a/zipline/optimize/example.py +++ b/zipline/optimize/example.py @@ -1,25 +1,26 @@ +# WARNING: This file is still work in progress and contains rather +# random code snippets. + import pandas as pd import numpy as np -#from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt import cProfile from zipline.gens.mavg import MovingAverage -from zipline.gens.cov import CovEventWindow, cov -from zipline.optimize.algorithms import TradingAlgorithm -from datetime import timedelta +from zipline.gens.cov import CovTransform, cov +from zipline.algorithm import TradingAlgorithm +from zipline.gens.transform import BatchTransform, batch_transform -#from mpi4py_map import map +@batch_transform +def cov(data): + return data.price.cov() -# Inherits from Algorithm base class class DMA(TradingAlgorithm): """Dual Moving Average algorithm. """ def initialize(self, amount=100, short_window=20, long_window=40): - self.orders = [] self.amount = amount - self.prices = [] self.events = 0 self.invested = {} @@ -34,14 +35,12 @@ class DMA(TradingAlgorithm): market_aware=True, days=long_window) - self.cov = CovEventWindow(sids=self.sids, refresh_period=1, days=5) - self.cov2 = cov(sids=self.sids, refresh_period=1, days=5) + self.cov = cov(sids=self.sids, refresh_period=1, days=5) def handle_data(self, data): self.events += 1 cov = self.cov.handle_data(data) - cov = self.cov2.handle_data(data) print cov for sid in self.sids: diff --git a/zipline/optimize/factory.py b/zipline/optimize/factory.py index ce16f20d..24a0783e 100644 --- a/zipline/optimize/factory.py +++ b/zipline/optimize/factory.py @@ -10,7 +10,7 @@ import zipline.protocol as zp from zipline.utils.factory import get_next_trading_dt, create_trading_environment from zipline.finance.sources import SpecificEquityTrades -from zipline.optimize.algorithms import BuySellAlgorithm +from zipline.optimize.algorithms import BuySellAlgorithmNew from zipline.lines import SimulatedTrading from zipline.finance.slippage import FixedSlippage diff --git a/zipline/utils/factory.py b/zipline/utils/factory.py index 58e58fd2..66804957 100644 --- a/zipline/utils/factory.py +++ b/zipline/utils/factory.py @@ -8,13 +8,14 @@ import random from os.path import join, abspath, dirname from operator import attrgetter +import pandas as pd +import numpy as np from datetime import datetime, timedelta -from zipline.utils.date_utils import tuple_to_date -from zipline.utils.protocol_utils import ndict import zipline.finance.risk as risk - -from zipline.gens.tradegens import SpecificEquityTrades +from zipline.utils.date_utils import tuple_to_date +from zipline.utils.protocol_utils import ndict +from zipline.gens.tradegens import SpecificEquityTrades, DataFrameSource from zipline.gens.utils import create_trade from zipline.finance.trading import TradingEnvironment @@ -90,7 +91,6 @@ def create_trade_history(sid, prices, amounts, interval, trading_calendar): current = trading_calendar.first_open for price, amount in zip(prices, amounts): - trade = create_trade(sid, price, amount, current) trades.append(trade) current = get_next_trading_dt(current, interval, trading_calendar) @@ -235,3 +235,12 @@ def create_trade_source(sids, trade_count, trade_time_increment, trading_environ #trading_environment.period_end = trade_history[-1].dt return source + +def create_test_df_source(): + start = pd.datetime(1990, 1, 1, 0, 0, 0, 0, pytz.utc) + end = pd.datetime(1990, 1, 10, 0, 0, 0, 0, pytz.utc) + index = pd.DatetimeIndex(start=start, end=end) + x = np.arange(0, 16).reshape((8, 2)) + df = pd.DataFrame(x, index=index, columns=[0, 1]) + + return DataFrameSource(df), df