# # 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. import unittest import datetime import pytz import numpy as np from zipline.finance.trading import SimulationParameters from zipline.finance import trading from zipline.algorithm import TradingAlgorithm from zipline.protocol import ( Event, DATASOURCE_TYPE ) class BuyAndHoldAlgorithm(TradingAlgorithm): SID_TO_BUY_AND_HOLD = 1 def initialize(self): self.holding = False def handle_data(self, data): if not self.holding: self.order(self.sid(self.SID_TO_BUY_AND_HOLD), 100) self.holding = True class TestEventsThroughRisk(unittest.TestCase): def test_daily_buy_and_hold(self): start_date = datetime.datetime( year=2006, month=1, day=3, hour=0, minute=0, tzinfo=pytz.utc) end_date = datetime.datetime( year=2006, month=1, day=5, hour=0, minute=0, tzinfo=pytz.utc) sim_params = SimulationParameters( period_start=start_date, period_end=end_date, data_frequency='daily', emission_rate='daily' ) algo = BuyAndHoldAlgorithm( identifiers=[1], sim_params=sim_params) first_date = datetime.datetime(2006, 1, 3, tzinfo=pytz.utc) second_date = datetime.datetime(2006, 1, 4, tzinfo=pytz.utc) third_date = datetime.datetime(2006, 1, 5, tzinfo=pytz.utc) trade_bar_data = [ Event({ 'open_price': 10, 'close_price': 15, 'price': 15, 'volume': 1000, 'sid': 1, 'dt': first_date, 'source_id': 'test-trade-source', 'type': DATASOURCE_TYPE.TRADE }), Event({ 'open_price': 15, 'close_price': 20, 'price': 20, 'volume': 2000, 'sid': 1, 'dt': second_date, 'source_id': 'test_list', 'type': DATASOURCE_TYPE.TRADE }), Event({ 'open_price': 20, 'close_price': 15, 'price': 15, 'volume': 1000, 'sid': 1, 'dt': third_date, 'source_id': 'test_list', 'type': DATASOURCE_TYPE.TRADE }), ] benchmark_data = [ Event({ 'returns': 0.1, 'dt': first_date, 'source_id': 'test-benchmark-source', 'type': DATASOURCE_TYPE.BENCHMARK }), Event({ 'returns': 0.2, 'dt': second_date, 'source_id': 'test-benchmark-source', 'type': DATASOURCE_TYPE.BENCHMARK }), Event({ 'returns': 0.4, 'dt': third_date, 'source_id': 'test-benchmark-source', 'type': DATASOURCE_TYPE.BENCHMARK }), ] algo.benchmark_return_source = benchmark_data algo.set_sources(list([trade_bar_data])) gen = algo._create_generator(sim_params) # TODO: Hand derive these results. # Currently, the output from the time of this writing to # at least be an early warning against changes. expected_algorithm_returns = { first_date: 0.0, second_date: -0.000350, third_date: -0.050018 } # TODO: Hand derive these results. # Currently, the output from the time of this writing to # at least be an early warning against changes. expected_sharpe = { first_date: np.nan, second_date: -22.322677, third_date: -9.353741 } for bar in gen: current_dt = algo.datetime crm = algo.perf_tracker.cumulative_risk_metrics dt_loc = crm.cont_index.get_loc(current_dt) np.testing.assert_almost_equal( crm.algorithm_returns[dt_loc], expected_algorithm_returns[current_dt], decimal=6) np.testing.assert_almost_equal( crm.sharpe[dt_loc], expected_sharpe[current_dt], decimal=6, err_msg="Mismatch at %s" % (current_dt,)) def test_minute_buy_and_hold(self): with trading.TradingEnvironment(): start_date = datetime.datetime( year=2006, month=1, day=3, hour=0, minute=0, tzinfo=pytz.utc) end_date = datetime.datetime( year=2006, month=1, day=5, hour=0, minute=0, tzinfo=pytz.utc) sim_params = SimulationParameters( period_start=start_date, period_end=end_date, emission_rate='daily', data_frequency='minute') algo = BuyAndHoldAlgorithm( identifiers=[1], sim_params=sim_params) first_date = datetime.datetime(2006, 1, 3, tzinfo=pytz.utc) first_open, first_close = \ trading.environment.get_open_and_close(first_date) second_date = datetime.datetime(2006, 1, 4, tzinfo=pytz.utc) second_open, second_close = \ trading.environment.get_open_and_close(second_date) third_date = datetime.datetime(2006, 1, 5, tzinfo=pytz.utc) third_open, third_close = \ trading.environment.get_open_and_close(third_date) benchmark_data = [ Event({ 'returns': 0.1, 'dt': first_close, 'source_id': 'test-benchmark-source', 'type': DATASOURCE_TYPE.BENCHMARK }), Event({ 'returns': 0.2, 'dt': second_close, 'source_id': 'test-benchmark-source', 'type': DATASOURCE_TYPE.BENCHMARK }), Event({ 'returns': 0.4, 'dt': third_close, 'source_id': 'test-benchmark-source', 'type': DATASOURCE_TYPE.BENCHMARK }), ] trade_bar_data = [ Event({ 'open_price': 10, 'close_price': 15, 'price': 15, 'volume': 1000, 'sid': 1, 'dt': first_open, 'source_id': 'test-trade-source', 'type': DATASOURCE_TYPE.TRADE }), Event({ 'open_price': 10, 'close_price': 15, 'price': 15, 'volume': 1000, 'sid': 1, 'dt': first_open + datetime.timedelta(minutes=10), 'source_id': 'test-trade-source', 'type': DATASOURCE_TYPE.TRADE }), Event({ 'open_price': 15, 'close_price': 20, 'price': 20, 'volume': 2000, 'sid': 1, 'dt': second_open, 'source_id': 'test-trade-source', 'type': DATASOURCE_TYPE.TRADE }), Event({ 'open_price': 15, 'close_price': 20, 'price': 20, 'volume': 2000, 'sid': 1, 'dt': second_open + datetime.timedelta(minutes=10), 'source_id': 'test-trade-source', 'type': DATASOURCE_TYPE.TRADE }), Event({ 'open_price': 20, 'close_price': 15, 'price': 15, 'volume': 1000, 'sid': 1, 'dt': third_open, 'source_id': 'test-trade-source', 'type': DATASOURCE_TYPE.TRADE }), Event({ 'open_price': 20, 'close_price': 15, 'price': 15, 'volume': 1000, 'sid': 1, 'dt': third_open + datetime.timedelta(minutes=10), 'source_id': 'test-trade-source', 'type': DATASOURCE_TYPE.TRADE }), ] algo.benchmark_return_source = benchmark_data algo.set_sources(list([trade_bar_data])) gen = algo._create_generator(sim_params) crm = algo.perf_tracker.cumulative_risk_metrics dt_loc = crm.cont_index.get_loc(algo.datetime) first_msg = next(gen) self.assertIsNotNone(first_msg, "There should be a message emitted.") # Protects against bug where the positions appeared to be # a day late, because benchmarks were triggering # calculations before the events for the day were # processed. self.assertEqual(1, len(algo.portfolio.positions), "There should " "be one position after the first day.") self.assertEquals( 0, crm.algorithm_volatility[dt_loc], "On the first day algorithm volatility does not exist.") second_msg = next(gen) self.assertIsNotNone(second_msg, "There should be a message " "emitted.") self.assertEqual(1, len(algo.portfolio.positions), "Number of positions should stay the same.") # TODO: Hand derive. Current value is just a canary to # detect changes. np.testing.assert_almost_equal( 0.050022510129558301, crm.algorithm_returns[-1], decimal=6) third_msg = next(gen) self.assertEqual(1, len(algo.portfolio.positions), "Number of positions should stay the same.") self.assertIsNotNone(third_msg, "There should be a message " "emitted.") # TODO: Hand derive. Current value is just a canary to # detect changes. np.testing.assert_almost_equal( -0.047639464532418657, crm.algorithm_returns[-1], decimal=6)