PageRenderTime 72ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/pipeline/test_dividends.py

https://gitlab.com/lbennett/zipline
Python | 407 lines | 342 code | 43 blank | 22 comment | 6 complexity | 4835f7282399e0988479f461341966e6 MD5 | raw file
  1. """
  2. Tests for the reference loader for Dividends datasets.
  3. """
  4. import blaze as bz
  5. from blaze.compute.core import swap_resources_into_scope
  6. import pandas as pd
  7. from six import iteritems
  8. from zipline.pipeline.common import (
  9. ANNOUNCEMENT_FIELD_NAME,
  10. DAYS_SINCE_PREV_DIVIDEND_ANNOUNCEMENT,
  11. DAYS_SINCE_PREV_EX_DATE,
  12. DAYS_TO_NEXT_EX_DATE,
  13. NEXT_AMOUNT,
  14. NEXT_EX_DATE,
  15. NEXT_PAY_DATE,
  16. PREVIOUS_ANNOUNCEMENT,
  17. PREVIOUS_EX_DATE,
  18. PREVIOUS_PAY_DATE,
  19. PREVIOUS_AMOUNT,
  20. SID_FIELD_NAME,
  21. TS_FIELD_NAME,
  22. CASH_AMOUNT_FIELD_NAME,
  23. EX_DATE_FIELD_NAME,
  24. PAY_DATE_FIELD_NAME
  25. )
  26. from zipline.pipeline.data.dividends import (
  27. DividendsByAnnouncementDate,
  28. DividendsByExDate,
  29. DividendsByPayDate
  30. )
  31. from zipline.pipeline.factors.events import (
  32. BusinessDaysSinceDividendAnnouncement,
  33. BusinessDaysSincePreviousExDate,
  34. BusinessDaysUntilNextExDate
  35. )
  36. from zipline.pipeline.loaders.blaze.dividends import (
  37. BlazeDividendsByAnnouncementDateLoader,
  38. BlazeDividendsByPayDateLoader,
  39. BlazeDividendsByExDateLoader
  40. )
  41. from zipline.pipeline.loaders.dividends import (
  42. DividendsByAnnouncementDateLoader,
  43. DividendsByExDateLoader,
  44. DividendsByPayDateLoader
  45. )
  46. from zipline.pipeline.loaders.utils import (
  47. zip_with_dates,
  48. zip_with_floats
  49. )
  50. from zipline.testing.fixtures import (
  51. WithPipelineEventDataLoader,
  52. ZiplineTestCase
  53. )
  54. dividends_cases = [
  55. # K1--K2--A1--A2.
  56. pd.DataFrame({
  57. CASH_AMOUNT_FIELD_NAME: [1, 15],
  58. EX_DATE_FIELD_NAME: pd.to_datetime(['2014-01-15', '2014-01-20']),
  59. PAY_DATE_FIELD_NAME: pd.to_datetime(['2014-01-15', '2014-01-20']),
  60. TS_FIELD_NAME: pd.to_datetime(['2014-01-05', '2014-01-10']),
  61. ANNOUNCEMENT_FIELD_NAME: pd.to_datetime(['2014-01-04', '2014-01-09'])
  62. }),
  63. # K1--K2--A2--A1.
  64. pd.DataFrame({
  65. CASH_AMOUNT_FIELD_NAME: [7, 13],
  66. EX_DATE_FIELD_NAME: pd.to_datetime(['2014-01-20', '2014-01-15']),
  67. PAY_DATE_FIELD_NAME: pd.to_datetime(['2014-01-20', '2014-01-15']),
  68. TS_FIELD_NAME: pd.to_datetime(['2014-01-05', '2014-01-10']),
  69. ANNOUNCEMENT_FIELD_NAME: pd.to_datetime(['2014-01-04', '2014-01-09'])
  70. }),
  71. # K1--A1--K2--A2.
  72. pd.DataFrame({
  73. CASH_AMOUNT_FIELD_NAME: [3, 1],
  74. EX_DATE_FIELD_NAME: pd.to_datetime(['2014-01-10', '2014-01-20']),
  75. PAY_DATE_FIELD_NAME: pd.to_datetime(['2014-01-10', '2014-01-20']),
  76. TS_FIELD_NAME: pd.to_datetime(['2014-01-05', '2014-01-15']),
  77. ANNOUNCEMENT_FIELD_NAME: pd.to_datetime(['2014-01-04', '2014-01-14'])
  78. }),
  79. # K1 == K2.
  80. pd.DataFrame({
  81. CASH_AMOUNT_FIELD_NAME: [6, 23],
  82. EX_DATE_FIELD_NAME: pd.to_datetime(['2014-01-10', '2014-01-15']),
  83. PAY_DATE_FIELD_NAME: pd.to_datetime(['2014-01-10', '2014-01-15']),
  84. TS_FIELD_NAME: pd.to_datetime(['2014-01-05'] * 2),
  85. ANNOUNCEMENT_FIELD_NAME: pd.to_datetime(['2014-01-04', '2014-01-04'])
  86. }),
  87. pd.DataFrame(
  88. columns=[CASH_AMOUNT_FIELD_NAME,
  89. EX_DATE_FIELD_NAME,
  90. PAY_DATE_FIELD_NAME,
  91. TS_FIELD_NAME,
  92. ANNOUNCEMENT_FIELD_NAME],
  93. dtype='datetime64[ns]'
  94. ),
  95. ]
  96. prev_date_intervals = [
  97. [
  98. ['2014-01-01', '2014-01-14'], ['2014-01-15', '2014-01-19'],
  99. ['2014-01-20', '2014-01-31']
  100. ],
  101. [
  102. ['2014-01-01', '2014-01-14'], ['2014-01-15', '2014-01-19'],
  103. ['2014-01-20', '2014-01-31']
  104. ],
  105. [
  106. ['2014-01-01', '2014-01-09'], ['2014-01-10', '2014-01-19'],
  107. ['2014-01-20', '2014-01-31']
  108. ],
  109. [
  110. ['2014-01-01', '2014-01-09'], ['2014-01-10', '2014-01-14'],
  111. ['2014-01-15', '2014-01-31']
  112. ]
  113. ]
  114. next_date_intervals = [
  115. [
  116. ['2014-01-01', '2014-01-04'], ['2014-01-05', '2014-01-15'],
  117. ['2014-01-16', '2014-01-20'], ['2014-01-21', '2014-01-31']
  118. ],
  119. [
  120. ['2014-01-01', '2014-01-04'], ['2014-01-05', '2014-01-09'],
  121. ['2014-01-10', '2014-01-15'], ['2014-01-16', '2014-01-20'],
  122. ['2014-01-21', '2014-01-31']
  123. ],
  124. [
  125. ['2014-01-01', '2014-01-04'], ['2014-01-05', '2014-01-10'],
  126. ['2014-01-11', '2014-01-14'], ['2014-01-15', '2014-01-20'],
  127. ['2014-01-21', '2014-01-31']
  128. ],
  129. [
  130. ['2014-01-01', '2014-01-04'], ['2014-01-05', '2014-01-10'],
  131. ['2014-01-11', '2014-01-15'], ['2014-01-16', '2014-01-31']
  132. ]
  133. ]
  134. next_ex_and_pay_dates = [['NaT', '2014-01-15', '2014-01-20', 'NaT'],
  135. ['NaT', '2014-01-20', '2014-01-15', '2014-01-20',
  136. 'NaT'],
  137. ['NaT', '2014-01-10', 'NaT', '2014-01-20', 'NaT'],
  138. ['NaT', '2014-01-10', '2014-01-15', 'NaT']]
  139. prev_ex_and_pay_dates = [['NaT', '2014-01-15', '2014-01-20'],
  140. ['NaT', '2014-01-15', '2014-01-20'],
  141. ['NaT', '2014-01-10', '2014-01-20'],
  142. ['NaT', '2014-01-10', '2014-01-15']]
  143. prev_amounts = [['NaN', 1, 15],
  144. ['NaN', 13, 7],
  145. ['NaN', 3, 1],
  146. ['NaN', 6, 23]]
  147. next_amounts = [['NaN', 1, 15, 'NaN'],
  148. ['NaN', 7, 13, 7, 'NaN'],
  149. ['NaN', 3, 'NaN', 1, 'NaN'],
  150. ['NaN', 6, 23, 'NaN']]
  151. class DividendsByAnnouncementDateTestCase(WithPipelineEventDataLoader,
  152. ZiplineTestCase):
  153. """
  154. Tests for loading the dividends by announcement date data.
  155. """
  156. pipeline_columns = {
  157. PREVIOUS_ANNOUNCEMENT:
  158. DividendsByAnnouncementDate.previous_announcement_date.latest,
  159. PREVIOUS_AMOUNT: DividendsByAnnouncementDate.previous_amount.latest,
  160. DAYS_SINCE_PREV_DIVIDEND_ANNOUNCEMENT:
  161. BusinessDaysSinceDividendAnnouncement(),
  162. }
  163. @classmethod
  164. def get_dataset(cls):
  165. return {sid:
  166. frame.drop([EX_DATE_FIELD_NAME,
  167. PAY_DATE_FIELD_NAME], axis=1)
  168. for sid, frame
  169. in enumerate(dividends_cases)}
  170. loader_type = DividendsByAnnouncementDateLoader
  171. def setup(self, dates):
  172. date_intervals = [
  173. [
  174. ['2014-01-01', '2014-01-04'], ['2014-01-05', '2014-01-09'],
  175. ['2014-01-10', '2014-01-31']
  176. ],
  177. [
  178. ['2014-01-01', '2014-01-04'], ['2014-01-05', '2014-01-09'],
  179. ['2014-01-10', '2014-01-31']
  180. ],
  181. [
  182. ['2014-01-01', '2014-01-04'], ['2014-01-05', '2014-01-14'],
  183. ['2014-01-15', '2014-01-31']
  184. ],
  185. [
  186. ['2014-01-01', '2014-01-04'], ['2014-01-05', '2014-01-31']
  187. ]
  188. ]
  189. announcement_dates = [['NaT', '2014-01-04', '2014-01-09'],
  190. ['NaT', '2014-01-04', '2014-01-09'],
  191. ['NaT', '2014-01-04', '2014-01-14'],
  192. ['NaT', '2014-01-04']]
  193. amounts = [['NaN', 1, 15], ['NaN', 7, 13], ['NaN', 3, 1], ['NaN', 23]]
  194. cols = {
  195. PREVIOUS_ANNOUNCEMENT: self.get_sids_to_frames(
  196. zip_with_dates, announcement_dates, date_intervals, dates
  197. ),
  198. PREVIOUS_AMOUNT: self.get_sids_to_frames(
  199. zip_with_floats, amounts, date_intervals, dates
  200. ),
  201. }
  202. cols[
  203. DAYS_SINCE_PREV_DIVIDEND_ANNOUNCEMENT
  204. ] = self._compute_busday_offsets(cols[PREVIOUS_ANNOUNCEMENT])
  205. return cols
  206. class BlazeDividendsByAnnouncementDateTestCase(
  207. DividendsByAnnouncementDateTestCase
  208. ):
  209. loader_type = BlazeDividendsByAnnouncementDateLoader
  210. def pipeline_event_loader_args(self, dates):
  211. _, mapping = super(
  212. BlazeDividendsByAnnouncementDateTestCase,
  213. self,
  214. ).pipeline_event_loader_args(dates)
  215. return (bz.Data(pd.concat(
  216. pd.DataFrame({
  217. ANNOUNCEMENT_FIELD_NAME: df[ANNOUNCEMENT_FIELD_NAME],
  218. TS_FIELD_NAME: df[TS_FIELD_NAME],
  219. SID_FIELD_NAME: sid,
  220. CASH_AMOUNT_FIELD_NAME: df[CASH_AMOUNT_FIELD_NAME]
  221. })
  222. for sid, df in iteritems(mapping)
  223. ).reset_index(drop=True)),)
  224. class BlazeDividendsByAnnouncementDateNotInteractiveTestCase(
  225. BlazeDividendsByAnnouncementDateTestCase):
  226. """Test case for passing a non-interactive symbol and a dict of resources.
  227. """
  228. def pipeline_event_loader_args(self, dates):
  229. (bound_expr,) = super(
  230. BlazeDividendsByAnnouncementDateNotInteractiveTestCase,
  231. self,
  232. ).pipeline_event_loader_args(dates)
  233. return swap_resources_into_scope(bound_expr, {})
  234. class DividendsByExDateTestCase(WithPipelineEventDataLoader, ZiplineTestCase):
  235. """
  236. Tests for loading the dividends by ex date data.
  237. """
  238. pipeline_columns = {
  239. NEXT_EX_DATE: DividendsByExDate.next_date.latest,
  240. PREVIOUS_EX_DATE: DividendsByExDate.previous_date.latest,
  241. NEXT_AMOUNT: DividendsByExDate.next_amount.latest,
  242. PREVIOUS_AMOUNT: DividendsByExDate.previous_amount.latest,
  243. DAYS_TO_NEXT_EX_DATE: BusinessDaysUntilNextExDate(),
  244. DAYS_SINCE_PREV_EX_DATE: BusinessDaysSincePreviousExDate()
  245. }
  246. @classmethod
  247. def get_dataset(cls):
  248. return {sid:
  249. frame.drop([ANNOUNCEMENT_FIELD_NAME,
  250. PAY_DATE_FIELD_NAME], axis=1)
  251. for sid, frame
  252. in enumerate(dividends_cases)}
  253. loader_type = DividendsByExDateLoader
  254. def setup(self, dates):
  255. cols = {
  256. NEXT_EX_DATE: self.get_sids_to_frames(
  257. zip_with_dates, next_ex_and_pay_dates, next_date_intervals,
  258. dates,
  259. ),
  260. PREVIOUS_EX_DATE: self.get_sids_to_frames(
  261. zip_with_dates, prev_ex_and_pay_dates, prev_date_intervals,
  262. dates
  263. ),
  264. NEXT_AMOUNT: self.get_sids_to_frames(
  265. zip_with_floats, next_amounts, next_date_intervals, dates
  266. ),
  267. PREVIOUS_AMOUNT: self.get_sids_to_frames(
  268. zip_with_floats, prev_amounts, prev_date_intervals, dates
  269. )
  270. }
  271. cols[DAYS_TO_NEXT_EX_DATE] = self._compute_busday_offsets(
  272. cols[NEXT_EX_DATE]
  273. )
  274. cols[DAYS_SINCE_PREV_EX_DATE] = self._compute_busday_offsets(
  275. cols[PREVIOUS_EX_DATE]
  276. )
  277. return cols
  278. class BlazeDividendsByExDateLoaderTestCase(DividendsByExDateTestCase):
  279. loader_type = BlazeDividendsByExDateLoader
  280. def pipeline_event_loader_args(self, dates):
  281. _, mapping = super(
  282. BlazeDividendsByExDateLoaderTestCase,
  283. self,
  284. ).pipeline_event_loader_args(dates)
  285. return (bz.Data(pd.concat(
  286. pd.DataFrame({
  287. EX_DATE_FIELD_NAME: df[EX_DATE_FIELD_NAME],
  288. TS_FIELD_NAME: df[TS_FIELD_NAME],
  289. SID_FIELD_NAME: sid,
  290. CASH_AMOUNT_FIELD_NAME: df[CASH_AMOUNT_FIELD_NAME]
  291. })
  292. for sid, df in iteritems(mapping)
  293. ).reset_index(drop=True)),)
  294. class BlazeDividendsByExDateLoaderNotInteractiveTestCase(
  295. BlazeDividendsByExDateLoaderTestCase):
  296. """Test case for passing a non-interactive symbol and a dict of resources.
  297. """
  298. def pipeline_event_loader_args(self, dates):
  299. (bound_expr,) = super(
  300. BlazeDividendsByExDateLoaderNotInteractiveTestCase,
  301. self,
  302. ).pipeline_event_loader_args(dates)
  303. return swap_resources_into_scope(bound_expr, {})
  304. class DividendsByPayDateTestCase(WithPipelineEventDataLoader, ZiplineTestCase):
  305. """
  306. Tests for loading the dividends by pay date data.
  307. """
  308. pipeline_columns = {
  309. NEXT_PAY_DATE: DividendsByPayDate.next_date.latest,
  310. PREVIOUS_PAY_DATE: DividendsByPayDate.previous_date.latest,
  311. NEXT_AMOUNT: DividendsByPayDate.next_amount.latest,
  312. PREVIOUS_AMOUNT: DividendsByPayDate.previous_amount.latest,
  313. }
  314. @classmethod
  315. def get_dataset(cls):
  316. return {sid:
  317. frame.drop([ANNOUNCEMENT_FIELD_NAME,
  318. EX_DATE_FIELD_NAME], axis=1)
  319. for sid, frame
  320. in enumerate(dividends_cases)}
  321. loader_type = DividendsByPayDateLoader
  322. def setup(self, dates):
  323. return {
  324. NEXT_PAY_DATE: self.get_sids_to_frames(
  325. zip_with_dates, next_ex_and_pay_dates, next_date_intervals,
  326. dates
  327. ),
  328. PREVIOUS_PAY_DATE: self.get_sids_to_frames(
  329. zip_with_dates, prev_ex_and_pay_dates, prev_date_intervals,
  330. dates
  331. ),
  332. NEXT_AMOUNT: self.get_sids_to_frames(
  333. zip_with_floats, next_amounts, next_date_intervals, dates
  334. ),
  335. PREVIOUS_AMOUNT: self.get_sids_to_frames(
  336. zip_with_floats, prev_amounts, prev_date_intervals, dates
  337. )
  338. }
  339. class BlazeDividendsByPayDateLoaderTestCase(DividendsByPayDateTestCase):
  340. loader_type = BlazeDividendsByPayDateLoader
  341. def pipeline_event_loader_args(self, dates):
  342. _, mapping = super(
  343. BlazeDividendsByPayDateLoaderTestCase,
  344. self,
  345. ).pipeline_event_loader_args(dates)
  346. return (bz.Data(pd.concat(
  347. pd.DataFrame({
  348. PAY_DATE_FIELD_NAME: df[PAY_DATE_FIELD_NAME],
  349. TS_FIELD_NAME: df[TS_FIELD_NAME],
  350. SID_FIELD_NAME: sid,
  351. CASH_AMOUNT_FIELD_NAME: df[CASH_AMOUNT_FIELD_NAME]
  352. })
  353. for sid, df in iteritems(mapping)
  354. ).reset_index(drop=True)),)
  355. class BlazeDividendsByPayDateLoaderNotInteractiveTestCase(
  356. BlazeDividendsByPayDateLoaderTestCase):
  357. """Test case for passing a non-interactive symbol and a dict of resources.
  358. """
  359. def pipeline_event_loader_args(self, dates):
  360. (bound_expr,) = super(
  361. BlazeDividendsByPayDateLoaderNotInteractiveTestCase,
  362. self,
  363. ).pipeline_event_loader_args(dates)
  364. return swap_resources_into_scope(bound_expr, {})