diff --git a/tests/test_perf_tracking.py b/tests/test_perf_tracking.py index 9eaf8afa..2a46bf83 100644 --- a/tests/test_perf_tracking.py +++ b/tests/test_perf_tracking.py @@ -182,6 +182,60 @@ class TestCommissionEvents(unittest.TestCase): self.assertEqual(results[-1]['daily_perf']['positions'] [0]['cost_basis'], 320.0) + def test_commission_zero_position(self): + """ + Ensure no div-by-zero errors. + """ + with trading.TradingEnvironment(): + events = factory.create_trade_history( + 1, + [10, 10, 10, 10, 10], + [100, 100, 100, 100, 100], + oneday, + self.sim_params + ) + + cash_adj_dt = self.sim_params.period_start \ + + datetime.timedelta(hours=3) + cash_adjustment = factory.create_commission(1, 300.0, + cash_adj_dt) + + # Insert a purchase order. + events.insert(0, create_txn(events[0], 20, 1)) + + # Sell that order. + events.insert(1, create_txn(events[1], 20, -1)) + + events.insert(2, cash_adjustment) + results = calculate_results(self, events) + # Validate that we lost 300 dollars from our cash pool. + self.assertEqual(results[-1]['cumulative_perf']['ending_cash'], + 9700) + + def test_commission_no_position(self): + """ + Ensure no position-not-found or sid-not-found errors. + """ + with trading.TradingEnvironment(): + events = factory.create_trade_history( + 1, + [10, 10, 10, 10, 10], + [100, 100, 100, 100, 100], + oneday, + self.sim_params + ) + + cash_adj_dt = self.sim_params.period_start \ + + datetime.timedelta(hours=3) + cash_adjustment = factory.create_commission(1, 300.0, + cash_adj_dt) + + events.insert(0, cash_adjustment) + results = calculate_results(self, events) + # Validate that we lost 300 dollars from our cash pool. + self.assertEqual(results[-1]['cumulative_perf']['ending_cash'], + 9700) + class TestDividendPerformance(unittest.TestCase): diff --git a/zipline/finance/performance.py b/zipline/finance/performance.py index defc8428..43b66035 100644 --- a/zipline/finance/performance.py +++ b/zipline/finance/performance.py @@ -596,6 +596,11 @@ class Position(object): if commission.cost == 0.0: return + # If we no longer hold this position, there is no cost basis to + # adjust. + if self.amount == 0: + return + prev_cost = self.cost_basis * self.amount new_cost = prev_cost + commission.cost self.cost_basis = new_cost / self.amount