From 53dae6320cde10e4283f81965f65ba5280553836 Mon Sep 17 00:00:00 2001 From: Eddie Hebert Date: Wed, 25 Nov 2015 10:16:19 -0500 Subject: [PATCH 1/2] BUG: Fix volume value returned by daily spot price Volumes were incorrectly having the thousands factor applied, however the volume is written as is (without the factor, since it volume is an int, not float value.) Fix by adding a special case for volume which returns the price as is. --- tests/data/test_us_equity_pricing.py | 5 +++++ zipline/data/us_equity_pricing.py | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/data/test_us_equity_pricing.py b/tests/data/test_us_equity_pricing.py index a6431875..35e329f4 100644 --- a/tests/data/test_us_equity_pricing.py +++ b/tests/data/test_us_equity_pricing.py @@ -291,6 +291,11 @@ class BcolzDailyBarTestCase(TestCase): 'close') self.assertEqual(235651.0, price) + # Ensure that volume does not have float adjustment applied. + volume = reader.spot_price(1, Timestamp('2015-06-02', tz='UTC'), + 'volume') + self.assertEqual(145631, volume) + def test_unadjusted_spot_price_no_data(self): table = self.writer.write(self.dest, self.trading_days, self.assets) reader = BcolzDailyBarReader(table) diff --git a/zipline/data/us_equity_pricing.py b/zipline/data/us_equity_pricing.py index 0d308c75..121e56d5 100644 --- a/zipline/data/us_equity_pricing.py +++ b/zipline/data/us_equity_pricing.py @@ -498,7 +498,11 @@ class BcolzDailyBarReader(object): raise NoDataOnDate( "No data on or after day={0} for sid={1}".format( day, sid)) - return self._spot_col(colname)[ix] * 0.001 + price = self._spot_col(colname)[ix] + if colname != 'volume': + return price * 0.001 + else: + return price class SQLiteAdjustmentWriter(object): From 5f81acea059ac5c6ede7fe04304d3f84f63d5a99 Mon Sep 17 00:00:00 2001 From: Eddie Hebert Date: Wed, 25 Nov 2015 10:16:30 -0500 Subject: [PATCH 2/2] ENH: Return -1 for missing spot prices. Return -1 when there is a zero value for a spot price. Intended for use by the incoming data portal changes. When the data portal will see a -1 value, the portal will seek back a trading day until a non-negative value is returned. --- tests/data/test_us_equity_pricing.py | 17 ++++++++++++ zipline/data/us_equity_pricing.py | 39 ++++++++++++++++++++++------ 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/tests/data/test_us_equity_pricing.py b/tests/data/test_us_equity_pricing.py index 35e329f4..e68cc2ea 100644 --- a/tests/data/test_us_equity_pricing.py +++ b/tests/data/test_us_equity_pricing.py @@ -306,3 +306,20 @@ class BcolzDailyBarTestCase(TestCase): # after with self.assertRaises(NoDataOnDate): reader.spot_price(4, Timestamp('2015-06-16', tz='UTC'), 'close') + + def test_unadjusted_spot_price_empty_value(self): + table = self.writer.write(self.dest, self.trading_days, self.assets) + reader = BcolzDailyBarReader(table) + + # A sid, day and corresponding index into which to overwrite a zero. + zero_sid = 1 + zero_day = Timestamp('2015-06-02', tz='UTC') + zero_ix = reader.sid_day_index(zero_sid, zero_day) + + # Write a zero into the synthetic pricing data at the day and sid, + # so that a read should now return -1. + # This a little hacky, in lieu of changing the synthetic data set. + reader._spot_col('close')[zero_ix] = 0 + + close = reader.spot_price(zero_sid, zero_day, 'close') + self.assertEqual(-1, close) diff --git a/zipline/data/us_equity_pricing.py b/zipline/data/us_equity_pricing.py index 121e56d5..236dae5b 100644 --- a/zipline/data/us_equity_pricing.py +++ b/zipline/data/us_equity_pricing.py @@ -469,23 +469,21 @@ class BcolzDailyBarReader(object): col = self._spot_cols[colname] = self._table[colname][:] return col - def spot_price(self, sid, day, colname): + def sid_day_index(self, sid, day): """ Parameters ---------- sid : int The asset identifier. - day : datetime64 + day : datetime64-like Midnight of the day for which data is requested. - colname : string - The price field. e.g. ('open', 'high', 'low', 'close', 'volume') Returns ------- - float - The spot price for colname of the given sid on the given day. - Raises a NoDataOnDate exception if there is no data available - for the given day and sid. + int + Index into the data tape for the given sid and day. + Raises a NoDataOnDate exception if the given day and sid is before + or after the date range of the equity. """ day_loc = self._calendar.get_loc(day) offset = day_loc - self._calendar_offsets[sid] @@ -498,7 +496,32 @@ class BcolzDailyBarReader(object): raise NoDataOnDate( "No data on or after day={0} for sid={1}".format( day, sid)) + return ix + + def spot_price(self, sid, day, colname): + """ + Parameters + ---------- + sid : int + The asset identifier. + day : datetime64-like + Midnight of the day for which data is requested. + colname : string + The price field. e.g. ('open', 'high', 'low', 'close', 'volume') + + Returns + ------- + float + The spot price for colname of the given sid on the given day. + Raises a NoDataOnDate exception if the given day and sid is before + or after the date range of the equity. + Returns -1 if the day is within the date range, but the price is + 0. + """ + ix = self.sid_day_index(sid, day) price = self._spot_col(colname)[ix] + if price == 0: + return -1 if colname != 'volume': return price * 0.001 else: