mirror of
https://github.com/wassname/catalyst.git
synced 2026-08-11 11:16:15 +08:00
ENH: Add adjusted history for continuous futures.
Add `.adj('mul')` and `.adj('add')` methods on ContinuousFuture, which
when used with `history`, will calculate and apply adjustments so that
the values are adjusted to account for discounts and premiums during
rolls.
Example usage in an algo:
```
from zipline.api import continuous_future
def initialize(context):
context.cl_add = continuous_future('CL', offset=0, roll='calendar').adj('add')
context.cl_mul = continuous_future('CL', offset=0, roll='calendar').adj('mul')
context.cl = continuous_future('CL', offset=0, roll='calendar')
schedule_function(print_history)
def print_history(context, data):
frame = data.history([context.cl, context.cl_add, context.cl_mul],
['price', 'sid'],
20,
'1d')
print 'unadjusted'
print frame.loc[:, :, context.cl]
print 'adjusted add'
print frame.loc[:, :, context.cl_add]
print 'adjusted mul'
print frame.loc[:, :, context.cl_mul]
```
This commit is contained in:
@@ -483,6 +483,132 @@ def record_current_contract(algo, data):
|
||||
135441.440,
|
||||
err_msg="On session after roll, Should be FOJ16's 44th value.")
|
||||
|
||||
def test_history_close_session_adjusted(self):
|
||||
cf = self.data_portal.asset_finder.create_continuous_future(
|
||||
'FO', 0, 'calendar')
|
||||
cf_mul = self.data_portal.asset_finder.create_continuous_future(
|
||||
'FO', 0, 'calendar').adj('mul')
|
||||
cf_add = self.data_portal.asset_finder.create_continuous_future(
|
||||
'FO', 0, 'calendar').adj('add')
|
||||
window = self.data_portal.get_history_window(
|
||||
[cf, cf_mul, cf_add],
|
||||
Timestamp('2016-03-06', tz='UTC'), 30, '1d', 'close')
|
||||
|
||||
# Unadjusted value is: 115011.44
|
||||
# Adjustment is based on hop from 115231.44 to 122240.001
|
||||
# a ratio of ~1.06
|
||||
assert_almost_equal(
|
||||
window.loc['2016-01-26', cf_mul],
|
||||
122006.62,
|
||||
err_msg="At beginning of window, should be FOG16's first value, "
|
||||
"adjusted.")
|
||||
|
||||
# Difference of 7008.561
|
||||
assert_almost_equal(
|
||||
window.loc['2016-01-26', cf_add],
|
||||
122020.001,
|
||||
err_msg="At beginning of window, should be FOG16's first value, "
|
||||
"adjusted.")
|
||||
|
||||
assert_almost_equal(
|
||||
window.loc['2016-02-26', cf_mul],
|
||||
125241.440,
|
||||
err_msg="On session with roll, should be FOH16's 24th value, "
|
||||
"unadjusted.")
|
||||
|
||||
assert_almost_equal(
|
||||
window.loc['2016-02-26', cf_add],
|
||||
125241.440,
|
||||
err_msg="On session with roll, should be FOH16's 24th value, "
|
||||
"unadjusted.")
|
||||
|
||||
assert_almost_equal(
|
||||
window.loc['2016-02-29', cf_mul],
|
||||
125251.440,
|
||||
err_msg="After roll, Should be FOH16's 25th value, unadjusted.")
|
||||
|
||||
assert_almost_equal(
|
||||
window.loc['2016-02-29', cf_add],
|
||||
125251.440,
|
||||
err_msg="After roll, Should be FOH16's 25th value, unadjusted.")
|
||||
|
||||
# Advance the window a month.
|
||||
window = self.data_portal.get_history_window(
|
||||
[cf, cf_mul, cf_add],
|
||||
Timestamp('2016-04-06', tz='UTC'), 30, '1d', 'close')
|
||||
|
||||
# Unadjusted value: 115221.44
|
||||
# Adjustments based on hops:
|
||||
# 2016-02-25 00:00:00+00:00 115231.440
|
||||
# 2016-02-26 00:00:00+00:00 122240.001
|
||||
# ratio: ~1.061
|
||||
# difference: 7008.561
|
||||
# and
|
||||
# 2016-03-23 00:00:00+00:00 125421.440
|
||||
# 2016-03-24 00:00:00+00:00 132430.001
|
||||
# ratio: ~1.056
|
||||
# difference: 7008.56
|
||||
assert_almost_equal(
|
||||
window.loc['2016-02-24', cf_mul],
|
||||
129059.581,
|
||||
err_msg="At beginning of window, should be FOG16's 22nd value, "
|
||||
"with two adjustments.")
|
||||
|
||||
assert_almost_equal(
|
||||
window.loc['2016-02-24', cf_add],
|
||||
129238.561,
|
||||
err_msg="At beginning of window, should be FOG16's 22nd value, "
|
||||
"with two adjustments")
|
||||
|
||||
# Unadjusted: 125241.44
|
||||
assert_almost_equal(
|
||||
window.loc['2016-02-26', cf_mul],
|
||||
132239.942,
|
||||
err_msg="On session with roll, should be FOH16's 24th value, "
|
||||
"with one adjustment.")
|
||||
|
||||
assert_almost_equal(
|
||||
window.loc['2016-02-26', cf_add],
|
||||
132250.0,
|
||||
err_msg="On session with roll, should be FOH16's 24th value, "
|
||||
"with one adjustment.")
|
||||
|
||||
# Unadjusted: 125251.44
|
||||
assert_almost_equal(
|
||||
window.loc['2016-02-29', cf_mul],
|
||||
132250.500,
|
||||
err_msg="On session after roll, should be FOH16's 25th value, "
|
||||
"with one adjustment.")
|
||||
|
||||
assert_almost_equal(
|
||||
window.loc['2016-02-29', cf_add],
|
||||
132260.000,
|
||||
err_msg="On session after roll, should be FOH16's 25th value, "
|
||||
"unadjusted.")
|
||||
|
||||
# Unadjusted: 135431.44
|
||||
assert_almost_equal(
|
||||
window.loc['2016-03-24', cf_mul],
|
||||
135431.44,
|
||||
err_msg="On session with roll, should be FOJ16's 43rd value, "
|
||||
"unadjusted.")
|
||||
|
||||
assert_almost_equal(
|
||||
window.loc['2016-03-24', cf_add],
|
||||
135431.44,
|
||||
err_msg="On session with roll, should be FOJ16's 43rd value.")
|
||||
|
||||
# Unadjusted: 135441.44
|
||||
assert_almost_equal(
|
||||
window.loc['2016-03-28', cf_mul],
|
||||
135441.44,
|
||||
err_msg="On session after roll, Should be FOJ16's 44th value.")
|
||||
|
||||
assert_almost_equal(
|
||||
window.loc['2016-03-28', cf_add],
|
||||
135441.44,
|
||||
err_msg="On session after roll, Should be FOJ16's 44th value.")
|
||||
|
||||
def test_history_close_minute(self):
|
||||
cf = self.data_portal.asset_finder.create_continuous_future(
|
||||
'FO', 0, 'calendar')
|
||||
@@ -518,6 +644,69 @@ def record_current_contract(algo, data):
|
||||
125250.001,
|
||||
"Should remain FOH16 on next session.")
|
||||
|
||||
def test_history_close_minute_adjusted(self):
|
||||
cf = self.data_portal.asset_finder.create_continuous_future(
|
||||
'FO', 0, 'calendar')
|
||||
cf_mul = self.data_portal.asset_finder.create_continuous_future(
|
||||
'FO', 0, 'calendar').adj('mul')
|
||||
cf_add = self.data_portal.asset_finder.create_continuous_future(
|
||||
'FO', 0, 'calendar').adj('add')
|
||||
window = self.data_portal.get_history_window(
|
||||
[cf, cf_mul, cf_add],
|
||||
Timestamp('2016-02-25 18:01', tz='US/Eastern').tz_convert('UTC'),
|
||||
30, '1m', 'close')
|
||||
|
||||
# Unadjusted: 115231.412
|
||||
# Adjustment based on roll:
|
||||
# 2016-02-25 23:00:00+00:00 115231.440
|
||||
# 2016-02-25 23:01:00+00:00 122240.001
|
||||
# Ratio: ~1.061
|
||||
# Difference: 7008.561
|
||||
self.assertEqual(window.loc['2016-02-25 22:32', cf_mul],
|
||||
122239.971,
|
||||
"Should be FOG16 at beginning of window. A minute "
|
||||
"which is in the 02-25 session, before the roll.")
|
||||
|
||||
self.assertEqual(window.loc['2016-02-25 22:32', cf_add],
|
||||
122239.973,
|
||||
"Should be FOG16 at beginning of window. A minute "
|
||||
"which is in the 02-25 session, before the roll.")
|
||||
|
||||
# Unadjusted: 115231.44
|
||||
# Should use same ratios as above.
|
||||
self.assertEqual(window.loc['2016-02-25 23:00', cf_mul],
|
||||
122240.001,
|
||||
"Should be FOG16 on on minute before roll minute, "
|
||||
"adjusted.")
|
||||
|
||||
self.assertEqual(window.loc['2016-02-25 23:00', cf_add],
|
||||
122240.001,
|
||||
"Should be FOG16 on on minute before roll minute, "
|
||||
"adjusted.")
|
||||
|
||||
self.assertEqual(window.loc['2016-02-25 23:01', cf_mul],
|
||||
125240.001,
|
||||
"Should be FOH16 on minute after roll, unadjusted.")
|
||||
|
||||
self.assertEqual(window.loc['2016-02-25 23:01', cf_add],
|
||||
125240.001,
|
||||
"Should be FOH16 on minute after roll, unadjusted.")
|
||||
|
||||
# Advance the window a session.
|
||||
window = self.data_portal.get_history_window(
|
||||
[cf, cf_mul, cf_add],
|
||||
Timestamp('2016-02-28 18:01', tz='US/Eastern').tz_convert('UTC'),
|
||||
30, '1m', 'close')
|
||||
|
||||
# No adjustments in this window.
|
||||
self.assertEqual(window.loc['2016-02-26 22:32', cf_mul],
|
||||
125241.412,
|
||||
"Should be FOH16 at beginning of window.")
|
||||
|
||||
self.assertEqual(window.loc['2016-02-28 23:01', cf_mul],
|
||||
125250.001,
|
||||
"Should remain FOH16 on next session.")
|
||||
|
||||
|
||||
class OrderedContractsTestCase(ZiplineTestCase):
|
||||
|
||||
|
||||
Reference in New Issue
Block a user