├── .gitignore ├── .vscode └── settings.json ├── AUTHORS.rst ├── CONTRIBUTING.rst ├── LICENSE.txt ├── README.rst ├── poetry.lock ├── pyproject.toml ├── requirements.txt ├── src └── binance_historical_data │ ├── __init__.py │ ├── __pycache__ │ ├── __init__.cpython-37.pyc │ ├── kline.cpython-37.pyc │ └── logger.cpython-37.pyc │ ├── data_dumper.py │ └── logger.py ├── tests ├── __init__.py └── test_binance_historical_data.py └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | # My ignore 2 | jupyter_notebooks/* 3 | 4 | 5 | # Temporary and binary files 6 | *~ 7 | *.py[cod] 8 | *.so 9 | *.cfg 10 | !.isort.cfg 11 | !setup.cfg 12 | *.orig 13 | *.log 14 | *.pot 15 | __pycache__/* 16 | .cache/* 17 | .*.swp 18 | */.ipynb_checkpoints/* 19 | .DS_Store 20 | 21 | # Project files 22 | .ropeproject 23 | .project 24 | .pydevproject 25 | .settings 26 | .idea 27 | tags 28 | 29 | # Package files 30 | *.egg 31 | *.eggs/ 32 | .installed.cfg 33 | *.egg-info 34 | 35 | # Unittest and coverage 36 | htmlcov/* 37 | .coverage 38 | .tox 39 | junit.xml 40 | coverage.xml 41 | .pytest_cache/ 42 | 43 | # Build and docs folder/files 44 | build/* 45 | dist/* 46 | sdist/* 47 | docs/api/* 48 | docs/_rst/* 49 | docs/_build/* 50 | cover/* 51 | MANIFEST 52 | 53 | # Per-project virtualenvs 54 | .venv*/ -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "klines", 4 | "mpire" 5 | ] 6 | } -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Authors 3 | ============ 4 | 5 | * Stanislav Prokopyev 6 | 7 | Contacts 8 | ======== 9 | 10 | * email: stas.prokopiev@gmail.com 11 | 12 | * `vk.com `_ 13 | 14 | * `Facebook `_ -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | - Fork it () 5 | - Create your feature branch (`git checkout -b feature/fooBar`) 6 | - Commit your changes (`git commit -am 'Add some fooBar'`) 7 | - Push to the branch (`git push origin feature/fooBar`) 8 | - Create a new Pull Request -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 stanislav 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ======================== 2 | binance_historical_data 3 | ======================== 4 | 5 | .. image:: https://img.shields.io/github/last-commit/stas-prokopiev/binance_historical_data 6 | :target: https://img.shields.io/github/last-commit/stas-prokopiev/binance_historical_data 7 | :alt: GitHub last commit 8 | 9 | .. image:: https://img.shields.io/github/license/stas-prokopiev/binance_historical_data 10 | :target: https://github.com/stas-prokopiev/binance_historical_data/blob/master/LICENSE.txt 11 | :alt: GitHub license 12 | 13 | .. image:: https://img.shields.io/pypi/v/binance_historical_data 14 | :target: https://img.shields.io/pypi/v/binance_historical_data 15 | :alt: PyPI 16 | 17 | .. image:: https://img.shields.io/pypi/pyversions/binance_historical_data 18 | :target: https://img.shields.io/pypi/pyversions/binance_historical_data 19 | :alt: PyPI - Python Version 20 | 21 | 22 | .. contents:: **Table of Contents** 23 | 24 | Short Overview. 25 | ========================= 26 | binance_historical_data is a python package (**py>=3.8**) 27 | which makes download of historical crypto data (prices and volumes) from binance server as simple as it can only be. 28 | **You don't even need to have an account at binance.com to download all history of crypto data** 29 | 30 | | Data is taken from here: https://data.binance.vision/?prefix=data/ 31 | | Dumped locally and then unzipped, 32 | | so you would have an identical local ready to use data copy 33 | 34 | | Using this package you will be able to have full historical data of prices and volumes with only 3 lines of python code 35 | | And if you need to update already downloaded data then once again 3 lines of python code will do the job 36 | 37 | 38 | | **Limitations**: The previous day data appears on binance server a few minutes after 0 a.m. UTC 39 | | So there is a delay in which you can get the data. 40 | 41 | Installation via pip: 42 | ====================== 43 | 44 | .. code-block:: bash 45 | 46 | pip install binance_historical_data 47 | 48 | How to use it 49 | =========================== 50 | 51 | Initialize main object: **data_dumper** 52 | --------------------------------------------- 53 | 54 | .. code-block:: python 55 | 56 | from binance_historical_data import BinanceDataDumper 57 | 58 | data_dumper = BinanceDataDumper( 59 | path_dir_where_to_dump=".", 60 | asset_class="spot", # spot, um, cm 61 | data_type="klines", # aggTrades, klines, trades 62 | data_frequency="1m", 63 | ) 64 | 65 | Arguments: 66 | 67 | #. **path_dir_where_to_dump**: 68 | | (string) Path to folder where to dump the data 69 | #. **asset_class**: 70 | | (string) Source of data: [spot, um, cm] um: usd(t) margined futures, cm: coin margined futures 71 | #. **data_type="klines"**: 72 | | (string) data type to dump: 73 | | [aggTrades, klines, trades] for spot 74 | | [aggTrades, klines, trades, indexPriceKlines, markPriceKlines, premiumIndexKlines, metrics] for futures (metrics only supported for um) 75 | | Refer to binance doc for additional info: https://github.com/binance/binance-public-data 76 | #. **str_data_frequency**: 77 | | (string) One of [1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h] 78 | | Frequency of price-volume data candles to get 79 | 80 | 1) The only method to dump the data 81 | ------------------------------------------ 82 | 83 | .. code-block:: python 84 | 85 | data_dumper.dump_data( 86 | tickers=None, 87 | date_start=None, 88 | date_end=None, 89 | is_to_update_existing=False, 90 | tickers_to_exclude=["UST"], 91 | ) 92 | 93 | Arguments: 94 | 95 | #. **tickers=None**: 96 | | (list) Trading pairs for which to dump data 97 | | *if equals to None* - all **USDT** pairs will be used 98 | #. **date_start=None**: 99 | | (datetime.date) The date from which to start dump 100 | | *if equals to None* - every trading pair will be dumped from the early begining (the earliest is 2017-01-01) 101 | #. **date_end=True=None**: 102 | | (datetime.date) The last date for which to dump data 103 | | *if equals to None* - Today's date will be used 104 | #. **is_to_update_existing=False**: 105 | | (bool) Flag if you want to update the data if it's already exist 106 | #. **tickers_to_exclude=None**: 107 | | (list) Tickers to exclude from dump 108 | 109 | 110 | 2) Delete outdated daily results 111 | ---------------------------------------------------- 112 | 113 | Delete all daily data for which full month monthly data was already dumped 114 | 115 | .. code-block:: python 116 | 117 | data_dumper.delete_outdated_daily_results() 118 | 119 | .csv klines (candles) files columns 120 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 121 | 122 | | "Open time" - Timestamp 123 | | "Open" 124 | | "High" 125 | | "Low" 126 | | "Close" 127 | | "Volume" 128 | | "Close time" - Timestamp 129 | | "Quote asset volume" 130 | | "Number of trades" 131 | | "Taker buy base asset volume" 132 | | "Taker buy quote asset volume" 133 | | "Ignore" 134 | 135 | Examples 136 | =========================== 137 | 138 | How to dump all data for all USDT trading pairs 139 | ------------------------------------------------ 140 | 141 | Please be advised that the first data dump for all trading pairs might take some time (~40 minutes) 142 | 143 | .. code-block:: python 144 | 145 | data_dumper.dump_data() 146 | 147 | How to update data (get all new data) 148 | ---------------------------------------------- 149 | 150 | | It's as easy as running the exactly same method **dump_data** once again 151 | | The **data_dumper** will find all the dates for which data already exists 152 | | and will try to dump only the new data 153 | 154 | .. code-block:: python 155 | 156 | data_dumper.dump_data() 157 | 158 | How to update (reload) data for the asked time period 159 | ---------------------------------------------------------- 160 | 161 | .. code-block:: python 162 | 163 | data_dumper.dump_data( 164 | date_start=datetime.date(year=2021, month=1, day=1), 165 | date_end=datetime.date(year=2022, month=1, day=1), 166 | is_to_update_existing=True 167 | ) 168 | 169 | Other useful methods 170 | =========================== 171 | 172 | Get all trading pairs (tickers) from binance 173 | ---------------------------------------------------- 174 | 175 | .. code-block:: python 176 | 177 | print(data_dumper.get_list_all_trading_pairs()) 178 | 179 | 180 | Get the first data when data for the ticker can be found 181 | ---------------------------------------------------------- 182 | 183 | .. code-block:: python 184 | 185 | print(data_dumper.get_min_start_date_for_ticker()) 186 | 187 | 188 | Get all tickers with locally saved data 189 | ---------------------------------------------------- 190 | 191 | .. code-block:: python 192 | 193 | print( 194 | data_dumper.get_all_tickers_with_data(timeperiod_per_file="daily") 195 | ) 196 | 197 | 198 | Get all dates for which there is locally saved data 199 | ---------------------------------------------------- 200 | 201 | .. code-block:: python 202 | 203 | print( 204 | data_dumper.get_all_dates_with_data_for_ticker( 205 | ticker, 206 | timeperiod_per_file="monthly" 207 | ) 208 | ) 209 | 210 | Get directory where the local data of exact ticker lies 211 | -------------------------------------------------------- 212 | 213 | .. code-block:: python 214 | 215 | print( 216 | data_dumper.get_local_dir_to_data( 217 | ticker, 218 | timeperiod_per_file, 219 | ) 220 | ) 221 | 222 | Create file name for the local file 223 | ---------------------------------------------------- 224 | 225 | .. code-block:: python 226 | 227 | print( 228 | data_dumper.create_filename( 229 | ticker, 230 | date_obj, 231 | timeperiod_per_file="monthly", 232 | ) 233 | ) 234 | 235 | Links 236 | ===== 237 | 238 | * `PYPI `_ 239 | * `GitHub `_ 240 | 241 | Project local Links 242 | =================== 243 | 244 | * `CHANGELOG `_. 245 | * `CONTRIBUTING `_. 246 | 247 | Contacts 248 | ======== 249 | 250 | * Email: stas.prokopiev@gmail.com 251 | * `vk.com `_ 252 | * `Facebook `_ 253 | 254 | License 255 | ======= 256 | 257 | This project is licensed under the MIT License. 258 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "appnope" 3 | version = "0.1.2" 4 | description = "Disable App Nap on macOS >= 10.9" 5 | category = "main" 6 | optional = false 7 | python-versions = "*" 8 | 9 | [[package]] 10 | name = "argon2-cffi" 11 | version = "21.3.0" 12 | description = "The secure Argon2 password hashing algorithm." 13 | category = "main" 14 | optional = false 15 | python-versions = ">=3.6" 16 | 17 | [package.dependencies] 18 | argon2-cffi-bindings = "*" 19 | typing-extensions = {version = "*", markers = "python_version < \"3.8\""} 20 | 21 | [package.extras] 22 | dev = ["pre-commit", "cogapp", "tomli", "coverage[toml] (>=5.0.2)", "hypothesis", "pytest", "sphinx", "sphinx-notfound-page", "furo"] 23 | docs = ["sphinx", "sphinx-notfound-page", "furo"] 24 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"] 25 | 26 | [[package]] 27 | name = "argon2-cffi-bindings" 28 | version = "21.2.0" 29 | description = "Low-level CFFI bindings for Argon2" 30 | category = "main" 31 | optional = false 32 | python-versions = ">=3.6" 33 | 34 | [package.dependencies] 35 | cffi = ">=1.0.1" 36 | 37 | [package.extras] 38 | dev = ["pytest", "cogapp", "pre-commit", "wheel"] 39 | tests = ["pytest"] 40 | 41 | [[package]] 42 | name = "atomicwrites" 43 | version = "1.4.0" 44 | description = "Atomic file writes." 45 | category = "dev" 46 | optional = false 47 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 48 | 49 | [[package]] 50 | name = "attrs" 51 | version = "21.4.0" 52 | description = "Classes Without Boilerplate" 53 | category = "main" 54 | optional = false 55 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 56 | 57 | [package.extras] 58 | dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] 59 | docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] 60 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] 61 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] 62 | 63 | [[package]] 64 | name = "backcall" 65 | version = "0.2.0" 66 | description = "Specifications for callback functions passed in to an API" 67 | category = "main" 68 | optional = false 69 | python-versions = "*" 70 | 71 | [[package]] 72 | name = "bleach" 73 | version = "4.1.0" 74 | description = "An easy safelist-based HTML-sanitizing tool." 75 | category = "main" 76 | optional = false 77 | python-versions = ">=3.6" 78 | 79 | [package.dependencies] 80 | packaging = "*" 81 | six = ">=1.9.0" 82 | webencodings = "*" 83 | 84 | [[package]] 85 | name = "cffi" 86 | version = "1.15.0" 87 | description = "Foreign Function Interface for Python calling C code." 88 | category = "main" 89 | optional = false 90 | python-versions = "*" 91 | 92 | [package.dependencies] 93 | pycparser = "*" 94 | 95 | [[package]] 96 | name = "char" 97 | version = "0.1.2" 98 | description = "decorator for fast check types of arguments which are given to function" 99 | category = "main" 100 | optional = false 101 | python-versions = "*" 102 | 103 | [package.extras] 104 | testing = ["pytest", "pytest-cov"] 105 | 106 | [[package]] 107 | name = "colorama" 108 | version = "0.4.4" 109 | description = "Cross-platform colored terminal text." 110 | category = "main" 111 | optional = false 112 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 113 | 114 | [[package]] 115 | name = "dataclasses" 116 | version = "0.6" 117 | description = "A backport of the dataclasses module for Python 3.6" 118 | category = "main" 119 | optional = false 120 | python-versions = "*" 121 | 122 | [[package]] 123 | name = "debugpy" 124 | version = "1.5.1" 125 | description = "An implementation of the Debug Adapter Protocol for Python" 126 | category = "main" 127 | optional = false 128 | python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" 129 | 130 | [[package]] 131 | name = "decorator" 132 | version = "5.1.1" 133 | description = "Decorators for Humans" 134 | category = "main" 135 | optional = false 136 | python-versions = ">=3.5" 137 | 138 | [[package]] 139 | name = "defusedxml" 140 | version = "0.7.1" 141 | description = "XML bomb protection for Python stdlib modules" 142 | category = "main" 143 | optional = false 144 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 145 | 146 | [[package]] 147 | name = "entrypoints" 148 | version = "0.4" 149 | description = "Discover and load entry points from installed packages." 150 | category = "main" 151 | optional = false 152 | python-versions = ">=3.6" 153 | 154 | [[package]] 155 | name = "importlib-metadata" 156 | version = "4.11.2" 157 | description = "Read metadata from Python packages" 158 | category = "main" 159 | optional = false 160 | python-versions = ">=3.7" 161 | 162 | [package.dependencies] 163 | typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} 164 | zipp = ">=0.5" 165 | 166 | [package.extras] 167 | docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] 168 | perf = ["ipython"] 169 | testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] 170 | 171 | [[package]] 172 | name = "importlib-resources" 173 | version = "5.4.0" 174 | description = "Read resources from Python packages" 175 | category = "main" 176 | optional = false 177 | python-versions = ">=3.6" 178 | 179 | [package.dependencies] 180 | zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} 181 | 182 | [package.extras] 183 | docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] 184 | testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] 185 | 186 | [[package]] 187 | name = "ipykernel" 188 | version = "6.9.1" 189 | description = "IPython Kernel for Jupyter" 190 | category = "main" 191 | optional = false 192 | python-versions = ">=3.7" 193 | 194 | [package.dependencies] 195 | appnope = {version = "*", markers = "platform_system == \"Darwin\""} 196 | debugpy = ">=1.0.0,<2.0" 197 | ipython = ">=7.23.1" 198 | jupyter-client = "<8.0" 199 | matplotlib-inline = ">=0.1.0,<0.2.0" 200 | nest-asyncio = "*" 201 | tornado = ">=4.2,<7.0" 202 | traitlets = ">=5.1.0,<6.0" 203 | 204 | [package.extras] 205 | test = ["pytest (!=5.3.4)", "pytest-cov", "flaky", "ipyparallel"] 206 | 207 | [[package]] 208 | name = "ipython" 209 | version = "7.32.0" 210 | description = "IPython: Productive Interactive Computing" 211 | category = "main" 212 | optional = false 213 | python-versions = ">=3.7" 214 | 215 | [package.dependencies] 216 | appnope = {version = "*", markers = "sys_platform == \"darwin\""} 217 | backcall = "*" 218 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 219 | decorator = "*" 220 | jedi = ">=0.16" 221 | matplotlib-inline = "*" 222 | pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} 223 | pickleshare = "*" 224 | prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" 225 | pygments = "*" 226 | traitlets = ">=4.2" 227 | 228 | [package.extras] 229 | all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"] 230 | doc = ["Sphinx (>=1.3)"] 231 | kernel = ["ipykernel"] 232 | nbconvert = ["nbconvert"] 233 | nbformat = ["nbformat"] 234 | notebook = ["notebook", "ipywidgets"] 235 | parallel = ["ipyparallel"] 236 | qtconsole = ["qtconsole"] 237 | test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.17)"] 238 | 239 | [[package]] 240 | name = "ipython-genutils" 241 | version = "0.2.0" 242 | description = "Vestigial utilities from IPython" 243 | category = "main" 244 | optional = false 245 | python-versions = "*" 246 | 247 | [[package]] 248 | name = "ipywidgets" 249 | version = "7.6.5" 250 | description = "IPython HTML widgets for Jupyter" 251 | category = "main" 252 | optional = false 253 | python-versions = "*" 254 | 255 | [package.dependencies] 256 | ipykernel = ">=4.5.1" 257 | ipython = {version = ">=4.0.0", markers = "python_version >= \"3.3\""} 258 | ipython-genutils = ">=0.2.0,<0.3.0" 259 | jupyterlab-widgets = {version = ">=1.0.0", markers = "python_version >= \"3.6\""} 260 | nbformat = ">=4.2.0" 261 | traitlets = ">=4.3.1" 262 | widgetsnbextension = ">=3.5.0,<3.6.0" 263 | 264 | [package.extras] 265 | test = ["pytest (>=3.6.0)", "pytest-cov", "mock"] 266 | 267 | [[package]] 268 | name = "jedi" 269 | version = "0.18.1" 270 | description = "An autocompletion tool for Python that can be used for text editors." 271 | category = "main" 272 | optional = false 273 | python-versions = ">=3.6" 274 | 275 | [package.dependencies] 276 | parso = ">=0.8.0,<0.9.0" 277 | 278 | [package.extras] 279 | qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] 280 | testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<7.0.0)"] 281 | 282 | [[package]] 283 | name = "jinja2" 284 | version = "3.0.3" 285 | description = "A very fast and expressive template engine." 286 | category = "main" 287 | optional = false 288 | python-versions = ">=3.6" 289 | 290 | [package.dependencies] 291 | MarkupSafe = ">=2.0" 292 | 293 | [package.extras] 294 | i18n = ["Babel (>=2.7)"] 295 | 296 | [[package]] 297 | name = "jsonschema" 298 | version = "4.4.0" 299 | description = "An implementation of JSON Schema validation for Python" 300 | category = "main" 301 | optional = false 302 | python-versions = ">=3.7" 303 | 304 | [package.dependencies] 305 | attrs = ">=17.4.0" 306 | importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} 307 | importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} 308 | pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" 309 | typing-extensions = {version = "*", markers = "python_version < \"3.8\""} 310 | 311 | [package.extras] 312 | format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] 313 | format_nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] 314 | 315 | [[package]] 316 | name = "jupyter-client" 317 | version = "7.1.2" 318 | description = "Jupyter protocol implementation and client libraries" 319 | category = "main" 320 | optional = false 321 | python-versions = ">=3.6.1" 322 | 323 | [package.dependencies] 324 | entrypoints = "*" 325 | jupyter-core = ">=4.6.0" 326 | nest-asyncio = ">=1.5" 327 | python-dateutil = ">=2.1" 328 | pyzmq = ">=13" 329 | tornado = ">=4.1" 330 | traitlets = "*" 331 | 332 | [package.extras] 333 | doc = ["myst-parser", "sphinx (>=1.3.6)", "sphinx-rtd-theme", "sphinxcontrib-github-alt"] 334 | test = ["codecov", "coverage", "ipykernel", "ipython", "mock", "mypy", "pre-commit", "pytest", "pytest-asyncio", "pytest-cov", "pytest-timeout", "jedi (<0.18)"] 335 | 336 | [[package]] 337 | name = "jupyter-core" 338 | version = "4.9.2" 339 | description = "Jupyter core package. A base package on which Jupyter projects rely." 340 | category = "main" 341 | optional = false 342 | python-versions = ">=3.6" 343 | 344 | [package.dependencies] 345 | pywin32 = {version = ">=1.0", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} 346 | traitlets = "*" 347 | 348 | [[package]] 349 | name = "jupyterlab-pygments" 350 | version = "0.1.2" 351 | description = "Pygments theme using JupyterLab CSS variables" 352 | category = "main" 353 | optional = false 354 | python-versions = "*" 355 | 356 | [package.dependencies] 357 | pygments = ">=2.4.1,<3" 358 | 359 | [[package]] 360 | name = "jupyterlab-widgets" 361 | version = "1.0.2" 362 | description = "A JupyterLab extension." 363 | category = "main" 364 | optional = false 365 | python-versions = ">=3.6" 366 | 367 | [[package]] 368 | name = "markupsafe" 369 | version = "2.1.0" 370 | description = "Safely add untrusted strings to HTML/XML markup." 371 | category = "main" 372 | optional = false 373 | python-versions = ">=3.7" 374 | 375 | [[package]] 376 | name = "matplotlib-inline" 377 | version = "0.1.3" 378 | description = "Inline Matplotlib backend for Jupyter" 379 | category = "main" 380 | optional = false 381 | python-versions = ">=3.5" 382 | 383 | [package.dependencies] 384 | traitlets = "*" 385 | 386 | [[package]] 387 | name = "mistune" 388 | version = "0.8.4" 389 | description = "The fastest markdown parser in pure Python" 390 | category = "main" 391 | optional = false 392 | python-versions = "*" 393 | 394 | [[package]] 395 | name = "more-itertools" 396 | version = "8.12.0" 397 | description = "More routines for operating on iterables, beyond itertools" 398 | category = "dev" 399 | optional = false 400 | python-versions = ">=3.5" 401 | 402 | [[package]] 403 | name = "mpire" 404 | version = "2.3.3" 405 | description = "A Python package for easy multiprocessing, but faster than multiprocessing" 406 | category = "main" 407 | optional = false 408 | python-versions = "*" 409 | 410 | [package.dependencies] 411 | dataclasses = "*" 412 | tqdm = "*" 413 | 414 | [package.extras] 415 | dashboard = ["flask"] 416 | dill = ["multiprocess"] 417 | docs = ["docutils (==0.17.1)", "sphinx (==3.2.1)", "sphinx-rtd-theme (==0.5.0)", "sphinx-autodoc-typehints (==1.11.0)", "sphinxcontrib-images (==0.9.2)", "sphinx-versions (==1.0.1)"] 418 | testing = ["multiprocess", "numpy", "dataclasses"] 419 | 420 | [[package]] 421 | name = "nbclient" 422 | version = "0.5.11" 423 | description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." 424 | category = "main" 425 | optional = false 426 | python-versions = ">=3.7.0" 427 | 428 | [package.dependencies] 429 | jupyter-client = ">=6.1.5" 430 | nbformat = ">=5.0" 431 | nest-asyncio = "*" 432 | traitlets = ">=4.2" 433 | 434 | [package.extras] 435 | sphinx = ["Sphinx (>=1.7)", "sphinx-book-theme", "mock", "moto", "myst-parser"] 436 | test = ["ipython (<8.0.0)", "ipykernel", "ipywidgets (<8.0.0)", "pytest (>=4.1)", "pytest-asyncio", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "xmltodict", "black", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)"] 437 | 438 | [[package]] 439 | name = "nbconvert" 440 | version = "6.4.2" 441 | description = "Converting Jupyter Notebooks" 442 | category = "main" 443 | optional = false 444 | python-versions = ">=3.7" 445 | 446 | [package.dependencies] 447 | bleach = "*" 448 | defusedxml = "*" 449 | entrypoints = ">=0.2.2" 450 | jinja2 = ">=2.4" 451 | jupyter-core = "*" 452 | jupyterlab-pygments = "*" 453 | mistune = ">=0.8.1,<2" 454 | nbclient = ">=0.5.0,<0.6.0" 455 | nbformat = ">=4.4" 456 | pandocfilters = ">=1.4.1" 457 | pygments = ">=2.4.1" 458 | testpath = "*" 459 | traitlets = ">=5.0" 460 | 461 | [package.extras] 462 | all = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (>=1,<1.1)", "tornado (>=4.0)", "sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"] 463 | docs = ["sphinx (>=1.5.1)", "sphinx-rtd-theme", "nbsphinx (>=0.2.12)", "ipython"] 464 | serve = ["tornado (>=4.0)"] 465 | test = ["pytest", "pytest-cov", "pytest-dependency", "ipykernel", "ipywidgets (>=7)", "pyppeteer (>=1,<1.1)"] 466 | webpdf = ["pyppeteer (>=1,<1.1)"] 467 | 468 | [[package]] 469 | name = "nbformat" 470 | version = "5.1.3" 471 | description = "The Jupyter Notebook format" 472 | category = "main" 473 | optional = false 474 | python-versions = ">=3.5" 475 | 476 | [package.dependencies] 477 | ipython-genutils = "*" 478 | jsonschema = ">=2.4,<2.5.0 || >2.5.0" 479 | jupyter-core = "*" 480 | traitlets = ">=4.1" 481 | 482 | [package.extras] 483 | fast = ["fastjsonschema"] 484 | test = ["check-manifest", "fastjsonschema", "testpath", "pytest", "pytest-cov"] 485 | 486 | [[package]] 487 | name = "nest-asyncio" 488 | version = "1.5.4" 489 | description = "Patch asyncio to allow nested event loops" 490 | category = "main" 491 | optional = false 492 | python-versions = ">=3.5" 493 | 494 | [[package]] 495 | name = "notebook" 496 | version = "6.4.8" 497 | description = "A web-based notebook environment for interactive computing" 498 | category = "main" 499 | optional = false 500 | python-versions = ">=3.6" 501 | 502 | [package.dependencies] 503 | argon2-cffi = "*" 504 | ipykernel = "*" 505 | ipython-genutils = "*" 506 | jinja2 = "*" 507 | jupyter-client = ">=5.3.4" 508 | jupyter-core = ">=4.6.1" 509 | nbconvert = "*" 510 | nbformat = "*" 511 | nest-asyncio = ">=1.5" 512 | prometheus-client = "*" 513 | pyzmq = ">=17" 514 | Send2Trash = ">=1.8.0" 515 | terminado = ">=0.8.3" 516 | tornado = ">=6.1" 517 | traitlets = ">=4.2.1" 518 | 519 | [package.extras] 520 | docs = ["sphinx", "nbsphinx", "sphinxcontrib-github-alt", "sphinx-rtd-theme", "myst-parser"] 521 | json-logging = ["json-logging"] 522 | test = ["pytest", "coverage", "requests", "nbval", "selenium", "pytest-cov", "requests-unixsocket"] 523 | 524 | [[package]] 525 | name = "packaging" 526 | version = "21.3" 527 | description = "Core utilities for Python packages" 528 | category = "main" 529 | optional = false 530 | python-versions = ">=3.6" 531 | 532 | [package.dependencies] 533 | pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" 534 | 535 | [[package]] 536 | name = "pandocfilters" 537 | version = "1.5.0" 538 | description = "Utilities for writing pandoc filters in python" 539 | category = "main" 540 | optional = false 541 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 542 | 543 | [[package]] 544 | name = "parso" 545 | version = "0.8.3" 546 | description = "A Python Parser" 547 | category = "main" 548 | optional = false 549 | python-versions = ">=3.6" 550 | 551 | [package.extras] 552 | qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] 553 | testing = ["docopt", "pytest (<6.0.0)"] 554 | 555 | [[package]] 556 | name = "pexpect" 557 | version = "4.8.0" 558 | description = "Pexpect allows easy control of interactive console applications." 559 | category = "main" 560 | optional = false 561 | python-versions = "*" 562 | 563 | [package.dependencies] 564 | ptyprocess = ">=0.5" 565 | 566 | [[package]] 567 | name = "pickleshare" 568 | version = "0.7.5" 569 | description = "Tiny 'shelve'-like database with concurrency support" 570 | category = "main" 571 | optional = false 572 | python-versions = "*" 573 | 574 | [[package]] 575 | name = "pluggy" 576 | version = "0.13.1" 577 | description = "plugin and hook calling mechanisms for python" 578 | category = "dev" 579 | optional = false 580 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 581 | 582 | [package.dependencies] 583 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 584 | 585 | [package.extras] 586 | dev = ["pre-commit", "tox"] 587 | 588 | [[package]] 589 | name = "prometheus-client" 590 | version = "0.13.1" 591 | description = "Python client for the Prometheus monitoring system." 592 | category = "main" 593 | optional = false 594 | python-versions = ">=3.6" 595 | 596 | [package.extras] 597 | twisted = ["twisted"] 598 | 599 | [[package]] 600 | name = "prompt-toolkit" 601 | version = "3.0.28" 602 | description = "Library for building powerful interactive command lines in Python" 603 | category = "main" 604 | optional = false 605 | python-versions = ">=3.6.2" 606 | 607 | [package.dependencies] 608 | wcwidth = "*" 609 | 610 | [[package]] 611 | name = "ptyprocess" 612 | version = "0.7.0" 613 | description = "Run a subprocess in a pseudo terminal" 614 | category = "main" 615 | optional = false 616 | python-versions = "*" 617 | 618 | [[package]] 619 | name = "py" 620 | version = "1.11.0" 621 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 622 | category = "main" 623 | optional = false 624 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 625 | 626 | [[package]] 627 | name = "pycparser" 628 | version = "2.21" 629 | description = "C parser in Python" 630 | category = "main" 631 | optional = false 632 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 633 | 634 | [[package]] 635 | name = "pygments" 636 | version = "2.11.2" 637 | description = "Pygments is a syntax highlighting package written in Python." 638 | category = "main" 639 | optional = false 640 | python-versions = ">=3.5" 641 | 642 | [[package]] 643 | name = "pyparsing" 644 | version = "3.0.7" 645 | description = "Python parsing module" 646 | category = "main" 647 | optional = false 648 | python-versions = ">=3.6" 649 | 650 | [package.extras] 651 | diagrams = ["jinja2", "railroad-diagrams"] 652 | 653 | [[package]] 654 | name = "pyrsistent" 655 | version = "0.18.1" 656 | description = "Persistent/Functional/Immutable data structures" 657 | category = "main" 658 | optional = false 659 | python-versions = ">=3.7" 660 | 661 | [[package]] 662 | name = "pytest" 663 | version = "5.4.3" 664 | description = "pytest: simple powerful testing with Python" 665 | category = "dev" 666 | optional = false 667 | python-versions = ">=3.5" 668 | 669 | [package.dependencies] 670 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} 671 | attrs = ">=17.4.0" 672 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 673 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 674 | more-itertools = ">=4.0.0" 675 | packaging = "*" 676 | pluggy = ">=0.12,<1.0" 677 | py = ">=1.5.0" 678 | wcwidth = "*" 679 | 680 | [package.extras] 681 | checkqa-mypy = ["mypy (==v0.761)"] 682 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] 683 | 684 | [[package]] 685 | name = "python-dateutil" 686 | version = "2.8.2" 687 | description = "Extensions to the standard Python datetime module" 688 | category = "main" 689 | optional = false 690 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 691 | 692 | [package.dependencies] 693 | six = ">=1.5" 694 | 695 | [[package]] 696 | name = "pywin32" 697 | version = "303" 698 | description = "Python for Window Extensions" 699 | category = "main" 700 | optional = false 701 | python-versions = "*" 702 | 703 | [[package]] 704 | name = "pywinpty" 705 | version = "2.0.5" 706 | description = "Pseudo terminal support for Windows from Python." 707 | category = "main" 708 | optional = false 709 | python-versions = ">=3.7" 710 | 711 | [[package]] 712 | name = "pyzmq" 713 | version = "22.3.0" 714 | description = "Python bindings for 0MQ" 715 | category = "main" 716 | optional = false 717 | python-versions = ">=3.6" 718 | 719 | [package.dependencies] 720 | cffi = {version = "*", markers = "implementation_name == \"pypy\""} 721 | py = {version = "*", markers = "implementation_name == \"pypy\""} 722 | 723 | [[package]] 724 | name = "send2trash" 725 | version = "1.8.0" 726 | description = "Send file to trash natively under Mac OS X, Windows and Linux." 727 | category = "main" 728 | optional = false 729 | python-versions = "*" 730 | 731 | [package.extras] 732 | nativelib = ["pyobjc-framework-cocoa", "pywin32"] 733 | objc = ["pyobjc-framework-cocoa"] 734 | win32 = ["pywin32"] 735 | 736 | [[package]] 737 | name = "six" 738 | version = "1.16.0" 739 | description = "Python 2 and 3 compatibility utilities" 740 | category = "main" 741 | optional = false 742 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 743 | 744 | [[package]] 745 | name = "terminado" 746 | version = "0.13.2" 747 | description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library." 748 | category = "main" 749 | optional = false 750 | python-versions = ">=3.7" 751 | 752 | [package.dependencies] 753 | ptyprocess = {version = "*", markers = "os_name != \"nt\""} 754 | pywinpty = {version = ">=1.1.0", markers = "os_name == \"nt\""} 755 | tornado = ">=4" 756 | 757 | [package.extras] 758 | test = ["pytest"] 759 | 760 | [[package]] 761 | name = "testpath" 762 | version = "0.6.0" 763 | description = "Test utilities for code working with files and commands" 764 | category = "main" 765 | optional = false 766 | python-versions = ">= 3.5" 767 | 768 | [package.extras] 769 | test = ["pytest"] 770 | 771 | [[package]] 772 | name = "tornado" 773 | version = "6.1" 774 | description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." 775 | category = "main" 776 | optional = false 777 | python-versions = ">= 3.5" 778 | 779 | [[package]] 780 | name = "tqdm" 781 | version = "4.63.0" 782 | description = "Fast, Extensible Progress Meter" 783 | category = "main" 784 | optional = false 785 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" 786 | 787 | [package.dependencies] 788 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 789 | 790 | [package.extras] 791 | dev = ["py-make (>=0.1.0)", "twine", "wheel"] 792 | notebook = ["ipywidgets (>=6)"] 793 | telegram = ["requests"] 794 | 795 | [[package]] 796 | name = "traitlets" 797 | version = "5.1.1" 798 | description = "Traitlets Python configuration system" 799 | category = "main" 800 | optional = false 801 | python-versions = ">=3.7" 802 | 803 | [package.extras] 804 | test = ["pytest"] 805 | 806 | [[package]] 807 | name = "typing-extensions" 808 | version = "4.1.1" 809 | description = "Backported and Experimental Type Hints for Python 3.6+" 810 | category = "main" 811 | optional = false 812 | python-versions = ">=3.6" 813 | 814 | [[package]] 815 | name = "wcwidth" 816 | version = "0.2.5" 817 | description = "Measures the displayed width of unicode strings in a terminal" 818 | category = "main" 819 | optional = false 820 | python-versions = "*" 821 | 822 | [[package]] 823 | name = "webencodings" 824 | version = "0.5.1" 825 | description = "Character encoding aliases for legacy web content" 826 | category = "main" 827 | optional = false 828 | python-versions = "*" 829 | 830 | [[package]] 831 | name = "widgetsnbextension" 832 | version = "3.5.2" 833 | description = "IPython HTML widgets for Jupyter" 834 | category = "main" 835 | optional = false 836 | python-versions = "*" 837 | 838 | [package.dependencies] 839 | notebook = ">=4.4.1" 840 | 841 | [[package]] 842 | name = "zipp" 843 | version = "3.7.0" 844 | description = "Backport of pathlib-compatible object wrapper for zip files" 845 | category = "main" 846 | optional = false 847 | python-versions = ">=3.7" 848 | 849 | [package.extras] 850 | docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] 851 | testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] 852 | 853 | [metadata] 854 | lock-version = "1.1" 855 | python-versions = "^3.7" 856 | content-hash = "9aab9ebf1f3beacceffb1ff9bb6ebab44d73265dfda79f7bfb1debd56df3242f" 857 | 858 | [metadata.files] 859 | appnope = [ 860 | {file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"}, 861 | {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"}, 862 | ] 863 | argon2-cffi = [ 864 | {file = "argon2-cffi-21.3.0.tar.gz", hash = "sha256:d384164d944190a7dd7ef22c6aa3ff197da12962bd04b17f64d4e93d934dba5b"}, 865 | {file = "argon2_cffi-21.3.0-py3-none-any.whl", hash = "sha256:8c976986f2c5c0e5000919e6de187906cfd81fb1c72bf9d88c01177e77da7f80"}, 866 | ] 867 | argon2-cffi-bindings = [ 868 | {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, 869 | {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, 870 | {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, 871 | {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, 872 | {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, 873 | {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, 874 | {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, 875 | {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, 876 | {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, 877 | {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, 878 | {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, 879 | {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, 880 | {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, 881 | {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, 882 | {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, 883 | {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, 884 | {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, 885 | {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, 886 | {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, 887 | {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, 888 | {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, 889 | ] 890 | atomicwrites = [ 891 | {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, 892 | {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, 893 | ] 894 | attrs = [ 895 | {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, 896 | {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, 897 | ] 898 | backcall = [ 899 | {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, 900 | {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, 901 | ] 902 | bleach = [ 903 | {file = "bleach-4.1.0-py2.py3-none-any.whl", hash = "sha256:4d2651ab93271d1129ac9cbc679f524565cc8a1b791909c4a51eac4446a15994"}, 904 | {file = "bleach-4.1.0.tar.gz", hash = "sha256:0900d8b37eba61a802ee40ac0061f8c2b5dee29c1927dd1d233e075ebf5a71da"}, 905 | ] 906 | cffi = [ 907 | {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, 908 | {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"}, 909 | {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"}, 910 | {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"}, 911 | {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"}, 912 | {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"}, 913 | {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"}, 914 | {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"}, 915 | {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"}, 916 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"}, 917 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"}, 918 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"}, 919 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"}, 920 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"}, 921 | {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"}, 922 | {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"}, 923 | {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"}, 924 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"}, 925 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"}, 926 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"}, 927 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"}, 928 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"}, 929 | {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"}, 930 | {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"}, 931 | {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"}, 932 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"}, 933 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"}, 934 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"}, 935 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"}, 936 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"}, 937 | {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"}, 938 | {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"}, 939 | {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"}, 940 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"}, 941 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"}, 942 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"}, 943 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"}, 944 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"}, 945 | {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"}, 946 | {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"}, 947 | {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"}, 948 | {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"}, 949 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"}, 950 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"}, 951 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"}, 952 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"}, 953 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"}, 954 | {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"}, 955 | {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"}, 956 | {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"}, 957 | ] 958 | char = [ 959 | {file = "char-0.1.2-py2.py3-none-any.whl", hash = "sha256:e2719f713a62134657f51da07ebc6e5c2acb11862f65b8be14e6b596fa345e80"}, 960 | {file = "char-0.1.2.tar.gz", hash = "sha256:e6020cf4c81e43484e0b0a1d5d0668a5e56dfff4dadea24629d92a2a3934c79a"}, 961 | ] 962 | colorama = [ 963 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, 964 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, 965 | ] 966 | dataclasses = [ 967 | {file = "dataclasses-0.6-py3-none-any.whl", hash = "sha256:454a69d788c7fda44efd71e259be79577822f5e3f53f029a22d08004e951dc9f"}, 968 | {file = "dataclasses-0.6.tar.gz", hash = "sha256:6988bd2b895eef432d562370bb707d540f32f7360ab13da45340101bc2307d84"}, 969 | ] 970 | debugpy = [ 971 | {file = "debugpy-1.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:70b422c63a833630c33e3f9cdbd9b6971f8c5afd452697e464339a21bbe862ba"}, 972 | {file = "debugpy-1.5.1-cp310-cp310-win32.whl", hash = "sha256:3a457ad9c0059a21a6c7d563c1f18e924f5cf90278c722bd50ede6f56b77c7fe"}, 973 | {file = "debugpy-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:5d76a4fd028d8009c3faf1185b4b78ceb2273dd2499447664b03939e0368bb90"}, 974 | {file = "debugpy-1.5.1-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:16db27b4b91991442f91d73604d32080b30de655aca9ba821b1972ea8171021b"}, 975 | {file = "debugpy-1.5.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2b073ad5e8d8c488fbb6a116986858bab0c9c4558f28deb8832c7a5a27405bd6"}, 976 | {file = "debugpy-1.5.1-cp36-cp36m-win32.whl", hash = "sha256:318f81f37341e4e054b4267d39896b73cddb3612ca13b39d7eea45af65165e1d"}, 977 | {file = "debugpy-1.5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b5b3157372e0e0a1297a8b6b5280bcf1d35a40f436c7973771c972726d1e32d5"}, 978 | {file = "debugpy-1.5.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:1ec3a086e14bba6c472632025b8fe5bdfbaef2afa1ebd5c6615ce6ed8d89bc67"}, 979 | {file = "debugpy-1.5.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:26fbe53cca45a608679094791ce587b6e2798acd1d4777a8b303b07622e85182"}, 980 | {file = "debugpy-1.5.1-cp37-cp37m-win32.whl", hash = "sha256:d876db8c312eeb02d85611e0f696abe66a2c1515e6405943609e725d5ff36f2a"}, 981 | {file = "debugpy-1.5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4404a62fb5332ea5c8c9132290eef50b3a0ba38cecacad5529e969a783bcbdd7"}, 982 | {file = "debugpy-1.5.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:f3a3dca9104aa14fd4210edcce6d9ce2b65bd9618c0b222135a40b9d6e2a9eeb"}, 983 | {file = "debugpy-1.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2df2c373e85871086bd55271c929670cd4e1dba63e94a08d442db830646203b"}, 984 | {file = "debugpy-1.5.1-cp38-cp38-win32.whl", hash = "sha256:82f5f9ce93af6861a0713f804e62ab390bb12a17f113153e47fea8bbb1dfbe36"}, 985 | {file = "debugpy-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:17a25ce9d7714f92fc97ef00cc06269d7c2b163094990ada30156ed31d9a5030"}, 986 | {file = "debugpy-1.5.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:01e98c594b3e66d529e40edf314f849cd1a21f7a013298df58cd8e263bf8e184"}, 987 | {file = "debugpy-1.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f73988422b17f071ad3c4383551ace1ba5ed810cbab5f9c362783d22d40a08dc"}, 988 | {file = "debugpy-1.5.1-cp39-cp39-win32.whl", hash = "sha256:23df67fc56d59e386c342428a7953c2c06cc226d8525b11319153e96afb65b0c"}, 989 | {file = "debugpy-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:a2aa64f6d2ca7ded8a7e8a4e7cae3bc71866b09876b7b05cecad231779cb9156"}, 990 | {file = "debugpy-1.5.1-py2.py3-none-any.whl", hash = "sha256:194f95dd3e84568b5489aab5689a3a2c044e8fdc06f1890b8b4f70b6b89f2778"}, 991 | {file = "debugpy-1.5.1.zip", hash = "sha256:d2b09e91fbd1efa4f4fda121d49af89501beda50c18ed7499712c71a4bf3452e"}, 992 | ] 993 | decorator = [ 994 | {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, 995 | {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, 996 | ] 997 | defusedxml = [ 998 | {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, 999 | {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, 1000 | ] 1001 | entrypoints = [ 1002 | {file = "entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f"}, 1003 | {file = "entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4"}, 1004 | ] 1005 | importlib-metadata = [ 1006 | {file = "importlib_metadata-4.11.2-py3-none-any.whl", hash = "sha256:d16e8c1deb60de41b8e8ed21c1a7b947b0bc62fab7e1d470bcdf331cea2e6735"}, 1007 | {file = "importlib_metadata-4.11.2.tar.gz", hash = "sha256:b36ffa925fe3139b2f6ff11d6925ffd4fa7bc47870165e3ac260ac7b4f91e6ac"}, 1008 | ] 1009 | importlib-resources = [ 1010 | {file = "importlib_resources-5.4.0-py3-none-any.whl", hash = "sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45"}, 1011 | {file = "importlib_resources-5.4.0.tar.gz", hash = "sha256:d756e2f85dd4de2ba89be0b21dba2a3bbec2e871a42a3a16719258a11f87506b"}, 1012 | ] 1013 | ipykernel = [ 1014 | {file = "ipykernel-6.9.1-py3-none-any.whl", hash = "sha256:4fae9df6e192837552b2406a6052d707046dd2e153860be73c68484bacba18ed"}, 1015 | {file = "ipykernel-6.9.1.tar.gz", hash = "sha256:f95070a2dfd3147f8ab19f18ee46733310813758593745e07ec18fb08b409f1d"}, 1016 | ] 1017 | ipython = [ 1018 | {file = "ipython-7.32.0-py3-none-any.whl", hash = "sha256:86df2cf291c6c70b5be6a7b608650420e89180c8ec74f376a34e2dc15c3400e7"}, 1019 | {file = "ipython-7.32.0.tar.gz", hash = "sha256:468abefc45c15419e3c8e8c0a6a5c115b2127bafa34d7c641b1d443658793909"}, 1020 | ] 1021 | ipython-genutils = [ 1022 | {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, 1023 | {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, 1024 | ] 1025 | ipywidgets = [ 1026 | {file = "ipywidgets-7.6.5-py2.py3-none-any.whl", hash = "sha256:d258f582f915c62ea91023299603be095de19afb5ee271698f88327b9fe9bf43"}, 1027 | {file = "ipywidgets-7.6.5.tar.gz", hash = "sha256:00974f7cb4d5f8d494c19810fedb9fa9b64bffd3cda7c2be23c133a1ad3c99c5"}, 1028 | ] 1029 | jedi = [ 1030 | {file = "jedi-0.18.1-py2.py3-none-any.whl", hash = "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d"}, 1031 | {file = "jedi-0.18.1.tar.gz", hash = "sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab"}, 1032 | ] 1033 | jinja2 = [ 1034 | {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, 1035 | {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, 1036 | ] 1037 | jsonschema = [ 1038 | {file = "jsonschema-4.4.0-py3-none-any.whl", hash = "sha256:77281a1f71684953ee8b3d488371b162419767973789272434bbc3f29d9c8823"}, 1039 | {file = "jsonschema-4.4.0.tar.gz", hash = "sha256:636694eb41b3535ed608fe04129f26542b59ed99808b4f688aa32dcf55317a83"}, 1040 | ] 1041 | jupyter-client = [ 1042 | {file = "jupyter_client-7.1.2-py3-none-any.whl", hash = "sha256:d56f1c57bef42ff31e61b1185d3348a5b2bcde7c9a05523ae4dbe5ee0871797c"}, 1043 | {file = "jupyter_client-7.1.2.tar.gz", hash = "sha256:4ea61033726c8e579edb55626d8ee2e6bf0a83158ddf3751b8dd46b2c5cd1e96"}, 1044 | ] 1045 | jupyter-core = [ 1046 | {file = "jupyter_core-4.9.2-py3-none-any.whl", hash = "sha256:f875e4d27e202590311d468fa55f90c575f201490bd0c18acabe4e318db4a46d"}, 1047 | {file = "jupyter_core-4.9.2.tar.gz", hash = "sha256:d69baeb9ffb128b8cd2657fcf2703f89c769d1673c851812119e3a2a0e93ad9a"}, 1048 | ] 1049 | jupyterlab-pygments = [ 1050 | {file = "jupyterlab_pygments-0.1.2-py2.py3-none-any.whl", hash = "sha256:abfb880fd1561987efaefcb2d2ac75145d2a5d0139b1876d5be806e32f630008"}, 1051 | {file = "jupyterlab_pygments-0.1.2.tar.gz", hash = "sha256:cfcda0873626150932f438eccf0f8bf22bfa92345b814890ab360d666b254146"}, 1052 | ] 1053 | jupyterlab-widgets = [ 1054 | {file = "jupyterlab_widgets-1.0.2-py3-none-any.whl", hash = "sha256:f5d9efface8ec62941173ba1cffb2edd0ecddc801c11ae2931e30b50492eb8f7"}, 1055 | {file = "jupyterlab_widgets-1.0.2.tar.gz", hash = "sha256:7885092b2b96bf189c3a705cc3c412a4472ec5e8382d0b47219a66cccae73cfa"}, 1056 | ] 1057 | markupsafe = [ 1058 | {file = "MarkupSafe-2.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3028252424c72b2602a323f70fbf50aa80a5d3aa616ea6add4ba21ae9cc9da4c"}, 1059 | {file = "MarkupSafe-2.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:290b02bab3c9e216da57c1d11d2ba73a9f73a614bbdcc027d299a60cdfabb11a"}, 1060 | {file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e104c0c2b4cd765b4e83909cde7ec61a1e313f8a75775897db321450e928cce"}, 1061 | {file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24c3be29abb6b34052fd26fc7a8e0a49b1ee9d282e3665e8ad09a0a68faee5b3"}, 1062 | {file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204730fd5fe2fe3b1e9ccadb2bd18ba8712b111dcabce185af0b3b5285a7c989"}, 1063 | {file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d3b64c65328cb4cd252c94f83e66e3d7acf8891e60ebf588d7b493a55a1dbf26"}, 1064 | {file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:96de1932237abe0a13ba68b63e94113678c379dca45afa040a17b6e1ad7ed076"}, 1065 | {file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75bb36f134883fdbe13d8e63b8675f5f12b80bb6627f7714c7d6c5becf22719f"}, 1066 | {file = "MarkupSafe-2.1.0-cp310-cp310-win32.whl", hash = "sha256:4056f752015dfa9828dce3140dbadd543b555afb3252507348c493def166d454"}, 1067 | {file = "MarkupSafe-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:d4e702eea4a2903441f2735799d217f4ac1b55f7d8ad96ab7d4e25417cb0827c"}, 1068 | {file = "MarkupSafe-2.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f0eddfcabd6936558ec020130f932d479930581171368fd728efcfb6ef0dd357"}, 1069 | {file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ddea4c352a488b5e1069069f2f501006b1a4362cb906bee9a193ef1245a7a61"}, 1070 | {file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09c86c9643cceb1d87ca08cdc30160d1b7ab49a8a21564868921959bd16441b8"}, 1071 | {file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0a0abef2ca47b33fb615b491ce31b055ef2430de52c5b3fb19a4042dbc5cadb"}, 1072 | {file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:736895a020e31b428b3382a7887bfea96102c529530299f426bf2e636aacec9e"}, 1073 | {file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:679cbb78914ab212c49c67ba2c7396dc599a8479de51b9a87b174700abd9ea49"}, 1074 | {file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:84ad5e29bf8bab3ad70fd707d3c05524862bddc54dc040982b0dbcff36481de7"}, 1075 | {file = "MarkupSafe-2.1.0-cp37-cp37m-win32.whl", hash = "sha256:8da5924cb1f9064589767b0f3fc39d03e3d0fb5aa29e0cb21d43106519bd624a"}, 1076 | {file = "MarkupSafe-2.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:454ffc1cbb75227d15667c09f164a0099159da0c1f3d2636aa648f12675491ad"}, 1077 | {file = "MarkupSafe-2.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:142119fb14a1ef6d758912b25c4e803c3ff66920635c44078666fe7cc3f8f759"}, 1078 | {file = "MarkupSafe-2.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b2a5a856019d2833c56a3dcac1b80fe795c95f401818ea963594b345929dffa7"}, 1079 | {file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d1fb9b2eec3c9714dd936860850300b51dbaa37404209c8d4cb66547884b7ed"}, 1080 | {file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62c0285e91414f5c8f621a17b69fc0088394ccdaa961ef469e833dbff64bd5ea"}, 1081 | {file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc3150f85e2dbcf99e65238c842d1cfe69d3e7649b19864c1cc043213d9cd730"}, 1082 | {file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f02cf7221d5cd915d7fa58ab64f7ee6dd0f6cddbb48683debf5d04ae9b1c2cc1"}, 1083 | {file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5653619b3eb5cbd35bfba3c12d575db2a74d15e0e1c08bf1db788069d410ce8"}, 1084 | {file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7d2f5d97fcbd004c03df8d8fe2b973fe2b14e7bfeb2cfa012eaa8759ce9a762f"}, 1085 | {file = "MarkupSafe-2.1.0-cp38-cp38-win32.whl", hash = "sha256:3cace1837bc84e63b3fd2dfce37f08f8c18aeb81ef5cf6bb9b51f625cb4e6cd8"}, 1086 | {file = "MarkupSafe-2.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:fabbe18087c3d33c5824cb145ffca52eccd053061df1d79d4b66dafa5ad2a5ea"}, 1087 | {file = "MarkupSafe-2.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:023af8c54fe63530545f70dd2a2a7eed18d07a9a77b94e8bf1e2ff7f252db9a3"}, 1088 | {file = "MarkupSafe-2.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d66624f04de4af8bbf1c7f21cc06649c1c69a7f84109179add573ce35e46d448"}, 1089 | {file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c532d5ab79be0199fa2658e24a02fce8542df196e60665dd322409a03db6a52c"}, 1090 | {file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ec74fada3841b8c5f4c4f197bea916025cb9aa3fe5abf7d52b655d042f956"}, 1091 | {file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c653fde75a6e5eb814d2a0a89378f83d1d3f502ab710904ee585c38888816c"}, 1092 | {file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:961eb86e5be7d0973789f30ebcf6caab60b844203f4396ece27310295a6082c7"}, 1093 | {file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:598b65d74615c021423bd45c2bc5e9b59539c875a9bdb7e5f2a6b92dfcfc268d"}, 1094 | {file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:599941da468f2cf22bf90a84f6e2a65524e87be2fce844f96f2dd9a6c9d1e635"}, 1095 | {file = "MarkupSafe-2.1.0-cp39-cp39-win32.whl", hash = "sha256:e6f7f3f41faffaea6596da86ecc2389672fa949bd035251eab26dc6697451d05"}, 1096 | {file = "MarkupSafe-2.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:b8811d48078d1cf2a6863dafb896e68406c5f513048451cd2ded0473133473c7"}, 1097 | {file = "MarkupSafe-2.1.0.tar.gz", hash = "sha256:80beaf63ddfbc64a0452b841d8036ca0611e049650e20afcb882f5d3c266d65f"}, 1098 | ] 1099 | matplotlib-inline = [ 1100 | {file = "matplotlib-inline-0.1.3.tar.gz", hash = "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee"}, 1101 | {file = "matplotlib_inline-0.1.3-py3-none-any.whl", hash = "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c"}, 1102 | ] 1103 | mistune = [ 1104 | {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, 1105 | {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, 1106 | ] 1107 | more-itertools = [ 1108 | {file = "more-itertools-8.12.0.tar.gz", hash = "sha256:7dc6ad46f05f545f900dd59e8dfb4e84a4827b97b3cfecb175ea0c7d247f6064"}, 1109 | {file = "more_itertools-8.12.0-py3-none-any.whl", hash = "sha256:43e6dd9942dffd72661a2c4ef383ad7da1e6a3e968a927ad7a6083ab410a688b"}, 1110 | ] 1111 | mpire = [ 1112 | {file = "mpire-2.3.3-py3-none-any.whl", hash = "sha256:fe43fc00f5122a7fecc79753e94298e47f20dc246694b84f58bc9a67c2145f63"}, 1113 | {file = "mpire-2.3.3.tar.gz", hash = "sha256:1e8db8d0dd487731aa8bab65f52299b1af128c9ca01bef172337a083aadb7733"}, 1114 | ] 1115 | nbclient = [ 1116 | {file = "nbclient-0.5.11-py3-none-any.whl", hash = "sha256:03e857bea3012377289daa1e1c1651f4fc0295bcd109ccd36a337efcdbebaed7"}, 1117 | {file = "nbclient-0.5.11.tar.gz", hash = "sha256:751516992f34b58172bad54eef1e4bf7e4f4460d58e255ca1a4e5c9649476007"}, 1118 | ] 1119 | nbconvert = [ 1120 | {file = "nbconvert-6.4.2-py3-none-any.whl", hash = "sha256:7b006ae9979af56200e7fa3db39d9d12c99e811e8843b05dbe518e5b754bcb2e"}, 1121 | {file = "nbconvert-6.4.2.tar.gz", hash = "sha256:eb2803db18f6facce6bf3b01b684fe47907994bd156d15eaccdf011e3d7f8164"}, 1122 | ] 1123 | nbformat = [ 1124 | {file = "nbformat-5.1.3-py3-none-any.whl", hash = "sha256:eb8447edd7127d043361bc17f2f5a807626bc8e878c7709a1c647abda28a9171"}, 1125 | {file = "nbformat-5.1.3.tar.gz", hash = "sha256:b516788ad70771c6250977c1374fcca6edebe6126fd2adb5a69aa5c2356fd1c8"}, 1126 | ] 1127 | nest-asyncio = [ 1128 | {file = "nest_asyncio-1.5.4-py3-none-any.whl", hash = "sha256:3fdd0d6061a2bb16f21fe8a9c6a7945be83521d81a0d15cff52e9edee50101d6"}, 1129 | {file = "nest_asyncio-1.5.4.tar.gz", hash = "sha256:f969f6013a16fadb4adcf09d11a68a4f617c6049d7af7ac2c676110169a63abd"}, 1130 | ] 1131 | notebook = [ 1132 | {file = "notebook-6.4.8-py3-none-any.whl", hash = "sha256:3e702fcc54b8ae597533c3864793b7a1e971dec9e112f67235828d8a798fd654"}, 1133 | {file = "notebook-6.4.8.tar.gz", hash = "sha256:1e985c9dc6f678bdfffb9dc657306b5469bfa62d73e03f74e8defbf76d284312"}, 1134 | ] 1135 | packaging = [ 1136 | {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, 1137 | {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, 1138 | ] 1139 | pandocfilters = [ 1140 | {file = "pandocfilters-1.5.0-py2.py3-none-any.whl", hash = "sha256:33aae3f25fd1a026079f5d27bdd52496f0e0803b3469282162bafdcbdf6ef14f"}, 1141 | {file = "pandocfilters-1.5.0.tar.gz", hash = "sha256:0b679503337d233b4339a817bfc8c50064e2eff681314376a47cb582305a7a38"}, 1142 | ] 1143 | parso = [ 1144 | {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, 1145 | {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, 1146 | ] 1147 | pexpect = [ 1148 | {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, 1149 | {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, 1150 | ] 1151 | pickleshare = [ 1152 | {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, 1153 | {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, 1154 | ] 1155 | pluggy = [ 1156 | {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, 1157 | {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, 1158 | ] 1159 | prometheus-client = [ 1160 | {file = "prometheus_client-0.13.1-py3-none-any.whl", hash = "sha256:357a447fd2359b0a1d2e9b311a0c5778c330cfbe186d880ad5a6b39884652316"}, 1161 | {file = "prometheus_client-0.13.1.tar.gz", hash = "sha256:ada41b891b79fca5638bd5cfe149efa86512eaa55987893becd2c6d8d0a5dfc5"}, 1162 | ] 1163 | prompt-toolkit = [ 1164 | {file = "prompt_toolkit-3.0.28-py3-none-any.whl", hash = "sha256:30129d870dcb0b3b6a53efdc9d0a83ea96162ffd28ffe077e94215b233dc670c"}, 1165 | {file = "prompt_toolkit-3.0.28.tar.gz", hash = "sha256:9f1cd16b1e86c2968f2519d7fb31dd9d669916f515612c269d14e9ed52b51650"}, 1166 | ] 1167 | ptyprocess = [ 1168 | {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, 1169 | {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, 1170 | ] 1171 | py = [ 1172 | {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, 1173 | {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, 1174 | ] 1175 | pycparser = [ 1176 | {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, 1177 | {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, 1178 | ] 1179 | pygments = [ 1180 | {file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"}, 1181 | {file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"}, 1182 | ] 1183 | pyparsing = [ 1184 | {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, 1185 | {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, 1186 | ] 1187 | pyrsistent = [ 1188 | {file = "pyrsistent-0.18.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1"}, 1189 | {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d45866ececf4a5fff8742c25722da6d4c9e180daa7b405dc0a2a2790d668c26"}, 1190 | {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ed6784ceac462a7d6fcb7e9b663e93b9a6fb373b7f43594f9ff68875788e01e"}, 1191 | {file = "pyrsistent-0.18.1-cp310-cp310-win32.whl", hash = "sha256:e4f3149fd5eb9b285d6bfb54d2e5173f6a116fe19172686797c056672689daf6"}, 1192 | {file = "pyrsistent-0.18.1-cp310-cp310-win_amd64.whl", hash = "sha256:636ce2dc235046ccd3d8c56a7ad54e99d5c1cd0ef07d9ae847306c91d11b5fec"}, 1193 | {file = "pyrsistent-0.18.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e92a52c166426efbe0d1ec1332ee9119b6d32fc1f0bbfd55d5c1088070e7fc1b"}, 1194 | {file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7a096646eab884bf8bed965bad63ea327e0d0c38989fc83c5ea7b8a87037bfc"}, 1195 | {file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cdfd2c361b8a8e5d9499b9082b501c452ade8bbf42aef97ea04854f4a3f43b22"}, 1196 | {file = "pyrsistent-0.18.1-cp37-cp37m-win32.whl", hash = "sha256:7ec335fc998faa4febe75cc5268a9eac0478b3f681602c1f27befaf2a1abe1d8"}, 1197 | {file = "pyrsistent-0.18.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6455fc599df93d1f60e1c5c4fe471499f08d190d57eca040c0ea182301321286"}, 1198 | {file = "pyrsistent-0.18.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fd8da6d0124efa2f67d86fa70c851022f87c98e205f0594e1fae044e7119a5a6"}, 1199 | {file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bfe2388663fd18bd8ce7db2c91c7400bf3e1a9e8bd7d63bf7e77d39051b85ec"}, 1200 | {file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e3e1fcc45199df76053026a51cc59ab2ea3fc7c094c6627e93b7b44cdae2c8c"}, 1201 | {file = "pyrsistent-0.18.1-cp38-cp38-win32.whl", hash = "sha256:b568f35ad53a7b07ed9b1b2bae09eb15cdd671a5ba5d2c66caee40dbf91c68ca"}, 1202 | {file = "pyrsistent-0.18.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1b96547410f76078eaf66d282ddca2e4baae8964364abb4f4dcdde855cd123a"}, 1203 | {file = "pyrsistent-0.18.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f87cc2863ef33c709e237d4b5f4502a62a00fab450c9e020892e8e2ede5847f5"}, 1204 | {file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc66318fb7ee012071b2792024564973ecc80e9522842eb4e17743604b5e045"}, 1205 | {file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:914474c9f1d93080338ace89cb2acee74f4f666fb0424896fcfb8d86058bf17c"}, 1206 | {file = "pyrsistent-0.18.1-cp39-cp39-win32.whl", hash = "sha256:1b34eedd6812bf4d33814fca1b66005805d3640ce53140ab8bbb1e2651b0d9bc"}, 1207 | {file = "pyrsistent-0.18.1-cp39-cp39-win_amd64.whl", hash = "sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07"}, 1208 | {file = "pyrsistent-0.18.1.tar.gz", hash = "sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96"}, 1209 | ] 1210 | pytest = [ 1211 | {file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"}, 1212 | {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"}, 1213 | ] 1214 | python-dateutil = [ 1215 | {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, 1216 | {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, 1217 | ] 1218 | pywin32 = [ 1219 | {file = "pywin32-303-cp310-cp310-win32.whl", hash = "sha256:6fed4af057039f309263fd3285d7b8042d41507343cd5fa781d98fcc5b90e8bb"}, 1220 | {file = "pywin32-303-cp310-cp310-win_amd64.whl", hash = "sha256:51cb52c5ec6709f96c3f26e7795b0bf169ee0d8395b2c1d7eb2c029a5008ed51"}, 1221 | {file = "pywin32-303-cp311-cp311-win32.whl", hash = "sha256:d9b5d87ca944eb3aa4cd45516203ead4b37ab06b8b777c54aedc35975dec0dee"}, 1222 | {file = "pywin32-303-cp311-cp311-win_amd64.whl", hash = "sha256:fcf44032f5b14fcda86028cdf49b6ebdaea091230eb0a757282aa656e4732439"}, 1223 | {file = "pywin32-303-cp36-cp36m-win32.whl", hash = "sha256:aad484d52ec58008ca36bd4ad14a71d7dd0a99db1a4ca71072213f63bf49c7d9"}, 1224 | {file = "pywin32-303-cp36-cp36m-win_amd64.whl", hash = "sha256:2a09632916b6bb231ba49983fe989f2f625cea237219530e81a69239cd0c4559"}, 1225 | {file = "pywin32-303-cp37-cp37m-win32.whl", hash = "sha256:b1675d82bcf6dbc96363fca747bac8bff6f6e4a447a4287ac652aa4b9adc796e"}, 1226 | {file = "pywin32-303-cp37-cp37m-win_amd64.whl", hash = "sha256:c268040769b48a13367221fced6d4232ed52f044ffafeda247bd9d2c6bdc29ca"}, 1227 | {file = "pywin32-303-cp38-cp38-win32.whl", hash = "sha256:5f9ec054f5a46a0f4dfd72af2ce1372f3d5a6e4052af20b858aa7df2df7d355b"}, 1228 | {file = "pywin32-303-cp38-cp38-win_amd64.whl", hash = "sha256:793bf74fce164bcffd9d57bb13c2c15d56e43c9542a7b9687b4fccf8f8a41aba"}, 1229 | {file = "pywin32-303-cp39-cp39-win32.whl", hash = "sha256:7d3271c98434617a11921c5ccf74615794d97b079e22ed7773790822735cc352"}, 1230 | {file = "pywin32-303-cp39-cp39-win_amd64.whl", hash = "sha256:79cbb862c11b9af19bcb682891c1b91942ec2ff7de8151e2aea2e175899cda34"}, 1231 | ] 1232 | pywinpty = [ 1233 | {file = "pywinpty-2.0.5-cp310-none-win_amd64.whl", hash = "sha256:f86c76e2881c37e69678cbbf178109f8da1fa8584db24d58e1b9369b0276cfcb"}, 1234 | {file = "pywinpty-2.0.5-cp37-none-win_amd64.whl", hash = "sha256:ff9b52f182650cfdf3db1b264a6fe0963eb9d996a7a1fa843ac406c1e32111f8"}, 1235 | {file = "pywinpty-2.0.5-cp38-none-win_amd64.whl", hash = "sha256:651ee1467bd7eb6f64d44dbc954b7ab7d15ab6d8adacc4e13299692c67c5d5d2"}, 1236 | {file = "pywinpty-2.0.5-cp39-none-win_amd64.whl", hash = "sha256:e59a508ae78374febada3e53b5bbc90b5ad07ae68cbfd72a2e965f9793ae04f3"}, 1237 | {file = "pywinpty-2.0.5.tar.gz", hash = "sha256:e125d3f1804d8804952b13e33604ad2ca8b9b2cac92b27b521c005d1604794f8"}, 1238 | ] 1239 | pyzmq = [ 1240 | {file = "pyzmq-22.3.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:6b217b8f9dfb6628f74b94bdaf9f7408708cb02167d644edca33f38746ca12dd"}, 1241 | {file = "pyzmq-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2841997a0d85b998cbafecb4183caf51fd19c4357075dfd33eb7efea57e4c149"}, 1242 | {file = "pyzmq-22.3.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f89468059ebc519a7acde1ee50b779019535db8dcf9b8c162ef669257fef7a93"}, 1243 | {file = "pyzmq-22.3.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea12133df25e3a6918718fbb9a510c6ee5d3fdd5a346320421aac3882f4feeea"}, 1244 | {file = "pyzmq-22.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c532fd68b93998aab92356be280deec5de8f8fe59cd28763d2cc8a58747b7f"}, 1245 | {file = "pyzmq-22.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f907c7359ce8bf7f7e63c82f75ad0223384105f5126f313400b7e8004d9b33c3"}, 1246 | {file = "pyzmq-22.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:902319cfe23366595d3fa769b5b751e6ee6750a0a64c5d9f757d624b2ac3519e"}, 1247 | {file = "pyzmq-22.3.0-cp310-cp310-win32.whl", hash = "sha256:67db33bea0a29d03e6eeec55a8190e033318cee3cbc732ba8fd939617cbf762d"}, 1248 | {file = "pyzmq-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:7661fc1d5cb73481cf710a1418a4e1e301ed7d5d924f91c67ba84b2a1b89defd"}, 1249 | {file = "pyzmq-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79244b9e97948eaf38695f4b8e6fc63b14b78cc37f403c6642ba555517ac1268"}, 1250 | {file = "pyzmq-22.3.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab888624ed68930442a3f3b0b921ad7439c51ba122dbc8c386e6487a658e4a4e"}, 1251 | {file = "pyzmq-22.3.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18cd854b423fce44951c3a4d3e686bac8f1243d954f579e120a1714096637cc0"}, 1252 | {file = "pyzmq-22.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:de8df0684398bd74ad160afdc2a118ca28384ac6f5e234eb0508858d8d2d9364"}, 1253 | {file = "pyzmq-22.3.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:62bcade20813796c426409a3e7423862d50ff0639f5a2a95be4b85b09a618666"}, 1254 | {file = "pyzmq-22.3.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:ea5a79e808baef98c48c884effce05c31a0698c1057de8fc1c688891043c1ce1"}, 1255 | {file = "pyzmq-22.3.0-cp36-cp36m-win32.whl", hash = "sha256:3c1895c95be92600233e476fe283f042e71cf8f0b938aabf21b7aafa62a8dac9"}, 1256 | {file = "pyzmq-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:851977788b9caa8ed011f5f643d3ee8653af02c5fc723fa350db5125abf2be7b"}, 1257 | {file = "pyzmq-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b4ebed0977f92320f6686c96e9e8dd29eed199eb8d066936bac991afc37cbb70"}, 1258 | {file = "pyzmq-22.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42abddebe2c6a35180ca549fadc7228d23c1e1f76167c5ebc8a936b5804ea2df"}, 1259 | {file = "pyzmq-22.3.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1e41b32d6f7f9c26bc731a8b529ff592f31fc8b6ef2be9fa74abd05c8a342d7"}, 1260 | {file = "pyzmq-22.3.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:be4e0f229cf3a71f9ecd633566bd6f80d9fa6afaaff5489492be63fe459ef98c"}, 1261 | {file = "pyzmq-22.3.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:08c4e315a76ef26eb833511ebf3fa87d182152adf43dedee8d79f998a2162a0b"}, 1262 | {file = "pyzmq-22.3.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:badb868fff14cfd0e200eaa845887b1011146a7d26d579aaa7f966c203736b92"}, 1263 | {file = "pyzmq-22.3.0-cp37-cp37m-win32.whl", hash = "sha256:7c58f598d9fcc52772b89a92d72bf8829c12d09746a6d2c724c5b30076c1f11d"}, 1264 | {file = "pyzmq-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2b97502c16a5ec611cd52410bdfaab264997c627a46b0f98d3f666227fd1ea2d"}, 1265 | {file = "pyzmq-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d728b08448e5ac3e4d886b165385a262883c34b84a7fe1166277fe675e1c197a"}, 1266 | {file = "pyzmq-22.3.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:480b9931bfb08bf8b094edd4836271d4d6b44150da051547d8c7113bf947a8b0"}, 1267 | {file = "pyzmq-22.3.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7dc09198e4073e6015d9a8ea093fc348d4e59de49382476940c3dd9ae156fba8"}, 1268 | {file = "pyzmq-22.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ca6cd58f62a2751728016d40082008d3b3412a7f28ddfb4a2f0d3c130f69e74"}, 1269 | {file = "pyzmq-22.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:468bd59a588e276961a918a3060948ae68f6ff5a7fa10bb2f9160c18fe341067"}, 1270 | {file = "pyzmq-22.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c88fa7410e9fc471e0858638f403739ee869924dd8e4ae26748496466e27ac59"}, 1271 | {file = "pyzmq-22.3.0-cp38-cp38-win32.whl", hash = "sha256:c0f84360dcca3481e8674393bdf931f9f10470988f87311b19d23cda869bb6b7"}, 1272 | {file = "pyzmq-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:f762442bab706fd874064ca218b33a1d8e40d4938e96c24dafd9b12e28017f45"}, 1273 | {file = "pyzmq-22.3.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:954e73c9cd4d6ae319f1c936ad159072b6d356a92dcbbabfd6e6204b9a79d356"}, 1274 | {file = "pyzmq-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f43b4a2e6218371dd4f41e547bd919ceeb6ebf4abf31a7a0669cd11cd91ea973"}, 1275 | {file = "pyzmq-22.3.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:acebba1a23fb9d72b42471c3771b6f2f18dcd46df77482612054bd45c07dfa36"}, 1276 | {file = "pyzmq-22.3.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cf98fd7a6c8aaa08dbc699ffae33fd71175696d78028281bc7b832b26f00ca57"}, 1277 | {file = "pyzmq-22.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d072f7dfbdb184f0786d63bda26e8a0882041b1e393fbe98940395f7fab4c5e2"}, 1278 | {file = "pyzmq-22.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:53f4fd13976789ffafedd4d46f954c7bb01146121812b72b4ddca286034df966"}, 1279 | {file = "pyzmq-22.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1b5d457acbadcf8b27561deeaa386b0217f47626b29672fa7bd31deb6e91e1b"}, 1280 | {file = "pyzmq-22.3.0-cp39-cp39-win32.whl", hash = "sha256:e6a02cf7271ee94674a44f4e62aa061d2d049001c844657740e156596298b70b"}, 1281 | {file = "pyzmq-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:d3dcb5548ead4f1123851a5ced467791f6986d68c656bc63bfff1bf9e36671e2"}, 1282 | {file = "pyzmq-22.3.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3a4c9886d61d386b2b493377d980f502186cd71d501fffdba52bd2a0880cef4f"}, 1283 | {file = "pyzmq-22.3.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:80e043a89c6cadefd3a0712f8a1322038e819ebe9dbac7eca3bce1721bcb63bf"}, 1284 | {file = "pyzmq-22.3.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1621e7a2af72cced1f6ec8ca8ca91d0f76ac236ab2e8828ac8fe909512d566cb"}, 1285 | {file = "pyzmq-22.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:d6157793719de168b199194f6b6173f0ccd3bf3499e6870fac17086072e39115"}, 1286 | {file = "pyzmq-22.3.0.tar.gz", hash = "sha256:8eddc033e716f8c91c6a2112f0a8ebc5e00532b4a6ae1eb0ccc48e027f9c671c"}, 1287 | ] 1288 | send2trash = [ 1289 | {file = "Send2Trash-1.8.0-py3-none-any.whl", hash = "sha256:f20eaadfdb517eaca5ce077640cb261c7d2698385a6a0f072a4a5447fd49fa08"}, 1290 | {file = "Send2Trash-1.8.0.tar.gz", hash = "sha256:d2c24762fd3759860a0aff155e45871447ea58d2be6bdd39b5c8f966a0c99c2d"}, 1291 | ] 1292 | six = [ 1293 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 1294 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 1295 | ] 1296 | terminado = [ 1297 | {file = "terminado-0.13.2-py3-none-any.whl", hash = "sha256:d61f112f3beb7271d953d3934f056af185f6be0750303581fa1c511379a8a5d0"}, 1298 | {file = "terminado-0.13.2.tar.gz", hash = "sha256:e6147a7ea31d150f9df4a26cedde3dbb2e011be269f89ff0267ae4157f3ae426"}, 1299 | ] 1300 | testpath = [ 1301 | {file = "testpath-0.6.0-py3-none-any.whl", hash = "sha256:8ada9f80a2ac6fb0391aa7cdb1a7d11cfa8429f693eda83f74dde570fe6fa639"}, 1302 | {file = "testpath-0.6.0.tar.gz", hash = "sha256:2f1b97e6442c02681ebe01bd84f531028a7caea1af3825000f52345c30285e0f"}, 1303 | ] 1304 | tornado = [ 1305 | {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, 1306 | {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"}, 1307 | {file = "tornado-6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05"}, 1308 | {file = "tornado-6.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910"}, 1309 | {file = "tornado-6.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b"}, 1310 | {file = "tornado-6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675"}, 1311 | {file = "tornado-6.1-cp35-cp35m-win32.whl", hash = "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5"}, 1312 | {file = "tornado-6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68"}, 1313 | {file = "tornado-6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb"}, 1314 | {file = "tornado-6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c"}, 1315 | {file = "tornado-6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921"}, 1316 | {file = "tornado-6.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558"}, 1317 | {file = "tornado-6.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c"}, 1318 | {file = "tornado-6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085"}, 1319 | {file = "tornado-6.1-cp36-cp36m-win32.whl", hash = "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575"}, 1320 | {file = "tornado-6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795"}, 1321 | {file = "tornado-6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f"}, 1322 | {file = "tornado-6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102"}, 1323 | {file = "tornado-6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4"}, 1324 | {file = "tornado-6.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd"}, 1325 | {file = "tornado-6.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01"}, 1326 | {file = "tornado-6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d"}, 1327 | {file = "tornado-6.1-cp37-cp37m-win32.whl", hash = "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df"}, 1328 | {file = "tornado-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37"}, 1329 | {file = "tornado-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95"}, 1330 | {file = "tornado-6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a"}, 1331 | {file = "tornado-6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"}, 1332 | {file = "tornado-6.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288"}, 1333 | {file = "tornado-6.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f"}, 1334 | {file = "tornado-6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6"}, 1335 | {file = "tornado-6.1-cp38-cp38-win32.whl", hash = "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326"}, 1336 | {file = "tornado-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c"}, 1337 | {file = "tornado-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5"}, 1338 | {file = "tornado-6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe"}, 1339 | {file = "tornado-6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea"}, 1340 | {file = "tornado-6.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2"}, 1341 | {file = "tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0"}, 1342 | {file = "tornado-6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd"}, 1343 | {file = "tornado-6.1-cp39-cp39-win32.whl", hash = "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c"}, 1344 | {file = "tornado-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4"}, 1345 | {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"}, 1346 | ] 1347 | tqdm = [ 1348 | {file = "tqdm-4.63.0-py2.py3-none-any.whl", hash = "sha256:e643e071046f17139dea55b880dc9b33822ce21613b4a4f5ea57f202833dbc29"}, 1349 | {file = "tqdm-4.63.0.tar.gz", hash = "sha256:1d9835ede8e394bb8c9dcbffbca02d717217113adc679236873eeaac5bc0b3cd"}, 1350 | ] 1351 | traitlets = [ 1352 | {file = "traitlets-5.1.1-py3-none-any.whl", hash = "sha256:2d313cc50a42cd6c277e7d7dc8d4d7fedd06a2c215f78766ae7b1a66277e0033"}, 1353 | {file = "traitlets-5.1.1.tar.gz", hash = "sha256:059f456c5a7c1c82b98c2e8c799f39c9b8128f6d0d46941ee118daace9eb70c7"}, 1354 | ] 1355 | typing-extensions = [ 1356 | {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, 1357 | {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, 1358 | ] 1359 | wcwidth = [ 1360 | {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, 1361 | {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, 1362 | ] 1363 | webencodings = [ 1364 | {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, 1365 | {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, 1366 | ] 1367 | widgetsnbextension = [ 1368 | {file = "widgetsnbextension-3.5.2-py2.py3-none-any.whl", hash = "sha256:763a9fdc836d141fa080005a886d63f66f73d56dba1fb5961afc239c77708569"}, 1369 | {file = "widgetsnbextension-3.5.2.tar.gz", hash = "sha256:e0731a60ba540cd19bbbefe771a9076dcd2dde90713a8f87f27f53f2d1db7727"}, 1370 | ] 1371 | zipp = [ 1372 | {file = "zipp-3.7.0-py3-none-any.whl", hash = "sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"}, 1373 | {file = "zipp-3.7.0.tar.gz", hash = "sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d"}, 1374 | ] 1375 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "binance_historical_data" 3 | version = "0.1.14" 4 | description = "" 5 | authors = ["stanislav "] 6 | readme = "README.rst" 7 | license="MIT" 8 | repository = "https://github.com/stas-prokopiev/binance_historical_data" 9 | 10 | [tool.poetry.dependencies] 11 | python = "^3.7" 12 | tqdm = "^4.62.3" 13 | char = "^0.1.2" 14 | mpire = "^2.3.3" 15 | ipywidgets = "^7.6.5" 16 | 17 | [tool.poetry.dev-dependencies] 18 | pytest = "^5.2" 19 | 20 | [build-system] 21 | requires = ["poetry-core>=1.0.8"] 22 | build-backend = "poetry.core.masonry.api" 23 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | appnope==0.1.2; platform_system == "Darwin" and python_version >= "3.7" and sys_platform == "darwin" 2 | argon2-cffi-bindings==21.2.0; python_version >= "3.6" 3 | argon2-cffi==21.3.0; python_version >= "3.6" 4 | attrs==21.4.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" 5 | backcall==0.2.0; python_version >= "3.7" 6 | bleach==4.1.0; python_version >= "3.7" 7 | cffi==1.15.0; python_full_version >= "3.6.1" and python_version >= "3.7" and implementation_name == "pypy" 8 | char==0.1.2 9 | colorama==0.4.4; python_version >= "3.7" and python_full_version < "3.0.0" and platform_system == "Windows" and sys_platform == "win32" or python_full_version >= "3.5.0" and platform_system == "Windows" and sys_platform == "win32" and python_version >= "3.7" 10 | dataclasses==0.6 11 | debugpy==1.5.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" 12 | decorator==5.1.1; python_version >= "3.7" 13 | defusedxml==0.7.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" 14 | entrypoints==0.4; python_full_version >= "3.6.1" and python_version >= "3.7" 15 | importlib-metadata==4.11.2; python_version < "3.8" and python_version >= "3.7" 16 | importlib-resources==5.4.0; python_version < "3.9" and python_version >= "3.7" 17 | ipykernel==6.9.1; python_version >= "3.7" 18 | ipython-genutils==0.2.0; python_version >= "3.6" 19 | ipython==7.32.0; python_version >= "3.7" 20 | ipywidgets==7.6.5 21 | jedi==0.18.1; python_version >= "3.7" 22 | jinja2==3.0.3; python_version >= "3.7" 23 | jsonschema==4.4.0; python_version >= "3.7" 24 | jupyter-client==7.1.2; python_full_version >= "3.7.0" and python_version >= "3.7" 25 | jupyter-core==4.9.2; python_full_version >= "3.6.1" and python_version >= "3.7" 26 | jupyterlab-pygments==0.1.2; python_version >= "3.7" 27 | jupyterlab-widgets==1.0.2; python_version >= "3.6" 28 | markupsafe==2.1.0; python_version >= "3.7" 29 | matplotlib-inline==0.1.3; python_version >= "3.7" 30 | mistune==0.8.4; python_version >= "3.7" 31 | mpire==2.3.3 32 | nbclient==0.5.11; python_full_version >= "3.7.0" and python_version >= "3.7" 33 | nbconvert==6.4.2; python_version >= "3.7" 34 | nbformat==5.1.3; python_full_version >= "3.7.0" and python_version >= "3.7" 35 | nest-asyncio==1.5.4; python_full_version >= "3.7.0" and python_version >= "3.7" 36 | notebook==6.4.8; python_version >= "3.6" 37 | packaging==21.3; python_version >= "3.7" 38 | pandocfilters==1.5.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7" 39 | parso==0.8.3; python_version >= "3.7" 40 | pexpect==4.8.0; sys_platform != "win32" and python_version >= "3.7" 41 | pickleshare==0.7.5; python_version >= "3.7" 42 | prometheus-client==0.13.1; python_version >= "3.6" 43 | prompt-toolkit==3.0.28; python_full_version >= "3.6.2" and python_version >= "3.7" 44 | ptyprocess==0.7.0; sys_platform != "win32" and python_version >= "3.7" and os_name != "nt" 45 | py==1.11.0; python_full_version >= "3.6.1" and python_version >= "3.7" and implementation_name == "pypy" 46 | pycparser==2.21; python_full_version >= "3.6.1" and python_version >= "3.7" and implementation_name == "pypy" 47 | pygments==2.11.2; python_version >= "3.7" 48 | pyparsing==3.0.7; python_version >= "3.7" 49 | pyrsistent==0.18.1; python_version >= "3.7" 50 | python-dateutil==2.8.2; python_full_version >= "3.6.1" and python_version >= "3.7" 51 | pywin32==303; sys_platform == "win32" and platform_python_implementation != "PyPy" and python_version >= "3.7" and python_full_version >= "3.6.1" 52 | pywinpty==2.0.5; os_name == "nt" and python_version >= "3.7" 53 | pyzmq==22.3.0; python_full_version >= "3.6.1" and python_version >= "3.7" 54 | send2trash==1.8.0; python_version >= "3.6" 55 | six==1.16.0; python_full_version >= "3.6.1" and python_version >= "3.7" and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.7") 56 | terminado==0.13.2; python_version >= "3.7" 57 | testpath==0.6.0; python_version >= "3.7" 58 | tornado==6.1; python_full_version >= "3.6.1" and python_version >= "3.7" 59 | tqdm==4.63.0; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") 60 | traitlets==5.1.1; python_full_version >= "3.7.0" and python_version >= "3.7" 61 | typing-extensions==4.1.1; python_version < "3.8" and python_version >= "3.7" 62 | wcwidth==0.2.5; python_full_version >= "3.6.2" and python_version >= "3.7" 63 | webencodings==0.5.1; python_version >= "3.7" 64 | widgetsnbextension==3.5.2 65 | zipp==3.7.0; python_version < "3.8" and python_version >= "3.7" 66 | -------------------------------------------------------------------------------- /src/binance_historical_data/__init__.py: -------------------------------------------------------------------------------- 1 | """""" 2 | # Standard library imports 3 | 4 | # Third party imports 5 | 6 | # Local imports 7 | from . import logger 8 | from .data_dumper import BinanceDataDumper 9 | 10 | # Global constants 11 | __all__ = ["BinanceDataDumper"] 12 | 13 | logger.initialize_project_logger( 14 | name=__name__, 15 | path_dir_where_to_store_logs="", 16 | is_stdout_debug=False, 17 | is_to_propagate_to_root_logger=False, 18 | ) 19 | -------------------------------------------------------------------------------- /src/binance_historical_data/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stas-prokopiev/binance_historical_data/0ff488c51228fb8f4311041785adfc97df991c3e/src/binance_historical_data/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /src/binance_historical_data/__pycache__/kline.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stas-prokopiev/binance_historical_data/0ff488c51228fb8f4311041785adfc97df991c3e/src/binance_historical_data/__pycache__/kline.cpython-37.pyc -------------------------------------------------------------------------------- /src/binance_historical_data/__pycache__/logger.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stas-prokopiev/binance_historical_data/0ff488c51228fb8f4311041785adfc97df991c3e/src/binance_historical_data/__pycache__/logger.cpython-37.pyc -------------------------------------------------------------------------------- /src/binance_historical_data/data_dumper.py: -------------------------------------------------------------------------------- 1 | """Module with class to download candle historical data from binance""" 2 | # Standard library imports 3 | import os 4 | import re 5 | import urllib.request 6 | import xml.etree.ElementTree as ET 7 | import json 8 | import logging 9 | from collections import defaultdict 10 | from collections import Counter 11 | import zipfile 12 | import datetime 13 | from dateutil.relativedelta import relativedelta 14 | 15 | # Third party imports 16 | from tqdm.auto import tqdm 17 | from char import char 18 | from mpire import WorkerPool 19 | 20 | # Local imports 21 | 22 | # Global constants 23 | LOGGER = logging.getLogger(__name__) 24 | 25 | 26 | class BinanceDataDumper: 27 | _FUTURES_ASSET_CLASSES = ("um", "cm") 28 | _ASSET_CLASSES = ("spot",) 29 | _DICT_DATA_TYPES_BY_ASSET = { 30 | "spot": ("aggTrades", "klines", "trades"), 31 | "cm": ("aggTrades", "klines", "trades", "indexPriceKlines", "markPriceKlines", "premiumIndexKlines"), 32 | "um": ("aggTrades", "klines", "trades", "indexPriceKlines", "markPriceKlines", "premiumIndexKlines", "metrics") 33 | } 34 | _DATA_FREQUENCY_NEEDED_FOR_TYPE = ("klines", "indexPriceKlines", "markPriceKlines", "premiumIndexKlines") 35 | _DATA_FREQUENCY_ENUM = ('1s','1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h', '6h', '8h', '12h', 36 | '1d', '3d', '1w', '1mo') 37 | 38 | def __init__( 39 | self, 40 | path_dir_where_to_dump, 41 | asset_class="spot", # spot, um, cm 42 | data_type="klines", # aggTrades, klines, trades 43 | data_frequency="1m", # argument for data_type="klines" 44 | ) -> None: 45 | """Init object to dump all data from binance servers 46 | 47 | Args: 48 | path_dir_where_to_dump (str): Folder where to dump data 49 | asset_class (str): Asset class which data to get [spot, futures] 50 | data_type (str): data type to dump: [aggTrades, klines, trades] 51 | data_frequency (str): \ 52 | Data frequency. [1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h] 53 | Defaults to "1m". 54 | """ 55 | if asset_class not in (self._ASSET_CLASSES + self._FUTURES_ASSET_CLASSES): 56 | raise ValueError( 57 | f"Unknown asset class: {asset_class} " 58 | f"not in {self._ASSET_CLASSES + self._FUTURES_ASSET_CLASSES}") 59 | 60 | if data_type not in self._DICT_DATA_TYPES_BY_ASSET[asset_class]: 61 | raise ValueError( 62 | f"Unknown data type: {data_type} " 63 | f"not in {self._DICT_DATA_TYPES_BY_ASSET[asset_class]}") 64 | 65 | if data_type in self._DATA_FREQUENCY_NEEDED_FOR_TYPE: 66 | if data_frequency not in self._DATA_FREQUENCY_ENUM: 67 | raise ValueError( 68 | f"Unknown data frequency: {data_frequency} " 69 | f"not in {self._DATA_FREQUENCY_ENUM}") 70 | self._data_frequency = data_frequency 71 | else: 72 | self._data_frequency = "" 73 | 74 | self.path_dir_where_to_dump = path_dir_where_to_dump 75 | self.dict_new_points_saved_by_ticker = defaultdict(dict) 76 | self._base_url = "https://data.binance.vision/data" 77 | self._asset_class = asset_class 78 | self._data_type = data_type 79 | 80 | 81 | 82 | 83 | @char 84 | def dump_data( 85 | self, 86 | tickers=None, 87 | date_start=None, 88 | date_end=None, 89 | is_to_update_existing=False, 90 | int_max_tickers_to_get=None, 91 | tickers_to_exclude=None, 92 | ): 93 | """Main method to dump new of update existing historical data 94 | 95 | Args: 96 | tickers (list[str]):\ 97 | list trading pairs for which to dump data\ 98 | by default all ****USDT pairs will be taken 99 | tickers_to_exclude (list[str]):\ 100 | list trading pairs which to exclude from dump 101 | date_start (datetime.date): Date from which to start dump 102 | date_end (datetime.date): The last date for which to dump data 103 | is_to_update_existing (bool): \ 104 | Flag if you want to update data if it's already exists 105 | int_max_tickers_to_get (int): Max number of trading pairs to get 106 | """ 107 | self.dict_new_points_saved_by_ticker.clear() 108 | list_trading_pairs = self._get_list_trading_pairs_to_download( 109 | tickers=tickers, tickers_to_exclude=tickers_to_exclude) 110 | if int_max_tickers_to_get: 111 | list_trading_pairs = list_trading_pairs[:int_max_tickers_to_get] 112 | LOGGER.info( 113 | "Download full data for %d tickers: ", len(list_trading_pairs)) 114 | 115 | 116 | LOGGER.info( 117 | "---> Data will be saved here: %s", 118 | os.path.join(os.path.abspath(self.path_dir_where_to_dump), self._asset_class)) 119 | 120 | 121 | 122 | 123 | LOGGER.info("---> Data Frequency: %s", self._data_frequency) 124 | # Start date 125 | if date_start is None: 126 | date_start = datetime.date(year=2017, month=1, day=1) 127 | if date_start < datetime.date(year=2017, month=1, day=1): 128 | date_start = datetime.date(year=2017, month=1, day=1) 129 | # End date 130 | if date_end is None: 131 | date_end = datetime.datetime.utcnow().date() - relativedelta(days=1) 132 | if date_end > datetime.datetime.utcnow().date() - relativedelta(days=1): 133 | date_end = datetime.datetime.utcnow().date() - relativedelta(days=1) 134 | LOGGER.info("---> Start Date: %s", date_start.strftime("%Y%m%d")) 135 | LOGGER.info("---> End Date: %s", date_end.strftime("%Y%m%d")) 136 | date_end_first_day_of_month = datetime.date( 137 | year=date_end.year, month=date_end.month, day=1) 138 | for ticker in tqdm(list_trading_pairs, leave=True, desc="Tickers"): 139 | # 1) Download all monthly data 140 | if self._data_type != "metrics" and (date_end_first_day_of_month - relativedelta(days=1) > date_start): 141 | self._download_data_for_1_ticker( 142 | ticker=ticker, 143 | date_start=date_start, 144 | date_end=(date_end_first_day_of_month - relativedelta(days=1)), 145 | timeperiod_per_file="monthly", 146 | is_to_update_existing=is_to_update_existing, 147 | ) 148 | # 2) Download all daily date 149 | if self._data_type == "metrics": 150 | date_start_daily = date_start 151 | else: 152 | date_start_daily = date_end_first_day_of_month 153 | self._download_data_for_1_ticker( 154 | ticker=ticker, 155 | date_start=date_start_daily, 156 | date_end=date_end, 157 | timeperiod_per_file="daily", 158 | is_to_update_existing=is_to_update_existing, 159 | ) 160 | ##### 161 | # Print statistics 162 | self._print_dump_statistics() 163 | 164 | def get_list_all_trading_pairs(self): 165 | """Get all trading pairs available at binance now""" 166 | # Select the right Top Level Domain for US/non US 167 | country_code = self._get_user_country_from_ip() 168 | if country_code == "US": 169 | tld = "us" 170 | else: 171 | tld = "com" 172 | ##### 173 | if self._asset_class == 'um': 174 | response = urllib.request.urlopen(f"https://fapi.binance.{tld}/fapi/v1/exchangeInfo").read() 175 | elif self._asset_class == 'cm': 176 | response = urllib.request.urlopen(f"https://dapi.binance.{tld}/dapi/v1/exchangeInfo").read() 177 | else: 178 | # https://api.binance.us/api/v3/exchangeInfo 179 | response = urllib.request.urlopen(f"https://api.binance.{tld}/api/v3/exchangeInfo").read() 180 | return list(map( 181 | lambda symbol: symbol['symbol'], 182 | json.loads(response)['symbols'] 183 | )) 184 | 185 | @staticmethod 186 | def _get_user_country_from_ip() -> str: 187 | """Get user country to select the right binance url""" 188 | url = 'https://ipinfo.io/json' 189 | res = urllib.request.urlopen(url) 190 | data = json.load(res) 191 | return data.get("country", "Unknown") 192 | 193 | 194 | 195 | def _get_list_all_available_files(self, prefix=""): 196 | """Get all available files from the binance servers""" 197 | url = os.path.join(self._base_url, prefix).replace("\\", "/").replace("data/", "?prefix=data/") 198 | response = urllib.request.urlopen(url) 199 | html_content = response.read().decode('utf-8') 200 | 201 | # Extract the BUCKET_URL variable 202 | bucket_url_pattern = re.compile(r"var BUCKET_URL = '(.*?)';") 203 | match = bucket_url_pattern.search(html_content) 204 | 205 | if match: 206 | bucket_url = match.group(1) + "?delimiter=/&prefix=data/" + prefix.replace('\\', '/') + "/" 207 | 208 | # Retrieve the content of the BUCKET_URL 209 | bucket_response = urllib.request.urlopen(bucket_url) 210 | bucket_content = bucket_response.read().decode('utf-8') 211 | 212 | # Parse the XML content and extract all elements 213 | root = ET.fromstring(bucket_content) 214 | # Automatically retrieve the namespace 215 | namespace = {'s3': root.tag.split('}')[0].strip('{')} 216 | keys = [element.text for element in root.findall('.//s3:Key', namespace)] 217 | return keys 218 | else: 219 | raise ValueError("BUCKET_URL not found") 220 | 221 | def get_min_start_date_for_ticker(self, ticker): 222 | """Get minimum start date for ticker""" 223 | path_folder_prefix = self._get_path_suffix_to_dir_with_data("monthly", ticker) 224 | min_date = datetime.datetime(datetime.datetime.today().year, datetime.datetime.today().month, 1) 225 | 226 | try: 227 | date_found = False 228 | 229 | files = self._get_list_all_available_files(prefix=path_folder_prefix) 230 | for file in files: 231 | date_str = file.split('.')[0].split('-')[-2:] 232 | date_str = '-'.join(date_str) 233 | date_obj = datetime.datetime.strptime(date_str, '%Y-%m') 234 | if date_obj < min_date: 235 | date_found = True 236 | min_date = date_obj 237 | 238 | if not date_found: 239 | path_folder_prefix = self._get_path_suffix_to_dir_with_data("daily", ticker) 240 | files = self._get_list_all_available_files(prefix=path_folder_prefix) 241 | for file in files: 242 | date_str = file.split('.')[0].split('-')[-3:] 243 | date_str = '-'.join(date_str) 244 | date_obj = datetime.datetime.strptime(date_str, '%Y-%m-%d') 245 | if date_obj < min_date: 246 | min_date = date_obj 247 | 248 | except Exception as e: 249 | LOGGER.error('Min date not found: ', e) 250 | 251 | return min_date.date() 252 | 253 | def get_local_dir_to_data(self, ticker, timeperiod_per_file): 254 | """Path to directory where ticker data is saved 255 | 256 | Args: 257 | ticker (str): trading pair 258 | timeperiod_per_file (str): timeperiod per 1 file in [daily, monthly] 259 | 260 | Returns: 261 | str: path to folder where to save data 262 | """ 263 | path_folder_suffix = self._get_path_suffix_to_dir_with_data( 264 | timeperiod_per_file, ticker) 265 | return os.path.join( 266 | self.path_dir_where_to_dump, path_folder_suffix) 267 | 268 | @char 269 | def create_filename( 270 | self, 271 | ticker, 272 | date_obj, 273 | timeperiod_per_file="monthly", 274 | extension="csv", 275 | ): 276 | """Create file name in the format it's named on the binance server""" 277 | 278 | if timeperiod_per_file == "monthly": 279 | str_date = date_obj.strftime("%Y-%m") 280 | else: 281 | str_date = date_obj.strftime("%Y-%m-%d") 282 | 283 | return f"{ticker}-{self._data_frequency if self._data_frequency else self._data_type}-{str_date}.{extension}" 284 | 285 | def get_all_dates_with_data_for_ticker( 286 | self, 287 | ticker, 288 | timeperiod_per_file="monthly" 289 | ): 290 | """Get list with all dates for which there is saved data 291 | 292 | Args: 293 | ticker (str): trading pair 294 | timeperiod_per_file (str): timeperiod per 1 file in [daily, monthly] 295 | 296 | Returns: 297 | list[datetime.date]: dates with saved data 298 | """ 299 | date_start = datetime.date(year=2017, month=1, day=1) 300 | date_end = datetime.datetime.utcnow().date() 301 | list_dates = self._create_list_dates_for_timeperiod( 302 | date_start=date_start, 303 | date_end=date_end, 304 | timeperiod_per_file=timeperiod_per_file, 305 | ) 306 | list_dates_with_data = [] 307 | path_folder_suffix = self._get_path_suffix_to_dir_with_data( 308 | timeperiod_per_file, ticker) 309 | str_dir_where_to_save = os.path.join( 310 | self.path_dir_where_to_dump, path_folder_suffix) 311 | for date_obj in list_dates: 312 | file_name = self.create_filename( 313 | ticker, 314 | date_obj, 315 | timeperiod_per_file=timeperiod_per_file, 316 | extension="csv", 317 | ) 318 | path_where_to_save = os.path.join( 319 | str_dir_where_to_save, file_name) 320 | if os.path.exists(path_where_to_save): 321 | list_dates_with_data.append(date_obj) 322 | 323 | return list_dates_with_data 324 | 325 | def get_all_tickers_with_data(self, timeperiod_per_file="daily"): 326 | """Get all tickers for which data was dumped 327 | 328 | Args: 329 | timeperiod_per_file (str): timeperiod per 1 file in [daily, monthly] 330 | 331 | Returns: 332 | list[str]: all tickers with data 333 | """ 334 | folder_path = os.path.join(self.path_dir_where_to_dump, self._asset_class) 335 | folder_path = os.path.join(folder_path, timeperiod_per_file) 336 | folder_path = os.path.join(folder_path, self._data_type) 337 | tickers = [ 338 | d 339 | for d in os.listdir(folder_path) 340 | if os.path.isdir(os.path.join(folder_path, d)) 341 | ] 342 | return tickers 343 | 344 | def delete_outdated_daily_results(self): 345 | """ 346 | Deleta daily data for which full month monthly data was already dumped 347 | """ 348 | LOGGER.info("Delete old daily data for which there is monthly data") 349 | dict_files_deleted_by_ticker = defaultdict(int) 350 | tickers = self.get_all_tickers_with_data( 351 | timeperiod_per_file="daily") 352 | for ticker in tqdm(tickers, leave=False): 353 | list_saved_months_dates = self.get_all_dates_with_data_for_ticker( 354 | ticker, 355 | timeperiod_per_file="monthly" 356 | ) 357 | list_saved_days_dates = self.get_all_dates_with_data_for_ticker( 358 | ticker, 359 | timeperiod_per_file="daily" 360 | ) 361 | for date_saved_day in list_saved_days_dates: 362 | date_saved_day_tmp = date_saved_day.replace(day=1) 363 | if date_saved_day_tmp not in list_saved_months_dates: 364 | continue 365 | str_folder = self.get_local_dir_to_data( 366 | ticker, 367 | timeperiod_per_file="daily", 368 | ) 369 | str_filename = self.create_filename( 370 | ticker, 371 | date_saved_day, 372 | timeperiod_per_file="daily", 373 | extension="csv", 374 | ) 375 | try: 376 | os.remove(os.path.join(str_folder, str_filename)) 377 | dict_files_deleted_by_ticker[ticker] += 1 378 | except Exception: 379 | LOGGER.warning( 380 | "Unable to delete file: %s", 381 | os.path.join(str_folder, str_filename) 382 | ) 383 | LOGGER.info( 384 | "---> Done. Daily files deleted for %d tickers", 385 | len(dict_files_deleted_by_ticker) 386 | ) 387 | 388 | @char 389 | def _download_data_for_1_ticker( 390 | self, 391 | ticker, 392 | date_start, 393 | date_end=None, 394 | timeperiod_per_file="monthly", 395 | is_to_update_existing=False, 396 | ): 397 | """Dump data for 1 ticker""" 398 | min_start_date = self.get_min_start_date_for_ticker(ticker) 399 | LOGGER.debug( 400 | "Min Start date for ticker %s is %s", 401 | ticker, 402 | min_start_date.strftime("%Y%m%d") 403 | ) 404 | if date_start < min_start_date: 405 | date_start = min_start_date 406 | LOGGER.debug( 407 | "Start date for ticker %s is %s", 408 | ticker, 409 | date_start.strftime("%Y%m%d") 410 | ) 411 | # Create list dates to use 412 | list_dates = self._create_list_dates_for_timeperiod( 413 | date_start=date_start, 414 | date_end=date_end, 415 | timeperiod_per_file=timeperiod_per_file, 416 | ) 417 | LOGGER.debug("Created dates to dump data: %d", len(list_dates)) 418 | list_dates_with_data = self.get_all_dates_with_data_for_ticker( 419 | ticker, 420 | timeperiod_per_file=timeperiod_per_file 421 | ) 422 | LOGGER.debug("Dates with data found: %d", len(list_dates_with_data)) 423 | if is_to_update_existing: 424 | list_dates_cleared = list_dates 425 | else: 426 | list_dates_cleared = [ 427 | date_obj 428 | for date_obj in list_dates 429 | if date_obj not in list_dates_with_data 430 | ] 431 | LOGGER.debug("Dates to get data: %d", len(list_dates_cleared)) 432 | list_args = [ 433 | (ticker, date_obj, timeperiod_per_file) 434 | for date_obj in list_dates_cleared 435 | ] 436 | # 2) Create path to file where to save data 437 | dir_where_to_save = self.get_local_dir_to_data( 438 | ticker, 439 | timeperiod_per_file=timeperiod_per_file, 440 | ) 441 | LOGGER.debug("Local dir to where dump: %s", dir_where_to_save) 442 | if not os.path.exists(dir_where_to_save): 443 | try: 444 | os.makedirs(dir_where_to_save) 445 | except FileExistsError: 446 | pass 447 | ##### 448 | processes = min(len(list_args), 1 if "trades" in self._data_type.lower() else 10) 449 | # with WorkerPool(n_jobs=threads, start_method="threading") as pool: 450 | # with WorkerPool(n_jobs=processes, use_dill=True) as pool: 451 | with WorkerPool(n_jobs=processes) as pool: 452 | list_saved_dates = list(tqdm( 453 | pool.imap_unordered( 454 | self._download_data_for_1_ticker_1_date, 455 | list_args 456 | ), 457 | leave=False, 458 | total=len(list_args), 459 | desc=f"{timeperiod_per_file} files to download", 460 | unit="files" 461 | )) 462 | ##### 463 | list_saved_dates = [date for date in list_saved_dates if date] 464 | LOGGER.debug( 465 | "---> Downloaded %d files for ticker: %s", 466 | len(list_saved_dates), 467 | ticker 468 | ) 469 | self.dict_new_points_saved_by_ticker[ticker][ 470 | timeperiod_per_file] = len(list_saved_dates) 471 | 472 | @char 473 | def _download_data_for_1_ticker_1_date( 474 | self, 475 | ticker, 476 | date_obj, 477 | timeperiod_per_file="monthly", 478 | ): 479 | """Dump data for 1 ticker for 1 data""" 480 | # 1) Create path to file to save 481 | path_folder_suffix = self._get_path_suffix_to_dir_with_data( 482 | timeperiod_per_file, ticker) 483 | file_name = self.create_filename( 484 | ticker, 485 | date_obj, 486 | timeperiod_per_file=timeperiod_per_file, 487 | extension="zip", 488 | ) 489 | str_dir_where_to_save = os.path.join( 490 | self.path_dir_where_to_dump, path_folder_suffix) 491 | path_zip_raw_file = os.path.join( 492 | str_dir_where_to_save, file_name) 493 | # 2) Create URL to file to download 494 | url_file_to_download = os.path.join( 495 | self._base_url, path_folder_suffix, file_name) 496 | # 3) Download file and unzip it 497 | if not self._download_raw_file(url_file_to_download, path_zip_raw_file): 498 | return None 499 | # 4) Extract zip archive 500 | try: 501 | with zipfile.ZipFile(path_zip_raw_file, 'r') as zip_ref: 502 | zip_ref.extractall(os.path.dirname(path_zip_raw_file)) 503 | except Exception as ex: 504 | LOGGER.warning( 505 | "Unable to unzip file %s with error: %s", path_zip_raw_file, ex) 506 | return None 507 | # 5) Delete the zip archive 508 | try: 509 | os.remove(path_zip_raw_file) 510 | except Exception as ex: 511 | LOGGER.warning( 512 | "Unable to delete zip file %s with error: %s", 513 | path_zip_raw_file, ex) 514 | return None 515 | return date_obj 516 | 517 | def _get_path_suffix_to_dir_with_data(self, timeperiod_per_file, ticker): 518 | """_summary_ 519 | 520 | Args: 521 | timeperiod_per_file (str): Timeperiod per file: [daily, monthly] 522 | ticker (str): Trading pair 523 | 524 | Returns: 525 | str: suffix - https://data.binance.vision/data/ + suffix /filename 526 | """ 527 | folder_path = "" 528 | if self._asset_class in self._FUTURES_ASSET_CLASSES: 529 | folder_path = os.path.join(folder_path, "futures") 530 | folder_path = os.path.join(folder_path, self._asset_class) 531 | folder_path = os.path.join(folder_path, timeperiod_per_file) 532 | folder_path = os.path.join(folder_path, self._data_type) 533 | folder_path = os.path.join(folder_path, ticker) 534 | 535 | if self._data_frequency: 536 | folder_path = os.path.join(folder_path, self._data_frequency) 537 | 538 | return folder_path 539 | 540 | @staticmethod 541 | def _download_raw_file(str_url_path_to_file, str_path_where_to_save): 542 | """Download file from binance server by URL""" 543 | 544 | LOGGER.debug("Download file from: %s", str_url_path_to_file) 545 | str_url_path_to_file = str_url_path_to_file.replace("\\", "/") 546 | try: 547 | if "trades" not in str_url_path_to_file.lower(): 548 | urllib.request.urlretrieve(str_url_path_to_file, str_path_where_to_save) 549 | else: # only show progress bar for trades data as the files are usually big 550 | with tqdm(unit="B", unit_scale=True, miniters=1, 551 | desc="downloading: " + str_url_path_to_file.split("/")[-1]) as progress_bar: 552 | def progress_hook(count, block_size, total_size): 553 | current_size = block_size * count 554 | previous_progress = progress_bar.n / total_size * 100 555 | current_progress = current_size / total_size * 100 556 | 557 | if current_progress > previous_progress + 10: 558 | progress_bar.total = total_size 559 | progress_bar.update(current_size - progress_bar.n) 560 | 561 | urllib.request.urlretrieve( 562 | str_url_path_to_file, str_path_where_to_save, progress_hook) 563 | except urllib.error.URLError as ex: 564 | LOGGER.debug( 565 | "[WARNING] File not found: %s", str_url_path_to_file) 566 | return 0 567 | except Exception as ex: 568 | LOGGER.warning("Unable to download raw file: %s", ex) 569 | return 0 570 | return 1 571 | 572 | def _print_dump_statistics(self): 573 | """Print the latest dump statistics""" 574 | LOGGER.info( 575 | "Tried to dump data for %d tickers:", 576 | len(self.dict_new_points_saved_by_ticker) 577 | ) 578 | if len(self.dict_new_points_saved_by_ticker) < 50: 579 | self._print_full_dump_statististics() 580 | else: 581 | self._print_short_dump_statististics() 582 | 583 | def _print_full_dump_statististics(self): 584 | """""" 585 | for ticker in self.dict_new_points_saved_by_ticker: 586 | dict_stats = self.dict_new_points_saved_by_ticker[ticker] 587 | LOGGER.info( 588 | "---> For %s new data saved for: %d months %d days", 589 | ticker, 590 | dict_stats.get("monthly", 0), 591 | dict_stats.get("daily", 0), 592 | ) 593 | 594 | def _print_short_dump_statististics(self): 595 | """""" 596 | # Gather stats 597 | int_non_empty_dump_res = 0 598 | int_empty_dump_res = 0 599 | list_months_saved = [] 600 | list_days_saved = [] 601 | for ticker in self.dict_new_points_saved_by_ticker: 602 | dict_stats = self.dict_new_points_saved_by_ticker[ticker] 603 | list_months_saved.append(dict_stats.get("monthly", 0)) 604 | list_days_saved.append(dict_stats.get("daily", 0)) 605 | if dict_stats["monthly"] or dict_stats["daily"]: 606 | int_non_empty_dump_res += 1 607 | else: 608 | int_empty_dump_res += 1 609 | ##### 610 | # Print Stats 611 | LOGGER.info("---> General stats:") 612 | LOGGER.info( 613 | "------> NEW Data WAS dumped for %d trading pairs", 614 | int_non_empty_dump_res) 615 | LOGGER.info( 616 | "------> NEW Data WASN'T dumped for %d trading pairs", 617 | int_empty_dump_res) 618 | ##### 619 | LOGGER.info("---> New months saved:") 620 | counter_months = Counter(list_months_saved) 621 | for value, times in counter_months.most_common(5): 622 | LOGGER.info("------> For %d tickers saved: %s months", times, value) 623 | if len(counter_months) > 5: 624 | LOGGER.info("------> ...") 625 | LOGGER.info("---> New days saved:") 626 | counter_days = Counter(list_days_saved) 627 | for value, times in counter_days.most_common(5): 628 | LOGGER.info("------> For %d tickers saved: %s days", times, value) 629 | if len(counter_days) > 5: 630 | LOGGER.info("------> ...") 631 | LOGGER.info("=" * 79) 632 | 633 | def _get_list_trading_pairs_to_download( 634 | self, 635 | tickers=None, 636 | tickers_to_exclude=None 637 | ): 638 | """ 639 | Create list of tickers for which to get data (by default all **USDT) 640 | """ 641 | all_tickers = self.get_list_all_trading_pairs() 642 | LOGGER.info("---> Found overall tickers: %d", len(all_tickers)) 643 | 644 | if tickers: 645 | LOGGER.info("---> Filter to asked tickers: %d", len(tickers)) 646 | tickers_to_use = [ 647 | ticker 648 | for ticker in all_tickers 649 | if ticker in tickers 650 | ] 651 | else: 652 | LOGGER.info("---> Filter to USDT tickers") 653 | tickers_to_use = [ 654 | ticker 655 | for ticker in all_tickers 656 | if ticker.endswith("USDT") 657 | ] 658 | LOGGER.info("------> Tickers left: %d", len(tickers_to_use)) 659 | ##### 660 | if tickers_to_exclude: 661 | LOGGER.info("---> Exclude the asked tickers: %d", len(tickers_to_exclude)) 662 | tickers_to_use = [ 663 | ticker 664 | for ticker in tickers_to_use 665 | if ticker not in tickers_to_exclude 666 | ] 667 | LOGGER.info("------> Tickers left: %d", len(tickers_to_use)) 668 | return tickers_to_use 669 | 670 | @staticmethod 671 | def _create_list_dates_for_timeperiod( 672 | date_start, 673 | date_end=None, 674 | timeperiod_per_file="monthly", 675 | ): 676 | """Create list dates with asked frequency for [date_start, date_end]""" 677 | list_dates = [] 678 | if date_end is None: 679 | date_end = datetime.datetime.utcnow().date 680 | LOGGER.debug( 681 | "Create dates to dump data for: %s -> %s", date_start, date_end) 682 | ##### 683 | date_to_use = date_start 684 | while date_to_use <= date_end: 685 | list_dates.append(date_to_use) 686 | if timeperiod_per_file == "monthly": 687 | date_to_use = date_to_use + relativedelta(months=1) 688 | else: 689 | date_to_use = date_to_use + relativedelta(days=1) 690 | LOGGER.debug("---> Dates created: %d", len(list_dates)) 691 | return list_dates 692 | -------------------------------------------------------------------------------- /src/binance_historical_data/logger.py: -------------------------------------------------------------------------------- 1 | """This is a one file library with main function initialize_project_logger 2 | which can be used as first logger set up for new py packages 3 | Manual can be found here: https://github.com/stas-prokopiev/basic_package_logger 4 | """ 5 | import sys 6 | import os 7 | import io 8 | import logging 9 | 10 | # Define constants with msg formats 11 | # File msg formats 12 | STR_DEBUG_FILE_FORMAT = ( 13 | "{" 14 | "'levelname':'%(levelname)s', " 15 | "'asctime':'%(asctime)s', " 16 | "'filename':'%(filename)s', " 17 | "'name':'%(name)s', " 18 | "'funcName':'%(funcName)s', " 19 | "'lineno':'%(lineno)d', " 20 | "'message':'%(message)s'" 21 | "}" 22 | ) 23 | 24 | STR_ERROR_FILE_FORMAT = STR_DEBUG_FILE_FORMAT 25 | # stdout msg formats 26 | STR_DEBUG_FORMAT = '[%(levelname)s] %(message)s' 27 | STR_INFO_FORMAT = '%(message)s' 28 | STR_WARNING_FORMAT = '[%(levelname)s] %(message)s' 29 | STR_ERROR_FORMAT = ( 30 | '[%(levelname)s: %(asctime)s:%(filename)s:' 31 | '%(name)s:%(funcName)s:%(lineno)d] %(message)s') 32 | 33 | 34 | class OnlyLowerLevelFilter(): 35 | """Define filter to show only logs with level lower than given level 36 | """ 37 | def __init__(self, level): 38 | self.level = level 39 | 40 | def filter(self, record): 41 | """Filter messages with level lower than given""" 42 | return record.levelno < self.level 43 | 44 | 45 | def initialize_project_logger( 46 | name, 47 | path_dir_where_to_store_logs="", 48 | is_stdout_debug=False, 49 | is_to_propagate_to_root_logger=False, 50 | ): 51 | """function returns a perfectly set up logger for the new package""" 52 | logger_obj = logging.getLogger(name) 53 | # If logger already exists then just return it 54 | if logger_obj.handlers: 55 | return logger_obj 56 | # Create and set basic settings for logger 57 | logger_obj.setLevel(20-int(is_stdout_debug)*10) 58 | logger_obj.propagate = is_to_propagate_to_root_logger 59 | # If root logger has not been set 60 | # Or user don't know what is logging at all 61 | # logging.basicConfig(file="/dev/null", level=0) # Linux 62 | # logging.basicConfig(file="NUL", level=0) # Win 63 | logging.basicConfig(stream=io.StringIO(), level=0) # Any but eats memory 64 | ##### 65 | # 1) Set up stdout logs 66 | # 1.0) Add debug handler if necessary 67 | if is_stdout_debug: 68 | debug_handler = logging.StreamHandler(sys.stdout) 69 | debug_handler.setLevel(level=0) 70 | debug_handler.setFormatter(logging.Formatter(STR_DEBUG_FORMAT)) 71 | debug_handler.addFilter(OnlyLowerLevelFilter(20)) 72 | logger_obj.addHandler(debug_handler) 73 | # 1.1) Add info handler 74 | info_handler = logging.StreamHandler(sys.stdout) 75 | info_handler.setLevel(level=20) 76 | info_handler.setFormatter(logging.Formatter(STR_INFO_FORMAT)) 77 | info_handler.addFilter(OnlyLowerLevelFilter(30)) 78 | logger_obj.addHandler(info_handler) 79 | # 1.2) Add warning handler 80 | warning_handler = logging.StreamHandler(sys.stdout) 81 | warning_handler.setLevel(level=30) 82 | warning_handler.setFormatter(logging.Formatter(STR_WARNING_FORMAT)) 83 | warning_handler.addFilter(OnlyLowerLevelFilter(40)) 84 | logger_obj.addHandler(warning_handler) 85 | # 1.3) Add error handler 86 | error_handler = logging.StreamHandler(sys.stderr) 87 | error_handler.setLevel(level=40) 88 | error_handler.setFormatter(logging.Formatter(STR_ERROR_FORMAT)) 89 | logger_obj.addHandler(error_handler) 90 | if not path_dir_where_to_store_logs: 91 | return None 92 | if not os.path.isdir(path_dir_where_to_store_logs): 93 | raise TypeError( 94 | "Wrong type of argument 'path_dir_where_to_store_logs'\n" 95 | "Expected to get string with path to some directory " 96 | "But got: " + str(path_dir_where_to_store_logs) 97 | ) 98 | ##### 99 | # 2) Set up file handlers for logger 100 | # Create folder for Logs 101 | str_path_to_logs_dir = os.path.join(path_dir_where_to_store_logs, "Logs") 102 | if not os.path.isdir(str_path_to_logs_dir): 103 | os.makedirs(str_path_to_logs_dir) 104 | # 2.1) Debug handler 105 | debug_file_handler = logging.handlers.RotatingFileHandler( 106 | os.path.join(str_path_to_logs_dir, "debug.log"), 107 | maxBytes=10000, 108 | backupCount=1 109 | ) 110 | debug_file_handler.setLevel(level=0) 111 | debug_file_handler.setFormatter(logging.Formatter(STR_DEBUG_FILE_FORMAT)) 112 | logger_obj.addHandler(debug_file_handler) 113 | # 2.1) Warnings and above handler 114 | warnings_file_handler = logging.handlers.RotatingFileHandler( 115 | os.path.join(str_path_to_logs_dir, "errors.log"), 116 | maxBytes=10000, 117 | backupCount=1 118 | ) 119 | warnings_file_handler.setLevel(level=30) 120 | warnings_file_handler.setFormatter( 121 | logging.Formatter(STR_ERROR_FILE_FORMAT)) 122 | logger_obj.addHandler(warnings_file_handler) 123 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stas-prokopiev/binance_historical_data/0ff488c51228fb8f4311041785adfc97df991c3e/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_binance_historical_data.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | def test_import(): 5 | from binance_historical_data import BinanceDataDumper 6 | 7 | def test_main_class_init(): 8 | from binance_historical_data import BinanceDataDumper 9 | 10 | data_dumper = BinanceDataDumper( 11 | path_dir_where_to_dump=".", 12 | data_type="klines", # aggTrades, klines, trades 13 | data_frequency="1m", # argument for data_type="klines" 14 | ) 15 | 16 | ## usdm futures 17 | data_dumper = BinanceDataDumper( 18 | asset_class="um", # spot, um, cm 19 | path_dir_where_to_dump="./BianceFutures", 20 | data_type="klines", # aggTrades, klines, trades 21 | data_frequency="1m", # argument for data_type="klines" 22 | ) 23 | 24 | data_dumper.dump_data( 25 | tickers='BTCUSDT', 26 | date_start=None, 27 | date_end=None, 28 | is_to_update_existing=False, 29 | tickers_to_exclude=["UST"], 30 | ) -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | py37 4 | py38 5 | py39 6 | py310 7 | skipsdist = true 8 | 9 | [testenv] 10 | deps = 11 | pytest 12 | pytest-cov 13 | commands = 14 | ; python -m pip install --upgrade pip 15 | python -m pip install -e . 16 | python -m pytest --------------------------------------------------------------------------------