radion-sdk 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- radion/__init__.py +85 -0
- radion/_config.py +25 -0
- radion/client.py +50 -0
- radion/errors.py +28 -0
- radion/py.typed +0 -0
- radion/realtime/__init__.py +79 -0
- radion/realtime/channels.py +71 -0
- radion/realtime/client.py +370 -0
- radion/realtime/dispatcher.py +93 -0
- radion/realtime/heartbeat.py +59 -0
- radion/realtime/payloads.py +223 -0
- radion/realtime/protocol.py +207 -0
- radion/realtime/reconnect_manager.py +42 -0
- radion/realtime/subscription_manager.py +35 -0
- radion_sdk-0.2.0.dist-info/METADATA +210 -0
- radion_sdk-0.2.0.dist-info/RECORD +18 -0
- radion_sdk-0.2.0.dist-info/WHEEL +4 -0
- radion_sdk-0.2.0.dist-info/licenses/LICENSE +21 -0
radion/__init__.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Official async SDK for the Radion platform."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from ._config import DEFAULT_BASE_URL, DEFAULT_WS_URL, RadionConfig
|
|
6
|
+
from .client import Radion
|
|
7
|
+
from .errors import RadionConnectionError, RadionError, RadionServerError
|
|
8
|
+
from .realtime import (
|
|
9
|
+
CHANNELS,
|
|
10
|
+
FILTER_REQUIREMENTS,
|
|
11
|
+
ActivityPayload,
|
|
12
|
+
AnyChannelPayload,
|
|
13
|
+
AnyConfirmedPayload,
|
|
14
|
+
Channel,
|
|
15
|
+
ChannelEvent,
|
|
16
|
+
ChannelFilters,
|
|
17
|
+
CollateralPayload,
|
|
18
|
+
CombosPayload,
|
|
19
|
+
ErrorFrame,
|
|
20
|
+
EventDispatcher,
|
|
21
|
+
EventFrame,
|
|
22
|
+
FilterKey,
|
|
23
|
+
InboundFrame,
|
|
24
|
+
LifecyclePayload,
|
|
25
|
+
MempoolChannel,
|
|
26
|
+
OraclePayload,
|
|
27
|
+
PongFrame,
|
|
28
|
+
PricesPayload,
|
|
29
|
+
RealtimeClient,
|
|
30
|
+
ReconnectManager,
|
|
31
|
+
SubscribableChannel,
|
|
32
|
+
SubscribedFrame,
|
|
33
|
+
Subscription,
|
|
34
|
+
SubscriptionManager,
|
|
35
|
+
TradesPayload,
|
|
36
|
+
UnsubscribedFrame,
|
|
37
|
+
is_channel,
|
|
38
|
+
is_mempool_channel,
|
|
39
|
+
is_subscribable_channel,
|
|
40
|
+
validate_subscription_filters,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
__all__ = [
|
|
44
|
+
"CHANNELS",
|
|
45
|
+
"DEFAULT_BASE_URL",
|
|
46
|
+
"DEFAULT_WS_URL",
|
|
47
|
+
"FILTER_REQUIREMENTS",
|
|
48
|
+
"ActivityPayload",
|
|
49
|
+
"AnyChannelPayload",
|
|
50
|
+
"AnyConfirmedPayload",
|
|
51
|
+
"Channel",
|
|
52
|
+
"ChannelEvent",
|
|
53
|
+
"ChannelFilters",
|
|
54
|
+
"CollateralPayload",
|
|
55
|
+
"CombosPayload",
|
|
56
|
+
"ErrorFrame",
|
|
57
|
+
"EventDispatcher",
|
|
58
|
+
"EventFrame",
|
|
59
|
+
"FilterKey",
|
|
60
|
+
"InboundFrame",
|
|
61
|
+
"LifecyclePayload",
|
|
62
|
+
"MempoolChannel",
|
|
63
|
+
"OraclePayload",
|
|
64
|
+
"PongFrame",
|
|
65
|
+
"PricesPayload",
|
|
66
|
+
"Radion",
|
|
67
|
+
"RadionConfig",
|
|
68
|
+
"RadionConnectionError",
|
|
69
|
+
"RadionError",
|
|
70
|
+
"RadionServerError",
|
|
71
|
+
"RealtimeClient",
|
|
72
|
+
"ReconnectManager",
|
|
73
|
+
"SubscribableChannel",
|
|
74
|
+
"SubscribedFrame",
|
|
75
|
+
"Subscription",
|
|
76
|
+
"SubscriptionManager",
|
|
77
|
+
"TradesPayload",
|
|
78
|
+
"UnsubscribedFrame",
|
|
79
|
+
"is_channel",
|
|
80
|
+
"is_mempool_channel",
|
|
81
|
+
"is_subscribable_channel",
|
|
82
|
+
"validate_subscription_filters",
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
__version__ = "0.2.0"
|
radion/_config.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Shared configuration for the Radion SDK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
DEFAULT_BASE_URL = "https://api.radion.app"
|
|
8
|
+
"""Default base URL for the Radion REST API."""
|
|
9
|
+
|
|
10
|
+
DEFAULT_WS_URL = "wss://api.radion.app/ws"
|
|
11
|
+
"""Default endpoint for the Radion realtime (WebSocket) API."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True, slots=True)
|
|
15
|
+
class RadionConfig:
|
|
16
|
+
"""Shared configuration for every Radion product surface.
|
|
17
|
+
|
|
18
|
+
``base_url`` is reserved for the forthcoming REST resource namespaces
|
|
19
|
+
(``markets``, ``traders``, ``backtests``, …); the realtime client uses
|
|
20
|
+
``ws_url``.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
api_key: str
|
|
24
|
+
base_url: str = DEFAULT_BASE_URL
|
|
25
|
+
ws_url: str = DEFAULT_WS_URL
|
radion/client.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""The unified :class:`Radion` platform client."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from ._config import DEFAULT_BASE_URL, DEFAULT_WS_URL
|
|
8
|
+
from .errors import RadionConnectionError
|
|
9
|
+
from .realtime import RealtimeClient
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Radion:
|
|
13
|
+
"""Unified async entry point for the Radion platform.
|
|
14
|
+
|
|
15
|
+
Holds shared configuration and exposes each product surface as an
|
|
16
|
+
attribute. Today that is :attr:`realtime`; REST resource namespaces
|
|
17
|
+
(``markets``, ``traders``, ``backtests``, ``auth``, ``health``) attach here
|
|
18
|
+
as they ship, built from ``base_url`` over a shared HTTP transport — the
|
|
19
|
+
constructor shape stays stable so adding them is purely additive.
|
|
20
|
+
|
|
21
|
+
Extra keyword arguments are forwarded to the realtime client (for example
|
|
22
|
+
``reconnect``, ``heartbeat``, ``heartbeat_interval``).
|
|
23
|
+
|
|
24
|
+
Example::
|
|
25
|
+
|
|
26
|
+
radion = Radion(api_key=os.getenv("RADION_API_KEY"))
|
|
27
|
+
await radion.realtime.connect()
|
|
28
|
+
await radion.realtime.subscribe("trades")
|
|
29
|
+
|
|
30
|
+
@radion.realtime.on("trades")
|
|
31
|
+
async def handle_trade(event):
|
|
32
|
+
print(event.data)
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
*,
|
|
38
|
+
api_key: str,
|
|
39
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
40
|
+
ws_url: str = DEFAULT_WS_URL,
|
|
41
|
+
**realtime_options: Any,
|
|
42
|
+
) -> None:
|
|
43
|
+
if not api_key:
|
|
44
|
+
raise RadionConnectionError("api_key is required")
|
|
45
|
+
self._base_url = base_url
|
|
46
|
+
self.realtime = RealtimeClient(
|
|
47
|
+
api_key=api_key,
|
|
48
|
+
url=ws_url,
|
|
49
|
+
**realtime_options,
|
|
50
|
+
)
|
radion/errors.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Error types raised by the SDK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class RadionError(Exception):
|
|
7
|
+
"""Base class for every error surfaced by the SDK."""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RadionConnectionError(RadionError):
|
|
11
|
+
"""Raised when the SDK is used against an invalid connection state."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RadionServerError(RadionError):
|
|
15
|
+
"""Raised when the server reports an ``error`` frame."""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
message: str,
|
|
20
|
+
*,
|
|
21
|
+
code: str | None = None,
|
|
22
|
+
channel: str | None = None,
|
|
23
|
+
id: str | None = None,
|
|
24
|
+
) -> None:
|
|
25
|
+
super().__init__(message)
|
|
26
|
+
self.code = code
|
|
27
|
+
self.channel = channel
|
|
28
|
+
self.id = id
|
radion/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Radion realtime (WebSocket) product surface."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .channels import (
|
|
6
|
+
CHANNELS,
|
|
7
|
+
FILTER_REQUIREMENTS,
|
|
8
|
+
Channel,
|
|
9
|
+
FilterKey,
|
|
10
|
+
MempoolChannel,
|
|
11
|
+
SubscribableChannel,
|
|
12
|
+
is_channel,
|
|
13
|
+
is_mempool_channel,
|
|
14
|
+
is_subscribable_channel,
|
|
15
|
+
)
|
|
16
|
+
from .client import RealtimeClient
|
|
17
|
+
from .dispatcher import EventDispatcher
|
|
18
|
+
from .payloads import (
|
|
19
|
+
ActivityPayload,
|
|
20
|
+
AnyChannelPayload,
|
|
21
|
+
AnyConfirmedPayload,
|
|
22
|
+
CollateralPayload,
|
|
23
|
+
CombosPayload,
|
|
24
|
+
LifecyclePayload,
|
|
25
|
+
OraclePayload,
|
|
26
|
+
PricesPayload,
|
|
27
|
+
TradesPayload,
|
|
28
|
+
)
|
|
29
|
+
from .protocol import (
|
|
30
|
+
ChannelEvent,
|
|
31
|
+
ChannelFilters,
|
|
32
|
+
ErrorFrame,
|
|
33
|
+
EventFrame,
|
|
34
|
+
InboundFrame,
|
|
35
|
+
PongFrame,
|
|
36
|
+
SubscribedFrame,
|
|
37
|
+
Subscription,
|
|
38
|
+
UnsubscribedFrame,
|
|
39
|
+
parse_inbound_frame,
|
|
40
|
+
validate_subscription_filters,
|
|
41
|
+
)
|
|
42
|
+
from .reconnect_manager import ReconnectManager
|
|
43
|
+
from .subscription_manager import SubscriptionManager
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
"CHANNELS",
|
|
47
|
+
"FILTER_REQUIREMENTS",
|
|
48
|
+
"ActivityPayload",
|
|
49
|
+
"AnyChannelPayload",
|
|
50
|
+
"AnyConfirmedPayload",
|
|
51
|
+
"Channel",
|
|
52
|
+
"ChannelEvent",
|
|
53
|
+
"ChannelFilters",
|
|
54
|
+
"CollateralPayload",
|
|
55
|
+
"CombosPayload",
|
|
56
|
+
"ErrorFrame",
|
|
57
|
+
"EventDispatcher",
|
|
58
|
+
"EventFrame",
|
|
59
|
+
"FilterKey",
|
|
60
|
+
"InboundFrame",
|
|
61
|
+
"LifecyclePayload",
|
|
62
|
+
"MempoolChannel",
|
|
63
|
+
"OraclePayload",
|
|
64
|
+
"PongFrame",
|
|
65
|
+
"PricesPayload",
|
|
66
|
+
"RealtimeClient",
|
|
67
|
+
"ReconnectManager",
|
|
68
|
+
"SubscribableChannel",
|
|
69
|
+
"SubscribedFrame",
|
|
70
|
+
"Subscription",
|
|
71
|
+
"SubscriptionManager",
|
|
72
|
+
"TradesPayload",
|
|
73
|
+
"UnsubscribedFrame",
|
|
74
|
+
"is_channel",
|
|
75
|
+
"is_mempool_channel",
|
|
76
|
+
"is_subscribable_channel",
|
|
77
|
+
"parse_inbound_frame",
|
|
78
|
+
"validate_subscription_filters",
|
|
79
|
+
]
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Typed channel names for the Radion realtime API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Literal, TypeGuard, get_args
|
|
6
|
+
|
|
7
|
+
Channel = Literal[
|
|
8
|
+
"global",
|
|
9
|
+
"trades",
|
|
10
|
+
"activity",
|
|
11
|
+
"lifecycle",
|
|
12
|
+
"oracle",
|
|
13
|
+
"collateral",
|
|
14
|
+
"combos",
|
|
15
|
+
"prices",
|
|
16
|
+
"wallets",
|
|
17
|
+
"markets",
|
|
18
|
+
"large_trades",
|
|
19
|
+
]
|
|
20
|
+
"""A confirmed channel name the SDK can subscribe to."""
|
|
21
|
+
|
|
22
|
+
CHANNELS: tuple[Channel, ...] = get_args(Channel)
|
|
23
|
+
"""Every supported confirmed channel, as a tuple."""
|
|
24
|
+
|
|
25
|
+
MempoolChannel = Literal[
|
|
26
|
+
"mempool.global",
|
|
27
|
+
"mempool.trades",
|
|
28
|
+
"mempool.activity",
|
|
29
|
+
"mempool.lifecycle",
|
|
30
|
+
"mempool.oracle",
|
|
31
|
+
"mempool.collateral",
|
|
32
|
+
"mempool.combos",
|
|
33
|
+
"mempool.prices",
|
|
34
|
+
"mempool.wallets",
|
|
35
|
+
"mempool.markets",
|
|
36
|
+
"mempool.large_trades",
|
|
37
|
+
]
|
|
38
|
+
"""A ``mempool.``-prefixed companion channel."""
|
|
39
|
+
|
|
40
|
+
SubscribableChannel = Channel | MempoolChannel
|
|
41
|
+
"""Any channel accepted by :meth:`RealtimeClient.subscribe`."""
|
|
42
|
+
|
|
43
|
+
FilterKey = Literal["wallets", "market_ids", "token_ids", "min_usd"]
|
|
44
|
+
"""A server-side filter key."""
|
|
45
|
+
|
|
46
|
+
_MEMPOOL_PREFIX = "mempool."
|
|
47
|
+
|
|
48
|
+
#: Per-channel filter requirements. ``required_any_of`` means at least one of
|
|
49
|
+
#: the listed filters must be present. Channels absent from this map accept no
|
|
50
|
+
#: required filters. Mempool companions share their confirmed channel's rules.
|
|
51
|
+
FILTER_REQUIREMENTS: dict[Channel, tuple[FilterKey, ...]] = {
|
|
52
|
+
"wallets": ("wallets",),
|
|
53
|
+
"markets": ("market_ids", "token_ids"),
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def is_channel(value: str) -> TypeGuard[Channel]:
|
|
58
|
+
"""Return whether ``value`` is a known confirmed channel name."""
|
|
59
|
+
return value in CHANNELS
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def is_mempool_channel(value: str) -> bool:
|
|
63
|
+
"""Return whether ``value`` is a ``mempool.``-prefixed channel."""
|
|
64
|
+
return value.startswith(_MEMPOOL_PREFIX) and is_channel(
|
|
65
|
+
value[len(_MEMPOOL_PREFIX) :]
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def is_subscribable_channel(value: str) -> bool:
|
|
70
|
+
"""Return whether ``value`` is any subscribable channel."""
|
|
71
|
+
return is_channel(value) or is_mempool_channel(value)
|