fa45d8aa5f
- health_checklist.json: 192.168.1.122→node122
- ocr_client.py: docstring IP→node122
- docs/market-data-requirements.md: IP→node122
- 所有API调用通过ProxyHandler({})绕过系统代理
Privoxy对node122:18003返回500,直连正常
342 lines
11 KiB
Python
342 lines
11 KiB
Python
#
|
|
# Copyright 2015 Quantopian, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
from __future__ import annotations
|
|
|
|
import typing
|
|
|
|
if typing.TYPE_CHECKING:
|
|
import pandas as pd
|
|
from exchange_calendars import ExchangeCalendar
|
|
|
|
# ruff: noqa: N818
|
|
|
|
|
|
class CalendarError(Exception):
|
|
msg = None
|
|
|
|
def __init__(self, **kwargs):
|
|
self.kwargs = kwargs
|
|
|
|
def __str__(self):
|
|
return self.msg.format(**self.kwargs)
|
|
|
|
__unicode__ = __str__
|
|
__repr__ = __str__
|
|
|
|
|
|
class InvalidCalendarName(CalendarError):
|
|
"""
|
|
Raised when a calendar with an invalid name is requested.
|
|
"""
|
|
|
|
msg = "The requested ExchangeCalendar, {calendar_name}, does not exist."
|
|
|
|
|
|
class CalendarNameCollision(CalendarError):
|
|
"""
|
|
Raised when the static calendar registry already has a calendar with a
|
|
given name.
|
|
"""
|
|
|
|
msg = "A calendar with the name {calendar_name} is already registered."
|
|
|
|
|
|
class CyclicCalendarAlias(CalendarError):
|
|
"""
|
|
Raised when calendar aliases form a cycle.
|
|
"""
|
|
|
|
msg = "Cycle in calendar aliases: [{cycle}]"
|
|
|
|
|
|
class NoSessionsError(CalendarError):
|
|
"""Raised if a requested calendar would have no sessions.
|
|
|
|
NoSessionsError should be raised if `get_calendar` `start` and `end`
|
|
parameters are passed as dates between and inclusive of which there are
|
|
no sessions for the given calendar.
|
|
"""
|
|
|
|
msg = (
|
|
"The requested ExchangeCalendar, {calendar_name}, cannot be created as"
|
|
" there would be no sessions between the requested `start` ('{start}')"
|
|
" and `end` ('{end}') dates."
|
|
)
|
|
|
|
|
|
class NotSessionError(ValueError):
|
|
"""Input does not represent a valid session.
|
|
|
|
Raised if parameter expecting a session label receives input that
|
|
parses correctly (UTC midnight) although is not a session.
|
|
|
|
Parameters
|
|
----------
|
|
calendar
|
|
Calendar for which `ts` assumed as a session.
|
|
|
|
ts
|
|
Timestamp assumed as a session.
|
|
|
|
param_name
|
|
Name of a parameter that was to receive a session label.
|
|
"""
|
|
|
|
def __init__(self, calendar: ExchangeCalendar, ts: pd.Timestamp, param_name: str):
|
|
self.calendar = calendar
|
|
self.ts = ts
|
|
self.param_name = param_name
|
|
|
|
def __str__(self) -> str:
|
|
msg = (
|
|
f"Parameter `{self.param_name}` takes a session"
|
|
f" although received input that parsed to '{self.ts}' which"
|
|
)
|
|
|
|
if self.ts < self.calendar.first_session:
|
|
msg += (
|
|
" is earlier than the first session of calendar"
|
|
f" '{self.calendar.name}' ('{self.calendar.first_session}')."
|
|
)
|
|
elif self.ts > self.calendar.last_session:
|
|
msg += (
|
|
" is later than the last session of calendar"
|
|
f" '{self.calendar.name}' ('{self.calendar.last_session}')."
|
|
)
|
|
else:
|
|
msg += f" is not a session of calendar '{self.calendar.name}'."
|
|
return msg
|
|
|
|
|
|
class DateOutOfBounds(ValueError):
|
|
"""A date required to be within sessions' bounds is not.
|
|
|
|
Parameters
|
|
----------
|
|
calendar
|
|
Calendar for which `date` required to be within sessions'
|
|
bounds.
|
|
|
|
date
|
|
Date required to be within `calendar`'s sessions' bounds.
|
|
|
|
param_name
|
|
Name of a parameter receiving date.
|
|
"""
|
|
|
|
def __init__(self, calendar: ExchangeCalendar, date: pd.Timestamp, param_name: str):
|
|
self.calendar = calendar
|
|
self.date = date
|
|
self.param_name = param_name
|
|
|
|
def __str__(self) -> str:
|
|
msg = f"Parameter `{self.param_name}` receieved as '{self.date}' although"
|
|
if self.date < self.calendar.first_session:
|
|
msg += (
|
|
" cannot be earlier than the first session of calendar"
|
|
f" '{self.calendar.name}' ('{self.calendar.first_session}')."
|
|
)
|
|
elif self.date > self.calendar.last_session:
|
|
msg += (
|
|
" cannot be later than the last session of calendar"
|
|
f" '{self.calendar.name}' ('{self.calendar.last_session}')."
|
|
)
|
|
else:
|
|
assert (
|
|
self.date < self.calendar.first_session
|
|
or self.date > self.calendar.last_session
|
|
)
|
|
return msg
|
|
|
|
|
|
class NotTradingMinuteError(ValueError):
|
|
"""A timestamp assumed as a trading minute is not a trading minute.
|
|
|
|
Parameters
|
|
----------
|
|
calendar
|
|
Calendar for which `minute` assumed as a trading minute.
|
|
|
|
minute
|
|
Minute assumed as a trading minute.
|
|
|
|
param_name
|
|
Name of a parameter that was to receive a trading minute.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
calendar: ExchangeCalendar,
|
|
minute: pd.Timestamp,
|
|
param_name: str,
|
|
):
|
|
self.calendar = calendar
|
|
self.minute = minute
|
|
self.param_name = param_name
|
|
|
|
def __str__(self) -> str:
|
|
msg = (
|
|
f"Parameter `{self.param_name}` takes a trading minute although"
|
|
f" received input that parsed to '{self.minute}' which"
|
|
)
|
|
if self.minute < self.calendar.first_minute:
|
|
msg += (
|
|
" is earlier than the first trading minute of calendar"
|
|
f" '{self.calendar.name}' ('{self.calendar.first_session}')."
|
|
)
|
|
elif self.minute > self.calendar.last_minute:
|
|
msg += (
|
|
" is later than the last trading minute of calendar"
|
|
f" '{self.calendar.name}' ('{self.calendar.last_session}')."
|
|
)
|
|
else:
|
|
msg += f" is not a trading minute of calendar '{self.calendar.name}'."
|
|
return msg
|
|
|
|
|
|
class MinuteOutOfBounds(ValueError):
|
|
"""A minute required to be within bounds of trading minutes is not.
|
|
|
|
Parameters
|
|
----------
|
|
calendar
|
|
Calendar for which `minute` required to be within bounds of
|
|
trading minutes.
|
|
|
|
minute
|
|
Minute required to be within bounds of `calendar`'s trading
|
|
minutes.
|
|
|
|
param_name
|
|
Name of a parameter receiving `minute`.
|
|
"""
|
|
|
|
def __init__(
|
|
self, calendar: ExchangeCalendar, minute: pd.Timestamp, param_name: str
|
|
):
|
|
self.calendar = calendar
|
|
self.minute = minute
|
|
self.param_name = param_name
|
|
|
|
def __str__(self) -> str:
|
|
msg = f"Parameter `{self.param_name}` receieved as '{self.minute}' although"
|
|
if self.minute < self.calendar.first_minute:
|
|
msg += (
|
|
" cannot be earlier than the first trading minute of calendar"
|
|
f" '{self.calendar.name}' ('{self.calendar.first_minute}')."
|
|
)
|
|
elif self.minute > self.calendar.last_minute:
|
|
msg += (
|
|
" cannot be later than the last trading minute of calendar"
|
|
f" '{self.calendar.name}' ('{self.calendar.last_minute}')."
|
|
)
|
|
else:
|
|
assert (
|
|
self.minute < self.calendar.first_minute
|
|
or self.minute > self.calendar.last_minute
|
|
)
|
|
return msg
|
|
|
|
|
|
class RequestedSessionOutOfBounds(ValueError):
|
|
"""The requested session would fall beyond calendar bounds.
|
|
|
|
Parameters
|
|
----------
|
|
calendar
|
|
Calendar for which session would be out-of-bounds.
|
|
|
|
too_early
|
|
True if requested session would be earlier than the first calendar
|
|
session.
|
|
False if requested session would be later than the last calendar
|
|
session.
|
|
"""
|
|
|
|
def __init__(self, calendar: ExchangeCalendar, too_early: bool):
|
|
self.calendar = calendar
|
|
self.adverb = "before" if too_early else "after"
|
|
self.position = "first" if too_early else "last"
|
|
self.bound = calendar.first_session if too_early else calendar.last_session
|
|
|
|
def __str__(self) -> str:
|
|
return (
|
|
f"Requested session would fall {self.adverb} the calendar's {self.position}"
|
|
f" session ('{self.bound}')."
|
|
)
|
|
|
|
|
|
class RequestedMinuteOutOfBounds(ValueError):
|
|
"""The requested trading minute would fall beyond calendar bounds.
|
|
|
|
Parameters
|
|
----------
|
|
calendar
|
|
Calendar for which minute would be out-of-bounds.
|
|
|
|
too_early
|
|
True if requested minute would be earlier than the first calendar
|
|
minute.
|
|
False if requested minute would be later than the last calendar
|
|
minute.
|
|
"""
|
|
|
|
def __init__(self, calendar: ExchangeCalendar, too_early: bool):
|
|
self.calendar = calendar
|
|
self.adverb = "before" if too_early else "after"
|
|
self.position = "first" if too_early else "last"
|
|
self.bound = calendar.first_minute if too_early else calendar.last_minute
|
|
|
|
def __str__(self) -> str:
|
|
return (
|
|
f"Requested minute would fall {self.adverb} the calendar's {self.position}"
|
|
f" trading minute ('{self.bound}')."
|
|
)
|
|
|
|
|
|
class IndexOverlapError(ValueError):
|
|
"""Periods implied by indices overlap."""
|
|
|
|
|
|
class IntervalsOverlapError(IndexOverlapError):
|
|
"""Intervals of requested trading index would overlap."""
|
|
|
|
# pylint: disable=missing-return-type-doc
|
|
def __str__(self):
|
|
return (
|
|
"Unable to create trading index as intervals would overlap."
|
|
" This can occur when the frequency is longer than a break or"
|
|
" the gap between one session's close and the next session's"
|
|
" open (as reduced by any alignment). To shorten intervals"
|
|
" that would otherwise overlap either pass `curtail_overlaps`"
|
|
" as True or pass `force_close` and/or `force_break_close` as True."
|
|
)
|
|
|
|
|
|
class IndicesOverlapError(IndexOverlapError):
|
|
"""Indices of requested trading index would overlap."""
|
|
|
|
# pylint: disable=missing-return-type-doc
|
|
def __str__(self):
|
|
return (
|
|
"Unable to create trading index as an indice would fall to the"
|
|
" right of (later than) the subsequent indice. This can occur"
|
|
" when the frequency is longer than a break or the gap between"
|
|
" one session's close and the next session's open (as reduced"
|
|
" by any alignment). Consider passing `closed` as `left` or"
|
|
" passing `force_close` and/or `force_break_close` as True."
|
|
)
|