From 013e23383c1dcec87b95d35919cb88da161b8239 Mon Sep 17 00:00:00 2001 From: Thomas Wiecki Date: Tue, 10 Apr 2012 10:18:18 -0400 Subject: [PATCH 1/5] ENH: Loading of benchmark data now path independent. BUG: Correctly close file descriptor after use. ENH: Use attrgetter instead of lambda function to sort keys. ENH: Added package_data so that benchmark datasets will get installed. --- pavement.py | 3 ++ zipline/test/factory.py | 100 +++++++++++++++++++++------------------- 2 files changed, 55 insertions(+), 48 deletions(-) diff --git a/pavement.py b/pavement.py index 5f33b32a..0e89f226 100644 --- a/pavement.py +++ b/pavement.py @@ -39,6 +39,7 @@ version='dev' install_requires = parse_requirements('./etc/requirements.txt') + parse_requirements('./etc/requirements_sci.txt') tests_require = install_requires + parse_requirements('./etc/requirements_dev.txt') + options( sphinx=Bunch( builddir="_build", @@ -48,6 +49,8 @@ options( version = version, classifiers = [], packages = find_packages(), + package_data = find_package_data("zipline", package="zipline", + only_in_packages=False), install_requires = install_requires, tests_require = tests_require, test_suite = 'nose.collector', diff --git a/zipline/test/factory.py b/zipline/test/factory.py index 4954cabd..b8d0cb04 100644 --- a/zipline/test/factory.py +++ b/zipline/test/factory.py @@ -4,40 +4,45 @@ Factory functions to prepare useful data for tests. import pytz import msgpack import random +from os.path import join +from operator import attrgetter from datetime import datetime, timedelta -import zipline.util as qutil +import zipline import zipline.finance.risk as risk import zipline.protocol as zp from zipline.sources import SpecificEquityTrades, RandomEquityTrades from zipline.finance.trading import TradingEnvironment def load_market_data(): - fp_bm = open("./zipline/test/benchmark.msgpack", "rb") - bm_list = msgpack.loads(fp_bm.read()) + data_path = join(zipline.__path__[0], "test") + with open(join(data_path, "benchmark.msgpack"), "rb") as fp_bm: + bm_list = msgpack.loads(fp_bm.read()) + bm_returns = [] for packed_date, returns in bm_list: event_dt = zp.tuple_to_date(packed_date) #event_dt = event_dt.replace( - # hour=0, - # minute=0, - # second=0, + # hour=0, + # minute=0, + # second=0, # tzinfo=pytz.utc #) - + daily_return = risk.DailyReturn(date=event_dt, returns=returns) bm_returns.append(daily_return) - bm_returns = sorted(bm_returns, key=lambda(x): x.date) - fp_tr = open("./zipline/test/treasury_curves.msgpack", "rb") - tr_list = msgpack.loads(fp_tr.read()) + + bm_returns = sorted(bm_returns, key=attrgetter('date')) + with open(join(data_path, "treasury_curves.msgpack"), "rb") as fp_tr: + tr_list = msgpack.loads(fp_tr.read()) tr_curves = {} for packed_date, curve in tr_list: tr_dt = zp.tuple_to_date(packed_date) #tr_dt = tr_dt.replace(hour=0, minute=0, second=0, tzinfo=pytz.utc) tr_curves[tr_dt] = curve - + return bm_returns, tr_curves - + def create_trading_environment(): """Construct a complete environment with reasonable defaults""" benchmark_returns, treasury_curves = load_market_data() @@ -51,7 +56,7 @@ def create_trading_environment(): period_end = end, capital_base = 100000.0 ) - + return trading_environment def create_trade(sid, price, amount, datetime): row = zp.namedict({ @@ -70,7 +75,7 @@ def get_next_trading_dt(current, interval, trading_calendar): next = next + interval if trading_calendar.is_trading_day(next): break - + return next def create_trade_history(sid, prices, amounts, interval, trading_calendar): @@ -78,7 +83,7 @@ def create_trade_history(sid, prices, amounts, interval, trading_calendar): current = trading_calendar.first_open for price, amount in zip(prices, amounts): - + current = get_next_trading_dt(current, interval, trading_calendar) trade = create_trade(sid, price, amount, current) trades.append(trade) @@ -89,9 +94,9 @@ def create_trade_history(sid, prices, amounts, interval, trading_calendar): def create_txn(sid, price, amount, datetime, btrid=None): txn = zp.namedict({ 'sid':sid, - 'amount':amount, + 'amount':amount, 'dt':datetime, - 'price':price, + 'price':price, }) return txn @@ -115,15 +120,15 @@ def create_returns(daycount, trading_calendar): test_range = [] current = trading_calendar.first_open one_day = timedelta(days = 1) - - for day in range(daycount): + + for day in range(daycount): current = current + one_day if trading_calendar.is_trading_day(current): r = risk.DailyReturn(current, random.random()) test_range.append(r) - + return test_range - + def create_returns_from_range(trading_calendar): current = trading_calendar.first_open @@ -134,47 +139,47 @@ def create_returns_from_range(trading_calendar): r = risk.DailyReturn(current, random.random()) test_range.append(r) current = get_next_trading_dt(current, one_day, trading_calendar) - + return test_range - + def create_returns_from_list(returns, trading_calendar): current = trading_calendar.first_open one_day = timedelta(days = 1) test_range = [] - + #sometimes the range starts with a non-trading day. if not trading_calendar.is_trading_day(current): current = get_next_trading_dt(current, one_day, trading_calendar) - - for return_val in returns: + + for return_val in returns: r = risk.DailyReturn(current, return_val) test_range.append(r) current = get_next_trading_dt(current, one_day, trading_calendar) - + return test_range def create_random_trade_source(sid, trade_count, trading_environment): # create the source source = RandomEquityTrades(sid, "rand-"+str(sid), trade_count) - + # make the period_end of trading_environment match cur = trading_environment.first_open one_day = timedelta(days = 1) for i in range(trade_count + 2): cur = get_next_trading_dt(cur, one_day, trading_environment) trading_environment.period_end = cur - + return source - + def create_daily_trade_source(sids, trade_count, trading_environment): """ - creates trade_count trades for each sid in sids list. - first trade will be on trading_environment.period_start, and daily - thereafter for each sid. Thus, two sids should result in two trades per - day. - + creates trade_count trades for each sid in sids list. + first trade will be on trading_environment.period_start, and daily + thereafter for each sid. Thus, two sids should result in two trades per + day. + Important side-effect: trading_environment.period_end will be modified - to match the day of the final trade. + to match the day of the final trade. """ trade_history = [] for sid in sids: @@ -183,22 +188,21 @@ def create_daily_trade_source(sids, trade_count, trading_environment): start_date = trading_environment.first_open trade_time_increment = timedelta(days=1) - generated_trades = create_trade_history( - sid, - price, - volume, - trade_time_increment, - trading_environment + generated_trades = create_trade_history( + sid, + price, + volume, + trade_time_increment, + trading_environment ) - + trade_history.extend(generated_trades) - - trade_history = sorted(trade_history, key=lambda(x): x.dt) - + + trade_history = sorted(trade_history, key=attrgetter('dt')) + #set the trading environment's end to same dt as the last trade in the #history. trading_environment.period_end = trade_history[-1].dt - + source = SpecificEquityTrades("flat", trade_history) return source - \ No newline at end of file From f1ce833995301ec12b73708dbaa2452727cd1811 Mon Sep 17 00:00:00 2001 From: Thomas Wiecki Date: Mon, 14 May 2012 18:02:40 -0400 Subject: [PATCH 2/5] Moved over files from optimize and incorporated into new infrastructure. Temporarily fixed control_out bug. get_id() bug remains however. --- zipline/component.py | 3 +- zipline/optimize/__init__.py | 0 zipline/optimize/algorithms.py | 49 +++++++++ zipline/optimize/factory.py | 65 ++++++++++++ zipline/sources.py | 18 ++-- zipline/test/test_optimize.py | 185 +++++++++++++++++++++++++++++++++ 6 files changed, 312 insertions(+), 8 deletions(-) create mode 100644 zipline/optimize/__init__.py create mode 100644 zipline/optimize/algorithms.py create mode 100644 zipline/optimize/factory.py create mode 100644 zipline/test/test_optimize.py diff --git a/zipline/component.py b/zipline/component.py index d82c8fb9..f522e946 100644 --- a/zipline/component.py +++ b/zipline/component.py @@ -103,6 +103,7 @@ class Component(object): """ pass + # ------------ # Core Methods # ------------ @@ -243,7 +244,7 @@ class Component(object): self.receive_sync_ack() # blocking self.confirmed = True - + def runtime(self): if self.ready() and self.start_tic and self.stop_tic: return self.stop_tic - self.start_tic diff --git a/zipline/optimize/__init__.py b/zipline/optimize/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/zipline/optimize/algorithms.py b/zipline/optimize/algorithms.py new file mode 100644 index 00000000..d0c84b60 --- /dev/null +++ b/zipline/optimize/algorithms.py @@ -0,0 +1,49 @@ +class BuySellAlgorithm(): + """Algorithm that buys and sells alternatingly. The amount for + each order can be specified. In addition, an offset that will + quadratically reduce the amount that will be bought can be + specified. + + This algorithm is used to test the parameter optimization + framework. If combined with the UpDown trade source, an offset of + 0 will produce maximum returns. + + """ + + def __init__(self, sid, amount, offset): + self.sid = sid + self.amount = amount + self.incr = 0 + self.done = False + self.order = None + self.frame_count = 0 + self.portfolio = None + self.buy_or_sell = -1 + self.offset = offset + self.orders = [] + self.prices = [] + + 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, frame): + order_size = self.buy_or_sell * (self.amount - (self.offset**2)) + self.order(self.sid, order_size) + + #sell next time around. + self.buy_or_sell *= -1 + + self.orders.append(order_size) + #self.prices.append(frame['price']) + + self.frame_count += 1 + self.incr += 1 + + def get_sid_filter(self): + return [self.sid] diff --git a/zipline/optimize/factory.py b/zipline/optimize/factory.py new file mode 100644 index 00000000..6b4ba8ad --- /dev/null +++ b/zipline/optimize/factory.py @@ -0,0 +1,65 @@ +""" +Factory functions to prepare useful data for optimize tests. + +Author: Thomas V. Wiecki (thomas.wiecki@gmail.com), 2012 +""" +from datetime import datetime, timedelta + +import zipline.protocol as zp + +from zipline.test.factory import get_next_trading_dt +from zipline.sources import SpecificEquityTrades +from zipline.optimize.algorithms import BuySellAlgorithm +from zipline.lines import SimulatedTrading + +def create_updown_trade_source(sid, trade_count, trading_environment, start_price, amplitude): + from itertools import cycle + volume = 1000 + events = [] + price = start_price-amplitude/2. + + cur = trading_environment.first_open + one_day = timedelta(days = 1) + + #create iterator to cycle through up and down phases + change = cycle([1,-1]) + + for i in xrange(trade_count + 2): + cur = get_next_trading_dt(cur, one_day, trading_environment) + + event = zp.namedict({ + "type" : zp.DATASOURCE_TYPE.TRADE, + "sid" : sid, + "price" : price, + "volume" : volume, + "dt" : cur, + }) + + events.append(event) + + price += change.next()*amplitude + + trading_environment.period_end = cur + + source = SpecificEquityTrades(sid, events) + + return source + + +def create_predictable_zipline(config, sid=133, amplitude=10, base_price=50, offset=0): + config = deepcopy(config) + trading_environment = create_trading_environment() + source = create_updown_trade_source(sid, + config['trade_count'], + trading_environment, + base_price, + amplitude) + + algo = RegularIntervalBuySellAlgorithm(sid, 100, offset) + config['algorithm'] = algo + config['trade_source'] = source + config['environment'] = trading_environment + zipline = SimulatedTrading.create_test_zipline(**config) + zipline.simulate(blocking=True) + + return zipline diff --git a/zipline/sources.py b/zipline/sources.py index bf08644c..513f0b6a 100644 --- a/zipline/sources.py +++ b/zipline/sources.py @@ -4,6 +4,7 @@ Provides data handlers that can push messages to a zipline.core.DataFeed import datetime import random import pytz +from mock import Mock import zipline.messaging as zm import zipline.protocol as zp @@ -18,9 +19,9 @@ class TradeDataSource(zm.DataSource): :py:func: `zipline.protocol.TRADE_FRAME` :rtype: None """ - + event.source_id = self.get_id - if event.sid in self.filter['SID']: + if event.sid in self.filter['SID']: message = zp.DATASOURCE_FRAME(event) else: blank = zp.namedict({ @@ -28,9 +29,9 @@ class TradeDataSource(zm.DataSource): "source_id" : self.get_id }) message = zp.DATASOURCE_FRAME(blank) - + self.data_socket.send(message) - + class RandomEquityTrades(TradeDataSource): """ @@ -67,7 +68,7 @@ class RandomEquityTrades(TradeDataSource): }) self.send(event) self.incr += 1 - + class SpecificEquityTrades(TradeDataSource): @@ -77,7 +78,7 @@ class SpecificEquityTrades(TradeDataSource): def __init__(self, source_id, event_list): """ - :param event_list: should be a chronologically ordered list of + :param event_list: should be a chronologically ordered list of dictionaries in the following form: event = { @@ -91,10 +92,13 @@ class SpecificEquityTrades(TradeDataSource): self.event_list = event_list self.count = 0 + # TODO temporary hack + self.control_out = Mock() + def get_type(self): zp.COMPONENT_TYPE.SOURCE - + def do_work(self): if(len(self.event_list) == 0): self.signal_done() diff --git a/zipline/test/test_optimize.py b/zipline/test/test_optimize.py new file mode 100644 index 00000000..076909d4 --- /dev/null +++ b/zipline/test/test_optimize.py @@ -0,0 +1,185 @@ +"""Tests for the zipline.finance package""" +import unittest +from unittest2 import TestCase +from nose.tools import timed +from collections import defaultdict +from datetime import datetime, timedelta + +import numpy as np + +from zipline.optimize.factory import create_updown_trade_source +import zipline.test.factory as factory +import zipline.util as qutil + +from zipline.simulator import AddressAllocator, Simulator +from zipline.optimize.algorithms import BuySellAlgorithm +from zipline.finance.trading import TradingEnvironment +from zipline.lines import SimulatedTrading +from zipline.finance.trading import SIMULATION_STYLE + +DEFAULT_TIMEOUT = 15 # seconds +EXTENDED_TIMEOUT = 90 + +allocator = AddressAllocator(1000) + +class FinanceTestCase(TestCase): + + leased_sockets = defaultdict(list) + + def setUp(self): + qutil.configure_logging() + self.zipline_test_config = { + 'allocator':allocator, + 'sid':133 + } + + @timed(DEFAULT_TIMEOUT) + def test_buysell(self): + #generate events + trade_count = 50 + sid = 133 + base_price = 50 + amplitude = 6 + offset = 0 + self.zipline_test_config['order_count'] = trade_count - 1 + self.zipline_test_config['trade_count'] = trade_count + self.zipline_test_config['simulation_style'] = \ + SIMULATION_STYLE.FIXED_SLIPPAGE + + trading_environment = factory.create_trading_environment() + source = factory.create_updown_trade_source(sid, + trade_count, + trading_environment, + base_price, + amplitude + ) + + prices = np.array([event.price for event in source.event_list]) + max_price_idx = np.where(prices==prices.max())[0] + min_price_idx = np.where(prices==prices.min())[0] + self.assertTrue(np.all(max_price_idx % 2 == 1), + "Maximum prices are not periodic." + ) + self.assertTrue(np.all(min_price_idx % 2 == 0), + "Minimum prices are not periodic." + ) + self.assertEqual(prices.max(), base_price+amplitude/2., + "Maximum price does not equal expected maximum price." + ) + self.assertEqual(prices.min(), base_price-amplitude/2., + "Minimum price does not equal expected maximum price." + ) + + algo = BuySellAlgorithm(sid, 100, 0) + + self.zipline_test_config['trade_source'] = source + self.zipline_test_config['algorithm'] = algo + self.zipline_test_config['environment'] = trading_environment + + zipline = SimulatedTrading.create_test_zipline(**self.zipline_test_config) + zipline.simulate(blocking=True) + + orders = np.asarray(algo.orders) + max_order_idx = np.where(orders==orders.max())[0] + min_order_idx = np.where(orders==orders.min())[0] + + self.assertTrue(np.all(max_order_idx % 2 == 1), + "Maximum orders are not periodic." + ) + self.assertTrue(np.all(min_order_idx % 2 == 0), + "Minimum orders are not periodic." + ) + self.assertTrue(np.all(max_order_idx == max_price_idx), + "Algorithm did not buy when price was going to drop." + ) + self.assertTrue(np.all(min_order_idx == min_price_idx), + "Algorithm did not sell when price was going to increase." + ) + + def test_buysell_concave(self): + #generate events + trade_count = 6 + sid = 133 + amplitude = 30 + base_price = 50 + self.zipline_test_config['order_count'] = trade_count - 1 + self.zipline_test_config['trade_count'] = trade_count + self.zipline_test_config['simulation_style'] = \ + SIMULATION_STYLE.FIXED_SLIPPAGE + + #test whether return-function is concave wrt repeats. + test_offsets = np.arange(-9, 9, 1.) + supposed_max = np.zeros(len(test_offsets), dtype=bool) + supposed_max[len(test_offsets) // 2] = True + + compound_returns = np.empty(len(test_offsets)) + ziplines = [] + for i, test_offset in enumerate(test_offsets): + trading_environment = factory.create_trading_environment() + source = factory.create_updown_trade_source(sid, + trade_count, + trading_environment, + base_price, + amplitude + ) + + algo = BuySellAlgorithm(sid, 100, test_offset) + self.zipline_test_config['algorithm'] = algo + self.zipline_test_config['trade_source'] = source + self.zipline_test_config['environment'] = trading_environment + zipline = SimulatedTrading.create_test_zipline(**self.zipline_test_config) + zipline.simulate(blocking=True) + ziplines.append(zipline) + compound_returns[i] = zipline.get_cumulative_performance()['returns'] + + self.assertTrue(np.all(compound_returns[supposed_max] > compound_returns[np.logical_not(supposed_max)]), + "Maximum compound returns are not where they are supposed to be." + ) + + # test for concavity + max_idx = np.where(supposed_max)[0][0] + idx = np.array([max_idx, max_idx]) + for i in range((len(test_offsets)-1)/2): + # going outwards, returns must decrease + self.assertTrue(compound_returns[idx[0]-1] < compound_returns[idx[0]], + "Compound returns are not convex." + ) + self.assertTrue(compound_returns[idx[1]+1] < compound_returns[idx[1]], + "Compound returns are not convex." + ) + idx[0] -= 1 + idx[1] += 1 + + + def test_optimize(self): + def simulate(offset): + #generate events + trade_count = 3 + sid = 133 + amplitude = 10 + base_price = 50 + self.zipline_test_config['order_count'] = trade_count - 1 + self.zipline_test_config['trade_count'] = trade_count + self.zipline_test_config['simulation_style'] = \ + SIMULATION_STYLE.FIXED_SLIPPAGE + trading_environment = factory.create_trading_environment() + source = create_updown_trade_source(sid, + trade_count, + trading_environment, + base_price, + amplitude + ) + + algo = BuySellAlgorithm(sid, 100, offset) + self.zipline_test_config['algorithm'] = algo + self.zipline_test_config['trade_source'] = source + self.zipline_test_config['environment'] = trading_environment + zipline = SimulatedTrading.create_test_zipline(**self.zipline_test_config) + zipline.simulate(blocking=True) + zipline.shutdown() + #function is getting minimized, so have return negative. + return -zipline.get_cumulative_performance()['returns'] + + from scipy import optimize + opt = optimize.fmin_powell(simulate, 1.5) + np.testing.assert_almost_equal(opt, 0, 5) From 5cf472c5b2bd650f0312a8d213f2e536ec455c9a Mon Sep 17 00:00:00 2001 From: Thomas Wiecki Date: Tue, 15 May 2012 17:28:13 -0400 Subject: [PATCH 3/5] Fixed wrong path for UpDownTradeSource. Added docs. --- zipline/test/test_optimize.py | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/zipline/test/test_optimize.py b/zipline/test/test_optimize.py index 076909d4..49c85b3a 100644 --- a/zipline/test/test_optimize.py +++ b/zipline/test/test_optimize.py @@ -22,8 +22,12 @@ EXTENDED_TIMEOUT = 90 allocator = AddressAllocator(1000) -class FinanceTestCase(TestCase): +class TestUpDown(TestCase): + """This unittest establishes that the BuySellAlgorithm in + combination with the UpDownSource are suitable for usage in an + optimization framework. + """ leased_sockets = defaultdict(list) def setUp(self): @@ -34,7 +38,13 @@ class FinanceTestCase(TestCase): } @timed(DEFAULT_TIMEOUT) - def test_buysell(self): + def test_source_and_orders(self): + """Establishes that the UpDownSource is having the correct + behavior and that the BuySellAlgorithm places the buy/sell + orders at the right time. Moreover, establishes that + UpDownSource and BuySellAlgorithm interact correctly." + + """ #generate events trade_count = 50 sid = 133 @@ -47,7 +57,7 @@ class FinanceTestCase(TestCase): SIMULATION_STYLE.FIXED_SLIPPAGE trading_environment = factory.create_trading_environment() - source = factory.create_updown_trade_source(sid, + source = create_updown_trade_source(sid, trade_count, trading_environment, base_price, @@ -96,7 +106,13 @@ class FinanceTestCase(TestCase): "Algorithm did not sell when price was going to increase." ) - def test_buysell_concave(self): + def test_concavity_of_returns(self): + """Establishes that the free parameter of the BuySellAlgorithm + and the returns have a (strictly) concave relationship in a + certain region around the max. Moreover, establishes that the + max returns is at the correct value (i.e. 0). + + """ #generate events trade_count = 6 sid = 133 @@ -116,7 +132,7 @@ class FinanceTestCase(TestCase): ziplines = [] for i, test_offset in enumerate(test_offsets): trading_environment = factory.create_trading_environment() - source = factory.create_updown_trade_source(sid, + source = create_updown_trade_source(sid, trade_count, trading_environment, base_price, @@ -152,6 +168,11 @@ class FinanceTestCase(TestCase): def test_optimize(self): + """Establishes that a simple gradient descent algorithm + (Powell's method) can find the free parameter of the + BuySellAlgorithm producing maximum returns. + + """ def simulate(offset): #generate events trade_count = 3 @@ -177,7 +198,7 @@ class FinanceTestCase(TestCase): zipline = SimulatedTrading.create_test_zipline(**self.zipline_test_config) zipline.simulate(blocking=True) zipline.shutdown() - #function is getting minimized, so have return negative. + #function is getting minimized, so have to return negative cum returns. return -zipline.get_cumulative_performance()['returns'] from scipy import optimize From 809841d6906a470c616335b93fc7400b5704dc46 Mon Sep 17 00:00:00 2001 From: Thomas Wiecki Date: Tue, 15 May 2012 17:28:59 -0400 Subject: [PATCH 4/5] Fixed get_id() bug per Stephen's suggestion. Changed handle_data() back to handle_frame(). --- zipline/optimize/algorithms.py | 2 +- zipline/sources.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/zipline/optimize/algorithms.py b/zipline/optimize/algorithms.py index d0c84b60..8c2e065d 100644 --- a/zipline/optimize/algorithms.py +++ b/zipline/optimize/algorithms.py @@ -32,7 +32,7 @@ class BuySellAlgorithm(): def set_portfolio(self, portfolio): self.portfolio = portfolio - def handle_data(self, frame): + def handle_frame(self, frame): order_size = self.buy_or_sell * (self.amount - (self.offset**2)) self.order(self.sid, order_size) diff --git a/zipline/sources.py b/zipline/sources.py index 513f0b6a..4c63978a 100644 --- a/zipline/sources.py +++ b/zipline/sources.py @@ -98,6 +98,14 @@ class SpecificEquityTrades(TradeDataSource): def get_type(self): zp.COMPONENT_TYPE.SOURCE + @property + def get_id(self): + """ + The descriptive name of the component. + """ + # Prevents the bug that Thomas ran into + return "Unique ID" + def do_work(self): if(len(self.event_list) == 0): From 58bdad05b9099c6f47b1c351e7c2833b3f8e0a16 Mon Sep 17 00:00:00 2001 From: Thomas Wiecki Date: Tue, 15 May 2012 18:02:58 -0400 Subject: [PATCH 5/5] Minor fixes to adhere to refactored structure. Updated docs to be in line with rest. Skip optimization test by default as it takes a very long time. --- {zipline/test => tests}/test_optimize.py | 28 ++++++++++++------------ zipline/optimize/algorithms.py | 2 +- zipline/optimize/factory.py | 4 ++-- 3 files changed, 17 insertions(+), 17 deletions(-) rename {zipline/test => tests}/test_optimize.py (90%) diff --git a/zipline/test/test_optimize.py b/tests/test_optimize.py similarity index 90% rename from zipline/test/test_optimize.py rename to tests/test_optimize.py index 49c85b3a..8998009c 100644 --- a/zipline/test/test_optimize.py +++ b/tests/test_optimize.py @@ -1,6 +1,6 @@ """Tests for the zipline.finance package""" import unittest -from unittest2 import TestCase +from unittest2 import TestCase, skip from nose.tools import timed from collections import defaultdict from datetime import datetime, timedelta @@ -8,7 +8,7 @@ from datetime import datetime, timedelta import numpy as np from zipline.optimize.factory import create_updown_trade_source -import zipline.test.factory as factory +import zipline.utils.factory as factory import zipline.util as qutil from zipline.simulator import AddressAllocator, Simulator @@ -23,7 +23,7 @@ EXTENDED_TIMEOUT = 90 allocator = AddressAllocator(1000) class TestUpDown(TestCase): - """This unittest establishes that the BuySellAlgorithm in + """This unittest verifies that the BuySellAlgorithm in combination with the UpDownSource are suitable for usage in an optimization framework. @@ -39,14 +39,14 @@ class TestUpDown(TestCase): @timed(DEFAULT_TIMEOUT) def test_source_and_orders(self): - """Establishes that the UpDownSource is having the correct - behavior and that the BuySellAlgorithm places the buy/sell + """verify that UpDownSource is having the correct + behavior and that BuySellAlgorithm places the buy/sell orders at the right time. Moreover, establishes that UpDownSource and BuySellAlgorithm interact correctly." """ #generate events - trade_count = 50 + trade_count = 5 sid = 133 base_price = 50 amplitude = 6 @@ -107,10 +107,10 @@ class TestUpDown(TestCase): ) def test_concavity_of_returns(self): - """Establishes that the free parameter of the BuySellAlgorithm - and the returns have a (strictly) concave relationship in a - certain region around the max. Moreover, establishes that the - max returns is at the correct value (i.e. 0). + """verify concave relationship between of free parameter and + returns in certain region around the max. Moreover, + establishes that the max returns is at the correct value + (i.e. 0). """ #generate events @@ -166,11 +166,11 @@ class TestUpDown(TestCase): idx[0] -= 1 idx[1] += 1 - + @skip def test_optimize(self): - """Establishes that a simple gradient descent algorithm - (Powell's method) can find the free parameter of the - BuySellAlgorithm producing maximum returns. + """verify that gradient descent (Powell's method) can find + the optimal free parameter under which the BuySellAlgorithm produces + maximum returns. """ def simulate(offset): diff --git a/zipline/optimize/algorithms.py b/zipline/optimize/algorithms.py index 8c2e065d..d0c84b60 100644 --- a/zipline/optimize/algorithms.py +++ b/zipline/optimize/algorithms.py @@ -32,7 +32,7 @@ class BuySellAlgorithm(): def set_portfolio(self, portfolio): self.portfolio = portfolio - def handle_frame(self, frame): + def handle_data(self, frame): order_size = self.buy_or_sell * (self.amount - (self.offset**2)) self.order(self.sid, order_size) diff --git a/zipline/optimize/factory.py b/zipline/optimize/factory.py index 6b4ba8ad..0b9adde4 100644 --- a/zipline/optimize/factory.py +++ b/zipline/optimize/factory.py @@ -8,7 +8,7 @@ from datetime import datetime, timedelta import zipline.protocol as zp from zipline.test.factory import get_next_trading_dt -from zipline.sources import SpecificEquityTrades +from zipline.finance.sources import SpecificEquityTrades from zipline.optimize.algorithms import BuySellAlgorithm from zipline.lines import SimulatedTrading @@ -27,7 +27,7 @@ def create_updown_trade_source(sid, trade_count, trading_environment, start_pric for i in xrange(trade_count + 2): cur = get_next_trading_dt(cur, one_day, trading_environment) - event = zp.namedict({ + event = zp.ndict({ "type" : zp.DATASOURCE_TYPE.TRADE, "sid" : sid, "price" : price,