Git Product home page Git Product logo

exchange_calendars's Introduction

exchange_calendars

PyPI Python Support PyPI Downloads Code style: black

A Python library for defining and querying calendars for security exchanges.

Calendars for more than 50 exchanges available out-the-box! If you still can't find the calendar you're looking for, create a new one!

Installation

$ pip install exchange_calendars

Quick Start

import exchange_calendars as xcals

Get a list of available calendars:

>>> xcals.get_calendar_names(include_aliases=False)[5:10]
['CMES', 'IEPA', 'XAMS', 'XASX', 'XBKK']

Get a calendar:

>>> xnys = xcals.get_calendar("XNYS")  # New York Stock Exchange
>>> xhkg = xcals.get_calendar("XHKG")  # Hong Kong Stock Exchange

Query the schedule:

>>> xhkg.schedule.loc["2021-12-29":"2022-01-04"]
open break_start break_end close
2021-12-29 2021-12-29 01:30:00+00:00 2021-12-29 04:00:00+00:00 2021-12-29 05:00:00+00:00 2021-12-29 08:00:00+00:00
2021-12-30 2021-12-30 01:30:00+00:00 2021-12-30 04:00:00+00:00 2021-12-30 05:00:00+00:00 2021-12-30 08:00:00+00:00
2021-12-31 2021-12-31 01:30:00+00:00 NaT NaT 2021-12-31 04:00:00+00:00
2022-01-03 2022-01-03 01:30:00+00:00 2022-01-03 04:00:00+00:00 2022-01-03 05:00:00+00:00 2022-01-03 08:00:00+00:00
2022-01-04 2022-01-04 01:30:00+00:00 2022-01-04 04:00:00+00:00 2022-01-04 05:00:00+00:00 2022-01-04 08:00:00+00:00

Working with sessions

>>> xnys.is_session("2022-01-01")
False

>>> xnys.sessions_in_range("2022-01-01", "2022-01-11")
DatetimeIndex(['2022-01-03', '2022-01-04', '2022-01-05', '2022-01-06',
               '2022-01-07', '2022-01-10', '2022-01-11'],
              dtype='datetime64[ns]', freq='C')

>>> xnys.sessions_window("2022-01-03", 7)
DatetimeIndex(['2022-01-03', '2022-01-04', '2022-01-05', '2022-01-06',
               '2022-01-07', '2022-01-10', '2022-01-11'],
              dtype='datetime64[ns]', freq='C')

>>> xnys.date_to_session("2022-01-01", direction="next")
Timestamp('2022-01-03 00:00:00', freq='C')

>>> xnys.previous_session("2022-01-11")
Timestamp('2022-01-10 00:00:00', freq='C')

>>> xhkg.trading_index(
...     "2021-12-30", "2021-12-31", period="90min", force=True
... )
IntervalIndex([[2021-12-30 01:30:00, 2021-12-30 03:00:00), [2021-12-30 03:00:00, 2021-12-30 04:00:00), [2021-12-30 05:00:00, 2021-12-30 06:30:00), [2021-12-30 06:30:00, 2021-12-30 08:00:00), [2021-12-31 01:30:00, 2021-12-31 03:00:00), [2021-12-31 03:00:00, 2021-12-31 04:00:00)], dtype='interval[datetime64[ns, UTC], left]')

See the sessions tutorial for a deeper dive into sessions.

Working with minutes

>>> xhkg.session_minutes("2022-01-03")
DatetimeIndex(['2022-01-03 01:30:00+00:00', '2022-01-03 01:31:00+00:00',
               '2022-01-03 01:32:00+00:00', '2022-01-03 01:33:00+00:00',
               '2022-01-03 01:34:00+00:00', '2022-01-03 01:35:00+00:00',
               '2022-01-03 01:36:00+00:00', '2022-01-03 01:37:00+00:00',
               '2022-01-03 01:38:00+00:00', '2022-01-03 01:39:00+00:00',
               ...
               '2022-01-03 07:50:00+00:00', '2022-01-03 07:51:00+00:00',
               '2022-01-03 07:52:00+00:00', '2022-01-03 07:53:00+00:00',
               '2022-01-03 07:54:00+00:00', '2022-01-03 07:55:00+00:00',
               '2022-01-03 07:56:00+00:00', '2022-01-03 07:57:00+00:00',
               '2022-01-03 07:58:00+00:00', '2022-01-03 07:59:00+00:00'],
              dtype='datetime64[ns, UTC]', length=330, freq=None)

>>> mins = [ "2022-01-03 " + tm for tm in ["01:29", "01:30", "04:20", "07:59", "08:00"] ]
>>> [ xhkg.is_trading_minute(minute) for minute in mins ]
[False, True, False, True, False]  # by default minutes are closed on the left side

>>> xhkg.is_break_minute("2022-01-03 04:20")
True

>>> xhkg.previous_close("2022-01-03 08:10")
Timestamp('2022-01-03 08:00:00+0000', tz='UTC')

>>> xhkg.previous_minute("2022-01-03 08:10")
Timestamp('2022-01-03 07:59:00+0000', tz='UTC')

Check out the minutes tutorial for a deeper dive that includes an explanation of the concept of 'minutes' and how the "side" option determines which minutes are treated as trading minutes.

Tutorials

Hopefully you'll find that exchange_calendars has the method you need to get the information you want. If it doesn't, either PR it or raise an issue and let us know!

Command Line Usage

Print a unix-cal like calendar straight from the command line (holidays are indicated by brackets)...

ecal XNYS 2020
                                        2020
        January                        February                        March
Su  Mo  Tu  We  Th  Fr  Sa     Su  Mo  Tu  We  Th  Fr  Sa     Su  Mo  Tu  We  Th  Fr  Sa
            [ 1]  2   3 [ 4]                           [ 1]
[ 5]  6   7   8   9  10 [11]   [ 2]  3   4   5   6   7 [ 8]   [ 1]  2   3   4   5   6 [ 7]
[12] 13  14  15  16  17 [18]   [ 9] 10  11  12  13  14 [15]   [ 8]  9  10  11  12  13 [14]
[19][20] 21  22  23  24 [25]   [16][17] 18  19  20  21 [22]   [15] 16  17  18  19  20 [21]
[26] 27  28  29  30  31        [23] 24  25  26  27  28 [29]   [22] 23  24  25  26  27 [28]
                                                              [29] 30  31

        April                           May                            June
Su  Mo  Tu  We  Th  Fr  Sa     Su  Mo  Tu  We  Th  Fr  Sa     Su  Mo  Tu  We  Th  Fr  Sa
              1   2   3 [ 4]                         1 [ 2]         1   2   3   4   5 [ 6]
[ 5]  6   7   8   9 [10][11]   [ 3]  4   5   6   7   8 [ 9]   [ 7]  8   9  10  11  12 [13]
[12] 13  14  15  16  17 [18]   [10] 11  12  13  14  15 [16]   [14] 15  16  17  18  19 [20]
[19] 20  21  22  23  24 [25]   [17] 18  19  20  21  22 [23]   [21] 22  23  24  25  26 [27]
[26] 27  28  29  30            [24][25] 26  27  28  29 [30]   [28] 29  30
                               [31]

            July                          August                       September
Su  Mo  Tu  We  Th  Fr  Sa     Su  Mo  Tu  We  Th  Fr  Sa     Su  Mo  Tu  We  Th  Fr  Sa
              1   2 [ 3][ 4]                           [ 1]             1   2   3   4 [ 5]
[ 5]  6   7   8   9  10 [11]   [ 2]  3   4   5   6   7 [ 8]   [ 6][ 7]  8   9  10  11 [12]
[12] 13  14  15  16  17 [18]   [ 9] 10  11  12  13  14 [15]   [13] 14  15  16  17  18 [19]
[19] 20  21  22  23  24 [25]   [16] 17  18  19  20  21 [22]   [20] 21  22  23  24  25 [26]
[26] 27  28  29  30  31        [23] 24  25  26  27  28 [29]   [27] 28  29  30
                               [30] 31

        October                        November                       December
Su  Mo  Tu  We  Th  Fr  Sa     Su  Mo  Tu  We  Th  Fr  Sa     Su  Mo  Tu  We  Th  Fr  Sa
                  1   2 [ 3]                                            1   2   3   4 [ 5]
[ 4]  5   6   7   8   9 [10]   [ 1]  2   3   4   5   6 [ 7]   [ 6]  7   8   9  10  11 [12]
[11] 12  13  14  15  16 [17]   [ 8]  9  10  11  12  13 [14]   [13] 14  15  16  17  18 [19]
[18] 19  20  21  22  23 [24]   [15] 16  17  18  19  20 [21]   [20] 21  22  23  24 [25][26]
[25] 26  27  28  29  30 [31]   [22] 23  24  25 [26] 27 [28]   [27] 28  29  30  31
                               [29] 30
ecal XNYS 1 2020
        January 2020
Su  Mo  Tu  We  Th  Fr  Sa
            [ 1]  2   3 [ 4]
[ 5]  6   7   8   9  10 [11]
[12] 13  14  15  16  17 [18]
[19][20] 21  22  23  24 [25]
[26] 27  28  29  30  31

Frequently Asked Questions

How can I create a new calendar?

First off, make sure the calendar you're after hasn't already been defined; exchange calendars comes with over 50 pre-defined calendars, including major security exchanges.

If you can't find what you're after, a custom calendar can be created as a subclass of ExchangeCalendar. This workflow describes the process to add a new calendar to exchange_calendars. Just follow the relevant parts.

To access the new calendar via get_calendar call either xcals.register_calendar or xcals.register_calendar_type to register, respectively, a specific calendar instance or a calendar factory (i.e. the subclass).

Can I contribute a new calendar to exchange calendars?

Yes please! The workflow can be found here.

<calendar> is missing a holiday, has a wrong time, should have a break etc...

All of the exchange calendars are maintained by user contributions. If a calendar you care about needs revising, please open a PR - that's how this thing works! (Never contributed to a project before and it all seems a bit daunting? Check this out and don't look back!)

You'll find the workflow to modify an existing calendar here.

What times are considered open and closed?

exchange_calendars attempts to be broadly useful by considering an exchange to be open only during periods of regular trading. During any pre-trading, post-trading or auction period the exchange is treated as closed. An exchange is also treated as closed during any observed lunch break.

See the minutes tutorial for a detailed explanation of which minutes an exchange is considered open over. If you previously used trading_calendars, or exchange_calendars prior to release 3.4, then this is the place to look for answers to questions of how the definition of trading minutes has changed over time (and is now stable and flexible!).

Calendars

Exchange ISO Code Country Version Added Exchange Website (English)
New York Stock Exchange XNYS USA 1.0 https://www.nyse.com/index
CBOE Futures XCBF USA 1.0 https://markets.cboe.com/us/futures/overview/
Chicago Mercantile Exchange CMES USA 1.0 https://www.cmegroup.com/
ICE US IEPA USA 1.0 https://www.theice.com/index
Toronto Stock Exchange XTSE Canada 1.0 https://www.tsx.com/
BMF Bovespa BVMF Brazil 1.0 http://www.b3.com.br/en_us/
London Stock Exchange XLON England 1.0 https://www.londonstockexchange.com/
Euronext Amsterdam XAMS Netherlands 1.2 https://www.euronext.com/en/regulation/amsterdam
Euronext Brussels XBRU Belgium 1.2 https://www.euronext.com/en/regulation/brussels
Euronext Lisbon XLIS Portugal 1.2 https://www.euronext.com/en/regulation/lisbon
Euronext Paris XPAR France 1.2 https://www.euronext.com/en/regulation/paris
Frankfurt Stock Exchange XFRA Germany 1.2 http://en.boerse-frankfurt.de/
SIX Swiss Exchange XSWX Switzerland 1.2 https://www.six-group.com/en/home.html
Tokyo Stock Exchange XTKS Japan 1.2 https://www.jpx.co.jp/english/
Australian Securities Exchange XASX Australia 1.3 https://www.asx.com.au/
Bolsa de Madrid XMAD Spain 1.3 https://www.bolsamadrid.es
Borsa Italiana XMIL Italy 1.3 https://www.borsaitaliana.it
New Zealand Exchange XNZE New Zealand 1.3 https://www.nzx.com/
Wiener Borse XWBO Austria 1.3 https://www.wienerborse.at/en/
Hong Kong Stock Exchange XHKG Hong Kong 1.3 https://www.hkex.com.hk/?sc_lang=en
Copenhagen Stock Exchange XCSE Denmark 1.4 http://www.nasdaqomxnordic.com/
Helsinki Stock Exchange XHEL Finland 1.4 http://www.nasdaqomxnordic.com/
Stockholm Stock Exchange XSTO Sweden 1.4 http://www.nasdaqomxnordic.com/
Oslo Stock Exchange XOSL Norway 1.4 https://www.oslobors.no/ob_eng/
Irish Stock Exchange XDUB Ireland 1.4 http://www.ise.ie/
Bombay Stock Exchange XBOM India 1.5 https://www.bseindia.com
Singapore Exchange XSES Singapore 1.5 https://www.sgx.com
Shanghai Stock Exchange XSHG China 1.5 http://english.sse.com.cn
Korea Exchange XKRX South Korea 1.6 http://global.krx.co.kr
Iceland Stock Exchange XICE Iceland 1.7 http://www.nasdaqomxnordic.com/
Poland Stock Exchange XWAR Poland 1.9 http://www.gpw.pl
Santiago Stock Exchange XSGO Chile 1.9 https://www.bolsadesantiago.com/
Colombia Securities Exchange XBOG Colombia 1.9 https://www.bvc.com.co/nueva/https://www.bvc.com.co/nueva/
Mexican Stock Exchange XMEX Mexico 1.9 https://www.bmv.com.mx
Lima Stock Exchange XLIM Peru 1.9 https://www.bvl.com.pe
Prague Stock Exchange XPRA Czech Republic 1.9 https://www.pse.cz/en/
Budapest Stock Exchange XBUD Hungary 1.10 https://bse.hu/
Athens Stock Exchange ASEX Greece 1.10 http://www.helex.gr/
Istanbul Stock Exchange XIST Turkey 1.10 https://www.borsaistanbul.com/en/
Johannesburg Stock Exchange XJSE South Africa 1.10 https://www.jse.co.za/z
Malaysia Stock Exchange XKLS Malaysia 1.11 http://www.bursamalaysia.com/market/
Moscow Exchange XMOS Russia 1.11 https://www.moex.com/en/
Philippine Stock Exchange XPHS Philippines 1.11 https://www.pse.com.ph/
Stock Exchange of Thailand XBKK Thailand 1.11 https://www.set.or.th/set/mainpage.do?language=en&country=US
Indonesia Stock Exchange XIDX Indonesia 1.11 https://www.idx.co.id/
Taiwan Stock Exchange Corp. XTAI Taiwan 1.11 https://www.twse.com.tw/en/
Buenos Aires Stock Exchange XBUE Argentina 1.11 https://www.bcba.sba.com.ar/
Pakistan Stock Exchange XKAR Pakistan 1.11 https://www.psx.com.pk/
Xetra XETR Germany 2.1 https://www.xetra.com/
Tel Aviv Stock Exchange XTAE Israel 2.1 https://www.tase.co.il/
Astana International Exchange AIXK Kazakhstan 3.2 https://www.aix.kz/
Bucharest Stock Exchange XBSE Romania 3.2 https://www.bvb.ro/
Saudi Stock Exchange XSAU Saudi Arabia 4.2 https://www.saudiexchange.sa/
European Energy Exchange AG XEEE Germany 4.5.5 https://www.eex.com
Hamburg Stock Exchange XHAM Germany 4.5.5 https://www.boerse-hamburg.de
Duesseldorf Stock Exchange XDUS Germany 4.5.5 https://www.boerse-duesseldorf.de

Note that exchange calendars are defined by their ISO-10383 market identifier code.

Much of the post v3 development of exchange_calendars has been driven by the market_prices library. Check it out if you like the idea of using exchange_calendars to create meaningful OHLCV datasets. It works out-the-box with freely available data!

Deprecations and Renaming

Methods renamed in version 4.0.3 and removed in 4.3

Previous name New name
bound_start bound_min
bound_end bound_max

Methods deprecated in 4.0 and removed in 4.3

Deprecated method Reason
sessions_closes use .closes[start:end]
sessions_opens use .opens[start:end]

Methods with a parameter renamed in 4.0

Method
is_session
is_open_on_minute
minutes_in_range
minutes_window
next_close
next_minute
next_open
previous_close
previous_minute
previous_open
session_break_end
session_break_start
session_close
session_open
sessions_in_range
sessions_window

Methods renamed in version 3.4 and removed in 4.0

Previous name New name
all_minutes minutes
all_minutes_nanos minutes_nanos
all_sessions sessions
break_start_and_end_for_session session_break_start_end
date_to_session_label date_to_session
first_trading_minute first_minute
first_trading_session first_session
has_breaks sessions_has_break
last_trading_minute last_minute
last_trading_session last_session
next_session_label next_session
open_and_close_for_session session_open_close
previous_session_label previous_session
market_break_ends_nanos break_ends_nanos
market_break_starts_nanos break_starts_nanos
market_closes_nanos closes_nanos
market_opens_nanos opens_nanos
minute_index_to_session_labels minutes_to_sessions
minute_to_session_label minute_to_session
minutes_count_for_sessions_in_range sessions_minutes_count
minutes_for_session session_minutes
minutes_for_sessions_in_range sessions_minutes
session_closes_in_range sessions_closes
session_distance sessions_distance
session_opens_in_range sessions_opens

Other methods deprecated in 3.4 and removed in 4.0

Removed Method
execution_minute_for_session
execution_minute_for_sessions_in_range
execution_time_from_close
execution_time_from_open

exchange_calendars's People

Contributors

analicia avatar buinvest avatar captainkanuk avatar dependabot[bot] avatar dmichalowicz avatar ehebert avatar elbakramer avatar freddiev4 avatar gerrymanoim avatar gmanoim-quantopian avatar herebebeasties avatar jbredeche avatar jenskeiner avatar jfkirk avatar jmccorriston avatar llllllllll avatar maread99 avatar mixilchenko avatar mrkgoh avatar pbabics avatar philiptromans avatar philtromans avatar quantophred avatar richafrank avatar romanzdk avatar srinivasakumar-a avatar ssanderson avatar valueraider avatar wec7 avatar willianpaixao avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

exchange_calendars's Issues

Why does `orthodox_easter` have a default end date?

The orthodox_easter method was used for some time to get Orthodox Easter holidays for ASEX. It has since been pulled out of the ASEX-specific code as it's now also used for XBSE.

The method has two arguments, start_date and end_date, with default values of 1980 and 2022, respectively.

This issue is to ask if it actually makes sense to configure an end date which coincides with the current or next year (as of the time the code is written/updated).

I think it actually makes sense to have this open-ended, or possibly restricted by some global end date that automatically slides forward as time progresses. This would be to reflect that an exchange that observes holidays relative to Orthodox Easter is probably more likely to continue that in the future rather than changing observances (status quo assumption).

Ideally, any restriction would also be done per-exchange as exchanges may start or stop observing the affected holidays independently of each other.

Setting the default end date to the current year or the one after, at the time of a code update, also carries a risk for anyone using the exchange_calendars package, but missing a few future updates. The problem then becomes that the package effectively will no longer spit out the correct holidays in future years as it will stop respecting the Orthodox Easter holidays when the end date has passed, despite the rules still being valid. So one would rely on updating the package in time, hoping that the end date has now been adjusted there. To minimize the risk, I think it would be best to put any end date far into the future.

So, in essence, should we remove the current configured default end date on the method orthodox_easter, and if so, what's the correct end date (ideally far into the future) to configure?

Documentation

Originally posted by @gerrymanoim in #61 (comment)

I'd add

  • Real documentation

My preference is probably some markdown backed sphinx system on github pages.

@maread99 edit for specific documentation tasks:

  • issue template for holiday additions/corrections. #59
  • template for new calendars. (see proposal on #59)
  • Documentation of ExchangeCalendar 'calendar-defintion' methods/properties.
  • Documentation of ExchangeCalendar 'functional' methods/properties (e.g. mintute_to_session_label etc). (completed #71)
  • Real documentation. To include:
    • API, generated by sphinx
    • content of README.md, to appropriate sections.
    • Reference to tutorials in docs/tutorials.
    • content of .github/pull_request_template.md to appropriate sections (or reference to file)

Figure out a better way to update requirements via CI

Dependabot cannot be made to look at a single file (only a single directory). For now, I'd like to keep the older lock file around, but aggressively update the newer one.

Some options:

  1. Move each individual requirement to a subfolder (does dependabot need requirements_locked and requirements.in to be in the same place?).
  2. Only use a lock file for the "old" dependencies (this would cause breakage in unrelated PRs).
  3. ???

Leaning to option 1.

[Feature idea] instrument term structures

I have a small set of metadata copied from project to project, currently looking a bit like this:

    'Hard Red Winter Wheat': {
        'is_rate': False,
        'slice': 2,
        'category': 'Grain & Oilseed',
        'yahoo_prefix': 'KE',
        'yahoo_suffix': '.CBT',
        'product_id': 348,
    },

For all instruments at CME.

I then have some code accepting one of these product names and scrapes the current calendar from the CME site, providing a data frame like:

eurodol.get_cme_ts(eurodol.CME_PRODUCTS['Hard Red Winter Wheat'])
Last Trade
2021-03-12    KEH21.CBT
2021-05-14    KEK21.CBT
2021-07-14    KEN21.CBT
2021-09-14    KEU21.CBT
2021-12-14    KEZ21.CBT
2022-03-14    KEH22.CBT
2022-05-13    KEK22.CBT
2022-07-14    KEN22.CBT
2022-09-14    KEU22.CBT
2022-12-14    KEZ22.CBT
2023-03-14    KEH23.CBT
2023-05-12    KEK23.CBT
2023-07-14    KEN23.CBT
Name: Symbol, dtype: object

Doing this generally for any instrument on any exchange requires a big database that nobody wants to maintain, but maybe if it lived in a project like exchange_calendars, there is a hope it could eventually build up over time.

What do you think? I notice there is currently no scraping in this project, but it seems to fit with the use case -- instrument calendars are still more or less calendars

Update BVMF holidays to account for 2022's changes

Forking todo list

Organizational

  • come up with a new name exchange_calendars
  • Rename all the relevant pieces
  • set up pypi
  • set up conda-forge
  • Update on rsheftel/pandas_market_calendars#120
  • Look into whether licensing needs to/can change
  • change links pointing everywhere
  • remove zipline references

Code

These will all be breaking changes.

Etc

  • Publish docs
  • Clarify user contributed nature of calendars in issue templates

while using XBOM.IS_SESSION getting DateOutOfBounds error:

Exchange_Calendars package was working good until 31/12/2021 and after that receiving the DateOutOfBounds error during xbom.is_session method. I have updated the package to 3.5 and did check the code yesterday to see XBOM.py file was missing the 2022 calendar entries and at the same time there was a PR pending with changes to be merged with main branch. Today, I see that the changes are merged with main branch. However, I still get the error.

Attaching the screenshot of error.
Screenshot 2022-01-04 150539

BUG: Calendars raising unexpected errors for some start dates

Probing bounds of calendars' start parameter has found following unexpected errors:

'XNYS' with start date prior to 1956-12-25 raises ValueError("Special dates [Timestamp('1956-12-24 19:00:00+0000', tz='UTC')] are not trading days."). Suspect related to issue quantopian#196 for which there was a pending PR quantopian@6f06484.

'XKLS' with start date prior to 1984-02-02 raises ValueError("Special dates [Timestamp('1984-02-01 04:30:00+0000', tz='UTC')] are not trading days."). Possibly same cause as 'XNYS'

'XPHS' with start date prior to 1901-12-14 raises NonExistentTimeError('1901-12-13 09:30:00').

Within #31 I'll investigate / incorporate quantopian@6f06484. Going to ignore 'XPHS' error given the date (exchange didn't open until 1927!).

Multiple special weekmasks error

Hello!
I was making a calendar for Kazakhstan Stock Exchange when I ran into a bug. I tried to add several special weekmasks for the extended working weeks before the holidays. According the instructions for creating new calendars, I overrided "special_weekmasks" property and added list of (date, date, str) tuples. But it didn't worked for me. When I tried to invoke "get_calendar" method, I received this error:

> ---------------------------------------------------------------------------
> KeyError                                  Traceback (most recent call last)
> ~/python_projects/qf_py310_project/qf_py310_venv/lib/python3.10/site-packages/exchange_calendars/utils/memoize.py in __get__(self, instance, owner)
>      46         try:
> ---> 47             return self._cache[instance]
>      48         except KeyError:
> 
> /usr/lib/python3.10/weakref.py in __getitem__(self, key)
>     415     def __getitem__(self, key):
> --> 416         return self.data[ref(key)]
>     417 
> 
> KeyError: <weakref at 0x7faad31dde90; to 'XKAZExchangeCalendar' at 0x7faaf6d5fc10>
> 
> During handling of the above exception, another exception occurred:
> 
> TypeError                                 Traceback (most recent call last)
> /tmp/ipykernel_14416/1278388085.py in <module>
>       4 # nyse = xcals.get_calendar("XNYS", side='left')
>       5 # xkrx = xcals.get_calendar("XKRX", side='left')
> ----> 6 kase = xcals.get_calendar("XKAZ", side='left')
> 
> ~/python_projects/qf_py310_project/qf_py310_venv/lib/python3.10/site-packages/exchange_calendars/calendar_utils.py in get_calendar(self, name, start, end, side)
>     287 
>     288         cached = self._get_cached_factory_output(name, **kwargs)
> --> 289         return cached if cached is not None else self._fabricate(name, **kwargs)
>     290 
>     291     def get_calendar_names(
> 
> ~/python_projects/qf_py310_project/qf_py310_venv/lib/python3.10/site-packages/exchange_calendars/calendar_utils.py in _fabricate(self, name, **kwargs)
>     183         except KeyError as e:
>     184             raise InvalidCalendarName(calendar_name=name) from e
> --> 185         calendar = factory(**kwargs)
>     186         self._factory_output_cache[name] = (calendar, kwargs)
>     187         return calendar
> 
> ~/python_projects/qf_py310_project/qf_py310_venv/lib/python3.10/site-packages/exchange_calendars/exchange_calendar.py in __init__(self, start, end, side)
>     251 
>     252         # Midnight in UTC for each trading day.
> --> 253         _all_days = date_range(start, end, freq=self.day, tz=UTC)
>     254         if _all_days.empty:
>     255             raise errors.NoSessionsError(calendar_name=self.name, start=start, end=end)
> 
> ~/python_projects/qf_py310_project/qf_py310_venv/lib/python3.10/site-packages/exchange_calendars/utils/memoize.py in __get__(self, instance, owner)
>      47             return self._cache[instance]
>      48         except KeyError:
> ---> 49             self._cache[instance] = val = self._get(instance)
>      50             return val
>      51 
> 
> ~/python_projects/qf_py310_project/qf_py310_venv/lib/python3.10/site-packages/exchange_calendars/exchange_calendar.py in day(self)
>     574     def day(self):
>     575         if self.special_weekmasks:
> --> 576             return MultipleWeekmaskCustomBusinessDay(
>     577                 holidays=self.adhoc_holidays,
>     578                 calendar=self.regular_holidays,
> 
> ~/python_projects/qf_py310_project/qf_py310_venv/lib/python3.10/site-packages/exchange_calendars/pandas_extensions/offsets.py in __init__(self, n, normalize, weekmask, holidays, calendar, offset, business_days, weekmasks)
>     291                 )
>     292             ]
> --> 293         CompositeCustomBusinessDay.__init__(
>     294             self, n, normalize, weekmask, holidays, calendar, offset, business_days
>     295         )
> 
> ~/python_projects/qf_py310_project/qf_py310_venv/lib/python3.10/site-packages/exchange_calendars/pandas_extensions/offsets.py in __init__(self, n, normalize, weekmask, holidays, calendar, offset, business_days)
>      40             self, n, normalize, weekmask, holidays, calendar, offset
>      41         )
> ---> 42         self.business_days = business_days
>      43 
>      44     def __setstate__(self, state):
> 
> ~/python_projects/qf_py310_project/qf_py310_venv/lib/python3.10/site-packages/exchange_calendars/pandas_extensions/offsets.py in business_days(self, business_days)
>      88                 2, toolz.concatv(self._business_days_index)
>      89             ):
> ---> 90                 if right.left - left.right > pd.Timestamp(1, unit="D"):
>      91                    # if right.left - left.right > pd.Timedelta(1, unit="D"):
>      92                        interval = pd.Interval(
> 
> TypeError: '>' not supported between instances of 'Timedelta' and 'Timestamp'

As I got it, on the line 90 of "offset.py" module, comparison between two periods was meant. So I tried to change "pd.Timestamp" to "pd.Timedelta" like in line 91 of the error log and it worked just fine.

todo for release 4.0

I've opened this to consolidate any breaking changes that would need to be delayed until / incorporated in any release 4.0.

  • Change times to UTC (opens, closes, break_starts and break_ends) (#42). (done #179)
  • Change sessions to tz-naive (only if #42 resolves in favour of this change). (done #179)
  • Move min supported version to 3.8. Changes as #68. (done #187)
  • session_label parameter of ExchangeCalendar methods to be renamed session (which is given context from Session type and checks imposed by subsequent parsing). Methods requiring change include:
    • previous_session_label (changed when renamed, #85)
    • next_session_label (changed when renamed, #85)
    • session_open (done #186)
    • session_close (done #186)
    • session_break_start (done #186)
    • session_break_end (done #186)
    • open_and_close_for_session (changed when renamed, #85)
    • break_start_and_end_for_session (changed when renamed, #85)
    • minutes_for_session (changed when renamed, #85)
    • sessions_window (done #184)
      For following methods, start_session_label / end_session_label parameters to be renamed start / end
    • sessions_in_range (done #186)
    • minutes_for_sessions_in_range (changed when renamed, #85)
    • session_opens_in_range (changed when renamed, #85)
    • session_closes_in_range (changed when renamed, #85)
    • session_distance (changed when renamed, #85)
      For following methods, start_session / end_session parameters to be renamed start / end
    • minutes_count_for_sessions_in_range (changed when renamed, #85)
      For following methods, start_minute / end_minute parameters to be renamed start / end
    • minutes_in_range (done #194)
      For following methods, dt to be renamed date:
    • is_session (done #186)
  • dt parameter of ExchangeCalendar methods to be renamed minute (given context from 'Minute' type and checks imposed by subsequent parsing). Methods requring change include:
    • minute_to_session_label (changed when renamed, #85)
    • is_open_on_minute (done #186)
    • previous_open (done #186)
    • previous_close (done #186)
    • next_open (done #186)
    • next_close (done #186)
    • previous_minute (done #186)
    • next_minute (done #186)
      For following methods, start_dt to be renamed minute:
    • minutes_window (done #184)
  • index parameter of minute_index_to_session_labels to be renamed minutes (changed when renamed, #85)
  • rename ExchangeCalendar.schedule columns: (done #186)
    • market_open to open
    • market_close to close
  • Remove ExchangeCalendar methods deprecated ahead of release 3.4 - all were renamed (#85) save for the four execution_* methods: (done #186)
    • execution_minutes_for_session
    • execution_minutes_for_sessions_in_range
    • execution_time_from_open
    • execution_time_from_close
    • previous_session_label
    • next_session_label
    • date_to_session_label
    • minute_to_session_label
    • open_and_close_for_session
    • break_start_and_end_for_session
    • minutes_for_session
    • session_opens_in_range
    • session_closes_in_range
    • minutes_for_sessions_in_range
    • minutes_count_for_sessions_in_range
    • session_distance
    • minute_index_to_session_labels
    • all_sessions
    • all_minutes
    • all_minutes_nanos
    • first_trading_minute
    • last_trading_minute
    • first_trading_session
    • last_trading_session
    • has_breaks
    • market_opens_nanos
    • market_closes_nanos
    • market_break_starts_nanos
    • market_break_ends_nanos
  • Remove sessions_opens and sessions_closes now that, with schedule times changing to UTC timestamps, cal.sessions_opens(start, end) will return the same as cal.opens[start:end] (there should be only one obvious way to do something...). (done #186)
  • Consider changing side default for all calendars to "left" (from "right" for 24 hour calendars and from "both" for others). See #181. (done #191)
  • Change behaviour of ExchangeCalendar methods sessions_window and minutes_window so that count parameter matches the length of the return (the length of which is currently count + 1). i.e. just add count -= 1 as methods' first line and raise ValueError if count received as 0. (done #184)
  • Consider changing behaviour of is_open_on_minute to distinguish it from is_trading_minute. Proposed that the ignore_breaks option be removed and the method act as if ignore_breaks were True, i.e. is_open_on_minute would act as is_trading_minute albeit treating any break minutes as open. Method documentation should make clear that market is considered open on break minutes. Considered, left as is.
  • Consider changing ExchangeCalendar properties bound_start and bound_end to class attributes (they are class-specific, not instance-specific) and default_start and default_end to class methods with intention of changing to class properties when min python version moves to 3.9 (as noted to #98 for valid_sides and default_side). 22/06/06 - done #184, made all class methods.
  • Amend following methods to raise errors.RequestedSessionOutOfBounds when request session/minute falls beyond calendar bound: (done #184)
    • next_session (remove from minute_to_future_session method the current_session == last_session check and raise.)
    • previous_session (remove from minute_to_past_session method the current_session == first_session check and raise.)
    • next_minute
    • previous_minute
  • Amend ExchangeCalendar.trading_index, or calendar_helpers._TradingIndex, to: (done #180)
    • allow start and end parameters to be passed as times (as opposed to only dates).
    • include convenience force option to define both force_close and force_break_close.
    • revise/add tests for added functionality.
  • Revise README to accommodate changes. (done #194)
  • Revise / re-execute .ipynb tutorials to accommodate changes. Also: (done #194)
    • Add note to top of all tutorials that best viewed in Jupyter Lab / Jupyter Notebook or via nbviewer.
    • Include clearer explanation of start and end calendar options to README Quick Start and an appropriate tutorial. (done #194)

Desired enhancements that are non-breaking:

  • Move internal representation of sessions to nanos (see discussion #87).
  • Real documentation. (#63). Going forward covered by issue #63
  • Break times to test answers. (#64)

ENH: expose get_calenadar kwargs including "side" option

@gerrymanoim, would you entertain a PR that introduces a side parameter to get_calendar to determine the minutes when a calendar should be considered open? Options would be as follows with default as "both" such that existing behaviour would remain unchanged:

  • "left" - treat session open and break_start as open, treat session close and break_end as closed.
  • "right" - treat session open and break_start as closed, treat session close and break_end as open.
  • "both" - treat all of session open, session close, break_start and break_end as open.
  • "neither" - treat none of session open, session close, break_start or break_end as open.

I feel this would give the user useful flexibility.

I've employed a hack to implement the above on something I'm working on. The implementation's fairly simple (it would also be possible to add a side option to methods such as minutes_in_range and minute_to_session_label to override the global side value for any particular call).

I'd happily offer a PR if you think a side option would be a worthy enhancement?

I notice that you previously got together a PR to expose the get_calendar start and end parameters (as referenced from #24). I could introduce this at the same time.

With regards to the cache, occurs to me to either:

  • ditch it
  • keep it but only retrieve from it if kwargs match those of the cached version, otherwise overwrite the cached version with a new calendar instance.

Thoughts?

Annotations

@gerrymanoim, would you have any objections to PRs including annotations to public methods/properties for the purpose of documenting expected types / return type?

Thank you for maintaining this fork. It's a great library!

Renaming of some ExchangeCalendar methods

Over recent PRs (culminating with #71) the ExchangeCalendar's more client-facing (as opposed to calendar-definition) properties/methods have been revised to include parsing, annotation and standardised documentation. Concepts of 'session(s)' and 'minute(s)' are now expressed consistently throughout the class:

  • in Session, Minute and Date type annotations (and anticipated TradingMinute type).
  • within method documentation.
  • within error messages of parsing functions.
  • from release 4.0, it's proposed (#61) that parameter names be consolidated from the various names currently employed to session and minute.
  • The README will be revised to clearly express these concepts (#86).

I'd propose going the final step and renaming the following methods to support this consistency and make the names less verbose (the lexicographical grouping of like methods would also aid users searching for methods from auto-complete menus).

Methods to be renamed to drop '_label' part:

  • previous_session_label to previous_session
  • next_session_label to next_session
  • date_to_session_label to date_to_session
  • minute_to_session_label to minute_to_session

Methods that interrogate a session (all to be consistent with the likes of session_open:

  • open_and_close_for_session to session_open_close.
  • break_start_and_end_for_session to session_break_start_end.
  • minutes_for_session to session_minutes.

Methods that interrogate a range of sessions:

  • session_opens_in_range to sessions_opens.
  • session_closes_in_range to sessions_closes.
  • minutes_for_sessions_in_range to or sessions_minutes.
  • minutes_count_for_sessions_in_range to sessions_minutes_count.
  • session_distance to sessions_distance.

Methods that interrogate a range of minutes:

  • minute_index_to_session_labels to minutes_to_sessions.

Calendar properties:

  • all_sessions to sessions
  • all_minutes to minutes
  • all_minutes_nanos to minutes_nanos
  • first_trading_minute to first_minute.
  • last_trading_minute to last_minute.
  • first_trading_session to already existing property first_session.
  • last_trading_session to already existing property last_session.
  • has_breaks to sessions_has_break (and add genuine property has_break that refers to all calendar sessions).

Calendar attributes:

  • market_opens_nanos to opens_nanos.
  • market_closes_nanos to closes_nanos.
  • market_break_starts_nanos to break_starts_nanos.
  • market_break_ends_nanos to break_ends_nanos.

Renaming Process
Intention would be to include newly named methods (with any newly named parameter names - #61) and deprecate the old-named methods in an upcoming release (3.4?). The deprecated methods would be retained (together with existing parameter names) although revised to:

  • dispatch to the corresponding newly named method.
  • raise a FutureWarning advising of renaming and that the deprecated name will be removed in version 4.0.

#71 adds a deprecate decorator to exchange_calendar.py. The decorator's currently only used for deprecating the execution_mintues_* methods although it can accommodate deprecations for renaming - under which it raises a FutureWarning that advises the user:

  • that the method has been renamed.
  • of the new method name.
  • that the method is deprecated from release 3.4.
  • that the method will be removed in release 4.0.

@gerrymanoim, what are you thoughts? Would you be happy for me to get a PR together prior to 3.4 to put the following name changes in motion?

last_session & last_session_close return 2023 year timestamps

I tested in my pc and colaboratory.
I followed below process.


!pip install exchange_calendars
import exchange_calendars as ecals
import pandas as pd

xkrx = ecals.get_calendar("XKRX")
xkrx.last_session_close


it returns "Timestamp('2023-03-03 06:30:00')"
it also happens in other markets.

Use a timezone-aware object instead.

Hi I received the following error:

volume1/homes/admin/Drive/stock/order/stock_buyer.py:58: FutureWarning: Indexing a timezone-aware DatetimeIndex with a timezone-naive datetime is deprecated and will raise KeyError in a future version. Use a timezone-aware object instead. if ecals.get_calendar("XKLS").is_session(pd.Timestamp(date.today().strftime('%Y-%m-%d'))):

date.today() refers to the local date. Local date is 'Asia/Kuala_lumpur'. Should I do the following instead?
if ecals.get_calendar("XKLS").is_session(pd.Timestamp(date.today().strftime('%Y-%m-%d'), tz='Asia/Kuala_lumpur')):

but if I do that the result is False when it should be True.

Zipline-Trader incompatibilities

Hi Gerry,

Community continues Zipline development in new fork, I noticed you have started to maintain Q trading_calendars under new name but depreciated some items that are currently used by Zipline. Would you be happy to work together to include also features required by zipline trader or we would need to maintain own trading calendar?

If you are interested please join us at Zipline Trader:

Move ExchangeCalendar.exectution_minutes_* methods to QuantopianUSFuturesCalendar ?

@gerrymanoim, what do you think about moving the ExchangeCalendar execution_minutes_for_session and execution_minutes_for_sessions_in_range methods (and the supporting execution_time_from_open and execution_time_from_close) out of ExchangeCalendar (or at least deprecating them) and into 'QuantopianUSFuturesCalendar'?

These methods seem to have been defined specifically for QuantopianUSFuturesCalendar (and indeed for a specific usage case of that calendar) - for all other calendars these methods return the same as the normal minutes_for_session and minutes_for_sessions_in_range methods. Would it not be clearer (particularly in light of the break with Zipline) to lose this clutter from ExchangeCalendar and have these methods represented only on the QuantopianUSFuturesCalendar?

BUG: Futures sessions in range are all empty after 2022-05-18

Hi,
Dates in the future after 2022-5-18 are not considered valid days.

I also just noticed that this is a duplicate of issue #14

import trading_calendars as tc
import exchange_calendars as ec
import pandas as pd
import datetime

tcal = tc.get_calendar("XLON")
ecal = ec.get_calendar("XLON")

print(
    tcal.sessions_in_range(
        pd.Timestamp(datetime.date(2022, 5, 1), tz="UTC"),
        pd.Timestamp(datetime.date(2022, 6, 30), tz="UTC"),
    ),
    ecal.sessions_in_range(
        pd.Timestamp(datetime.date(2022, 5, 1), tz="UTC"),
        pd.Timestamp(datetime.date(2022, 6, 30), tz="UTC"),
    ),
)

Good Friday on XCBF and CFE calendars

I think the XCBF and CFE calendars (they appear to be the same thing with CFE aliased to XCBF) have incorrect dates corresponding to some but not all Good Fridays (4/10/2009, 4/2/2010, 4/3/2015, 3/25/2016, 4/10/2020, 4/2/2021). I checked since 2008 when I have data and six times it appeared that Good Friday was listed as a valid CFE date despite the NYSE being closed. (I spot checked historical data from CBOE, and for example, 4/2/2021 was not a date that a historical futures contract had listed.)

Sessions to tz-naive? Schedule times to UTC?

Another issue following this latest pandas release is that the ExchangeCalendar constructor is now raising a FutureWarning.

The cause is within the creation of the schedule DataFrame, due to conversion of DatetimeIndex values with dtype 'datetime64[ns, UTC]' (self._opens etc.) to 'datetime64[ns]'.

        self.schedule = DataFrame(
            index=_all_days,
            data=OrderedDict(
                [
                    ("market_open", self._opens),
                    ("break_start", self._break_starts),
                    ("break_end", self._break_ends),
                    ("market_close", self._closes),
                ]
            ),
            dtype="datetime64[ns]",
        )

One option would be to change the target dtype to "datetime64[ns, UTC]" thereby defining the schedule columns in terms of UTC. This additional context would be meaningful although I suspect it would open a can of worms with respect to changes it would necessitate within exchange_calendars and its clients. For now I'll add a fix to PR #41 that retains the existing behaviour.

Originally posted by @maread99 in #40 (comment)

`test_prev_next_minute` is needlessly slow

Because it blanket tests the edges of all sessions, the test_prev_next_minute is one of the slowest tests of the suite.

The test could be sped up considerably by focusing on the edges of sessions of Answers.session_blocks (as a number of other tests already do).

If anyone wants to get familiar with the test suite, this would be a good PR to take up (maybe start by having a look at the test inputs available from the Answers class and then at how the tests for other similar methods are implemented).

XSES calendar ends at 2021-12-31

import exchange_calendars as xcals
import pandas as pd

xses = xcals.get_calendar("XSES")

xses.is_session(pd.Timestamp("2022-01-01"))

DateOutOfBounds Traceback (most recent call last)
in
----> 1 xses.is_session(pd.Timestamp("2022-01-01"))

~/anaconda3/lib/python3.7/site-packages/exchange_calendars/exchange_calendar.py in is_session(self, dt, _parse)
1141 """
1142 if _parse:
-> 1143 dt = parse_date(dt, "dt", self)
1144 idx = self._get_date_idx(dt, parse=False)
1145 return bool(self.sessions_nanos[idx] == dt.value) # convert from np.bool

~/anaconda3/lib/python3.7/site-packages/exchange_calendars/calendar_helpers.py in parse_date(date, param_name, calendar, raise_oob)
317 raise ValueError("calendar must be passed if raise_oob is True.")
318 if calendar._date_oob(ts):
--> 319 raise errors.DateOutOfBounds(calendar, ts, param_name)
320
321 return ts

DateOutOfBounds: Parameter dt receieved as '2022-01-01 00:00:00+00:00' although cannot be later than the last session of calendar 'XSES' ('2021-12-31 00:00:00+00:00').

get_calendar 20yr default override

Hi there,
I'm cleaning up a 20yr + data set before attempting my zipline backtesting and I'm calling the following example to get valid trade days:
start = '2000-01-13'
end = '2022-01-13'
nys = xcals.get_calendar("XNYS") # New York Stock Exchange
caldatelist = nys.sessions_in_range(start, end) #valid trading days list
caldatelist

return:
DateOutOfBounds: Parameter start receieved as '2000-01-13 00:00:00+00:00' although cannot be earlier than the first session of calendar 'XNYS' ('2002-01-15 00:00:00+00:00').

It seems that passing the start value isn't overriding the 20yr default as suggested here in a similar issue:
#112

Thank you.

int() argument must be a string, a bytes-like object or a number, not 'NaTType'

I have written a simple program as shown in the screenshot. However, while executing import exchange_calendars as xcals statement, i'm getting the following error ,

 File "C:\Users\Seena\anaconda3\lib\site-packages\exchange_calendars\calendar_helpers.py", line 6, in <module>
    NP_NAT = np.array([pd.NaT], dtype=np.int64)[0]

TypeError: int() argument must be a string, a bytes-like object or a number, not 'NaTType'

the detailed error is shown in screenshot, could someone help me fix this?

My Environment:
Anaconda Navigator 2.1.1
Sypder IDE 5.1.5
Python 3.8.5 64-bit | Qt 5.9.7 | PyQt5 5.9.2 | Windows 10

QuantopianUSFuturesCalendar default start time is 2000-01-01 - is this still an issue?

Within us_futures_calendar.py, the class QuantopianUSFuturesCalendar has a property of default_start returning a Teimstamp dated 2000-01-01, with the comment:
# XXX: Override the default start date. This is a stopgap for memory
# issues caused by upgrading to pandas 18. This calendar is the most
# severely affected since it has the most total minutes of any of the
# zipline calendars.

This is seemingly referencing Pandas 0.18, which was released in 2016, and of course, we're many releases since there.

As exchange-calendars is used by Zipline (Reloaded), this prevents users from backtesting on futures prior to this date, requiring manual patching by the user to extend prior to this.

I'm only working with daily data and there's no noticeble particular issue with that periodicity as far as RAM is concerned, but I'm unable to say whether this is still a problem for other periodicities (such as minutes).

Given we're now 5 yeras later, could this default be extended to, say, 1970-01-01 (or earlier).

The other exchange calendars (such as NYSE) seem to default to the earliest holiday definition. For example, NYSE goes back to 1848 so is probably a non-issue regarding RAM for futures....

XKRX late_opens empty although calendar has late opens...?

Hi @elbakramer, I'm migrating the calendar test modules to a new test base and XKRX is failing a new test that checks late_opens against late opens as evaluated from the answers file.

The calendar appears to have regular late opens although the late_opens property returns an empty DatetimeIndex?

The following example shows the late open on 2021-01-04:

> import exchange_calendars as xcals
> cal = xcals.get_calendar("XKRX", start="2020-12-29", end="2021-01-06")
> cal.schedule
market_open break_start break_end market_close
2020-12-29 00:00:00+00:00 2020-12-29 00:00:00 NaT NaT 2020-12-29 06:30:00
2020-12-30 00:00:00+00:00 2020-12-30 00:00:00 NaT NaT 2020-12-30 06:30:00
2021-01-04 00:00:00+00:00 2021-01-04 01:00:00 NaT NaT 2021-01-04 06:30:00
2021-01-05 00:00:00+00:00 2021-01-05 00:00:00 NaT NaT 2021-01-05 06:30:00
2021-01-06 00:00:00+00:00 2021-01-06 00:00:00 NaT NaT 2021-01-06 06:30:00

yet:

> cal.late_opens
DatetimeIndex([], dtype='datetime64[ns, UTC]', freq=None)

For the default calendar, all the following sessions appear to have late opens...

DatetimeIndex(['1986-01-04 00:00:00+00:00', '1987-01-05 00:00:00+00:00',
               '1988-01-04 00:00:00+00:00', '1989-01-04 00:00:00+00:00',
               '1990-01-04 00:00:00+00:00', '1991-01-03 00:00:00+00:00',
               '1992-01-03 00:00:00+00:00', '1993-01-04 00:00:00+00:00',
               '1993-08-20 00:00:00+00:00', '1993-11-16 00:00:00+00:00',
               '1994-11-23 00:00:00+00:00', '1995-01-03 00:00:00+00:00',
               '1995-11-22 00:00:00+00:00', '1996-01-03 00:00:00+00:00',
               '1996-11-13 00:00:00+00:00', '1997-01-03 00:00:00+00:00',
               '1997-11-19 00:00:00+00:00', '1998-01-03 00:00:00+00:00',
               '1998-11-18 00:00:00+00:00', '1999-01-04 00:00:00+00:00',
               '1999-11-17 00:00:00+00:00', '2000-11-15 00:00:00+00:00',
               '2001-01-02 00:00:00+00:00', '2001-11-07 00:00:00+00:00',
               '2002-01-02 00:00:00+00:00', '2002-11-06 00:00:00+00:00',
               '2003-01-02 00:00:00+00:00', '2003-11-05 00:00:00+00:00',
               '2004-01-02 00:00:00+00:00', '2004-11-17 00:00:00+00:00',
               '2005-01-03 00:00:00+00:00', '2005-11-23 00:00:00+00:00',
               '2006-01-02 00:00:00+00:00', '2006-11-16 00:00:00+00:00',
               '2007-01-02 00:00:00+00:00', '2007-11-15 00:00:00+00:00',
               '2008-01-02 00:00:00+00:00', '2008-11-13 00:00:00+00:00',
               '2009-01-02 00:00:00+00:00', '2009-11-12 00:00:00+00:00',
               '2010-01-04 00:00:00+00:00', '2010-11-18 00:00:00+00:00',
               '2011-01-03 00:00:00+00:00', '2011-11-10 00:00:00+00:00',
               '2012-01-02 00:00:00+00:00', '2012-11-08 00:00:00+00:00',
               '2013-01-02 00:00:00+00:00', '2013-11-07 00:00:00+00:00',
               '2014-01-02 00:00:00+00:00', '2014-11-13 00:00:00+00:00',
               '2015-01-02 00:00:00+00:00', '2015-11-12 00:00:00+00:00',
               '2016-01-04 00:00:00+00:00', '2016-11-17 00:00:00+00:00',
               '2017-01-02 00:00:00+00:00', '2017-11-16 00:00:00+00:00',
               '2017-11-23 00:00:00+00:00', '2018-01-02 00:00:00+00:00',
               '2018-11-15 00:00:00+00:00', '2019-01-02 00:00:00+00:00',
               '2019-11-14 00:00:00+00:00', '2020-01-02 00:00:00+00:00',
               '2020-12-03 00:00:00+00:00', '2021-01-04 00:00:00+00:00',
               '2022-01-03 00:00:00+00:00', '2022-09-07 00:00:00+00:00'],
              dtype='datetime64[ns, UTC]', freq=None)

Break times to answers

For those calendars with breaks, break times should probably be declared as columns of the answers csv files in the same way as opens and closes. Associated tests could then be revised to get their expected values directly from answers.

Path to release 3.4

I'm taking the liberty to assume that the process started with PR #71 (to add a 'side' option and overhaul the test suite) will culminate in cutting 3.4. The process...

  • Complete, review and merge #71.
  • PR to replace ExchangeCalendarTestBase with ExchangeCalendarTestBaseProposal for the first five calendar test modules to which proposed base already incorporated by #71 (to include moving over calendar-specific tests).
  • Move all calendar test modules over to new test base (over various PRs).
    • Migrate all subclasses of EuronextCalendarTestBase together under same commit
    • When all calendars migrated, remove following classes which are no longer required:
      • OpenDetectionTestCase (minute_index_to_session_labels now comprehensively tested under new suite.)
      • NoDSTExchangeCalendarTestBase (this case is now handled by new test base internally.)
    • End by removing now old ExchangeCalendarTestBase and renaming ExchangeCalendarTestBaseNew as ExchangeCalendarTestBase
    • See if can now lose 'parameterized' from development requirements (change setup.py extras_require and update requirements).
    • Merge #96
  • Add new minute-related methods and corresponding tests.
    • PR changes.
  • Review whether to move *minute_nanos(side=None) methods , which can currently override default side, to properties that return for default side only (would require revising minute_index_to_session_labels).
    • Change these to properties. Check if minute_index_to_session_labels requires any revisions.
    • PR changes.
  • Rename ExchangeCalendar methods (as #85).
    • PR changes.
    • Revise/re-run tutorials to accommodate any name changes
    • Revise README to accommodate any name changes.
  • Revise README / introduce tutorials (see 'readme' branch, awaiting PR):
    • PR changes
    • Clearly express concepts of 'session(s}' and 'minute(s)'.
    • Explain 'side' option.
    • Update FAQ covering creating / modifying calendars to reference new .md file introduced by #99.
  • Cut 3.4.

BUG: Trading calendar with end date treats last business day as regular even if it is a special close/open day.

While working on #4, I discovered what is likely a bug in the TradingCalendar base class.

When a calendar, e.g. for XETR or XWBO, is created with end date, say, 2020-12-30, then that day itself always seems to be treated as a regular business day if the exchange is open at all that day.

However, XETR and XWBO have rules in that make the last trading day of a year a special close day. This seems to be ignored in this case. If the end date is chosen to be after the special close day in question, then the special close is again reflected accurately.

The bug seems to manifest itself only in the edge case where the end date is a special open/close day.

@gerrymanoim Can you have look? You're probably quicker at finding the root cause. Let me know if I can help.

sessions_in_range for LEAPS returning incorrect number

Trying to get trading sessions for long term options. Always returns 255 even though it is definitely much more than that. Am I not using the library correctly?

xnys = ecals.get_calendar("XCBF")

trading_days = xnys.sessions_in_range(pd.Timestamp("2021-03-11", tz=pytz.UTC), pd.Timestamp("2023-01-20", tz=pytz.UTC))

print(len(trading_days))

255

thanks!

minute_to_session_label wraps from first to last session

Requesting a session label for a minute prior to first calendar session, with direction as 'previous', returns the last calendar session...

>>> import trading_calendars as tc
>>> import pandas as pd
>>> c = tc.get_calendar('XNYS')
>>> ts = c.first_session - pd.Timedelta(days=1)
>>> session = c.minute_to_session_label(ts, direction='previous')
>>> session == c.last_session
True

Would have expected a ValueError similar in manner to that raised by:

>>> c.previous_session_label(c.first_session)
ValueError: There is no previous session as this is the beginning of the exchange calendar.

version: 2.1.1

todo when min support moves to python 3.8

  • lose memoize.py \ @lazyval in favour of @functools.cached_property
  • on Answers and ExchangeCalendar classes consolidate properties returning functions decorated @functools.lru_cache with simple properties decorated with @functools.cached_property
  • use Literal to annotate selection of string values, to include at least:
    • direction parameter of ExchangeCalendar, minute_to_session_label and date_to_session_label methods as Literal["next", "previous", "none"]
    • side parameter of ExchangeCalendar, test_exchange_calendar.py and calendar_helper.py methods as Literal["left", "right", "both", "neither"]
    • column parameter of Answers._get_sessions_with_times_different_to_a_contiguous_session as Literal["opens", "closes", "break_starts", "break_ends"]

Enforce minimum python version

There seems to be an import issue using version 3.2 that isn't in 3.1:

I imagine it is something new that is missing in python 3.6, but could a patch be made to support older python 3 versions?

annotation import failure

todo when min support moves to python 3.9

  • ExchangeCalendar class methods valid_sides and default_side to be changed to class properties (like this). EDIT 06/06/22 - NOTE: seems that defining class properties in this way may not be an option from 3.11 (same reference). If it's not going to stable then leave as is.
  • Remove following ExchangeCalendar methods deprecated in 4.0 (will be about time to remove them).
    • sessions_opens
    • sessions_closes
  • Check if it's still necessary to have the 'near-empty' setup.py file to allow for editable installs when defining meta in pyproject.toml (reference). Remove setup.py if it's no longer needed, otherwise roll this item forwards to review when bumping min support to 3.10.
  • Drop pytz in favor of zoneinfo standard library (either now or later). Looks likely that pandas 2.0 will switch to zoneinfo as its default timezone package. If it looks like pandas 2.0 could be released any time soon we might want to address this now. If it looks still to be a way off then we could roll this item forwards (to dedicated issue), although in any event pandas already supports zoneinfo and we'd be better off depending on a stdlib library than a third-party one. See #322.

On bumping min version to 3.9, roll anything not addressed to a new issue 'TODO when min support moves to python 3.10'.

Error with latest version of Pandas

I recently updated my packages, and now when calling from exchange_calendars import get_calendar, I get the following error:

Traceback (most recent call last):
  File "test.py", line 1, in <module>
    from exchange_calendars import get_calendar
  File "/opt/miniconda3/lib/python3.7/site-packages/exchange_calendars/__init__.py", line 16, in <module>
    from .calendar_utils import (
  File "/opt/miniconda3/lib/python3.7/site-packages/exchange_calendars/calendar_utils.py", line 3, in <module>
    from .always_open import AlwaysOpenCalendar
  File "/opt/miniconda3/lib/python3.7/site-packages/exchange_calendars/always_open.py", line 5, in <module>
    from .exchange_calendar import ExchangeCalendar
  File "/opt/miniconda3/lib/python3.7/site-packages/exchange_calendars/exchange_calendar.py", line 27, in <module>
    from .calendar_helpers import (
  File "/opt/miniconda3/lib/python3.7/site-packages/exchange_calendars/calendar_helpers.py", line 6, in <module>
    NP_NAT = np.array([pd.NaT], dtype=np.int64)[0]
TypeError: int() argument must be a string, a bytes-like object or a number, not 'NaTType'

This occurs both on macOS with Python 3.7 and Ubuntu with Python 3.8.

End of calendar reached for XETR and XFRA

Sorry, if this might be a stupid question, since I don't know how the calendars are implemented. However, when trying to check the trading days for the next year 2022, I ran into an error, with the defined end being later than the last available calendar date.
Please see the following code example, which triggers the error:

import exchange_calendars as ecal
cal_FRA = ecal.get_calendar("XFRA")
cal_XETR = ecal.get_calendar("XETR")

start, end = "2022-01-01", "2022-12-31"
df_trading_days_xetra =  pd.DataFrame((cal_XETR.trading_index(start, end, "24h", intervals=False)), columns=['Dates'])
print(df_trading_days_xetra)

This results in the following error

Traceback (most recent call last): File "R:\Zentrale\ZB-S\Daten\S5\S52\AD\JL\python\python_eikon\join_test.py", line 63, in <module> df_trading_days_xetra = pd.DataFrame((cal_XETR.trading_index(start, end, "24h", intervals=False)), columns=['Dates']) File "C:\Workspace\Python_Runtime\Envs\python_eikon\lib\site-packages\exchange_calendars\exchange_calendar.py", line 2347, in trading_index start, end = self._parse_start_end_dates(start, end, parse) File "C:\Workspace\Python_Runtime\Envs\python_eikon\lib\site-packages\exchange_calendars\exchange_calendar.py", line 1980, in _parse_start_end_dates return parse_date(start, "start", self), parse_date(end, "end", self) File "C:\Workspace\Python_Runtime\Envs\python_eikon\lib\site-packages\exchange_calendars\calendar_helpers.py", line 323, in parse_date raise errors.DateOutOfBounds(calendar, ts, param_name) exchange_calendars.errors.DateOutOfBounds: Parameter end receieved as '2022-12-31 00:00:00+00:00' although cannot be later than the last session of calendar 'XETR' ('2022-12-29 00:00:00+00:00').

When changing the start date to days before 2022-12-28, the dataframe gets created without any issue.

XTAI is missing 2022-01-27 as a holiday

import exchange_calendars as xcals
xtai = xcals.get_calendar("XTAI")
xtai.schedule.loc["2022-01-01":"2022-02-01"]
Out[4]: 
                                  market_open  ...        market_close
2022-01-03 00:00:00+00:00 2022-01-03 01:00:00  ... 2022-01-03 05:30:00
2022-01-04 00:00:00+00:00 2022-01-04 01:00:00  ... 2022-01-04 05:30:00
2022-01-05 00:00:00+00:00 2022-01-05 01:00:00  ... 2022-01-05 05:30:00
2022-01-06 00:00:00+00:00 2022-01-06 01:00:00  ... 2022-01-06 05:30:00
2022-01-07 00:00:00+00:00 2022-01-07 01:00:00  ... 2022-01-07 05:30:00
2022-01-10 00:00:00+00:00 2022-01-10 01:00:00  ... 2022-01-10 05:30:00
2022-01-11 00:00:00+00:00 2022-01-11 01:00:00  ... 2022-01-11 05:30:00
2022-01-12 00:00:00+00:00 2022-01-12 01:00:00  ... 2022-01-12 05:30:00
2022-01-13 00:00:00+00:00 2022-01-13 01:00:00  ... 2022-01-13 05:30:00
2022-01-14 00:00:00+00:00 2022-01-14 01:00:00  ... 2022-01-14 05:30:00
2022-01-17 00:00:00+00:00 2022-01-17 01:00:00  ... 2022-01-17 05:30:00
2022-01-18 00:00:00+00:00 2022-01-18 01:00:00  ... 2022-01-18 05:30:00
2022-01-19 00:00:00+00:00 2022-01-19 01:00:00  ... 2022-01-19 05:30:00
2022-01-20 00:00:00+00:00 2022-01-20 01:00:00  ... 2022-01-20 05:30:00
2022-01-21 00:00:00+00:00 2022-01-21 01:00:00  ... 2022-01-21 05:30:00
2022-01-24 00:00:00+00:00 2022-01-24 01:00:00  ... 2022-01-24 05:30:00
2022-01-25 00:00:00+00:00 2022-01-25 01:00:00  ... 2022-01-25 05:30:00
2022-01-26 00:00:00+00:00 2022-01-26 01:00:00  ... 2022-01-26 05:30:00
2022-01-27 00:00:00+00:00 2022-01-27 01:00:00  ... 2022-01-27 05:30:00

while https://www.tradinghours.com/markets/twse/holidays indicates 2022-01-27 is a holiday for TWSE.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.