mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-15 11:22:18 +08:00
267 lines
8.4 KiB
Python
267 lines
8.4 KiB
Python
from zipline.errors import (
|
|
CalendarNameCollision,
|
|
CyclicCalendarAlias,
|
|
InvalidCalendarName,
|
|
)
|
|
from zipline.utils.calendars.exchange_calendar_cfe import CFEExchangeCalendar
|
|
from zipline.utils.calendars.exchange_calendar_ice import ICEExchangeCalendar
|
|
from zipline.utils.calendars.exchange_calendar_nyse import NYSEExchangeCalendar
|
|
from zipline.utils.calendars.exchange_calendar_cme import CMEExchangeCalendar
|
|
from zipline.utils.calendars.exchange_calendar_bmf import BMFExchangeCalendar
|
|
from zipline.utils.calendars.exchange_calendar_lse import LSEExchangeCalendar
|
|
from zipline.utils.calendars.exchange_calendar_tsx import TSXExchangeCalendar
|
|
from zipline.utils.calendars.us_futures_calendar import (
|
|
QuantopianUSFuturesCalendar,
|
|
)
|
|
|
|
_default_calendar_factories = {
|
|
'NYSE': NYSEExchangeCalendar,
|
|
'CME': CMEExchangeCalendar,
|
|
'ICE': ICEExchangeCalendar,
|
|
'CFE': CFEExchangeCalendar,
|
|
'BMF': BMFExchangeCalendar,
|
|
'LSE': LSEExchangeCalendar,
|
|
'TSX': TSXExchangeCalendar,
|
|
'us_futures': QuantopianUSFuturesCalendar,
|
|
}
|
|
_default_calendar_aliases = {
|
|
'NASDAQ': 'NYSE',
|
|
'BATS': 'NYSE',
|
|
'CBOT': 'CME',
|
|
'COMEX': 'CME',
|
|
'NYMEX': 'CME',
|
|
'ICEUS': 'ICE',
|
|
'NYFE': 'ICE',
|
|
}
|
|
|
|
|
|
class TradingCalendarDispatcher(object):
|
|
"""
|
|
A class for dispatching and caching trading calendars.
|
|
|
|
Methods of a global instance of this class are provided by
|
|
zipline.utils.calendar_utils.
|
|
|
|
Parameters
|
|
----------
|
|
calendars : dict[str -> TradingCalendar]
|
|
Initial set of calendars.
|
|
calendar_factories : dict[str -> function]
|
|
Factories for lazy calendar creation.
|
|
aliases : dict[str -> str]
|
|
Calendar name aliases.
|
|
"""
|
|
def __init__(self, calendars, calendar_factories, aliases):
|
|
self._calendars = calendars
|
|
self._calendar_factories = calendar_factories
|
|
self._aliases = aliases
|
|
|
|
def get_calendar(self, name):
|
|
"""
|
|
Retrieves an instance of an TradingCalendar whose name is given.
|
|
|
|
Parameters
|
|
----------
|
|
name : str
|
|
The name of the TradingCalendar to be retrieved.
|
|
|
|
Returns
|
|
-------
|
|
TradingCalendar
|
|
The desired calendar.
|
|
"""
|
|
canonical_name = self.resolve_alias(name)
|
|
|
|
try:
|
|
return self._calendars[canonical_name]
|
|
except KeyError:
|
|
# We haven't loaded this calendar yet, so make a new one.
|
|
pass
|
|
|
|
try:
|
|
factory = self._calendar_factories[canonical_name]
|
|
except KeyError:
|
|
# We don't have a factory registered for this name. Barf.
|
|
raise InvalidCalendarName(calendar_name=name)
|
|
|
|
# Cache the calendar for future use.
|
|
calendar = self._calendars[canonical_name] = factory()
|
|
return calendar
|
|
|
|
def has_calendar(self, name):
|
|
"""
|
|
Do we have (or have the ability to make) a calendar with ``name``?
|
|
"""
|
|
return (
|
|
name in self._calendars
|
|
or name in self._calendar_factories
|
|
or name in self._aliases
|
|
)
|
|
|
|
def register_calendar(self, name, calendar, force=False):
|
|
"""
|
|
Registers a calendar for retrieval by the get_calendar method.
|
|
|
|
Parameters
|
|
----------
|
|
name: str
|
|
The key with which to register this calendar.
|
|
calendar: TradingCalendar
|
|
The calendar to be registered for retrieval.
|
|
force : bool, optional
|
|
If True, old calendars will be overwritten on a name collision.
|
|
If False, name collisions will raise an exception.
|
|
Default is False.
|
|
|
|
Raises
|
|
------
|
|
CalendarNameCollision
|
|
If a calendar is already registered with the given calendar's name.
|
|
"""
|
|
if force:
|
|
self.deregister_calendar(name)
|
|
|
|
if self.has_calendar(name):
|
|
raise CalendarNameCollision(calendar_name=name)
|
|
|
|
self._calendars[name] = calendar
|
|
|
|
def register_calendar_type(self, name, calendar_type, force=False):
|
|
"""
|
|
Registers a calendar by type.
|
|
|
|
This is useful for registering a new calendar to be lazily instantiated
|
|
at some future point in time.
|
|
|
|
Parameters
|
|
----------
|
|
name: str
|
|
The key with which to register this calendar.
|
|
calendar_type: type
|
|
The type of the calendar to register.
|
|
force : bool, optional
|
|
If True, old calendars will be overwritten on a name collision.
|
|
If False, name collisions will raise an exception.
|
|
Default is False.
|
|
|
|
Raises
|
|
------
|
|
CalendarNameCollision
|
|
If a calendar is already registered with the given calendar's name.
|
|
"""
|
|
if force:
|
|
self.deregister_calendar(name)
|
|
|
|
if self.has_calendar(name):
|
|
raise CalendarNameCollision(calendar_name=name)
|
|
|
|
self._calendar_factories[name] = calendar_type
|
|
|
|
def register_calendar_alias(self, alias, real_name, force=False):
|
|
"""
|
|
Register an alias for a calendar.
|
|
|
|
This is useful when multiple exchanges should share a calendar, or when
|
|
there are multiple ways to refer to the same exchange.
|
|
|
|
After calling ``register_alias('alias', 'real_name')``, subsequent
|
|
calls to ``get_calendar('alias')`` will return the same result as
|
|
``get_calendar('real_name')``.
|
|
|
|
Parameters
|
|
----------
|
|
alias : str
|
|
The name to be used to refer to a calendar.
|
|
real_name : str
|
|
The canonical name of the registered calendar.
|
|
force : bool, optional
|
|
If True, old calendars will be overwritten on a name collision.
|
|
If False, name collisions will raise an exception.
|
|
Default is False.
|
|
"""
|
|
if force:
|
|
self.deregister_calendar(alias)
|
|
|
|
if self.has_calendar(alias):
|
|
raise CalendarNameCollision(calendar_name=alias)
|
|
|
|
self._aliases[alias] = real_name
|
|
|
|
# Ensure that the new alias doesn't create a cycle, and back it out if
|
|
# we did.
|
|
try:
|
|
self.resolve_alias(alias)
|
|
except CyclicCalendarAlias:
|
|
del self._aliases[alias]
|
|
raise
|
|
|
|
def resolve_alias(self, name):
|
|
"""
|
|
Resolve a calendar alias for retrieval.
|
|
|
|
Parameters
|
|
----------
|
|
name : str
|
|
The name of the requested calendar.
|
|
|
|
Returns
|
|
-------
|
|
canonical_name : str
|
|
The real name of the calendar to create/return.
|
|
"""
|
|
# Use an OrderedDict as an ordered set so that we can return the order
|
|
# of aliases in the event of a cycle.
|
|
seen = []
|
|
|
|
while name in self._aliases:
|
|
seen.append(name)
|
|
name = self._aliases[name]
|
|
|
|
# This is O(N ** 2), but if there's an alias chain longer than 2,
|
|
# something strange has happened.
|
|
if name in seen:
|
|
seen.append(name)
|
|
raise CyclicCalendarAlias(
|
|
cycle=" -> ".join(repr(k) for k in seen)
|
|
)
|
|
|
|
return name
|
|
|
|
def deregister_calendar(self, name):
|
|
"""
|
|
If a calendar is registered with the given name, it is de-registered.
|
|
|
|
Parameters
|
|
----------
|
|
cal_name : str
|
|
The name of the calendar to be deregistered.
|
|
"""
|
|
self._calendars.pop(name, None)
|
|
self._calendar_factories.pop(name, None)
|
|
self._aliases.pop(name, None)
|
|
|
|
def clear_calendars(self):
|
|
"""
|
|
Deregisters all current registered calendars
|
|
"""
|
|
self._calendars.clear()
|
|
self._calendar_factories.clear()
|
|
self._aliases.clear()
|
|
|
|
|
|
# We maintain a global calendar dispatcher so that users can just do
|
|
# `register_calendar('my_calendar', calendar) and then use `get_calendar`
|
|
# without having to thread around a dispatcher.
|
|
global_calendar_dispatcher = TradingCalendarDispatcher(
|
|
calendars={},
|
|
calendar_factories=_default_calendar_factories,
|
|
aliases=_default_calendar_aliases,
|
|
)
|
|
|
|
get_calendar = global_calendar_dispatcher.get_calendar
|
|
clear_calendars = global_calendar_dispatcher.clear_calendars
|
|
deregister_calendar = global_calendar_dispatcher.deregister_calendar
|
|
register_calendar = global_calendar_dispatcher.register_calendar
|
|
register_calendar_type = global_calendar_dispatcher.register_calendar_type
|
|
register_calendar_alias = global_calendar_dispatcher.register_calendar_alias
|