Files
catalyst/zipline/data/_minute_bar_internal.pyx
T
Jean Bredeche 0fd35e7fd1 ENH: Make reader.get_value raise NoDataOnDate if the date is not in the calendar.
DataPortal now catches the NoDataOnDate exception and returns nan for
OHLC and 0 for V.

Price is still forward filled, unchanged.
2016-09-14 22:21:43 -04:00

151 lines
4.3 KiB
Cython

from numpy cimport ndarray, long_t
from numpy import searchsorted
from cpython cimport bool
cimport cython
cdef inline int int_min(int a, int b): return a if a <= b else b
@cython.cdivision(True)
def minute_value(ndarray[long_t, ndim=1] market_opens,
Py_ssize_t pos,
short minutes_per_day):
"""
Finds the value of the minute represented by `pos` in the given array of
market opens.
Parameters
----------
market_opens: numpy array of ints
Market opens, in minute epoch values.
pos: int
The index of the desired minute.
minutes_per_day: int
The number of minutes per day (e.g. 390 for NYSE).
Returns
-------
int: The minute epoch value of the desired minute.
"""
cdef short q, r
q = cython.cdiv(pos, minutes_per_day)
r = cython.cmod(pos, minutes_per_day)
return market_opens[q] + r
def find_position_of_minute(ndarray[long_t, ndim=1] market_opens,
ndarray[long_t, ndim=1] market_closes,
long_t minute_val,
short minutes_per_day,
bool forward_fill):
"""
Finds the position of a given minute in the given array of market opens.
If not a market minute, adjusts to the last market minute.
Parameters
----------
market_opens: numpy array of ints
Market opens, in minute epoch values.
market_closes: numpy array of ints
Market closes, in minute epoch values.
minute_val: int
The desired minute, as a minute epoch.
minutes_per_day: int
The number of minutes per day (e.g. 390 for NYSE).
forward_fill: bool
Whether to use the previous market minute if the given minute does
not fall within an open/close pair.
Returns
-------
int: The position of the given minute in the market opens array.
Raises
------
ValueError
If the given minute is not between a single open/close pair AND
forward_fill is False. For example, if minute_val is 17:00 Eastern
for a given day whose normal hours are 9:30 to 16:00, and we are not
forward filling, ValueError is raised.
"""
cdef Py_ssize_t market_open_loc, market_open, delta
market_open_loc = \
searchsorted(market_opens, minute_val, side='right') - 1
market_open = market_opens[market_open_loc]
market_close = market_closes[market_open_loc]
if not forward_fill and ((minute_val - market_open) >= minutes_per_day):
raise ValueError("Given minute is not between an open and a close")
delta = int_min(minute_val - market_open, market_close - market_open)
return (market_open_loc * minutes_per_day) + delta
def find_last_traded_position_internal(
ndarray[long_t, ndim=1] market_opens,
ndarray[long_t, ndim=1] market_closes,
long_t end_minute,
long_t start_minute,
volumes,
short minutes_per_day):
"""
Finds the position of the last traded minute for the given volumes array.
Parameters
----------
market_opens: numpy array of ints
Market opens, in minute epoch values.
market_closes: numpy array of ints
Market closes, in minute epoch values.
end_minute: int
The minute from which to start looking backwards, as a minute epoch.
start_minute: int
The asset's start date, as a minute epoch. Acts as the bottom limit of
how far we can look backwards.
volumes: bcolz carray
The volume history for the given asset.
minutes_per_day: int
The number of minutes per day (e.g. 390 for NYSE).
Returns
-------
int: The position of the last traded minute, starting from `minute_val`
"""
cdef Py_ssize_t minute_pos, current_minute
minute_pos = int_min(
find_position_of_minute(market_opens, market_closes, end_minute,
minutes_per_day, True),
len(volumes) - 1
)
while minute_pos >= 0:
current_minute = minute_value(
market_opens, minute_pos, minutes_per_day
)
if current_minute < start_minute:
return -1
if volumes[minute_pos] != 0:
return minute_pos
minute_pos -= 1
# we've gone to the beginning of this asset's range, and still haven't
# found a trade event
return -1