diff --git a/.github/config_new_release.yml b/.github/config_new_release.yml index 08fc849..4e950d7 100644 --- a/.github/config_new_release.yml +++ b/.github/config_new_release.yml @@ -1,4 +1,3 @@ - new_version: '4.3.2' change_log: | diff --git a/.github/workflows/check_branch.yml b/.github/workflows/check_branch.yml index ef5ab8f..a2a0ac2 100644 --- a/.github/workflows/check_branch.yml +++ b/.github/workflows/check_branch.yml @@ -1,19 +1,19 @@ - name: check_branch - on: - pull_request_target: - branches: - - master - - workflow_dispatch: +name: check_branch +on: + pull_request_target: + branches: + - master - jobs: - wrong_branch: - runs-on: ubuntu-latest - if: ${{ github.event.pull_request.head.repo.fork || github.head_ref != 'dev' }} - - steps: - - uses: superbrothers/close-pull-request@v3 - with: - comment: Please re-open this PR against the dev branch. For more information, consult CONTRIBUTING.md. - - run: echo PR against master that is not based on dev && exit 1 + workflow_dispatch: + +jobs: + wrong_branch: + runs-on: ubuntu-latest + if: ${{ github.event.pull_request.head.repo.fork || github.head_ref != 'dev' }} + + steps: + - uses: superbrothers/close-pull-request@v3 + with: + comment: Please re-open this PR against the dev branch. For more information, consult CONTRIBUTING.md. + - run: echo PR against master that is not based on dev && exit 1 diff --git a/.github/workflows/releaser.yml b/.github/workflows/releaser.yml index 0d49c22..9b81412 100644 --- a/.github/workflows/releaser.yml +++ b/.github/workflows/releaser.yml @@ -1,52 +1,52 @@ name: releaser on: push: - branches: [master] + branches: [ master ] env: NEWV: '' OLDV: '' -jobs: +jobs: run_tests: uses: ./.github/workflows/test_runner.yml make_new_release: needs: run_tests - runs-on: ubuntu-latest - + runs-on: ubuntu-latest + steps: - uses: actions/checkout@v3 - - name: get new_version + - name: get new_version id: new_version - uses: KJ002/read-yaml@1.6 + uses: KJ002/read-yaml@1.6 with: - file: .github/config_new_release.yml + file: .github/config_new_release.yml key-path: '["new_version"]' - + - name: get_new_current_version - run: | + run: | echo "NEWV=${{ steps.new_version.outputs.data }}" >> $GITHUB_ENV echo "OLDV=$(grep "VERSION" -m1 pyproject.toml | cut -d"=" -f2 | sed "s/['\" ]//g")" >> $GITHUB_ENV - + - name: verify ${{env.NEWV}} > ${{env.OLDV}} if: ${{ env.NEWV <= env.OLDV }} run: echo you did not increment the version number && exit 1 - + - name: get changes id: changes - uses: KJ002/read-yaml@1.6 + uses: KJ002/read-yaml@1.6 with: - file: .github/config_new_release.yml - key-path: '["change_log"]' - + file: .github/config_new_release.yml + key-path: '["change_log"]' + - name: get body id: body - uses: KJ002/read-yaml@1.6 + uses: KJ002/read-yaml@1.6 with: - file: .github/config_new_release.yml - key-path: '["release_body"]' - + file: .github/config_new_release.yml + key-path: '["release_body"]' + - name: update change log run: | sed 5q docs/change_log.rst > docs/new_log.rst @@ -58,8 +58,8 @@ jobs: mv docs/new_log.rst docs/change_log.rst - name: set up new version - run: | - sed -i "s/$OLDV/$NEWV/" pyproject.toml + run: | + sed -i "s/$OLDV/$NEWV/" pyproject.toml - name: Set up Python uses: actions/setup-python@v4 @@ -70,7 +70,7 @@ jobs: run: | python -m pip install --upgrade pip pip install build twine jupyter pytest - + - name: build package run: python -m build @@ -78,36 +78,36 @@ jobs: with: user: __token__ password: ${{ secrets.PYPI_TOKEN }} - + - name: install from pypi run: | - while [ "$NEWV" != $(pip index versions pandas_market_calendars | cut -d'(' -f2 | cut -d')' -f1 | sed 1q) ];\ - do echo not found yet, sleeping 5s; sleep 5s; done - pip install pandas_market_calendars==$NEWV + while [ "$NEWV" != $(pip index versions pandas_market_calendars | cut -d'(' -f2 | cut -d')' -f1 | sed 1q) ];\ + do echo not found yet, sleeping 5s; sleep 5s; done + pip install pandas_market_calendars==$NEWV - name: run tests run: | - pip install . - mv pandas_market_calendars pandas_market_calendars_copy - python -c 'import pandas_market_calendars;print(pandas_market_calendars.__version__)' - pytest tests - + pip install . + mv pandas_market_calendars pandas_market_calendars_copy + python -c 'import pandas_market_calendars;print(pandas_market_calendars.__version__)' + pytest tests + - name: prepare usage.rst run: | sudo apt install pandoc jupyter nbconvert --execute --ExecutePreprocessor.kernel_name='python3' --to rst --output-dir docs --output usage.rst examples/usage.ipynb - + - uses: stefanzweifel/git-auto-commit-action@v4 with: file_pattern: pyproject.toml docs/change_log.rst docs/usage.rst commit_message: '[GH-Actions] v${{ env.NEWV }} -- updated configuration and documentation files.' - - - name: Create Release + + - name: Create Release uses: softprops/action-gh-release@v1 with: name: v${{ env.NEWV }} tag_name: v${{ env.NEWV }} - body: | + body: | Changes: ${{ steps.changes.outputs.data }} diff --git a/.github/workflows/test_release.yml b/.github/workflows/test_release.yml index 393182f..c53bd43 100644 --- a/.github/workflows/test_release.yml +++ b/.github/workflows/test_release.yml @@ -1,88 +1,88 @@ - name: test_release - on: - pull_request: - branches: [master] - - workflow_dispatch: - - env: - NEWV: "" - OLDV: "" - - jobs: - check: - runs-on: ubuntu-latest - steps: - - if: ${{ github.event.pull_request.head.repo.fork || github.head_ref != 'dev' }} - run: echo PR against main that is not based on dev && exit 1 - - run_tests: - needs: check - uses: ./.github/workflows/test_runner.yml +name: test_release +on: + pull_request: + branches: [ master ] + + workflow_dispatch: + +env: + NEWV: "" + OLDV: "" + +jobs: + check: + runs-on: ubuntu-latest + steps: + - if: ${{ github.event.pull_request.head.repo.fork || github.head_ref != 'dev' }} + run: echo PR against main that is not based on dev && exit 1 + + run_tests: + needs: check + uses: ./.github/workflows/test_runner.yml - test_new_release: - needs: run_tests - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: get new_version - id: new_version - uses: KJ002/read-yaml@1.6 - with: - file: .github/config_new_release.yml - key-path: '["new_version"]' - - - name: get_new_current_version - run: | - echo "NEWV=${{ steps.new_version.outputs.data }}" >> $GITHUB_ENV - echo "OLDV=$(grep "VERSION" -m1 pyproject.toml | cut -d"=" -f2 | sed "s/['\" ]//g")" >> $GITHUB_ENV - - - name: warn_no_version - if: ${{ env.NEWV <= env.OLDV }} - uses: thollander/actions-comment-pull-request@v1 - with: - message: WARNING - Version number in change_log is not incremented. Will not test release. - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: exit_no_version - if: ${{ env.NEWV <= env.OLDV }} - run: echo you did not update the change_log && exit 1 - - - name: make rc vnumber - run: echo "NEWV=${{env.NEWV}}rc${{github.run_number}}.dev${{github.run_attempt}}" >> $GITHUB_ENV - - - name: set version - run: | - echo release candidate: $NEWV - sed -i "s/$OLDV/$NEWV/" pyproject.toml - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.10" - - - name: Install dependencies - run: | - pip install --upgrade pip - pip install build twine pytest - - - name: Build source distribution and wheel files - run: python -m build - - - name: Upload files to TestPyPI - run: python -m twine upload --verbose --repository testpypi dist/* -u__token__ -p${{ secrets.TEST_PYPI_TOKEN }} - - - name: Install from testpypi - run: | - while [ "$NEWV" != $(pip index versions -i https://test.pypi.org/simple --pre pandas_market_calendars | cut -d'(' -f2 | cut -d')' -f1 | sed 1q) ];\ - do echo not found yet, sleeping 5s; sleep 5s; done - pip install -i https://test.pypi.org/simple pandas_market_calendars==$NEWV --no-deps - - - name: test new release - run: | - pip install . - mv pandas_market_calendars pandas_market_calendars_copy - python -c 'import pandas_market_calendars;print(pandas_market_calendars.__version__)' - pytest tests + test_new_release: + needs: run_tests + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: get new_version + id: new_version + uses: KJ002/read-yaml@1.6 + with: + file: .github/config_new_release.yml + key-path: '["new_version"]' + + - name: get_new_current_version + run: | + echo "NEWV=${{ steps.new_version.outputs.data }}" >> $GITHUB_ENV + echo "OLDV=$(grep "VERSION" -m1 pyproject.toml | cut -d"=" -f2 | sed "s/['\" ]//g")" >> $GITHUB_ENV + + - name: warn_no_version + if: ${{ env.NEWV <= env.OLDV }} + uses: thollander/actions-comment-pull-request@v1 + with: + message: WARNING - Version number in change_log is not incremented. Will not test release. + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: exit_no_version + if: ${{ env.NEWV <= env.OLDV }} + run: echo you did not update the change_log && exit 1 + + - name: make rc vnumber + run: echo "NEWV=${{env.NEWV}}rc${{github.run_number}}.dev${{github.run_attempt}}" >> $GITHUB_ENV + + - name: set version + run: | + echo release candidate: $NEWV + sed -i "s/$OLDV/$NEWV/" pyproject.toml + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + + - name: Install dependencies + run: | + pip install --upgrade pip + pip install build twine pytest + + - name: Build source distribution and wheel files + run: python -m build + + - name: Upload files to TestPyPI + run: python -m twine upload --verbose --repository testpypi dist/* -u__token__ -p${{ secrets.TEST_PYPI_TOKEN }} + + - name: Install from testpypi + run: | + while [ "$NEWV" != $(pip index versions -i https://test.pypi.org/simple --pre pandas_market_calendars | cut -d'(' -f2 | cut -d')' -f1 | sed 1q) ];\ + do echo not found yet, sleeping 5s; sleep 5s; done + pip install -i https://test.pypi.org/simple pandas_market_calendars==$NEWV --no-deps + + - name: test new release + run: | + pip install . + mv pandas_market_calendars pandas_market_calendars_copy + python -c 'import pandas_market_calendars;print(pandas_market_calendars.__version__)' + pytest tests diff --git a/.github/workflows/test_runner.yml b/.github/workflows/test_runner.yml index 3602ec3..bfcde14 100644 --- a/.github/workflows/test_runner.yml +++ b/.github/workflows/test_runner.yml @@ -1,18 +1,18 @@ name: test_runner -on: - pull_request: - branches: [dev] +on: + pull_request: + branches: [ dev ] workflow_call: workflow_dispatch: - + jobs: run_tests: runs-on: ubuntu-latest strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ['3.8', '3.9', '3.10', '3.11'] - + os: [ ubuntu-latest, windows-latest, macos-latest ] + python-version: [ '3.8', '3.9', '3.10', '3.11' ] + env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -21,9 +21,9 @@ jobs: - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - + - name: install dependencies - run: | + run: | python -m pip install --upgrade pip pip install pytest coveralls black===23.7.0 pip install . @@ -32,10 +32,10 @@ jobs: run: python -m black --check pandas_market_calendars tests - name: run tests - run: | + run: | coverage run --source=pandas_market_calendars -m pytest tests coverage lcov -o coverage.lcov - + - name: coveralls parallel uses: coverallsapp/github-action@master with: @@ -49,9 +49,9 @@ jobs: needs: run_tests runs-on: ubuntu-latest - steps: + steps: - uses: actions/checkout@v3 - + - name: coveralls finalize id: coveralls_finalize uses: coverallsapp/github-action@master diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 15d347d..6d5638b 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,5 +1,5 @@ # Contributor Code of Conduct -This project adheres to No Code of Conduct. We are all adults. We accept anyone's contributions. Nothing else matters. +This project adheres to No Code of Conduct. We are all adults. We accept anyone's contributions. Nothing else matters. For more information please visit the [No Code of Conduct](https://nocodeofconduct.com) homepage. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 78fe1d4..7f35692 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,18 @@ # Contributing -This document outlines the ways to contribute to `pandas_market_calendars`. This is a fairly small, low-traffic project, so most of the contribution norms (coding style, acceptance criteria) have been developed ad hoc and this document will not be exhaustive. If you are interested in contributing code or documentation, please take a moment to at least review the license section to understand how your code will be licensed. +This document outlines the ways to contribute to `pandas_market_calendars`. This is a fairly small, low-traffic project, so most of the contribution norms (coding style, acceptance criteria) have been +developed ad hoc and this document will not be exhaustive. If you are interested in contributing code or documentation, please take a moment to at least review the license section to understand how +your code will be licensed. ## Types of contribution ### Bug reports -Bug reports are an important type of contribution - it's important to get feedback about how the library is failing, and there's no better way to do that than to hear about real-life failure cases. A good bug report will include: -1. A minimal, reproducible example - a small, self-contained script that can reproduce the behavior is the best way to get your bug fixed. For more information and tips on how to structure these, read [Stack Overflow's guide to creating a minimal, complete, verified example](https://stackoverflow.com/help/mcve). +Bug reports are an important type of contribution - it's important to get feedback about how the library is failing, and there's no better way to do that than to hear about real-life failure cases. A +good bug report will include: + +1. A minimal, reproducible example - a small, self-contained script that can reproduce the behavior is the best way to get your bug fixed. For more information and tips on how to structure these, + read [Stack Overflow's guide to creating a minimal, complete, verified example](https://stackoverflow.com/help/mcve). 2. The platform and versions of everything involved, at a minimum please include operating system, `python` version and `pandas_market_calendars` version. Instructions on getting your versions: - `pandas_market_calendars`: `python -c "import pandas_market_calendars; print(pandas_market_calendars.__version__)"` @@ -19,11 +24,12 @@ While pull requests fixing bugs are accepted, they are *not* required - the bug ### Feature requests -If you would like to see a new feature in `pandas_market_calendars`, it is probably best to start an issue for discussion rather than taking the time to implement a feature which may or may not be appropriate for `pandas_market_calendars`'s API. For minor features (ones where you don't have to put a lot of effort into the PR), a pull request is fine but still not necessary. +If you would like to see a new feature in `pandas_market_calendars`, it is probably best to start an issue for discussion rather than taking the time to implement a feature which may or may not be +appropriate for `pandas_market_calendars`'s API. For minor features (ones where you don't have to put a lot of effort into the PR), a pull request is fine but still not necessary. ### Pull requests -Please format all code using black. +Please format all code using black. If you create an editable install with the dev extras you can get a pre-commit hook set-up. @@ -35,8 +41,8 @@ pip install -e .[dev] pre-commit install ``` -If you would like to fix something in `pandas_market_calendars` - improvements to documentation, bug fixes, feature implementations, etc - pull requests are welcome! +If you would like to fix something in `pandas_market_calendars` - improvements to documentation, bug fixes, feature implementations, etc - pull requests are welcome! -All pull requests should be opened against the `dev` branch and should include a clear and concise summary of the changes you made. +All pull requests should be opened against the `dev` branch and should include a clear and concise summary of the changes you made. The most important thing to include in your pull request are *tests* - please write one or more tests to cover the behavior you intend your patch to improve. diff --git a/docs/calendars.rst b/docs/calendars.rst index 8f65a1d..3627524 100644 --- a/docs/calendars.rst +++ b/docs/calendars.rst @@ -32,8 +32,9 @@ Futures Calendars ========== ================= =================================== ============ ============ CME CME_Equity CMEEquityExchangeCalendar Yes rsheftel CME CME_Bond CMEBondExchangeCalendar Yes rsheftel -CME CME_Agricultural CMEAgriculturalExchangeCalendar Yes lionelyoung +CME CME_Agriculture CMEAgriculturalExchangeCalendar Yes lionelyoung CME CME Globex Crypto CMEGlobexCryptoExchangeCalendar Yes Coinbase Asset Management +EUREX EUREX_Bond EUREXFixedIncomeCalendar Yes rundef ========== ================= =================================== ============ ============ Bond Market Calendars diff --git a/docs/change_log.rst b/docs/change_log.rst index 14ee615..668d428 100644 --- a/docs/change_log.rst +++ b/docs/change_log.rst @@ -3,6 +3,11 @@ Change Log Updates ------- +4.3.3 (12/30/2023) +~~~~~~~~~~~~~~~~~~ +- PR #310 to add EUREX Fixed Income calendars +- PR #311 to add good friday special closes for CME Globex + 4.3.2 (12/09/2023) ~~~~~~~~~~~~~~~~~~ - Reformat all code using Black and make black a standard PR #290 diff --git a/docs/conf.py b/docs/conf.py index 26881e8..eb63fad 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -2,9 +2,9 @@ # -*- coding: utf-8 -*- # -- Project information ----------------------------------------------------- -project = 'pandas_market_calendars' -copyright = '2016, Ryan Sheftel' -author = 'Ryan Sheftel' +project = "pandas_market_calendars" +copyright = "2016, Ryan Sheftel" +author = "Ryan Sheftel" # -- General configuration ------------------------------------------------ @@ -12,26 +12,26 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.autosummary', - 'sphinx.ext.todo', + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx.ext.todo", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'index' +master_doc = "index" # generate docstring for __init__ -autoclass_content = 'both' +autoclass_content = "both" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build', '**.ipynb_checkpoints'] +exclude_patterns = ["_build", "**.ipynb_checkpoints"] # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True diff --git a/docs/index.rst b/docs/index.rst index d20ea7e..c22fbdf 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,5 @@ .. pandas_market_calendars documentation master file, created by - sphinx-quickstart on Tue Dec 27 08:02:38 2016. +sphinx-quickstart on Tue Dec 27 08:02:38 2016. .. include:: ../README.rst diff --git a/pandas_market_calendars/calendar_registry.py b/pandas_market_calendars/calendar_registry.py index 81a385a..ded8e70 100644 --- a/pandas_market_calendars/calendar_registry.py +++ b/pandas_market_calendars/calendar_registry.py @@ -14,6 +14,7 @@ from .calendars.cme_globex_fx import CMEGlobexFXExchangeCalendar from .calendars.cme_globex_fixed_income import CMEGlobexFixedIncomeCalendar from .calendars.eurex import EUREXExchangeCalendar +from .calendars.eurex_fixed_income import EUREXFixedIncomeCalendar from .calendars.hkex import HKEXExchangeCalendar from .calendars.ice import ICEExchangeCalendar from .calendars.iex import IEXExchangeCalendar diff --git a/pandas_market_calendars/calendar_utils.py b/pandas_market_calendars/calendar_utils.py index 94c6e38..5ec357e 100644 --- a/pandas_market_calendars/calendar_utils.py +++ b/pandas_market_calendars/calendar_utils.py @@ -4,8 +4,8 @@ import itertools import warnings -import pandas as pd import numpy as np +import pandas as pd def merge_schedules(schedules, how="outer"): @@ -104,9 +104,9 @@ class _date_range: """ def __init__(self, schedule=None, frequency=None, closed="right", force_close=True): - if not closed in ("left", "right", "both", None): + if closed not in ("left", "right", "both", None): raise ValueError("closed must be 'left', 'right', 'both' or None.") - elif not force_close in (True, False, None): + elif force_close not in (True, False, None): raise ValueError("force_close must be True, False or None.") self.closed = closed @@ -127,11 +127,11 @@ def __init__(self, schedule=None, frequency=None, closed="right", force_close=Tr if "break_start" in schedule: if not all( - [ - schedule.market_open.le(schedule.break_start).all(), - schedule.break_start.le(schedule.break_end).all(), - schedule.break_end.le(schedule.market_close).all(), - ] + [ + schedule.market_open.le(schedule.break_start).all(), + schedule.break_start.le(schedule.break_end).all(), + schedule.break_end.le(schedule.market_close).all(), + ] ): raise ValueError( "Not all rows match the condition: " @@ -152,9 +152,9 @@ def _check_overlap(self, schedule): if end_times.gt(schedule.start.shift(-1)).any(): raise ValueError( - f"The chosen frequency will lead to overlaps in the calculated index. " - f"Either choose a higher frequency or avoid setting force_close to None " - f"when setting closed to 'right', 'both' or None." + "The chosen frequency will lead to overlaps in the calculated index. " + "Either choose a higher frequency or avoid setting force_close to None " + "when setting closed to 'right', 'both' or None." ) def _check_disappearing_session(self, schedule): @@ -190,21 +190,21 @@ def _calc_time_series(self, schedule): if self.closed == "left": opens = schedule.start.repeat(num_bars) # keep as is time_series = ( - opens.groupby(opens.index).cumcount() - ) * self.frequency + opens + opens.groupby(opens.index).cumcount() + ) * self.frequency + opens elif self.closed == "right": opens = schedule.start.repeat(num_bars) # dont add row but shift up time_series = ( - opens.groupby(opens.index).cumcount() + 1 - ) * self.frequency + opens + opens.groupby(opens.index).cumcount() + 1 + ) * self.frequency + opens else: num_bars += 1 opens = schedule.start.repeat(num_bars) # add row but dont shift up time_series = ( - opens.groupby(opens.index).cumcount() - ) * self.frequency + opens + opens.groupby(opens.index).cumcount() + ) * self.frequency + opens - if not self.force_close is None: + if self.force_close is not None: time_series = time_series[time_series.le(schedule.end.repeat(num_bars))] if self.force_close: time_series = pd.concat([time_series, schedule.end]).sort_values() diff --git a/pandas_market_calendars/calendars/bse.py b/pandas_market_calendars/calendars/bse.py index a53b432..4f367bc 100644 --- a/pandas_market_calendars/calendars/bse.py +++ b/pandas_market_calendars/calendars/bse.py @@ -2,11 +2,12 @@ Bombay Stock Exchnage """ +from datetime import time + from pandas import Timestamp from pytz import timezone -from datetime import time -from pandas_market_calendars.market_calendar import MarketCalendar +from pandas_market_calendars.market_calendar import MarketCalendar BSEClosedDay = [ Timestamp("1997-01-23", tz="UTC"), @@ -364,8 +365,7 @@ Timestamp("2023-04-14", tz="UTC"), # Fri, Ambedkar Jayanti Timestamp("2023-04-22", tz="UTC"), # Sat, EID AL FITR Timestamp("2023-05-01", tz="UTC"), # Mon, Maharashtra Din - Timestamp("2023-06-28", tz="UTC"), # Wed, Bakri Id / Eid ul-Adha - Timestamp("2023-06-29", tz="UTC"), # GitHub Issue #277 + Timestamp("2023-06-29", tz="UTC"), # Wed, Bakri Id / Eid ul-Adha Timestamp("2023-08-15", tz="UTC"), # Tue, Independence Day Timestamp("2023-09-19", tz="UTC"), # Tue, Ganesh Chaturthi Timestamp("2023-10-02", tz="UTC"), # Mon, Gandhi Jayanti diff --git a/pandas_market_calendars/calendars/cboe.py b/pandas_market_calendars/calendars/cboe.py index 4e20b9f..4858c95 100644 --- a/pandas_market_calendars/calendars/cboe.py +++ b/pandas_market_calendars/calendars/cboe.py @@ -1,5 +1,7 @@ from datetime import time +from itertools import chain +import pandas as pd from pandas.tseries.holiday import ( AbstractHolidayCalendar, GoodFriday, @@ -9,8 +11,6 @@ Holiday, ) from pytz import timezone -from itertools import chain -import pandas as pd from pandas_market_calendars.holidays.us import ( Christmas, diff --git a/pandas_market_calendars/calendars/cme_globex_agriculture.py b/pandas_market_calendars/calendars/cme_globex_agriculture.py index 4b4cce0..c19a07d 100644 --- a/pandas_market_calendars/calendars/cme_globex_agriculture.py +++ b/pandas_market_calendars/calendars/cme_globex_agriculture.py @@ -14,8 +14,6 @@ # limitations under the License. from abc import abstractmethod -from .cme_globex_base import CMEGlobexBaseExchangeCalendar - from datetime import time from pandas.tseries.holiday import ( @@ -36,6 +34,7 @@ USMemorialDay, USNewYearsDay, ) +from .cme_globex_base import CMEGlobexBaseExchangeCalendar class CMEGlobexAgricultureExchangeCalendar(CMEGlobexBaseExchangeCalendar): diff --git a/pandas_market_calendars/calendars/cme_globex_crypto.py b/pandas_market_calendars/calendars/cme_globex_crypto.py index 96d1978..d8195b7 100644 --- a/pandas_market_calendars/calendars/cme_globex_crypto.py +++ b/pandas_market_calendars/calendars/cme_globex_crypto.py @@ -1,12 +1,12 @@ import datetime as dt -from pandas.tseries.holiday import AbstractHolidayCalendar import pytz +from pandas.tseries.holiday import AbstractHolidayCalendar -from .cme_globex_base import CMEGlobexBaseExchangeCalendar from pandas_market_calendars.holidays.cme import ( GoodFriday2021, - GoodFridayAfter2021, + GoodFriday2022, + GoodFridayAfter2022, GoodFridayBefore2021, USIndependenceDayBefore2022PreviousDay, ) @@ -32,6 +32,7 @@ ChristmasEveInOrAfter1993, USNewYearsDay, ) +from .cme_globex_base import CMEGlobexBaseExchangeCalendar # https://github.com/rsheftel/pandas_market_calendars/blob/master/docs/new_market.rst @@ -87,7 +88,7 @@ def regular_holidays(self): return AbstractHolidayCalendar( rules=[ GoodFridayBefore2021, - GoodFridayAfter2021, + GoodFriday2022, ChristmasCME, USNewYearsDay, ] @@ -100,7 +101,19 @@ def special_closes(self): return [ ( dt.time(8, 15, tzinfo=pytz.timezone("America/Chicago")), - AbstractHolidayCalendar(rules=[GoodFriday2021]), + AbstractHolidayCalendar( + rules=[ + GoodFriday2021, + ] + ), + ), + ( + dt.time(10, 15, tzinfo=pytz.timezone("America/Chicago")), + AbstractHolidayCalendar( + rules=[ + GoodFridayAfter2022, + ] + ), ), ( dt.time(12, tzinfo=pytz.timezone("America/Chicago")), diff --git a/pandas_market_calendars/calendars/cme_globex_energy_and_metals.py b/pandas_market_calendars/calendars/cme_globex_energy_and_metals.py index 2b29a74..9621123 100644 --- a/pandas_market_calendars/calendars/cme_globex_energy_and_metals.py +++ b/pandas_market_calendars/calendars/cme_globex_energy_and_metals.py @@ -13,8 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from .cme_globex_base import CMEGlobexBaseExchangeCalendar - from datetime import time from pandas.tseries.holiday import ( @@ -22,10 +20,6 @@ ) # , GoodFriday, USLaborDay, USPresidentsDay, USThanksgivingDay from pytz import timezone -# from .holidays_us import (Christmas, ChristmasEveBefore1993, ChristmasEveInOrAfter1993, USBlackFridayInOrAfter1993, -# USIndependenceDay, USMartinLutherKingJrAfter1998, USMemorialDay, USJuneteenthAfter2022, -# USNationalDaysofMourning, USNewYearsDay) - from pandas_market_calendars.holidays.cme_globex import ( USMartinLutherKingJrFrom2022, USMartinLutherKingJrPre2022, @@ -44,6 +38,12 @@ FridayAfterThanksgiving, ChristmasCME, ) +from .cme_globex_base import CMEGlobexBaseExchangeCalendar + + +# from .holidays_us import (Christmas, ChristmasEveBefore1993, ChristmasEveInOrAfter1993, USBlackFridayInOrAfter1993, +# USIndependenceDay, USMartinLutherKingJrAfter1998, USMemorialDay, USJuneteenthAfter2022, +# USNationalDaysofMourning, USNewYearsDay) class CMEGlobexEnergyAndMetalsExchangeCalendar(CMEGlobexBaseExchangeCalendar): diff --git a/pandas_market_calendars/calendars/cme_globex_equities.py b/pandas_market_calendars/calendars/cme_globex_equities.py index c6e403e..e402ae4 100644 --- a/pandas_market_calendars/calendars/cme_globex_equities.py +++ b/pandas_market_calendars/calendars/cme_globex_equities.py @@ -1,6 +1,5 @@ -from .cme_globex_base import CMEGlobexBaseExchangeCalendar - from datetime import time + from pandas.tseries.holiday import AbstractHolidayCalendar from pytz import timezone @@ -10,11 +9,12 @@ USPresidentsDayBefore2015, USPresidentsDayAfter2015, GoodFridayBefore2021NotEarlyClose, - GoodFridayAfter2021, GoodFriday2010, GoodFriday2012, GoodFriday2015, GoodFriday2021, + GoodFriday2022, + GoodFridayAfter2022, USMemorialDay2013AndPrior, USMemorialDayAfter2013, USIndependenceDayBefore2022PreviousDay, @@ -32,6 +32,7 @@ Christmas, USJuneteenthAfter2022, ) +from .cme_globex_base import CMEGlobexBaseExchangeCalendar class CMEGlobexEquitiesExchangeCalendar(CMEGlobexBaseExchangeCalendar): @@ -61,7 +62,7 @@ def regular_holidays(self): rules=[ USNewYearsDay, GoodFridayBefore2021NotEarlyClose, - GoodFridayAfter2021, + GoodFriday2022, Christmas, ] ) @@ -115,6 +116,7 @@ def special_closes(self): GoodFriday2012, GoodFriday2015, GoodFriday2021, + GoodFridayAfter2022, ] ), ), diff --git a/pandas_market_calendars/calendars/cme_globex_fixed_income.py b/pandas_market_calendars/calendars/cme_globex_fixed_income.py index f472599..72ed207 100644 --- a/pandas_market_calendars/calendars/cme_globex_fixed_income.py +++ b/pandas_market_calendars/calendars/cme_globex_fixed_income.py @@ -1,6 +1,5 @@ -from .cme_globex_base import CMEGlobexBaseExchangeCalendar - from datetime import time + from pandas.tseries.holiday import AbstractHolidayCalendar from pandas_market_calendars.holidays.cme import ( @@ -11,12 +10,13 @@ USPresidentsDayBefore2015, USPresidentsDayAfter2015, GoodFridayBefore2021NotEarlyClose, - GoodFridayAfter2021, GoodFriday2009, GoodFriday2010, GoodFriday2012, GoodFriday2015, GoodFriday2021, + GoodFriday2022, + GoodFridayAfter2022, USMemorialDay2013AndPrior, USMemorialDayAfter2013, USMemorialDay2015AndPriorFridayBefore, @@ -35,6 +35,7 @@ Christmas, USJuneteenthAfter2022, ) +from .cme_globex_base import CMEGlobexBaseExchangeCalendar class CMEGlobexFixedIncomeCalendar(CMEGlobexBaseExchangeCalendar): @@ -63,7 +64,7 @@ def regular_holidays(self): rules=[ USNewYearsDay, GoodFridayBefore2021NotEarlyClose, - GoodFridayAfter2021, + GoodFriday2022, Christmas, ] ) @@ -128,6 +129,7 @@ def special_closes(self): GoodFriday2012, GoodFriday2015, GoodFriday2021, + GoodFridayAfter2022, ] ), ), diff --git a/pandas_market_calendars/calendars/cme_globex_fx.py b/pandas_market_calendars/calendars/cme_globex_fx.py index edcf0f3..cb12de9 100644 --- a/pandas_market_calendars/calendars/cme_globex_fx.py +++ b/pandas_market_calendars/calendars/cme_globex_fx.py @@ -10,7 +10,8 @@ USPresidentsDayBefore2022, GoodFridayBefore2021, GoodFriday2021, - GoodFridayAfter2021, + GoodFriday2022, + GoodFridayAfter2022, USMemorialDay2021AndPrior, USIndependenceDayBefore2022, USLaborDayStarting1887Before2022, @@ -55,7 +56,7 @@ def regular_holidays(self): rules=[ USNewYearsDay, GoodFridayBefore2021, - GoodFridayAfter2021, + GoodFriday2022, Christmas, ] ) @@ -69,7 +70,15 @@ def special_closes(self): """ # Source https://www.cmegroup.com/tools-information/holiday-calendar.html return [ - (_1015, AbstractHolidayCalendar(rules=[GoodFriday2021])), + ( + _1015, + AbstractHolidayCalendar( + rules=[ + GoodFriday2021, + GoodFridayAfter2022, + ] + ), + ), ( _1200, AbstractHolidayCalendar( diff --git a/pandas_market_calendars/calendars/eurex_fixed_income.py b/pandas_market_calendars/calendars/eurex_fixed_income.py new file mode 100644 index 0000000..550bed9 --- /dev/null +++ b/pandas_market_calendars/calendars/eurex_fixed_income.py @@ -0,0 +1,98 @@ +from datetime import time + +from pandas.tseries.holiday import ( + AbstractHolidayCalendar, + EasterMonday, + GoodFriday, + Holiday, +) +from pytz import timezone + +from pandas_market_calendars.market_calendar import ( + FRIDAY, + MONDAY, + MarketCalendar, + THURSDAY, + TUESDAY, + WEDNESDAY, +) + +# New Year's Day +EUREXNewYearsDay = Holiday( + "New Year's Day", + month=1, + day=1, + days_of_week=(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY), +) +# Early May bank holiday +MayBank = Holiday( + "Early May Bank Holiday", + month=5, + day=1, + days_of_week=(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY), +) +# Christmas Eve +ChristmasEve = Holiday( + "Christmas Eve", + month=12, + day=24, + days_of_week=(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY), +) +# Christmas +Christmas = Holiday( + "Christmas", + month=12, + day=25, + days_of_week=(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY), +) +# Boxing day +BoxingDay = Holiday( + "Boxing Day", + month=12, + day=26, + days_of_week=(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY), +) +# New Year's Eve +NewYearsEve = Holiday( + "New Year's Eve", + month=12, + day=31, + days_of_week=(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY), +) + + +class EUREXFixedIncomeCalendar(MarketCalendar): + """ + Trading calendar available here: + https://www.eurex.com/resource/blob/3378814/910cf372738890f691bc1bfbccfd3aef/data/tradingcalendar_2023_en.pdf + """ + + aliases = ["EUREX_Bond"] + + regular_market_times = { + "market_open": ((None, time(1, 10)), ("2018-12-10", time(8, 0))), + "market_close": ((None, time(22)),), + } + + @property + def name(self): + return "EUREX_Bond" + + @property + def tz(self): + return timezone("Europe/Berlin") + + @property + def regular_holidays(self): + return AbstractHolidayCalendar( + rules=[ + EUREXNewYearsDay, + GoodFriday, + EasterMonday, + MayBank, + ChristmasEve, + Christmas, + BoxingDay, + NewYearsEve, + ] + ) diff --git a/pandas_market_calendars/calendars/hkex.py b/pandas_market_calendars/calendars/hkex.py index a5e6d98..d17493a 100644 --- a/pandas_market_calendars/calendars/hkex.py +++ b/pandas_market_calendars/calendars/hkex.py @@ -12,7 +12,6 @@ from pandas.tseries.offsets import LastWeekOfMonth, WeekOfMonth from pytz import timezone -from pandas_market_calendars.holidays.us import USNewYearsDay from pandas_market_calendars.holidays.cn import ( bsd_mapping, dbf_mapping, @@ -21,6 +20,7 @@ sf_mapping, tsd_mapping, ) +from pandas_market_calendars.holidays.us import USNewYearsDay from pandas_market_calendars.market_calendar import MarketCalendar diff --git a/pandas_market_calendars/calendars/iex.py b/pandas_market_calendars/calendars/iex.py index 9626488..edba7fe 100644 --- a/pandas_market_calendars/calendars/iex.py +++ b/pandas_market_calendars/calendars/iex.py @@ -1,6 +1,6 @@ from datetime import time from itertools import chain -from .nyse import NYSEExchangeCalendar + from pandas.tseries.holiday import AbstractHolidayCalendar from pytz import timezone @@ -18,6 +18,7 @@ DaysBeforeIndependenceDay1pmEarlyCloseAdhoc, ChristmasEvesAdhoc, ) +from .nyse import NYSEExchangeCalendar class IEXExchangeCalendar(NYSEExchangeCalendar): diff --git a/pandas_market_calendars/calendars/jpx.py b/pandas_market_calendars/calendars/jpx.py index 42f27af..62d7aef 100644 --- a/pandas_market_calendars/calendars/jpx.py +++ b/pandas_market_calendars/calendars/jpx.py @@ -24,7 +24,7 @@ class JPXExchangeCalendar(MarketCalendar): Close Time: 4:00 PM, Asia/Tokyo """ - aliases = ["JPX"] + aliases = ["JPX", "XJPX"] regular_market_times = { "market_open": ((None, time(9)),), "market_close": ((None, time(15)),), diff --git a/pandas_market_calendars/calendars/mirror.py b/pandas_market_calendars/calendars/mirror.py index c1534ac..33ab316 100644 --- a/pandas_market_calendars/calendars/mirror.py +++ b/pandas_market_calendars/calendars/mirror.py @@ -4,9 +4,10 @@ GitHub: https://github.com/gerrymanoim/exchange_calendars """ -from pandas_market_calendars.market_calendar import MarketCalendar import exchange_calendars +from pandas_market_calendars.market_calendar import MarketCalendar + class TradingCalendar(MarketCalendar): """ diff --git a/pandas_market_calendars/calendars/sifma.py b/pandas_market_calendars/calendars/sifma.py index 8e9a20d..a592d0b 100644 --- a/pandas_market_calendars/calendars/sifma.py +++ b/pandas_market_calendars/calendars/sifma.py @@ -59,9 +59,24 @@ UKWeekendBoxingDay, UKPlatinumJubilee2022, ) - from pandas_market_calendars.market_calendar import MarketCalendar + +######################################################################################################################## +# SIFMA Financial Markets Calendar for US, UK, JP +# +# https://www.sifma.com/ +# +# US: SIFMAUSExchangeCalendar() ['SIFMAUS', 'SIFMA_US', "Capital_Markets_US", "Financial_Markets_US", "Bond_Markets_US"] +# UK: SIFMAUKExchangeCalendar() ['SIFMAUK', 'SIFMA_UK', "Capital_Markets_UK", "Financial_Markets_UK", "Bond_Markets_UK"] +# JP: SIFMAJPExchangeCalendar() ['SIFMAJP', 'SIFMA_JP', "Capital_Markets_JP", "Financial_Markets_JP", "Bond_Markets_JP"] +# +# Trading Hours: +# US: 7:00 to 17:30 +# UK: 8:00 to 17:00 +# JP: 8:30 to 18:30 +######################################################################################################################## + # AbstractHolidayCalendar.start_date = '1998-01-01' ############################################################ diff --git a/pandas_market_calendars/calendars/tase.py b/pandas_market_calendars/calendars/tase.py index 526b6dd..1bb34e9 100644 --- a/pandas_market_calendars/calendars/tase.py +++ b/pandas_market_calendars/calendars/tase.py @@ -1,6 +1,8 @@ from datetime import time + from pandas import Timestamp from pytz import timezone + from pandas_market_calendars.market_calendar import MarketCalendar TASEClosedDay = [ diff --git a/pandas_market_calendars/calendars/tsx.py b/pandas_market_calendars/calendars/tsx.py index 824e63d..e2140ca 100644 --- a/pandas_market_calendars/calendars/tsx.py +++ b/pandas_market_calendars/calendars/tsx.py @@ -1,4 +1,5 @@ from datetime import time +from itertools import chain import pandas as pd from pandas.tseries.holiday import ( @@ -10,7 +11,6 @@ weekend_to_monday, ) from pytz import timezone -from itertools import chain from pandas_market_calendars.holidays.uk import ( BoxingDay, diff --git a/pandas_market_calendars/holidays/cme.py b/pandas_market_calendars/holidays/cme.py index 5f04bc9..f6ddcc2 100644 --- a/pandas_market_calendars/holidays/cme.py +++ b/pandas_market_calendars/holidays/cme.py @@ -2,11 +2,9 @@ from dateutil.relativedelta import MO, TH, FR from pandas import DateOffset, Timestamp -from pandas.tseries.holiday import Holiday, Easter -from pandas.tseries.holiday import nearest_workday +from pandas.tseries.holiday import Holiday, Easter, nearest_workday from pandas.tseries.offsets import Day - ######### # Martin Luther King ######### @@ -119,7 +117,7 @@ def not_0815_close(dt): end_date=Timestamp("2020-12-31"), ) -## CME Interest Rate Products have this odd close +# CME Interest Rate Products have this odd close GoodFriday2009 = Holiday( "Good Friday", month=1, @@ -144,6 +142,21 @@ def not_0815_close(dt): offset=[Easter(), Day(-2)], start_date=Timestamp("2022-01-01"), ) +GoodFriday2022 = Holiday( + "Good Friday", + month=1, + day=1, + offset=[Easter(), Day(-2)], + start_date=Timestamp("2022-01-01"), + end_date=Timestamp("2022-12-31"), +) +GoodFridayAfter2022 = Holiday( + "Good Friday", + month=1, + day=1, + offset=[Easter(), Day(-2)], + start_date=Timestamp("2023-01-01"), +) # Dates when equities closed at 08:15 GoodFriday2010 = Holiday( "Good Friday", @@ -182,7 +195,7 @@ def not_0815_close(dt): start_date=Timestamp("1971-01-01"), end_date=Timestamp("2021-12-31"), offset=DateOffset(weekday=MO(1)), -) #### Equity Products +) # Equity Products USMemorialDay2013AndPrior = Holiday( "Memorial Day", month=5, @@ -288,7 +301,6 @@ def previous_workday_if_july_4th_is_tue_to_fri(dt): offset=DateOffset(weekday=MO(1)), ) - ######### # Thanksgiving ######### @@ -317,8 +329,9 @@ def previous_workday_if_july_4th_is_tue_to_fri(dt): offset=DateOffset(weekday=TH(4)), ) -######## The following Holidays shouldn't be set with the FR offset -## In 2013, Nov 1st is a friday, so the 4th Friday is before the 4th Thursday... + +# The following Holidays shouldn't be set with the FR offset +# In 2013, Nov 1st is a friday, so the 4th Friday is before the 4th Thursday... # the observance rule defined herafter fixes this # USThanksgivingFridayBefore2022 = Holiday( diff --git a/pandas_market_calendars/holidays/cme_globex.py b/pandas_market_calendars/holidays/cme_globex.py index 3189ff8..0decaa2 100644 --- a/pandas_market_calendars/holidays/cme_globex.py +++ b/pandas_market_calendars/holidays/cme_globex.py @@ -1,8 +1,7 @@ -from dateutil.relativedelta import MO, TU, WE, TH, FR, SA, SU -from pandas import DateOffset, Timestamp, date_range -from datetime import timedelta -from pandas.tseries.holiday import Holiday, nearest_workday, sunday_to_monday, Easter -from pandas.tseries.offsets import Day, CustomBusinessDay +from dateutil.relativedelta import MO, TH +from pandas import DateOffset, Timestamp +from pandas.tseries.holiday import Holiday, nearest_workday, Easter +from pandas.tseries.offsets import Day from pandas_market_calendars.market_calendar import ( MONDAY, @@ -10,11 +9,8 @@ WEDNESDAY, THURSDAY, FRIDAY, - SATURDAY, - SUNDAY, ) - #################################################### # US New Years Day Jan 1 ##################################################### @@ -33,7 +29,6 @@ ), ) - ######################################################################### # Martin Luther King Jr. # Starting 1998 @@ -62,7 +57,6 @@ offset=DateOffset(weekday=MO(3)), ) - ######################################################################### # US Presidents Day Feb ########################################################################## @@ -82,7 +76,6 @@ offset=DateOffset(weekday=MO(3)), ) - ############################################################ # Good Friday ############################################################ @@ -142,7 +135,6 @@ observance=nearest_workday, ) - ################################################# # US Labor Day Starting 1887 ################################################# @@ -168,7 +160,6 @@ offset=DateOffset(weekday=MO(1)), ) - ################################################ # US Thanksgiving Nov 30 ################################################ diff --git a/pandas_market_calendars/holidays/cn.py b/pandas_market_calendars/holidays/cn.py index ee3226f..dff9ccb 100644 --- a/pandas_market_calendars/holidays/cn.py +++ b/pandas_market_calendars/holidays/cn.py @@ -564,25 +564,25 @@ Timestamp("2023-10-04"), Timestamp("2023-10-05"), Timestamp("2023-10-06"), - Timestamp('2024-01-01'), - Timestamp('2024-02-12'), - Timestamp('2024-02-13'), - Timestamp('2024-02-14'), - Timestamp('2024-02-15'), - Timestamp('2024-02-16'), - Timestamp('2024-04-04'), - Timestamp('2024-04-05'), - Timestamp('2024-05-01'), - Timestamp('2024-05-02'), - Timestamp('2024-05-03'), - Timestamp('2024-06-10'), - Timestamp('2024-09-16'), - Timestamp('2024-09-17'), - Timestamp('2024-10-01'), - Timestamp('2024-10-02'), - Timestamp('2024-10-03'), - Timestamp('2024-10-04'), - Timestamp('2024-10-07'), + Timestamp("2024-01-01"), + Timestamp("2024-02-12"), + Timestamp("2024-02-13"), + Timestamp("2024-02-14"), + Timestamp("2024-02-15"), + Timestamp("2024-02-16"), + Timestamp("2024-04-04"), + Timestamp("2024-04-05"), + Timestamp("2024-05-01"), + Timestamp("2024-05-02"), + Timestamp("2024-05-03"), + Timestamp("2024-06-10"), + Timestamp("2024-09-16"), + Timestamp("2024-09-17"), + Timestamp("2024-10-01"), + Timestamp("2024-10-02"), + Timestamp("2024-10-03"), + Timestamp("2024-10-04"), + Timestamp("2024-10-07"), ] # The following holidays are based on Solar terms or Chinese lunisolar calendar, diff --git a/pandas_market_calendars/holidays/jp.py b/pandas_market_calendars/holidays/jp.py index 23bf5bf..932403c 100644 --- a/pandas_market_calendars/holidays/jp.py +++ b/pandas_market_calendars/holidays/jp.py @@ -198,7 +198,8 @@ ) JapanMarineDay2021 = Holiday( - name="Marine Day", # shift for Olympics (Olympics and Paralympics postponed until 2021 due to the COVID-19 pandemic) + name="Marine Day", + # shift for Olympics (Olympics and Paralympics postponed until 2021 due to the COVID-19 pandemic) year=2021, month=7, day=22, @@ -229,14 +230,16 @@ ) JapanMountainDay2021 = Holiday( - name="Mountain Day", # shift for Olympics (Olympics and Paralympics postponed until 2021 due to the COVID-19 pandemic) + name="Mountain Day", + # shift for Olympics (Olympics and Paralympics postponed until 2021 due to the COVID-19 pandemic) year=2021, month=8, day=8, ) JapanMountainDay2021NextDay = Holiday( - name="Mountain Day", # shift for Olympics (Olympics and Paralympics postponed until 2021 due to the COVID-19 pandemic) + name="Mountain Day", + # shift for Olympics (Olympics and Paralympics postponed until 2021 due to the COVID-19 pandemic) year=2021, month=8, day=9, @@ -318,7 +321,8 @@ ) JapanSportsDay2021 = Holiday( - name="Sports Day", # shift for Olympics (Olympics and Paralympics postponed until 2021 due to the COVID-19 pandemic) + name="Sports Day", + # shift for Olympics (Olympics and Paralympics postponed until 2021 due to the COVID-19 pandemic) year=2021, month=7, day=23, diff --git a/pandas_market_calendars/holidays/nyse.py b/pandas_market_calendars/holidays/nyse.py index 5e89b4b..eb9163c 100644 --- a/pandas_market_calendars/holidays/nyse.py +++ b/pandas_market_calendars/holidays/nyse.py @@ -1,7 +1,7 @@ -import pandas as pd +from datetime import timedelta + from dateutil.relativedelta import MO, TH, TU from pandas import DateOffset, Timestamp, date_range -from datetime import timedelta from pandas.tseries.holiday import Holiday, nearest_workday, sunday_to_monday, Easter from pandas.tseries.offsets import Day, CustomBusinessDay @@ -15,6 +15,7 @@ SUNDAY, ) + ################################################################################################ # main reference: # https://github.com/rsheftel/pandas_market_calendars/files/6827110/Stocks.NYSE-Closings.pdf @@ -584,7 +585,7 @@ def next_saturday(dt): Timestamp("1975-12-24", tz="UTC"), Timestamp( "1990-12-24", tz="UTC" - ), #### This one was also in the 1pm list, in the tests you check for 2pm + ), # This one was also in the 1pm list, in the tests you check for 2pm Timestamp("1991-12-24", tz="UTC"), Timestamp("1992-12-24", tz="UTC"), ] @@ -611,7 +612,6 @@ def next_saturday(dt): Timestamp("1936-12-26", tz="UTC"), ] - ##################################### # Retired holidays ##################################### @@ -644,7 +644,6 @@ def next_saturday(dt): Timestamp("1945-10-13", tz="UTC"), ] - ########################## # Non-recurring holidays ########################## @@ -665,7 +664,6 @@ def next_saturday(dt): Timestamp("1889-05-01", tz="UTC"), ] - # 1892 ColumbianCelebration1892 = [ Timestamp("1892-10-12", tz="UTC"), @@ -993,7 +991,6 @@ def next_saturday(dt): end_date=Timestamp("1933-08-04"), ) - HeavyVolume1933 = [ Timestamp("1933-07-29", tz="UTC"), Timestamp("1933-08-05", tz="UTC"), @@ -1003,7 +1000,6 @@ def next_saturday(dt): Timestamp("1933-09-02", tz="UTC"), ] - HeavyVolume12pmLateOpen1933 = [ Timestamp("1933-07-24", tz="UTC"), Timestamp("1933-07-25", tz="UTC"), @@ -1176,12 +1172,10 @@ def next_saturday(dt): tz="UTC", ) - MLKdayOfMourning1968 = [ Timestamp("1968-04-09", tz="UTC"), ] - PaperworkCrisis1968 = [ Timestamp("1968-06-12", tz="UTC"), Timestamp("1968-06-19", tz="UTC"), @@ -1208,7 +1202,6 @@ def next_saturday(dt): Timestamp("1968-12-18", tz="UTC"), ] - # 1969 PaperworkCrisis2pmEarlyCloses1969 = date_range( "1969-01-01", @@ -1371,7 +1364,6 @@ def next_saturday(dt): # http://en.wikipedia.org/wiki/Hurricane_Gloria HurricaneGloriaClosings1985 = [Timestamp("1985-09-27", tz="UTC")] - # 1987 Backlog2pmEarlyCloses1987 = date_range("1987-10-23", "1987-10-30", tz="UTC") Backlog230pmEarlyCloses1987 = date_range("1987-11-02", "1987-11-04", tz="UTC") diff --git a/pandas_market_calendars/holidays/oz.py b/pandas_market_calendars/holidays/oz.py index 4c67603..2b16c3f 100644 --- a/pandas_market_calendars/holidays/oz.py +++ b/pandas_market_calendars/holidays/oz.py @@ -47,7 +47,6 @@ observance=weekend_to_monday, ) - # Boxing day BoxingDay = Holiday( "Boxing Day", @@ -56,7 +55,6 @@ observance=next_monday_or_tuesday, ) - # One-off holiday additions and removals in Australia UniqueCloses = [] diff --git a/pandas_market_calendars/holidays/sifma.py b/pandas_market_calendars/holidays/sifma.py index be55ff0..4540cf8 100644 --- a/pandas_market_calendars/holidays/sifma.py +++ b/pandas_market_calendars/holidays/sifma.py @@ -1,6 +1,5 @@ from dateutil.relativedelta import MO, TH from pandas import DateOffset, Timestamp -from datetime import timedelta from pandas.tseries.holiday import ( Holiday, nearest_workday, @@ -17,11 +16,8 @@ WEDNESDAY, THURSDAY, FRIDAY, - SATURDAY, - SUNDAY, ) - #################################################### # US New Years Day Jan 1 # When Jan 1 is a Sunday, US markets observe the subsequent Monday. @@ -90,7 +86,6 @@ offset=DateOffset(weekday=MO(3)), ) - ############################################################ # Good Friday ############################################################ @@ -114,7 +109,6 @@ Timestamp("2023-04-07", tz="UTC"), ] - DayBeforeGoodFriday2pmEarlyCloseThru2020 = Holiday( "Day Before Good Friday Thru 2020", end_date=Timestamp("2020-12-31"), @@ -187,13 +181,11 @@ days_of_week=(THURSDAY,), ) - ################################################# # US Labor Day ################################################# USLaborDay = Holiday("Labor Day", month=9, day=1, offset=DateOffset(weekday=MO(1))) - ################################################# # Columbus Day ################################################# @@ -225,7 +217,6 @@ "Thanksgiving", month=11, day=1, offset=DateOffset(weekday=TH(4)) ) - DayAfterThanksgiving2pmEarlyClose = Holiday( "Black Friday", month=11, @@ -233,7 +224,6 @@ offset=[DateOffset(weekday=TH(4)), Day(1)], ) - ################################ # Christmas Dec 25 ################################ @@ -260,7 +250,6 @@ days_of_week=(THURSDAY,), ) - ############################################################################ # UK Specific Holidays ############################################################################ @@ -306,7 +295,6 @@ offset=DateOffset(weekday=MO(-1)), ) - # UK observes Christmas on Tuesday when Boxing Day is on Monday # UK observes Christmas on Monday when Christmas is on Saturday UKChristmaEve = Holiday( diff --git a/pandas_market_calendars/holidays/us.py b/pandas_market_calendars/holidays/us.py index 2034574..c5cd96b 100644 --- a/pandas_market_calendars/holidays/us.py +++ b/pandas_market_calendars/holidays/us.py @@ -1,4 +1,4 @@ -from dateutil.relativedelta import MO, TH, TU, FR +from dateutil.relativedelta import MO, TH, TU from pandas import DateOffset, Timestamp, date_range from pandas.tseries.holiday import Holiday, nearest_workday, sunday_to_monday from pandas.tseries.offsets import Day @@ -364,7 +364,6 @@ def following_tuesday_every_four_years_observance(dt): Timestamp("2018-12-05", tz="UTC"), ] - ####################################### # US Juneteenth (June 19th) ####################################### diff --git a/pandas_market_calendars/market_calendar.py b/pandas_market_calendars/market_calendar.py index 4102369..c055d0d 100644 --- a/pandas_market_calendars/market_calendar.py +++ b/pandas_market_calendars/market_calendar.py @@ -104,10 +104,10 @@ def __init__(self, open_time=None, close_time=None): self.open_close_map = self.open_close_map.copy() self._customized_market_times = [] - if not open_time is None: + if open_time is not None: self.change_time("market_open", open_time) - if not close_time is None: + if close_time is not None: self.change_time("market_close", close_time) if not hasattr(self, "_market_times"): @@ -257,7 +257,7 @@ def add_time(self, market_time, times, opens=DEFAULT): :param opens: see .change_time docstring :return: None """ - assert not market_time in self.regular_market_times, ( + assert market_time not in self.regular_market_times, ( f"{market_time} is already in regular_market_times:" f"\n{self._market_times}" ) @@ -715,7 +715,7 @@ def schedule( ) _adj_others = force_special_times is True - _adj_col = not force_special_times is None + _adj_col = force_special_times is not None _open_adj = _close_adj = [] schedule = pd.DataFrame() diff --git a/pyproject.toml b/pyproject.toml index 0d6b711..fb340ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,31 +1,28 @@ [project] name = "pandas_market_calendars" -version = "4.3.2" +version = "4.3.3" authors = [ - { name="Ryan Sheftel", email="rsheftel@alumni.upenn.edu" }, + { name = "Ryan Sheftel", email = "rsheftel@alumni.upenn.edu" }, ] description = "Market and exchange trading calendars for pandas" readme = "README.rst" requires-python = ">=3.8" keywords = ["trading", "exchanges", "markets", "OTC", "datetime", "holiday", "business days"] -license = { text="MIT" } +license = { text = "MIT" } classifiers = [ - 'Development Status :: 5 - Production/Stable', - - # Indicate who your project is intended for - 'Intended Audience :: Developers', - 'Topic :: Software Development', - - # Pick your license as you wish (should match "license" above) - 'License :: OSI Approved :: MIT License', - - # Specify the Python versions you support here. - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', + 'Development Status :: 5 - Production/Stable', + # Indicate who your project is intended for + 'Intended Audience :: Developers', + 'Topic :: Software Development', + # Pick your license as you wish (should match "license" above) + 'License :: OSI Approved :: MIT License', + # Specify the Python versions you support here. + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', ] -dependencies = ['pandas>=1.1', 'pytz', 'python-dateutil', 'exchange_calendars>=3.3'] +dependencies = ['pandas>=1.1, <2.0', 'pytz', 'python-dateutil', 'exchange-calendars>=3.3'] [project.optional-dependencies] dev = ['pytest', 'black==23.7.0', 'pre-commit'] @@ -52,17 +49,17 @@ exclude_also = [ # Don't complain about missing debug-only code: "def __repr__", "if self\\.debug", - # Don't complain if tests don't hit defensive assertion code: "raise AssertionError", "raise NotImplementedError", - # Don't complain if non-runnable code isn't run: "if 0:", "if __name__ == .__main__.:", - # Don't complain about abstract methods, they aren't run: "@(abc\\.)?abstractmethod", - ] +] ignore_errors = true + +[tool.ruff.lint] +ignore = ["F403", "F405"] diff --git a/tests/test_XNYS_calendar.py b/tests/test_XNYS_calendar.py index 4cbc1cb..c53c3b1 100644 --- a/tests/test_XNYS_calendar.py +++ b/tests/test_XNYS_calendar.py @@ -1,7 +1,6 @@ """ This tests the NYSE calendar from the exchange_calendars package that is brought in via the mirror """ -import os import pandas as pd import pytz diff --git a/tests/test_asx_calendar.py b/tests/test_asx_calendar.py index d82cc8e..4932a7c 100644 --- a/tests/test_asx_calendar.py +++ b/tests/test_asx_calendar.py @@ -1,7 +1,7 @@ from itertools import chain -import pytz import pandas as pd +import pytz from pandas_market_calendars.calendars.asx import ASXExchangeCalendar diff --git a/tests/test_bmf_calendar.py b/tests/test_bmf_calendar.py index 6a1802d..35baea9 100644 --- a/tests/test_bmf_calendar.py +++ b/tests/test_bmf_calendar.py @@ -1,5 +1,5 @@ import datetime -import pytest + import pandas as pd import pytz @@ -27,13 +27,13 @@ def test_post_2022_regulation_change(): for year in [2017, 2018, 2019, 2021]: # skip 2020 due to test above for month, day in [(1, 25), (7, 9), (11, 20)]: assert ( - pd.Timestamp(datetime.date(year, month, day), tz="UTC").to_datetime64() - in holidays + pd.Timestamp(datetime.date(year, month, day), tz="UTC").to_datetime64() + in holidays ) for year in range(2022, 2040): for month, day in [(1, 25), (7, 9), (11, 20)]: assert ( - pd.Timestamp(datetime.date(year, month, day), tz="UTC").to_datetime64() - not in holidays + pd.Timestamp(datetime.date(year, month, day), tz="UTC").to_datetime64() + not in holidays ) diff --git a/tests/test_bse_calendar.py b/tests/test_bse_calendar.py index b0d51a4..bfd3b5c 100644 --- a/tests/test_bse_calendar.py +++ b/tests/test_bse_calendar.py @@ -1,6 +1,7 @@ import datetime -import pytest + import pandas as pd +import pytest import pytz from pandas_market_calendars.calendars.bse import BSEExchangeCalendar, BSEClosedDay diff --git a/tests/test_class_registry.py b/tests/test_class_registry.py index ace4d22..cf0dd87 100644 --- a/tests/test_class_registry.py +++ b/tests/test_class_registry.py @@ -190,7 +190,6 @@ def test_protected_dict(): s = "ProtectedDict(\n" + pformat(dict(dct), sort_dicts=False) + "\n)" assert str(dct) == s - # if __name__ == '__main__': # # for ref, obj in locals().copy().items(): diff --git a/tests/test_eurex_fixed_income_calendar.py b/tests/test_eurex_fixed_income_calendar.py new file mode 100644 index 0000000..26ee607 --- /dev/null +++ b/tests/test_eurex_fixed_income_calendar.py @@ -0,0 +1,143 @@ +import pandas as pd +import pytz + +from pandas_market_calendars.calendars.eurex_fixed_income import ( + EUREXFixedIncomeCalendar, +) + + +def test_time_zone(): + assert EUREXFixedIncomeCalendar().tz == pytz.timezone("Europe/Berlin") + assert EUREXFixedIncomeCalendar().name == "EUREX_Bond" + + +def _test_year_holiday(year, bad_dates): + eurex = EUREXFixedIncomeCalendar() + good_dates = eurex.valid_days(f"{year}-01-01", f"{year}-12-31") + + # Make sure holiday dates aren't in the schedule + for date in bad_dates: + assert pd.Timestamp(date, tz="UTC") not in good_dates + + # Make sure all other weekdays are in the schedule + expected_good_dates = [ + d.strftime("%Y-%m-%d") + for d in pd.date_range(f"{year}-01-01", f"{year}-12-31", freq="D") + if d.weekday() < 5 and d.strftime("%Y-%m-%d") not in bad_dates + ] + for date in expected_good_dates: + assert pd.Timestamp(date, tz="UTC") in good_dates + + +def test_2017_holidays(): + """ + Eurex is closed for trading and clearing (exercise, settlement and cash) + in all derivatives: 14 April, 17 April, 1 May, 25 December, 26 December + """ + bad_dates = ["2017-04-14", "2017-04-17", "2017-05-01", "2017-12-25", "2017-12-26"] + _test_year_holiday(2017, bad_dates) + + +def test_2018_holidays(): + """ + Eurex is closed for trading and clearing (exercise, settlement and cash) + in all derivatives: 1 January, 30 March, 2 April, 1 May, 25 December, 26 December + Eurex is closed for trading in all derivatives: 24 December, 31 December + """ + bad_dates = [ + "2018-01-01", + "2018-03-30", + "2018-04-02", + "2018-05-01", + "2018-12-24", + "2018-12-25", + "2018-12-26", + "2018-12-31", + ] + _test_year_holiday(2018, bad_dates) + + +def test_2019_holidays(): + """ + Eurex is closed for trading and clearing (exercise, settlement and cash) + in all derivatives: 1 January, 19 April, 22 April, 1 May, 25 December, 26 December + Eurex is closed for trading in all derivatives: 24 December, 31 December + """ + bad_dates = [ + "2019-01-01", + "2019-04-19", + "2019-04-22", + "2019-05-01", + "2019-12-24", + "2019-12-25", + "2019-12-26", + "2019-12-31", + ] + _test_year_holiday(2019, bad_dates) + + +def test_2020_holidays(): + """ + Eurex is closed for trading and clearing (exercise, settlement and cash) + in all derivatives: 1 January, 10 April, 13 April, 1 May, 25 December + Eurex is closed for trading in all derivatives: 24 December, 31 December + """ + bad_dates = [ + "2020-01-01", + "2020-04-10", + "2020-04-13", + "2020-05-01", + "2020-12-24", + "2020-12-25", + "2020-12-31", + ] + _test_year_holiday(2020, bad_dates) + + +def test_2021_holidays(): + """ + Eurex is closed for trading and clearing (exercise, settlement and cash) + in all derivatives: 1 January, 2 April, 5 April + Eurex is closed for trading in all derivatives: 24 December, 31 December + """ + bad_dates = [ + "2021-01-01", + "2021-04-02", + "2021-04-05", + "2021-05-01", + "2021-12-24", + "2021-12-31", + ] + _test_year_holiday(2021, bad_dates) + + +def test_2022_holidays(): + """ + Eurex is closed for trading and clearing (exercise, settlement and cash) + in all derivatives: 15 April, 18 April, 26 December + """ + bad_dates = ["2022-04-15", "2022-04-18", "2022-12-26"] + _test_year_holiday(2022, bad_dates) + + +def test_2023_holidays(): + """ + Eurex is closed for trading and clearing (exercise, settlement and cash) + in all derivatives: 7 April, 10 April, 1 May, 25 December, 26 December + """ + bad_dates = ["2023-04-07", "2023-04-10", "2023-05-01", "2023-12-25", "2023-12-26"] + _test_year_holiday(2023, bad_dates) + + +def test_2024_holidays(): + bad_dates = [ + "2024-01-01", + "2024-03-29", + "2024-04-01", + "2024-05-01", + "2024-12-24", + "2024-12-25", + "2024-12-26", + "2024-12-31", + ] + _test_year_holiday(2024, bad_dates) diff --git a/tests/test_exchange_calendar_cme_globex_crypto.py b/tests/test_exchange_calendar_cme_globex_crypto.py index cecfcc4..3f2a4b4 100644 --- a/tests/test_exchange_calendar_cme_globex_crypto.py +++ b/tests/test_exchange_calendar_cme_globex_crypto.py @@ -1,4 +1,5 @@ import datetime as dt + import pandas as pd import pytest from pandas.tseries.offsets import Day, Hour, Minute @@ -260,6 +261,7 @@ def test_is_different(): ("2022-12-30", "open"), ("2023-01-02", "closed"), ("2023-01-03", "open"), + ("2023-04-07", "1015"), ], ids=lambda x: f"{x[0]} {x[1]}", ) @@ -270,7 +272,7 @@ def test_2020_through_2022_and_prior_holidays(day_status): year = int(day_str.split("-")[0]) under_test = CMEGlobexCryptoExchangeCalendar() - schedule = under_test.schedule(f"{year}-01-01", f"{year+1}-01-01", tz=TZ) + schedule = under_test.schedule(f"{year}-01-01", f"{year + 1}-01-01", tz=TZ) if expected_status == "open": s = schedule.loc[day_str] diff --git a/tests/test_exchange_calendar_cme_globex_energy_and_metals.py b/tests/test_exchange_calendar_cme_globex_energy_and_metals.py index f574243..28de458 100644 --- a/tests/test_exchange_calendar_cme_globex_energy_and_metals.py +++ b/tests/test_exchange_calendar_cme_globex_energy_and_metals.py @@ -1,14 +1,11 @@ -import pytz - import pandas as pd +import pytz from pandas.testing import assert_index_equal -from pandas.tseries.offsets import CustomBusinessDay from pandas_market_calendars.calendars.cme_globex_energy_and_metals import ( CMEGlobexEnergyAndMetalsExchangeCalendar, ) - cal = CMEGlobexEnergyAndMetalsExchangeCalendar() @@ -68,7 +65,7 @@ def _test_has_late_opens(late_opens, start, end): expected = cal.late_opens(schedule) assert len(expected) == len(late_opens) for ts in late_opens: - assert _test_verify_late_open_time(schedule, ts) == True + assert _test_verify_late_open_time(schedule, ts) is True def _test_verify_early_close_time(schedule, timestamp): @@ -84,7 +81,7 @@ def _test_has_early_closes(early_closes, start, end): expected = cal.early_closes(schedule) assert len(expected) == len(early_closes) for ts in early_closes: - assert _test_verify_early_close_time(schedule, ts) == True + assert _test_verify_early_close_time(schedule, ts) is True ######################################################################### diff --git a/tests/test_exchange_calendar_cme_globex_equities.py b/tests/test_exchange_calendar_cme_globex_equities.py index 6f0cf1c..41e54a9 100644 --- a/tests/test_exchange_calendar_cme_globex_equities.py +++ b/tests/test_exchange_calendar_cme_globex_equities.py @@ -1,4 +1,5 @@ import datetime as dt + import pandas as pd import pytest from pandas.tseries.offsets import Day, Hour, Minute @@ -598,6 +599,7 @@ def test_is_different(): ("2022-12-30", "open"), ("2023-01-02", "closed"), ("2023-01-03", "open"), + ("2023-04-07", "0815"), ], ids=lambda x: f"{x[0]} {x[1]}", ) @@ -608,7 +610,7 @@ def test_2020_through_2022_and_prior_holidays(day_status): year = int(day_str.split("-")[0]) under_test = CMEGlobexEquitiesExchangeCalendar() - schedule = under_test.schedule(f"{year}-01-01", f"{year+1}-01-01", tz=TZ) + schedule = under_test.schedule(f"{year}-01-01", f"{year + 1}-01-01", tz=TZ) if expected_status == "open": s = schedule.loc[day_str] diff --git a/tests/test_exchange_calendar_cme_globex_fixed_income.py b/tests/test_exchange_calendar_cme_globex_fixed_income.py index 9bc04d5..aae118f 100644 --- a/tests/test_exchange_calendar_cme_globex_fixed_income.py +++ b/tests/test_exchange_calendar_cme_globex_fixed_income.py @@ -1,6 +1,5 @@ import pandas as pd import pytest -import pytz from pandas.tseries.offsets import Day, Hour, Minute from pandas_market_calendars.calendars.cme_globex_fixed_income import ( @@ -558,6 +557,7 @@ ("2022-12-30", "open"), ("2023-01-02", "closed"), ("2023-01-03", "open"), + ("2023-04-07", "1015"), ], ids=lambda x: f"{x[0]} {x[1]}", ) @@ -568,7 +568,7 @@ def test_2020_through_2022_and_prior_holidays(day_status): year = int(day_str.split("-")[0]) under_test = CMEGlobexFixedIncomeCalendar() - schedule = under_test.schedule(f"{year}-01-01", f"{year+1}-01-01", tz=TZ) + schedule = under_test.schedule(f"{year}-01-01", f"{year + 1}-01-01", tz=TZ) if expected_status == "open": s = schedule.loc[day_str] diff --git a/tests/test_exchange_calendar_cme_globex_fx.py b/tests/test_exchange_calendar_cme_globex_fx.py index 1863f21..ffecebe 100644 --- a/tests/test_exchange_calendar_cme_globex_fx.py +++ b/tests/test_exchange_calendar_cme_globex_fx.py @@ -143,6 +143,7 @@ def test_sunday_opens(): ("2022-12-30", "open"), ("2023-01-02", "closed"), ("2023-01-03", "open"), + ("2023-04-07", "1015"), ], ids=lambda x: f"{x[0]} {x[1]}", ) @@ -152,7 +153,7 @@ def test_2020_through_2022_and_prior_holidays(day_status): expected_status = day_status[1] under_test = CMEGlobexFXExchangeCalendar() - schedule = under_test.schedule("2020-01-01", "2023-02-28", tz=TZ) + schedule = under_test.schedule("2020-01-01", "2023-04-28", tz=TZ) if expected_status == "open": s = schedule.loc[day_str] diff --git a/tests/test_iex_calendar.py b/tests/test_iex_calendar.py index 36e0393..d713e65 100644 --- a/tests/test_iex_calendar.py +++ b/tests/test_iex_calendar.py @@ -1,7 +1,9 @@ -import pandas as pd -import numpy as np from datetime import time + +import numpy as np +import pandas as pd from pytz import timezone + from pandas_market_calendars.calendars.iex import IEXExchangeCalendar from pandas_market_calendars.class_registry import ProtectedDict diff --git a/tests/test_market_calendar.py b/tests/test_market_calendar.py index 67af971..6eb1d52 100644 --- a/tests/test_market_calendar.py +++ b/tests/test_market_calendar.py @@ -12,19 +12,22 @@ # 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. +import pickle from datetime import time from itertools import chain -from pytz import timezone -import pickle - -import pandas as pd +import exchange_calendars as ecal import numpy as np +import pandas as pd import pytest from pandas.testing import assert_frame_equal, assert_index_equal, assert_series_equal from pandas.tseries.holiday import AbstractHolidayCalendar +from pytz import timezone from pandas_market_calendars import get_calendar, get_calendar_names +from pandas_market_calendars.calendars.mirror import TradingCalendar +from pandas_market_calendars.calendars.nyse import NYSEExchangeCalendar +from pandas_market_calendars.holidays.nyse import Sept11Anniversary12pmLateOpen2002 from pandas_market_calendars.holidays.us import ( Christmas, HurricaneSandyClosings, @@ -32,14 +35,9 @@ USNationalDaysofMourning, USNewYearsDay, ) -from pandas_market_calendars.holidays.nyse import Sept11Anniversary12pmLateOpen2002 from pandas_market_calendars.market_calendar import ( MarketCalendar, ) # , clean_dates, days_at_time -from pandas_market_calendars.calendars.mirror import TradingCalendar -from pandas_market_calendars.calendars.nyse import NYSEExchangeCalendar - -import exchange_calendars as ecal class FakeCalendar(MarketCalendar): @@ -159,17 +157,17 @@ def get_fake_time(): def test_protected_dictionary(cal=None): cal = FakeCalendar() if cal is None else cal # shouldn't be able to add - with pytest.raises(TypeError) as e: + with pytest.raises(TypeError): cal.regular_market_times["market_open"] = time(12) - with pytest.raises(TypeError) as e: + with pytest.raises(TypeError): cal.open_close_map["anything"] = time(12) # nor delete - with pytest.raises(TypeError) as e: + with pytest.raises(TypeError): del cal.regular_market_times["market_open"] - with pytest.raises(TypeError) as e: + with pytest.raises(TypeError): del cal.open_close_map["break_start"] @@ -186,7 +184,7 @@ class WrongCal(FakeCalendar): } with pytest.raises(ValueError) as e: - cal = WrongCal() + WrongCal() assert "'interruption_' prefix is reserved" in e.exconly(), e.exconly() @@ -231,10 +229,10 @@ def test_get_time(): cal.remove_time("market_open") with pytest.raises(NotImplementedError): - t = cal.open_time + cal.open_time with pytest.raises(KeyError): - t = cal.get_time_on("pre", "1900-01-01") + cal.get_time_on("pre", "1900-01-01") def test_get_offset(): @@ -334,13 +332,13 @@ def test_add_change_remove_time_w_open_close_map(): ## Non standard time cal.add_time("newtime", time(10)) - assert not "newtime" in cal.open_close_map and "newtime" in cal.regular_market_times + assert "newtime" not in cal.open_close_map and "newtime" in cal.regular_market_times cal.remove_time("newtime") assert "newtime" not in cal.regular_market_times cal.add_time("newtime", time(10), opens=None) - assert not "newtime" in cal.open_close_map and "newtime" in cal.regular_market_times + assert "newtime" not in cal.open_close_map and "newtime" in cal.regular_market_times cal.change_time("newtime", time(11), opens=False) assert cal.open_close_map["newtime"] is False @@ -355,8 +353,8 @@ def test_add_change_remove_time_w_open_close_map(): cal.remove_time("market_close") assert ( - not "market_close" in cal.open_close_map - and not "market_close" in cal.regular_market_times + "market_close" not in cal.open_close_map + and "market_close" not in cal.regular_market_times ) cal.add_time("market_close", time(15)) @@ -368,7 +366,7 @@ def test_add_change_remove_time_w_open_close_map(): cal.remove_time("market_close") cal.add_time("market_close", time(15), opens=None) assert ( - not "market_close" in cal.open_close_map + "market_close" not in cal.open_close_map and "market_close" in cal.regular_market_times ) @@ -397,14 +395,14 @@ def test_open_close_map(): "pre": True, "post": False, } - assert not cal.open_close_map is FakeCalendar.open_close_map + assert cal.open_close_map is not FakeCalendar.open_close_map assert cal.open_close_map == FakeCalendar.open_close_map class WrongCal(FakeCalendar): open_close_map = {**FakeCalendar.open_close_map, "pre": None, "post": "string"} with pytest.raises(AssertionError) as e: - cal = WrongCal() + WrongCal() assert "Values in open_close_map need to be True or False" in str(e) @@ -429,7 +427,7 @@ def test_dunder_methods(): def test_default_calendars(): - for name in filter(lambda n: not n[:4] in ("Test", "_Tst"), get_calendar_names()): + for name in filter(lambda n: n[:4] not in ("Test", "_Tst"), get_calendar_names()): # XKRX has discontinued market times, which should raise a warning if name == "XKRX": with pytest.warns(UserWarning): @@ -1618,7 +1616,7 @@ def test_ec_property(): mcaliepa = get_calendar("IEPA") assert mcaliepa._EC_NOT_INITIALIZED - ec = mcaliepa.ec + mcaliepa.ec assert not mcaliepa._EC_NOT_INITIALIZED assert test_cal._EC_NOT_INITIALIZED diff --git a/tests/test_nyse_calendar.py b/tests/test_nyse_calendar.py index 81bfe52..8db474d 100644 --- a/tests/test_nyse_calendar.py +++ b/tests/test_nyse_calendar.py @@ -1,5 +1,6 @@ -import os import datetime as dt +import os + import pandas as pd import pytest import pytz @@ -14,7 +15,7 @@ def test_custom_open_close(): assert sched.market_open.iat[0] == pd.Timestamp("2021-08-16 13:00:00+00:00") assert sched.market_close.iat[0] == pd.Timestamp("2021-08-16 14:00:00+00:00") - assert not NYSEExchangeCalendar.regular_market_times is cal.regular_market_times + assert NYSEExchangeCalendar.regular_market_times is not cal.regular_market_times @pytest.mark.parametrize( @@ -128,16 +129,16 @@ def test_days_at_time_custom(): def test_valid_days(): cal = NYSEExchangeCalendar() - assert not cal.valid_days("1999-01-01", "2014-01-01") is None + assert cal.valid_days("1999-01-01", "2014-01-01") is not None # used to raise an error because tz= None - assert not cal.valid_days("1999-01-01", "2014-01-01", tz=None) is None + assert cal.valid_days("1999-01-01", "2014-01-01", tz=None) is not None assert ( - not cal.special_dates("market_close", "1999-01-01", "2014-01-01", False) is None + cal.special_dates("market_close", "1999-01-01", "2014-01-01", False) is not None ) # calls valid_days internally assert ( - not cal.special_dates("market_close", "1999-01-01", "2014-01-01", True) is None + cal.special_dates("market_close", "1999-01-01", "2014-01-01", True) is not None ) start, end = "2000-01-01", "2000-01-30" diff --git a/tests/test_nyse_calendar_early_years.py b/tests/test_nyse_calendar_early_years.py index b140992..d23d9dc 100644 --- a/tests/test_nyse_calendar_early_years.py +++ b/tests/test_nyse_calendar_early_years.py @@ -1,6 +1,5 @@ -import pytz - import pandas as pd +import pytz from pandas.testing import assert_index_equal from pandas.tseries.offsets import CustomBusinessDay @@ -65,7 +64,7 @@ def _test_has_late_opens(late_opens, start, end): expected = nyse.late_opens(schedule) assert len(expected) == len(late_opens) for ts in late_opens: - assert _test_verify_late_open_time(schedule, ts) == True + assert _test_verify_late_open_time(schedule, ts) is True def _test_verify_early_close_time(schedule, timestamp): @@ -81,7 +80,7 @@ def _test_has_early_closes(early_closes, start, end): expected = nyse.early_closes(schedule) assert len(expected) == len(early_closes) for ts in early_closes: - assert _test_verify_early_close_time(schedule, ts) == True + assert _test_verify_early_close_time(schedule, ts) is True ######################################################################### @@ -3982,9 +3981,7 @@ def test_2024(): # early closes we expect: early_closes = [ - pd.Timestamp( - "2024-07-03 1:00PM", tz="America/New_York" - ), # Day before July 4th + pd.Timestamp("2024-07-03 1:00PM", tz="America/New_York"), # Day before July 4th pd.Timestamp( "2024-11-29 1:00PM", tz="America/New_York" ), # Day after Thanksgiving @@ -4013,12 +4010,10 @@ def test_2025(): # early closes we expect: early_closes = [ - pd.Timestamp( - "2025-07-03 1:00PM", tz="America/New_York" - ), # Day before July 4th + pd.Timestamp("2025-07-03 1:00PM", tz="America/New_York"), # Day before July 4th pd.Timestamp( "2025-11-28 1:00PM", tz="America/New_York" ), # Day after Thanksgiving - pd.Timestamp("2025-12-24 1:00PM", tz="America/New_York"), # Christmas eve + pd.Timestamp("2025-12-24 1:00PM", tz="America/New_York"), # Christmas Eve ] _test_has_early_closes(early_closes, start, end) diff --git a/tests/test_sifma_calendars.py b/tests/test_sifma_calendars.py index 439c916..dd617e7 100644 --- a/tests/test_sifma_calendars.py +++ b/tests/test_sifma_calendars.py @@ -1,8 +1,7 @@ -import pytz import pandas as pd +import pytz from pandas.testing import assert_index_equal - from pandas_market_calendars.calendars.sifma import ( SIFMAUSExchangeCalendar, SIFMAUKExchangeCalendar, @@ -52,7 +51,7 @@ def _test_has_late_opens(cal, late_opens, start, end): expected = cal.late_opens(schedule) assert len(expected) == len(late_opens) for ts in late_opens: - assert _test_verify_late_open_time(schedule, ts) == True + assert _test_verify_late_open_time(schedule, ts) is True def _test_verify_early_close_time(schedule, timestamp): @@ -68,7 +67,7 @@ def _test_has_early_closes(cal, early_closes, start, end): expected = cal.early_closes(schedule) assert len(expected) == len(early_closes) for ts in early_closes: - assert _test_verify_early_close_time(schedule, ts) == True + assert _test_verify_early_close_time(schedule, ts) is True #########################################################################