mirror of
https://github.com/wassname/catalyst.git
synced 2026-06-29 08:29:45 +08:00
BUG: Fix continuous future end dates.
The end date of the last contract with a sufficient start date was being used for the continuous future overall end date; however the end date of that contract (which is the last day for which there is data for the contract) is not necessarily the greatest end date out of all contracts. It is possible for the furthest out contract to have some, but very few, trades before it is more actively traded. Which would give it a start date within in the range of the simulation, but an end date is earlier than the other contracts which are active during the simulation. This bug would result in `nan`s when getting the current price because of the `end_date` check in `get_spot_value`. When the current simulation time was greater than the `end_date` of the last contract the condition which guards against attempting to get data for an instrument past its end date would return a `nan`, even when the current underlying contract did have data for that date. Use max end date of all contracts instead of the last one, to ensure that the continuous future last date is always great enough to allow access to all contracts with in the chain. Also, use min start date to accurately mirror the end date behavior.
This commit is contained in:
@@ -64,42 +64,54 @@ class ContinuousFuturesTestCase(WithCreateBarData,
|
||||
@classmethod
|
||||
def make_futures_info(self):
|
||||
return DataFrame({
|
||||
'symbol': ['FOF16', 'FOG16', 'FOH16', 'FOJ16', 'FOK16', 'FOF22'],
|
||||
'root_symbol': ['FO'] * 6,
|
||||
'asset_name': ['Foo'] * 6,
|
||||
'symbol': ['FOF16', 'FOG16', 'FOH16', 'FOJ16', 'FOK16', 'FOF22',
|
||||
'FOG22'],
|
||||
'root_symbol': ['FO'] * 7,
|
||||
'asset_name': ['Foo'] * 7,
|
||||
'start_date': [Timestamp('2015-01-05', tz='UTC'),
|
||||
Timestamp('2015-02-05', tz='UTC'),
|
||||
Timestamp('2015-03-05', tz='UTC'),
|
||||
Timestamp('2015-04-05', tz='UTC'),
|
||||
Timestamp('2015-05-05', tz='UTC'),
|
||||
Timestamp('2021-01-05', tz='UTC')],
|
||||
Timestamp('2021-01-05', tz='UTC'),
|
||||
Timestamp('2015-01-05', tz='UTC')],
|
||||
'end_date': [Timestamp('2016-08-19', tz='UTC'),
|
||||
Timestamp('2016-09-19', tz='UTC'),
|
||||
Timestamp('2016-10-19', tz='UTC'),
|
||||
Timestamp('2016-11-19', tz='UTC'),
|
||||
Timestamp('2016-12-19', tz='UTC'),
|
||||
Timestamp('2022-08-19', tz='UTC')],
|
||||
Timestamp('2022-08-19', tz='UTC'),
|
||||
# Set the last contract's end date (which is the last
|
||||
# date for which there is data to a value that is
|
||||
# within the range of the dates being tested. This
|
||||
# models real life scenarios where the end date of the
|
||||
# furthest out contract is not necessarily the
|
||||
# greatest end date all contracts in the chain.
|
||||
Timestamp('2015-02-05', tz='UTC')],
|
||||
'notice_date': [Timestamp('2016-01-27', tz='UTC'),
|
||||
Timestamp('2016-02-26', tz='UTC'),
|
||||
Timestamp('2016-03-24', tz='UTC'),
|
||||
Timestamp('2016-04-26', tz='UTC'),
|
||||
Timestamp('2016-05-26', tz='UTC'),
|
||||
Timestamp('2022-01-26', tz='UTC')],
|
||||
Timestamp('2022-01-26', tz='UTC'),
|
||||
Timestamp('2022-02-26', tz='UTC')],
|
||||
'expiration_date': [Timestamp('2016-01-27', tz='UTC'),
|
||||
Timestamp('2016-02-26', tz='UTC'),
|
||||
Timestamp('2016-03-24', tz='UTC'),
|
||||
Timestamp('2016-04-26', tz='UTC'),
|
||||
Timestamp('2016-05-26', tz='UTC'),
|
||||
Timestamp('2022-01-26', tz='UTC')],
|
||||
Timestamp('2022-01-26', tz='UTC'),
|
||||
Timestamp('2022-02-26', tz='UTC')],
|
||||
'auto_close_date': [Timestamp('2016-01-27', tz='UTC'),
|
||||
Timestamp('2016-02-26', tz='UTC'),
|
||||
Timestamp('2016-03-24', tz='UTC'),
|
||||
Timestamp('2016-04-26', tz='UTC'),
|
||||
Timestamp('2016-05-26', tz='UTC'),
|
||||
Timestamp('2022-01-26', tz='UTC')],
|
||||
'tick_size': [0.001] * 6,
|
||||
'multiplier': [1000.0] * 6,
|
||||
'exchange': ['CME'] * 6,
|
||||
Timestamp('2022-01-26', tz='UTC'),
|
||||
Timestamp('2022-02-26', tz='UTC')],
|
||||
'tick_size': [0.001] * 7,
|
||||
'multiplier': [1000.0] * 7,
|
||||
'exchange': ['CME'] * 7,
|
||||
})
|
||||
|
||||
@classmethod
|
||||
@@ -185,6 +197,10 @@ class ContinuousFuturesTestCase(WithCreateBarData,
|
||||
self.assertEqual(cf_primary.root_symbol, 'FO')
|
||||
self.assertEqual(cf_primary.offset, 0)
|
||||
self.assertEqual(cf_primary.roll_style, 'calendar')
|
||||
self.assertEqual(cf_primary.start_date,
|
||||
Timestamp('2015-01-05', tz='UTC'))
|
||||
self.assertEqual(cf_primary.end_date,
|
||||
Timestamp('2022-08-19', tz='UTC'))
|
||||
|
||||
retrieved_primary = self.asset_finder.retrieve_asset(
|
||||
cf_primary.sid)
|
||||
@@ -197,6 +213,10 @@ class ContinuousFuturesTestCase(WithCreateBarData,
|
||||
self.assertEqual(cf_secondary.root_symbol, 'FO')
|
||||
self.assertEqual(cf_secondary.offset, 1)
|
||||
self.assertEqual(cf_secondary.roll_style, 'calendar')
|
||||
self.assertEqual(cf_primary.start_date,
|
||||
Timestamp('2015-01-05', tz='UTC'))
|
||||
self.assertEqual(cf_primary.end_date,
|
||||
Timestamp('2022-08-19', tz='UTC'))
|
||||
|
||||
retrieved = self.asset_finder.retrieve_asset(
|
||||
cf_secondary.sid)
|
||||
@@ -270,6 +290,20 @@ class ContinuousFuturesTestCase(WithCreateBarData,
|
||||
'Auto close at beginning of session so FOG16 is now '
|
||||
'the current contract.')
|
||||
|
||||
# Check a value which occurs after the end date of the last known
|
||||
# contract, to prevent a regression where the end date of the last
|
||||
# contract was used instead of the max date of all contracts.
|
||||
value = self.data_portal.get_spot_value(
|
||||
cf_primary,
|
||||
'close',
|
||||
pd.Timestamp('2016-03-26', tz='UTC'),
|
||||
'daily',
|
||||
)
|
||||
|
||||
self.assertEqual(value, 135441.44,
|
||||
'Value should be for FOJ16, even though last '
|
||||
'contract ends before query date.')
|
||||
|
||||
def test_current_contract_volume_roll(self):
|
||||
cf_primary = self.asset_finder.create_continuous_future(
|
||||
'FO', 0, 'volume')
|
||||
|
||||
@@ -922,8 +922,9 @@ class AssetFinder(object):
|
||||
|
||||
def create_continuous_future(self, root_symbol, offset, roll_style):
|
||||
oc = self.get_ordered_contracts(root_symbol)
|
||||
start_date = self.retrieve_asset(oc.contract_sids[0]).start_date
|
||||
end_date = self.retrieve_asset(oc.contract_sids[-1]).end_date
|
||||
contracts = self.retrieve_all(oc.contract_sids)
|
||||
start_date = min(c.start_date for c in contracts)
|
||||
end_date = max(c.end_date for c in contracts)
|
||||
exchange = self._get_root_symbol_exchange(root_symbol)
|
||||
|
||||
sid = _encode_continuous_future_sid(root_symbol, offset,
|
||||
|
||||
Reference in New Issue
Block a user