Урок 2.3 — Курсы валют и конвертация
Освоим методы get_exchange_rates(), get_currencies(), exchange() и get_rates_image() для работы с курсами криптовалют и фиатных валют.
Курсы валют — сердце любой платёжной системы. Crypto Pay API предоставляет не только курсы криптовалют к фиатным деньгам, но и удобные инструменты для конвертации. В этом уроке мы научимся получать актуальные курсы, список поддерживаемых валют, конвертировать суммы и даже генерировать изображения с курсами.
Метод get_exchange_rates() — все курсы валют
Метод get_exchange_rates() возвращает список всех текущих курсов обмена, поддерживаемых Crypto Pay API. Это базовый метод для любой работы с конвертацией.
Сигнатура метода
async def get_exchange_rates(self) -> list[ExchangeRate]:
Не требует параметров. Возвращает список ExchangeRate.
import asyncio
from aiosend import CryptoPay
async def main():
cp = CryptoPay(token="1234:TOKEN")
rates = await cp.get_exchange_rates()
print(f"Всего курсов: {len(rates)}")
for r in rates[:10]: # первые 10
print(f"{r.source} -> {r.target}: {r.rate}")
asyncio.run(main())
# Пример вывода:
# USDT -> USD: 1.0001
# USDT -> EUR: 0.9250
# USDT -> RUB: 92.5000
# TON -> USD: 3.4512
# TON -> EUR: 3.1924
# BTC -> USD: 67500.00
# BTC -> EUR: 62437.50
# ETH -> USD: 3450.00
# ETH -> RUB: 319125.00
# LTC -> USD: 78.50
Обратите внимание: курсов обычно около 200+ (количество криптовалют × количество фиатных валют).
Объект ExchangeRate и его поля
| Поле | Тип | Описание |
|---|---|---|
source |
Asset | Fiat | str |
Исходная валюта (крипто или фиат). Например: "USDT", "TON", "BTC". |
target |
Fiat | str |
Целевая валюта (всегда фиат). Например: "USD", "EUR", "RUB". |
rate |
float |
Текущий курс: сколько единиц target стоит 1 единица source. |
is_valid |
bool |
True, если курс актуален. Устаревшие курсы помечаются False. |
is_crypto |
bool |
True, если source — криптовалюта. |
is_fiat |
bool |
True, если source — фиатная валюта. |
import asyncio
from aiosend import CryptoPay
async def main():
cp = CryptoPay(token="1234:TOKEN")
rates = await cp.get_exchange_rates()
for r in rates:
if r.source == "USDT" and r.target == "USD":
print(f"Курс USDT → USD: {r.rate}")
print(f" Актуален: {r.is_valid}")
print(f" Source — криптовалюта: {r.is_crypto}")
print(f" Source — фиат: {r.is_fiat}")
break
asyncio.run(main())
# Вывод:
# Курс USDT → USD: 1.0001
# Актуален: True
# Source — криптовалюта: True
# Source — фиат: False
Объект ExchangeRate также имеет метод update():
import asyncio
from aiosend import CryptoPay
async def main():
cp = CryptoPay(token="1234:TOKEN")
rates = await cp.get_exchange_rates()
# Находим нужный курс
ton_usd = None
for r in rates:
if r.source == "TON" and r.target == "USD":
ton_usd = r
break
if ton_usd:
print(f"Курс TON/USD: {ton_usd.rate}")
await ton_usd.update()
print(f"Обновлённый курс: {ton_usd.rate}")
asyncio.run(main())
🔍 Исходный код ExchangeRate
class ExchangeRate(CryptoPayObject):
is_valid: bool # курс актуален?
is_crypto: bool # source — крипта?
is_fiat: bool # source — фиат?
source: Asset | Fiat | str # исходная валюта
target: Fiat | str # целевая (всегда фиат)
rate: float # курс
async def update(self) -> None:
exchange_rates = await self._client.get_exchange_rates()
for ex_rate in exchange_rates:
if ex_rate.source == self.source \
and ex_rate.target == self.target:
self.__dict__ = ex_rate.__dict__
break
Метод get_currencies() — список валют
Метод get_currencies() возвращает список всех валют (криптовалют и фиатных), поддерживаемых Crypto Pay API. В отличие от курсов, этот список статичен и меняется редко (только когда добавляются новые валюты).
Сигнатура метода
async def get_currencies(self) -> list[Currency]:
Не требует параметров. Возвращает список Currency.
Объект Currency и его поля
| Поле | Тип | Описание |
|---|---|---|
code |
Fiat | Asset | str |
Код валюты. Например: "USDT", "TON", "USD", "EUR". |
name |
str |
Полное название валюты. Например: "Tether", "Toncoin", "United States Dollar". |
decimals |
int |
Количество знаков после запятой. Например: USDT = 2, BTC = 8, TON = 9. |
is_blockchain |
bool |
True, если это нативная монета блокчейна (TON, TRX). |
is_stablecoin |
bool |
True, если это стейблкоин (USDT, USDC). |
is_fiat |
bool |
True, если это фиатная валюта (USD, EUR, RUB). |
url |
str | None |
Ссылка на страницу валюты (обычно CoinMarketCap или CoinGecko). Может быть None. |
import asyncio
from aiosend import CryptoPay
async def main():
cp = CryptoPay(token="1234:TOKEN")
currencies = await cp.get_currencies()
print("Криптовалюты:")
for c in currencies:
if not c.is_fiat:
info = []
if c.is_blockchain:
info.append("нативная монета")
if c.is_stablecoin:
info.append("стейблкоин")
if c.url:
info.append(f"url: {c.url}")
tag = f" ({', '.join(info)})" if info else ""
print(f" {c.code:<8} | {c.name:<30} | decimals={c.decimals}{tag}")
print("\nФиатные валюты:")
for c in currencies:
if c.is_fiat:
print(f" {c.code:<8} | {c.name:<30} | decimals={c.decimals}")
asyncio.run(main())
# Пример вывода:
# Криптовалюты:
# USDT | Tether | decimals=2 (стейблкоин)
# TON | Toncoin | decimals=9 (нативная монета)
# BTC | Bitcoin | decimals=8
# ETH | Ethereum | decimals=18
#
# Фиатные валюты:
# USD | United States Dollar | decimals=2
# EUR | Euro | decimals=2
# RUB | Russian Ruble | decimals=2
Поле decimals особенно важно для правильного форматирования сумм. Например, если вы показываете пользователю баланс в BTC, нужно округлять до 8 знаков, а не до 2:
import asyncio
from aiosend import CryptoPay
async def format_balance(cp, asset_code: str, amount: float) -> str:
"""Форматирует сумму с правильным количеством знаков."""
currencies = await cp.get_currencies()
for c in currencies:
if c.code == asset_code:
return f"{amount:.{c.decimals}f} {c.code}"
return f"{amount} {asset_code}"
async def main():
cp = CryptoPay(token="1234:TOKEN")
print(await format_balance(cp, "USDT", 150.5)) # "150.50 USDT"
print(await format_balance(cp, "BTC", 0.12345678)) # "0.12345678 BTC"
print(await format_balance(cp, "TON", 100.0)) # "100.000000000 TON"
asyncio.run(main())
💡 Для чего нужен decimals?
Разные криптовалюты имеют разную точность. BTC делится до 8 знаков (сатоши), USDT до 2 (центы), TON до 9 (нанотоны). Используйте decimals из объекта Currency, чтобы всегда показывать правильное количество знаков.
Метод exchange() — конвертация сумм
Метод exchange() — это удобная обёртка над get_exchange_rates(). Он конвертирует указанную сумму из одной валюты в другую по текущему курсу.
Сигнатура метода
async def exchange(
self,
amount: float,
source: Asset | Fiat | str,
target: Asset | Fiat | str,
) -> float:
amount — сумма в исходной валюте. source — код исходной валюты. target — код целевой валюты. Возвращает float. Выбрасывает CryptoPayError, если курс не найден.
| Параметр | Тип | Описание |
|---|---|---|
amount |
float |
Сумма для конвертации. Например: 100.0. |
source |
Asset | Fiat | str |
Код исходной валюты. Например: "USDT", "USD". |
target |
Asset | Fiat | str |
Код целевой валюты. Например: "USD", "RUB". |
import asyncio
from aiosend import CryptoPay
async def main():
cp = CryptoPay(token="1234:TOKEN")
# 10 USDT → USD
result = await cp.exchange(10, "USDT", "USD")
print(f"10 USDT = {result:.2f} USD") # 10.00 USD
# 1000 RUB → USDT
result = await cp.exchange(1000, "RUB", "USDT")
print(f"1000 RUB = {result:.2f} USDT") # ~10.81 USDT
# 1 BTC → EUR
result = await cp.exchange(1, "BTC", "EUR")
print(f"1 BTC = {result:.2f} EUR") # ~62437.50 EUR
# 500 TON → USD
result = await cp.exchange(500, "TON", "USD")
print(f"500 TON = {result:.2f} USD") # ~1725.60 USD
asyncio.run(main())
import asyncio
from aiosend import CryptoPay
from aiosend.enums import Asset, Fiat
async def main():
cp = CryptoPay(token="1234:TOKEN")
# Используем Enum вместо строк (безопаснее)
result = await cp.exchange(100, Asset.USDT, Fiat.USD)
print(f"100 USDT = {result:.2f} USD")
result = await cp.exchange(50, Asset.TON, Fiat.RUB)
print(f"50 TON = {result:.2f} RUB")
result = await cp.exchange(0.5, Asset.BTC, Fiat.EUR)
print(f"0.5 BTC = {result:.2f} EUR")
asyncio.run(main())
async def exchange(self, amount, source, target):
# Получаем все курсы
ex_rates = await self.get_exchange_rates()
for ex_rate in ex_rates:
# Прямой курс: 1 source = rate target
if ex_rate.source == source and ex_rate.target == target:
return ex_rate.rate * amount
# Обратный курс: 1 target = rate source
if ex_rate.source == target and ex_rate.target == source:
return amount / ex_rate.rate
# Курс не найден
msg = f"Exchange rate for {source} => {target} not found"
raise CryptoPayError(msg)
Как видите, метод умеет работать в обе стороны: если прямого курса нет, он попробует обратный (разделив сумму на курс). Это очень удобно.
⚠️ Важно: точность конвертации
Курсы берутся из Crypto Pay API и могут незначительно отличаться от рыночных. API добавляет небольшую маржу. Для точных расчётов используйте официальный курс Crypto Pay.
import asyncio
from aiosend import CryptoPay
from aiosend.exceptions import CryptoPayError
async def main():
cp = CryptoPay(token="1234:TOKEN")
try:
result = await cp.exchange(100, "SOL", "USD")
print(f"100 SOL = {result:.2f} USD")
except CryptoPayError as e:
print(f"Не удалось конвертировать: {e}")
# Exchange rate for SOL => USD not found
asyncio.run(main())
Метод get_rates_image() — изображение с курсами
Метод get_rates_image() генерирует URL изображения с курсом валюты. Это полезно для создания красивых карточек курсов для публикации в соцсетях или мессенджерах.
Сигнатура метода
def get_rates_image(
self,
base: Asset | Fiat | str,
quote: Asset | Fiat | str,
rate: float,
percent: float,
) -> str:
Возвращает str — URL изображения. Внимание: метод синхронный (не async), так как не делает HTTP-запрос, а только формирует URL.
| Параметр | Тип | Описание |
|---|---|---|
base |
Asset | Fiat | str |
Базовая валюта (слева). Например: "USDT", "TON". |
quote |
Asset | Fiat | str |
Котируемая валюта (справа). Например: "USD", "EUR". |
rate |
float |
Текущий курс. |
percent |
float |
Процент изменения курса (положительное = рост, отрицательное = падение). |
import asyncio
from aiosend import CryptoPay
async def main():
cp = CryptoPay(token="1234:TOKEN")
# Получаем курс TON → USD
rates = await cp.get_exchange_rates()
ton_usd = None
for r in rates:
if r.source == "TON" and r.target == "USD":
ton_usd = r
break
# Генерируем изображение (метод синхронный, без await)
image_url = cp.get_rates_image(
base="TON",
quote="USD",
rate=ton_usd.rate,
percent=5.2, # процент изменения
)
print(f"Изображение: {image_url}")
# https://imggen.send.tg/rates/image?base=TON"e=USD&rate=3.45&percent=5.2
asyncio.run(main())
import asyncio
from aiosend import CryptoPay
async def create_rate_card(
cp: CryptoPay,
base: str,
quote: str,
percent: float = 0.0,
) -> str:
"""Создаёт карточку курса и возвращает URL изображения."""
rates = await cp.get_exchange_rates()
for r in rates:
if r.source == base and r.target == quote:
return cp.get_rates_image(
base=base,
quote=quote,
rate=r.rate,
percent=percent,
)
msg = f"Курс {base} → {quote} не найден"
raise ValueError(msg)
async def main():
cp = CryptoPay(token="1234:TOKEN")
# Карточка TON/USD
url = await create_rate_card(cp, "TON", "USD", percent=3.5)
print(f"TON/USD: {url}")
# Карточка BTC/USD
url = await create_rate_card(cp, "BTC", "USD", percent=-1.2)
print(f"BTC/USD: {url}")
asyncio.run(main())
🔍 Как это работает?
get_rates_image() не генерирует изображение на вашем сервере. Он возвращает URL сервиса imggen.send.tg, который динамически создаёт PNG-картинку с курсом. Вы можете использовать этот URL в Telegram, вебе или где угодно.
Практический пример: конвертер валют
Давайте создадим полноценный конвертер валют, который конвертирует любую поддерживаемую валюту и показывает красивый отчёт.
import asyncio
from aiosend import CryptoPay
from aiosend.exceptions import CryptoPayError
class CurrencyConverter:
"""Конвертер криптовалют и фиатных валют."""
def __init__(self, token: str):
self.cp = CryptoPay(token=token)
async def get_supported_pairs(self) -> list[str]:
"""Возвращает список доступных валютных пар."""
rates = await self.cp.get_exchange_rates()
pairs = []
for r in rates:
pairs.append(f"{r.source} → {r.target} (курс: {r.rate})")
return pairs
async def convert(
self,
amount: float,
source: str,
target: str,
) -> dict:
"""
Конвертирует сумму и возвращает детальную информацию.
Возвращает словарь с полями:
- amount: исходная сумма
- source: исходная валюта
- target: целевая валюта
- result: результат конвертации
- rate: использованный курс
"""
rate = None
rates = await self.cp.get_exchange_rates()
for r in rates:
if r.source == source and r.target == target:
result = r.rate * amount
rate = r.rate
break
if r.source == target and r.target == source:
result = amount / r.rate
rate = r.rate
break
if rate is None:
msg = f"Курс {source} → {target} не найден"
raise CryptoPayError(msg)
return {
"amount": amount,
"source": source,
"target": target,
"result": result,
"rate": rate,
}
async def show_all_rates(self, base: str) -> None:
"""Показывает курсы базовой валюты ко всем доступным."""
rates = await self.cp.get_exchange_rates()
print(f"\nКурсы для {base}:")
print("-" * 50)
for r in rates:
if r.source == base:
print(f" → {r.target:<6} {r.rate:<15.6f} {'✅' if r.is_valid else '❌'}")
print("-" * 50)
async def main():
# Создаём конвертер
converter = CurrencyConverter("1234:TOKEN")
# Пример 1: простая конвертация
result = await converter.convert(100, "USDT", "RUB")
print(f"{result['amount']} {result['source']} = "
f"{result['result']:.2f} {result['target']} "
f"(курс: {result['rate']:.4f})")
# Пример 2: конвертация с деталями
result = await converter.convert(1, "BTC", "USD")
print(f"\nBTC → USD:")
print(f" Сумма: {result['amount']} {result['source']}")
print(f" Результат: ${result['result']:,.2f} {result['target']}")
print(f" Курс: {result['rate']:,.2f}")
# Пример 3: показать все курсы для TON
await converter.show_all_rates("TON")
# Пример 4: обратная конвертация (USD → USDT)
result = await converter.convert(100, "USD", "USDT")
print(f"\n100 USD = {result['result']:.4f} USDT")
asyncio.run(main())
# Пример вывода:
# 100 USDT = 9250.00 RUB (курс: 92.5000)
#
# BTC → USD:
# Сумма: 1 BTC
# Результат: $67,500.00 USD
# Курс: 67,500.00
#
# Курсы для TON:
# --------------------------------------------------
# → USD 3.451200 ✅
# → EUR 3.192360 ✅
# → RUB 319.236000 ✅
# --------------------------------------------------
#
# 100 USD = 99.99 USDT
Сравнение методов работы с курсами
| Метод | Возвращает | Async? | Когда использовать |
|---|---|---|---|
get_exchange_rates() |
list[ExchangeRate] |
✅ | Когда нужны все курсы или конкретная пара source→target |
get_currencies() |
list[Currency] |
✅ | Когда нужен список поддерживаемых валют и их свойства |
exchange() |
float |
✅ | Когда нужно сконвертировать сумму (удобная обёртка) |
get_rates_image() |
str (URL) |
❌ (синхронный) | Когда нужно получить URL изображения с курсом |
Как выбрать нужный метод?
- Нужно узнать курс TON к USD? →
get_exchange_rates()+ фильтр - Нужно сконвертировать 10 TON в USD? →
exchange(10, "TON", "USD") - Нужно узнать, стейблкоин ли USDT? →
get_currencies()+ полеis_stablecoin - Нужно показать картинку курса в чате? →
get_rates_image()
Что важно запомнить
Практическая задача
Задача: Крипто-дашборд с курсами
Напишите асинхронную функцию print_crypto_dashboard, которая:
- Принимает
cp(клиент CryptoPay). - Получает список всех курсов через
get_exchange_rates(). - Получает список всех валют через
get_currencies(). - Выводит таблицу: Криптовалюта | Курс к USD | Курс к EUR | Курс к RUB | Тип (стейблкоин/нативная/обычная)
- Для каждой криптовалюты находит курс к USD, EUR и RUB.
- Определяет тип криптовалюты из Currency (is_stablecoin, is_blockchain).
Пример вывода:
Крипто-дашборд:
═══════════════════════════════════════════════════════════════════
Актив USD EUR RUB Тип
───────────────────────────────────────────────────────────────────
USDT 1.0001 0.9250 92.5000 стейблкоин
TON 3.4512 3.1924 319.2360 нативная монета
BTC 67500.00 62437.50 6250000.0 —
ETH 3450.00 3191.25 319125.0 —
═══════════════════════════════════════════════════════════════════
Урок 2.3: Курсы валют и конвертация
8 вопросов