главная

Начало работы

SDK для Python и Rust

Официальные SDK Seeryx для Python и Rust: рыночные данные, вход через Solana, подписанные ордера, пополнения и выводы с проверкой перед подписью, потоки.

Обзор

Официальные SDK оборачивают API из этой документации для Python и Rust, с одинаковыми понятиями и названиями в обоих языках. Их код открыт по лицензии MIT: github.com/Seeryx/seeryx-python и github.com/Seeryx/seeryx-rust.

  • Рыночные данные: рынки, текущие и прошлые раунды, стаканы, сделки, объём, комиссии, статистика, рейтинг, профили и событийные рынки — без аккаунта.
  • Sign-In With Solana с ключевой парой вашего кошелька; когда токен истекает, SDK сам входит заново.
  • Торговля: торговая сессия, открытая одной подписью кошелька, затем лимитные, рыночные ордера и ордера «Только мейкер», пакеты до 15 ордеров, отмены, разделения и объединения — всё подписывается на вашем компьютере.
  • Средства: пополнение с кошелька, вывод на ваш собственный кошелёк и отложенный вывод; каждая транзакция проверяется перед подписью.
  • Реальное время: потоки WebSocket по раунду и по вашему аккаунту, которые сами переподключаются.
  • Вспомогательные функции: сетка цен, комиссии, слаги и время раундов.

Установка

Python 3.9 или новее (блокирующий Client и асинхронный AsyncClient на asyncio с одинаковыми методами) или Rust 1.88 или новее (асинхронный, на tokio). Оба устанавливаются с GitHub:

# Python 3.9 or newer
pip install "git+https://github.com/Seeryx/seeryx-python"

Чтение рынка

Для рыночных данных аккаунт не нужен. Цены указаны в центах; уровни стакана — [цена, доли], лучшие первыми.

quickstart.py
"""The live BTC 5-minute round, its order book and its last trades. No account needed.

    python quickstart.py
"""
from seeryx import Client

with Client() as client:
    rnd = client.current_round("BTC", "5m")
    book = client.order_book(rnd["slug"], "up")  # levels are [cents, shares], best first
    trades = client.trades(rnd["slug"])

    bid = book["bids"][0][0] if book["bids"] else None
    ask = book["asks"][0][0] if book["asks"] else None
    print(rnd["slug"], "opened at", rnd["open"], "ends at", rnd["end"])
    print("Up bid", bid, "ask", ask, "chance of Up", f"{(bid + ask) / 2}%" if bid and ask else "no quotes yet")
    print("last trade", trades[0] if trades else "none yet")
    print("taker fee", client.fees()["rateBps"] / 100, "% of p x (1 - p) per share")

Вход

Загрузите кошелёк из файла ключевой пары Solana CLI или из секретного ключа в base58; он никогда не покидает ваш процесс. Прежде чем кошелёк подпишет текст входа, SDK проверяет, что в нём указаны этот сайт, этот кошелёк и только что выданный nonce, а затем сохраняет токен на 7 дней.

Если GET /api/funds сообщает onChainTrading, одна подпись кошелька открывает торговую сессию; её ключ создаётся на вашем компьютере и затем подписывает каждый ордер. Если указать путь, сессия сохраняется (читать её можете только вы) и используется при следующем запуске; без пути она хранится только в памяти.

from seeryx import Client, Keypair

client = Client()  # https://seeryx.com
wallet = Keypair.from_file("wallet.json")  # a solana-keygen file; it never leaves this process
client.sign_in(wallet)  # checks the sign-in text, signs it, keeps the 7-day token
if client.funds().get("onChainTrading"):
    # one wallet signature; the session key then signs every order. Saved with mode 0600 and reused next time
    session = client.open_session(wallet, path="session.json")
    print("session key", session.address, "until", session.expires_at)

Размещение ордера

Цены указываются в центах (с шагом 0,1¢, где рынок это допускает), размеры — в долях, суммы — в USDC. SDK сам выбирает форму подписи (PMO2, PMO3 или PMO4), id ордера и срок действия; рыночному ордеру нужен cap — худшая цена, которую вы принимаете.

limit_order.py
"""Sign in, rest a small post-only buy below the market, list it, then cancel it.

    SEERYX_WALLET=~/.config/solana/id.json python limit_order.py
"""
import os

from seeryx import Client, Keypair

wallet = Keypair.from_file(os.path.expanduser(os.environ.get("SEERYX_WALLET", "wallet.json")))

with Client() as client:
    client.sign_in(wallet)
    if client.funds().get("onChainTrading"):
        # one wallet signature opens a trading session; its key then signs every order. Saved (mode 0600) so the
        # next run reuses it; leave out `path` to keep the key in memory only.
        client.open_session(wallet, path="session.json")

    rnd = client.current_round("BTC", "5m")
    book = client.order_book(rnd["slug"], "up")
    bid = book["bids"][0][0] if book["bids"] else 10
    limit = max(1, int(bid) - 5)  # 5c under the best bid: it rests in the book
    placed = client.buy(rnd["slug"], "up", shares=5, limit=limit, post_only=True)  # post_only: never trades at once
    print("resting order", placed["order"], "at", limit, "c")

    print("open orders", client.open_orders())
    if placed["order"]:
        print(client.cancel_order(placed["order"]))
market_buy.py
"""Spend up to $5 on Up now, paying at most 2c over the best ask. What the book cannot fill is dropped.

    SEERYX_WALLET=~/.config/solana/id.json python market_buy.py
"""
import os

from seeryx import Client, Keypair

wallet = Keypair.from_file(os.path.expanduser(os.environ.get("SEERYX_WALLET", "wallet.json")))

with Client() as client:
    client.sign_in(wallet)
    if client.funds().get("onChainTrading"):
        client.open_session(wallet, path="session.json")
    slug = client.current_round("BTC", "5m")["slug"]
    cap = client.market_cap(slug, "up", "buy", slippage=2)  # the worst price you accept, on the price grid
    result = client.market_buy(slug, "up", amount=5, cap=cap)
    print(result["status"], "filled", result["filled"], "shares for", result["spent"], "USDC, average", result.get("avgPrice"))

Обновления в реальном времени

Поток следит за одним раундом или событийным рынком (оба стакана, сделки, объём), а после входа — и за вашим аккаунтом. Он переподключается с растущей задержкой и подписывается заново; когда раунд заканчивается, переключитесь на следующий в том же соединении.

stream.py
"""Live books and trades of the BTC 5-minute rounds, and your account, over the WebSocket. Reconnects by itself.

    python stream.py                                           # market data only
    SEERYX_WALLET=~/.config/solana/id.json python stream.py    # and your account
"""
import asyncio
import os
import time

from seeryx import AsyncClient, Keypair


async def main() -> None:
    async with AsyncClient() as client:
        path = os.environ.get("SEERYX_WALLET")
        if path:
            await client.sign_in(Keypair.from_file(os.path.expanduser(path)))
        rnd = await client.current_round("BTC", "5m")
        async with client.stream(rnd["slug"], account=bool(path)) as stream:
            while True:
                # a message comes only when something changed: wait at most until the round ends
                message = await stream.next(timeout=max(1.0, rnd["end"] - time.time()))
                if message is None or time.time() >= rnd["end"]:
                    rnd = await client.current_round("BTC", "5m")  # follow the next round, same connection
                    await stream.watch_round(rnd["slug"])
                    continue
                if message["type"] == "round":
                    up = message["up"]
                    print(message["slug"], "bid", up["bids"][:1], "ask", up["asks"][:1], "volume", message["volume"])
                elif message["type"] == "account":
                    print("cash", message["balance"]["available"], "open orders", len(message["orders"]))
                else:
                    print("error", message["error"])


asyncio.run(main())

Пополнение и вывод

Биржа готовит каждую транзакцию и оплачивает её комиссию сети, поэтому SOL на кошельке не нужен. SDK сверяет транзакцию ровно с тем, что вы запросили, и ничего другого не подписывает. Вывод подписывает ключ сессии, если включён sessionWithdraw (от 1 USDC и только на ваш собственный кошелёк), иначе — ваш кошелёк.

withdraw.py
"""Deposit USDC from your wallet, then withdraw some of it to the same wallet. The exchange pays every network fee:
the wallet needs no SOL. Every transaction is inspected before it is signed.

    SEERYX_WALLET=~/.config/solana/id.json python withdraw.py
"""
import os

from seeryx import Client, Keypair

wallet = Keypair.from_file(os.path.expanduser(os.environ.get("SEERYX_WALLET", "wallet.json")))

with Client(before_sign=lambda review: print("signing:", review.headline) or True) as client:
    client.sign_in(wallet)
    print("wallet", client.wallet_funds())  # USDC in the wallet; "account" is None until the first deposit

    print("deposit", client.deposit(wallet, 10))  # the first deposit is at least 1 USDC
    print("balance", client.balance())

    # signed by the trading session key when one is open and the server allows it, else by the wallet;
    # max_fee refuses a withdrawal fee above 1 USDC
    if client.funds().get("onChainTrading"):
        client.open_session(wallet, path="session.json")
    print("withdrawal", client.withdraw(4, wallet=wallet, max_fee=1))
    print("balance", client.balance())

Ошибки и повторы

Отказ приходит как ошибка со статусом, текстом сервера и его code; у кодов из списка ошибок есть собственные типы. Ответы 429 и 503 повторяются через время из Retry-After, а запросы на запись сохраняют свой Idempotency-Key при каждом повторе, поэтому ничего не размещается дважды.

from seeryx import ApiError, BelowMinimum, WouldTake

try:
    client.buy(slug, "up", shares=5, limit=60, post_only=True)
except WouldTake:
    print("it would trade at once: post only refused it")
except BelowMinimum as e:
    print("the smallest order is", e.minimum, "shares")
except ApiError as e:
    print(e.status, e.code, e.message)  # 429 and 503 were already retried as Retry-After said

Ключи и проверки

  • Ключ кошелька и ключ сессии никогда не покидают ваш процесс: отправляются только публичные ключи и подписи.
  • Каждая подготовленная транзакция декодируется и должна быть собственной инструкцией программы Seeryx с вашими счетами и вашими суммами: разрешение на списание, перевод, другой подписант или другая программа отклоняются.
  • На seeryx.com программа, минт USDC и кластер Solana зафиксированы, что бы ни сообщал сервер.
  • Ничего, что содержит секрет, не выводится и не попадает в логи, а сохранённый файл сессии может читать только его владелец.

Примеры

В обоих языках одни и те же программы лежат в папках examples:

ПримерЧто делает
quickstartТекущий 5-минутный раунд BTC, его стакан и последние сделки — без аккаунта
limit_orderВход, покупка «Только мейкер» ниже рынка в стакане, просмотр и отмена
market_buyПотратить до $5 сразу, заплатив не больше чем на 2¢ выше лучшей цены продажи
streamСтаканы, сделки и ваш аккаунт в реальном времени через WebSocket
market_makerНебольшой цикл двусторонних котировок с ордерами «Только мейкер» и объединениями
withdrawПополнение с кошелька и вывод обратно на него

Исходный код, все методы, тесты и список изменений: github.com/Seeryx/seeryx-python и github.com/Seeryx/seeryx-rust.