From c090d23d43e3353a5aa71afe5b671937cad9a149 Mon Sep 17 00:00:00 2001 From: "Leonov Artur (Depish)" Date: Tue, 23 Sep 2025 18:33:48 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20?= =?UTF-8?q?=D0=B8=D0=BD=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=86=D0=B8=D1=8E=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D0=BA=D1=83=D1=80=D1=81=D1=83=20=D1=80=D1=83?= =?UTF-8?q?=D0=B1=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cbrcurrency.py | 32 ++++++++++++++++++++++++++++++++ mcp-moex.py | 15 ++++++++++++++- moex.py | 21 ++++++++++++++------- session.py | 5 +++++ 4 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 cbrcurrency.py create mode 100644 session.py diff --git a/cbrcurrency.py b/cbrcurrency.py new file mode 100644 index 0000000..56b1422 --- /dev/null +++ b/cbrcurrency.py @@ -0,0 +1,32 @@ +from cachetools import TTLCache +import json + +from session import session + + +__CBR_DAYLY_URL = "https://www.cbr-xml-daily.ru/daily_json.js" +__cache: TTLCache = TTLCache(maxsize=10000, ttl=60 * 10) + + +def __fetch_currency() -> dict: + if __CBR_DAYLY_URL in __cache: + return __cache[__CBR_DAYLY_URL] + + resp = session.get(__CBR_DAYLY_URL) + data = resp.json() + + __cache[__CBR_DAYLY_URL] = data["Valute"] + return data["Valute"] + + +def get_currency(currency_charcode: str) -> dict: + if not currency_charcode in __cache: + currencies = __fetch_currency() + if currency_charcode in currencies: + currency = currencies[currency_charcode] + nominal = int(currency["Nominal"]) + value = float(currency["Value"]) + + return {f"1_{currency_charcode}": value / nominal, "CharCode": currency_charcode, "Name": currency["Name"]} + else: + return {"error": f'Unknow currency charcode {currency_charcode}'} \ No newline at end of file diff --git a/mcp-moex.py b/mcp-moex.py index 403775f..6f0d014 100644 --- a/mcp-moex.py +++ b/mcp-moex.py @@ -3,8 +3,8 @@ from starlette.routing import Mount from mcp.server.fastmcp import FastMCP from typing import TypedDict, List - from moex import bond_sequrities +from cbrcurrency import get_currency mcp = FastMCP("mcp-moex - Данные с московской биржы.", port=8021) @@ -22,5 +22,18 @@ def get_bond_securities(bond_ticker: str, board: str) -> dict: return bond_sequrities(bond_ticker, board) +@mcp.tool() +def get_currency_to_rub(currency_charcode: str) -> dict: + """ + Возвращает курс рубля центробанка России к валюте по ее коду (например, "USD") + + Args: + currency_charcode (str): Код валюты (например, "USD", указывается в uppercase). + """ + return get_currency(currency_charcode) + + + if __name__ == "__main__": mcp.run(transport="streamable-http", mount_path="/mcp") + diff --git a/moex.py b/moex.py index 9818d8d..5502eb0 100644 --- a/moex.py +++ b/moex.py @@ -1,18 +1,25 @@ -import requests import xmltodict +from cachetools import TTLCache +from session import session, MOEX_BASE_URL -BASE_URL: str = "https://iss.moex.com/iss" - -session = requests.Session() - +__cache: TTLCache = TTLCache(maxsize=10000, ttl=60 * 60) def bond_sequrities(bond_ticker: str, board: str) -> dict: - res = requests.get(f"{BASE_URL}/engines/stock/markets/BONDS/boards/{board}/securities.xml?securities={bond_ticker}&iss.meta=off&iss.only=securities") + url: str = f"{MOEX_BASE_URL}/engines/stock/markets/BONDS/boards/{board}/securities.xml?securities={bond_ticker}&iss.meta=off&iss.only=securities" + + if url in __cache: + return __cache[url] + + res = session.get(url) if res.status_code != 200: return {"error": True, "status_code": res.status_code} - return xmltodict.parse(res.text)["document"]["data"]["rows"]["row"] + data = xmltodict.parse(res.text)["document"]["data"]["rows"]["row"] + data["@PREVPRICE_PERCENT"] = data.pop("@PREVPRICE") + __cache[url] = data + + return data diff --git a/session.py b/session.py new file mode 100644 index 0000000..c3d1ec5 --- /dev/null +++ b/session.py @@ -0,0 +1,5 @@ +import requests + +session = requests.Session() + +MOEX_BASE_URL: str = "https://iss.moex.com/iss" \ No newline at end of file