diff --git a/docs/release-notes/zipline-0.7.1.md b/docs/release-notes/zipline-0.7.1.md index f38d2271..364b27c3 100644 --- a/docs/release-notes/zipline-0.7.1.md +++ b/docs/release-notes/zipline-0.7.1.md @@ -49,3 +49,16 @@ > # The standard deviation of the price in the last 3 days. > data[security].stdev(3) > ``` + +* New fields in Performance Period +[PR464](https://github.com/quantopian/zipline/pull/464) + + > Performance Period has new fields accessible in return value of to_dict: + + > - gross leverage + > - net leverage + > - short exposure + > - long exposure + > - shorts count + > - longs count + diff --git a/tests/test_perf_tracking.py b/tests/test_perf_tracking.py index f4828d3b..186e3c47 100644 --- a/tests/test_perf_tracking.py +++ b/tests/test_perf_tracking.py @@ -53,6 +53,29 @@ oneday = timedelta(days=1) tradingday = timedelta(hours=6, minutes=30) +def check_perf_period(pp, + gross_leverage, + net_leverage, + long_exposure, + longs_count, + short_exposure, + shorts_count): + + perf_data = pp.to_dict() + np.testing.assert_allclose( + gross_leverage, perf_data['gross_leverage'], rtol=1e-3) + np.testing.assert_allclose( + net_leverage, perf_data['net_leverage'], rtol=1e-3) + np.testing.assert_allclose( + long_exposure, perf_data['long_exposure'], rtol=1e-3) + np.testing.assert_allclose( + longs_count, perf_data['longs_count'], rtol=1e-3) + np.testing.assert_allclose( + short_exposure, perf_data['short_exposure'], rtol=1e-3) + np.testing.assert_allclose( + shorts_count, perf_data['shorts_count'], rtol=1e-3) + + def check_account(account, settled_cash, equity_with_loan, @@ -865,6 +888,14 @@ class TestPositionPerformance(unittest.TestCase): pp.calculate_performance() + check_perf_period( + pp, + gross_leverage=2.0, + net_leverage=0.0, + long_exposure=1000.0, + longs_count=1, + short_exposure=-1000.0, + shorts_count=1) # Validate that the account attributes were updated. account = pp.as_account() check_account(account, @@ -889,6 +920,15 @@ class TestPositionPerformance(unittest.TestCase): # Validate that the account attributes were updated. account = pp.as_account() + check_perf_period( + pp, + gross_leverage=2.5, + net_leverage=-0.25, + long_exposure=900.0, + longs_count=1, + short_exposure=-1100.0, + shorts_count=1) + check_account(account, settled_cash=1000.0, equity_with_loan=800.0, @@ -925,6 +965,15 @@ class TestPositionPerformance(unittest.TestCase): pp.calculate_performance() + check_perf_period( + pp, + gross_leverage=10.0, + net_leverage=10.0, + long_exposure=10000.0, + longs_count=1, + short_exposure=0.0, + shorts_count=0) + # Validate that the account attributes were updated. account = pp.as_account() check_account(account, @@ -944,6 +993,15 @@ class TestPositionPerformance(unittest.TestCase): pp.calculate_performance() + check_perf_period( + pp, + gross_leverage=5.5, + net_leverage=5.5, + long_exposure=11000.0, + longs_count=1, + short_exposure=0.0, + shorts_count=0) + # Validate that the account attributes were updated. account = pp.as_account() @@ -1039,6 +1097,15 @@ class TestPositionPerformance(unittest.TestCase): self.assertEqual(pp.pnl, 100, "gain of 1 on 100 shares should be 100") + check_perf_period( + pp, + gross_leverage=1.0, + net_leverage=1.0, + long_exposure=1100.0, + longs_count=1, + short_exposure=0.0, + shorts_count=0) + # Validate that the account attributes were updated. account = pp.as_account() check_account(account, @@ -1242,6 +1309,15 @@ cost of sole txn in test" "drop of 1 on -100 shares should be 100" ) + check_perf_period( + pp, + gross_leverage=0.8181, + net_leverage=-0.8181, + long_exposure=0.0, + longs_count=0, + short_exposure=-900.0, + shorts_count=1) + # Validate that the account attributes. account = ppTotal.as_account() check_account(account, @@ -1337,6 +1413,15 @@ shares in position" "gain of 1 on 100 shares should be 300" ) + check_perf_period( + pp, + gross_leverage=0.0, + net_leverage=0.0, + long_exposure=0.0, + longs_count=0, + short_exposure=0.0, + shorts_count=0) + account = pp.as_account() check_account(account, settled_cash=1300.0, diff --git a/zipline/finance/performance/period.py b/zipline/finance/performance/period.py index 3d4fa127..b14aeef5 100644 --- a/zipline/finance/performance/period.py +++ b/zipline/finance/performance/period.py @@ -320,25 +320,35 @@ class PerformancePeriod(object): def calculate_positions_value(self): return np.dot(self._position_amounts, self._position_last_sale_prices) - def _long_value(self): + def _longs_count(self): + longs = self._position_amounts[self._position_amounts > 0] + return longs.count() + + def _long_exposure(self): pos_values = self._position_amounts * self._position_last_sale_prices longs = pos_values[pos_values > 0] return longs.sum() - def _short_value(self): + def _shorts_count(self): + shorts = self._position_amounts[self._position_amounts < 0] + return shorts.count() + + def _short_exposure(self): pos_values = self._position_amounts * self._position_last_sale_prices shorts = pos_values[pos_values < 0] return shorts.sum() def _gross_exposure(self): - return self._long_value() + abs(self._short_value()) + return self._long_exposure() + abs(self._short_exposure()) def _net_exposure(self): return self.calculate_positions_value() @property def _net_liquidation_value(self): - return self.ending_cash + self._long_value() + self._short_value() + return self.ending_cash + \ + self._long_exposure() + \ + self._short_exposure() def _gross_leverage(self): net_liq = self._net_liquidation_value @@ -380,7 +390,12 @@ class PerformancePeriod(object): 'returns': self.returns, 'period_open': self.period_open, 'period_close': self.period_close, - 'gross_leverage': self._gross_leverage() + 'gross_leverage': self._gross_leverage(), + 'net_leverage': self._net_leverage(), + 'short_exposure': self._short_exposure(), + 'long_exposure': self._long_exposure(), + 'longs_count': self._longs_count(), + 'shorts_count': self._shorts_count() } return rval