Skip to content

Commit

Permalink
feat: ver 1.4.0
Browse files Browse the repository at this point in the history
  • Loading branch information
WLM1ke committed Jan 11, 2024
1 parent ec3b8e0 commit 9a29152
Show file tree
Hide file tree
Showing 37 changed files with 634 additions and 532 deletions.
103 changes: 10 additions & 93 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,106 +1,23 @@
# Byte-compiled / optimized / DLL files
# python generated files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
env/
develop-eggs/
*.py[oc]
build/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
*.egg-info

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# venv
.venv

# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# pyenv
.python-version

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# dotenv
.env

# virtualenv
.venv
venv/
ENV/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/

# PyCharm
.idea/

# PyTest
**.pytest_cache/

# Coverage.py
.coverage
# VSCode
.vscode
53 changes: 53 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
version: "3"

vars:
APP: apimoex
TESTS: tests
PYTHON: 3.12
TOOLS: go-task rye

tasks:
default:
desc: List available tasks
cmds:
- task -l

install:
desc: Setup venv, install tools and dependencies
cmds:
- brew install {{.TOOLS}}
- rye pin --relaxed {{.PYTHON}}
- rye sync --update-all --all-features --no-lock --force

update:
desc: Upgrade tools and dependencies
cmds:
- brew upgrade {{.TOOLS}}
- rye pin --relaxed {{.PYTHON}}
- rye sync --update-all --all-features

lint:
desc: Format and lint
cmds:
- rye run ruff format {{.APP}}
- rye run ruff format {{.TESTS}}
- rye run ruff {{.APP}} --unsafe-fixes
- rye run pyright {{.APP}}

test:
desc: Lint and test
deps: [lint]
cmds:
- rye run pytest {{.TESTS}} --cov={{.APP}}

docs:
desc: Update html docs
cmds:
- rye run sphinx-build -M html docs docs/build

publish:
desc: Publish to pypi
deps: [lint, docs]
cmds:
- rye build --clean
- rye publish
37 changes: 33 additions & 4 deletions apimoex/__init__.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,11 +1,40 @@
"""Реализовано несколько функций-запросов информации о торгуемых акциях и их исторических котировках, результаты которых
"""Реализация части запросов к MOEX ISS.
Реализовано несколько функций-запросов информации о торгуемых акциях и их исторических котировках, результаты которых
напрямую конвертируются в pandas.DataFrame.
Работа функций базируется на универсальном клиенте, позволяющем осуществлять произвольные запросы к MOEX ISS, поэтому
перечень доступных функций-запросов может быть легко расширен.
"""
from .client import ISSClient

from .requests import *
from apimoex.client import ISSClient
from apimoex.requests import (
find_securities,
find_security_description,
get_board_candle_borders,
get_board_candles,
get_board_dates,
get_board_history,
get_board_securities,
get_index_tickers,
get_market_candle_borders,
get_market_candles,
get_market_history,
get_reference,
)

__version__ = "1.3.0"
__all__ = [
"get_reference",
"find_securities",
"find_security_description",
"get_market_candle_borders",
"get_board_candle_borders",
"get_market_candles",
"get_board_candles",
"get_board_dates",
"get_board_securities",
"get_market_history",
"get_board_history",
"get_index_tickers",
"ISSClient",
]
44 changes: 23 additions & 21 deletions apimoex/client.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
"""Клиент для MOEX ISS."""
from collections import abc
from typing import Dict
from typing import List
from typing import Optional
from typing import Union
from typing import cast

import requests

# Стандартные настройки для запроса расширенного представления json без дополнительных метаданных.
Values = str | int | float
TableRow = dict[str, Values]
Table = list[TableRow]
TablesDict = dict[str, Table]
WebQuery = dict[str, str | int]

BASE_QUERY = {"iss.json": "extended", "iss.meta": "off"}


class ISSMoexError(Exception):
"""Базовое исключение."""
pass


class ISSClient(abc.Iterable):
class ISSClient(abc.Iterable[TablesDict]):
"""Клиент для MOEX ISS.
Для работы клиента необходимо передать requests.Session.
Expand All @@ -25,7 +26,7 @@ class ISSClient(abc.Iterable):
поддерживается протокол итерируемого для отдельных блоков или метод get_all для их автоматического сбора.
"""

def __init__(self, session: requests.Session, url: str, query: dict = None):
def __init__(self, session: requests.Session, url: str, query: WebQuery | None = None) -> None:
"""MOEX ISS является REST сервером.
Полный перечень запросов и параметров к ним https://iss.moex.com/iss/reference/
Expand All @@ -41,12 +42,13 @@ def __init__(self, session: requests.Session, url: str, query: dict = None):
"""
self._session = session
self._url = url
self._query = query or dict()
self._query = query or {}

def __repr__(self) -> str:
"""Наименование класса и содержание запроса к ISS Moex."""
return f"{self.__class__.__name__}(url={self._url}, query={self._query})"

def __iter__(self) -> Dict[str, List[Dict[str, Union[str, int, float]]]]:
def __iter__(self) -> abc.Iterator[TablesDict]:
"""Генератор по ответам состоящим из нескольких блоков.
На часть запросов выдается только начальный блок данных (обычно из 100 элементов). Генератор обеспечивает
Expand All @@ -68,8 +70,8 @@ def __iter__(self) -> Dict[str, List[Dict[str, Union[str, int, float]]]]:
)
del data["history.cursor"]
yield data
start += cursor["PAGESIZE"]
if start >= cursor["TOTAL"]:
start += cast(int, cursor["PAGESIZE"])
if start >= cast(int, cursor["TOTAL"]):
return
else:
# Наименование ключа может быть любым
Expand All @@ -80,9 +82,7 @@ def __iter__(self) -> Dict[str, List[Dict[str, Union[str, int, float]]]]:
return
start += block_size

def get(
self, start: Optional[int] = None
) -> Dict[str, List[Dict[str, Union[str, int, float]]]]:
def get(self, start: int | None = None) -> dict[str, list[dict[str, str | int | float]]]:
"""Загрузка данных.
:param start:
Expand All @@ -97,32 +97,34 @@ def get(
with self._session.get(self._url, params=query) as respond:
try:
respond.raise_for_status()
except requests.HTTPError:
raise ISSMoexError("Неверный url", respond.url)
except requests.HTTPError as err:
raise ISSMoexError("Неверный url", respond.url) from err
else:
_, data, *wrong_data = respond.json()
if len(wrong_data) != 0:
raise ISSMoexError("Ответ содержит некорректные данные", respond.url)
return data

def _make_query(self, start=None) -> Dict[str, Union[str, int]]:
def _make_query(self, start: int | None = None) -> WebQuery:
"""К общему набору параметров запроса добавляется требование предоставить ответ в виде расширенного json."""
query = dict(**BASE_QUERY, **self._query)
query: WebQuery = dict(**BASE_QUERY, **self._query)
if start:
query["start"] = start

return query

def get_all(self) -> Dict[str, List[Dict[str, Union[str, int, float]]]]:
def get_all(self) -> TablesDict:
"""Собирает все блоки данных для запросов, ответы на которые выдаются по частям отдельными блоками.
:return:
Объединенные из всех блоков данные с отброшенной вспомогательной информацией - словарь, каждый ключ которого
соответствует одной из таблиц с данными. Таблицы являются списками словарей, которые напрямую конвертируются
в pandas.DataFrame.
"""
all_data = dict()
all_data: TablesDict = {}
for data in self:
# noinspection PyUnresolvedReferences
for key, value in data.items():
all_data.setdefault(key, []).extend(value)

return all_data
Empty file added apimoex/py.typed
Empty file.
Loading

0 comments on commit 9a29152

Please sign in to comment.