diff --git a/tests/test_perf_tracking.py b/tests/test_perf_tracking.py index 6757df57..ebafcbfa 100644 --- a/tests/test_perf_tracking.py +++ b/tests/test_perf_tracking.py @@ -58,14 +58,19 @@ class TestDividendPerformance(unittest.TestCase): dividend = factory.create_dividend( 1, 10.00, - events[0].dt, + # declared date, when the algorithm finds out about + # the dividend events[1].dt, + # ex_date, when the algorithm is credited with the + # dividend + events[1].dt, + # pay date, when the algorithm receives the dividend. events[2].dt ) + txn = factory.create_txn(1, 10.0, 100, events[0].dt) + events[0].TRANSACTION = txn events.insert(1, dividend) - txn = factory.create_txn(1, 10.0, 100, self.dt+oneday) - events[2].TRANSACTION = txn perf_tracker = perf.PerformanceTracker(self.trading_environment) transformed_events = list(perf_tracker.transform( ((event.dt, [event]) for event in events)) @@ -94,10 +99,13 @@ class TestDividendPerformance(unittest.TestCase): daily_returns = [event['daily_perf']['returns'] for event in results] self.assertEqual(daily_returns, [0.0, 0.0, 0.10, 0.0, 0.0]) cash_flows = [event['daily_perf']['capital_used'] for event in results] - self.assertEqual(cash_flows, [0, -1000, 1000, 0, 0]) + self.assertEqual(cash_flows, [-1000, 0, 1000, 0, 0]) cumulative_cash_flows = \ [event['cumulative_perf']['capital_used'] for event in results] - self.assertEqual(cumulative_cash_flows, [0, -1000, 0, 0, 0]) + self.assertEqual(cumulative_cash_flows, [-1000, -1000, 0, 0, 0]) + cash_pos = \ + [event['cumulative_perf']['ending_cash'] for event in results] + self.assertEqual(cash_pos, [9000, 9000, 10000, 10000, 10000]) def test_post_ex_long_position_receives_no_dividend(self): #post some trades in the market @@ -165,8 +173,8 @@ class TestDividendPerformance(unittest.TestCase): events[3].dt ) - buy_txn = factory.create_txn(1, 10.0, 100, events[1].dt) - events[1].TRANSACTION = buy_txn + buy_txn = factory.create_txn(1, 10.0, 100, events[0].dt) + events[0].TRANSACTION = buy_txn sell_txn = factory.create_txn(1, 10.0, -100, events[2].dt) events[2].TRANSACTION = sell_txn events.insert(1, dividend) @@ -192,10 +200,10 @@ class TestDividendPerformance(unittest.TestCase): daily_returns = [event['daily_perf']['returns'] for event in results] self.assertEqual(daily_returns, [0, 0, 0, 0.1, 0]) cash_flows = [event['daily_perf']['capital_used'] for event in results] - self.assertEqual(cash_flows, [0, -1000, 1000, 1000, 0]) + self.assertEqual(cash_flows, [-1000, 0, 1000, 1000, 0]) cumulative_cash_flows = \ [event['cumulative_perf']['capital_used'] for event in results] - self.assertEqual(cumulative_cash_flows, [0, -1000, 0, 1000, 1000]) + self.assertEqual(cumulative_cash_flows, [-1000, -1000, 0, 1000, 1000]) def test_buy_and_sell_before_ex(self): #post some trades in the market @@ -316,9 +324,9 @@ class TestDividendPerformance(unittest.TestCase): events[2].dt ) - events.insert(1, dividend) txn = factory.create_txn(1, 10.0, -100, self.dt+oneday) - events[2].TRANSACTION = txn + events[0].TRANSACTION = txn + events.insert(0, dividend) perf_tracker = perf.PerformanceTracker(self.trading_environment) transformed_events = list(perf_tracker.transform( ((event.dt, [event]) for event in events)) @@ -341,10 +349,10 @@ class TestDividendPerformance(unittest.TestCase): daily_returns = [event['daily_perf']['returns'] for event in results] self.assertEqual(daily_returns, [0.0, 0.0, -0.1, 0.0, 0.0]) cash_flows = [event['daily_perf']['capital_used'] for event in results] - self.assertEqual(cash_flows, [0, 1000, -1000, 0, 0]) + self.assertEqual(cash_flows, [1000, 0, -1000, 0, 0]) cumulative_cash_flows = \ [event['cumulative_perf']['capital_used'] for event in results] - self.assertEqual(cumulative_cash_flows, [0, 1000, 0, 0, 0]) + self.assertEqual(cumulative_cash_flows, [1000, 1000, 0, 0, 0]) def test_no_position_receives_no_dividend(self): #post some trades in the market diff --git a/zipline/finance/performance.py b/zipline/finance/performance.py index fe523121..54fd82c7 100644 --- a/zipline/finance/performance.py +++ b/zipline/finance/performance.py @@ -278,10 +278,6 @@ class PerformanceTracker(object): # add the return results from today to the list of DailyReturn objects. todays_date = self.market_close.replace(hour=0, minute=0, second=0) - - self.cumulative_performance.update_dividends(todays_date) - self.todays_performance.update_dividends(todays_date) - todays_return_obj = risk.DailyReturn( todays_date, self.todays_performance.returns @@ -326,6 +322,15 @@ Last successful date: %s" % self.market_open) self.todays_performance.period_open = self.market_open self.todays_performance.period_close = self.market_close + # The dividend calculation for the daily needs to be made + # after the rollover. midnight_between is the last midnight + # hour between the close of markets and the next open. To + # make sure midnight_between matches identically with + # dividend data dates, it is in UTC. + midnight_between = self.market_open.replace(hour=0, minute=0, second=0) + self.cumulative_performance.update_dividends(midnight_between) + self.todays_performance.update_dividends(midnight_between) + return daily_update def handle_simulation_end(self): @@ -368,17 +373,25 @@ class Position(object): self.last_sale_date = 0.0 self.dividends = [] - def update_dividends(self, dt): + def update_dividends(self, midnight_utc): + """ + midnight_utc is the 0 hour for the current (not yet open) trading day. + This method will be invoked at the end of the market + close handling, before the next market open. + """ payment = 0.0 unpaid_dividends = [] for dividend in self.dividends: - if dt == dividend.ex_date: + if midnight_utc == dividend.ex_date: # if we own shares at midnight of the div_ex date # we are entitled to the dividend. dividend.amount_on_ex_date = self.amount - dividend.payment = self.amount * dividend.net_amount + if dividend.net_amount: + dividend.payment = self.amount * dividend.net_amount + else: + dividend.payment = self.amount * dividend.gross_amount - if dt == dividend.pay_date: + if midnight_utc == dividend.pay_date: # if it is the payment date, include this # dividend's actual payment (calculated on # ex_date)