ssi-sdk 3.0.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.
- ssi_sdk/__init__.py +24 -0
- ssi_sdk/_version.py +3 -0
- ssi_sdk/client.py +188 -0
- ssi_sdk/config.py +48 -0
- ssi_sdk/constant.py +81 -0
- ssi_sdk/enums/__init__.py +26 -0
- ssi_sdk/enums/account.py +10 -0
- ssi_sdk/enums/market_data.py +10 -0
- ssi_sdk/enums/streaming.py +42 -0
- ssi_sdk/enums/timeframe.py +14 -0
- ssi_sdk/enums/trading.py +35 -0
- ssi_sdk/exceptions.py +45 -0
- ssi_sdk/models/__init__.py +133 -0
- ssi_sdk/models/account.py +25 -0
- ssi_sdk/models/auth.py +72 -0
- ssi_sdk/models/market_data.py +271 -0
- ssi_sdk/models/portfolio.py +651 -0
- ssi_sdk/models/streaming.py +274 -0
- ssi_sdk/models/trading.py +147 -0
- ssi_sdk/services/__init__.py +1 -0
- ssi_sdk/services/account.py +62 -0
- ssi_sdk/services/market_data.py +890 -0
- ssi_sdk/services/portfolio.py +337 -0
- ssi_sdk/services/streaming.py +754 -0
- ssi_sdk/services/token_manager.py +446 -0
- ssi_sdk/services/trading.py +465 -0
- ssi_sdk/transport/__init__.py +1 -0
- ssi_sdk/transport/rest_client.py +264 -0
- ssi_sdk/transport/websocket_client.py +383 -0
- ssi_sdk/utils/__init__.py +37 -0
- ssi_sdk/utils/converter.py +72 -0
- ssi_sdk/utils/crypto.py +42 -0
- ssi_sdk/utils/datetime_formater.py +21 -0
- ssi_sdk/utils/id_generator.py +27 -0
- ssi_sdk/utils/logger.py +31 -0
- ssi_sdk/utils/retry.py +103 -0
- ssi_sdk/utils/serializer.py +30 -0
- ssi_sdk/utils/validator.py +88 -0
- ssi_sdk-3.0.0.dist-info/METADATA +776 -0
- ssi_sdk-3.0.0.dist-info/RECORD +42 -0
- ssi_sdk-3.0.0.dist-info/WHEEL +5 -0
- ssi_sdk-3.0.0.dist-info/top_level.txt +1 -0
ssi_sdk/__init__.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""SSI Python SDK."""
|
|
2
|
+
|
|
3
|
+
from ssi_sdk._version import __version__
|
|
4
|
+
from ssi_sdk.client import AsyncSSIClient, SSIClient
|
|
5
|
+
from ssi_sdk.config import Config
|
|
6
|
+
from ssi_sdk.exceptions import (
|
|
7
|
+
APIError,
|
|
8
|
+
AuthenticationError,
|
|
9
|
+
SSIError,
|
|
10
|
+
RateLimitError,
|
|
11
|
+
ValidationError,
|
|
12
|
+
WebSocketError,
|
|
13
|
+
)
|
|
14
|
+
__all__ = [
|
|
15
|
+
"APIError",
|
|
16
|
+
"AsyncSSIClient",
|
|
17
|
+
"AuthenticationError",
|
|
18
|
+
"Config",
|
|
19
|
+
"SSIClient",
|
|
20
|
+
"SSIError",
|
|
21
|
+
"RateLimitError",
|
|
22
|
+
"ValidationError",
|
|
23
|
+
"WebSocketError",
|
|
24
|
+
]
|
ssi_sdk/_version.py
ADDED
ssi_sdk/client.py
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""Main SSI client — unified entry point for the SDK (async and sync)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from ssi_sdk.config import Config
|
|
8
|
+
from ssi_sdk.services.account import AccountService, AsyncAccountService
|
|
9
|
+
from ssi_sdk.services.market_data import AsyncMarketDataService, MarketDataService
|
|
10
|
+
from ssi_sdk.services.portfolio import AsyncPortfolioService, PortfolioService
|
|
11
|
+
from ssi_sdk.services.streaming import AsyncStreamingService, StreamingService
|
|
12
|
+
from ssi_sdk.services.token_manager import AsyncTokenManager, TokenManager
|
|
13
|
+
from ssi_sdk.services.trading import AsyncTradingService, TradingService
|
|
14
|
+
from ssi_sdk.transport.rest_client import AsyncRestClient, RestClient
|
|
15
|
+
from ssi_sdk.transport.websocket_client import AsyncWebSocketClient, WebSocketClient
|
|
16
|
+
from ssi_sdk.utils.logger import get_logger
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger("ssi_sdk")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AsyncSSIClient:
|
|
22
|
+
"""Async unified client for the SSI trading platform.
|
|
23
|
+
|
|
24
|
+
Provides access to all SDK services:
|
|
25
|
+
|
|
26
|
+
- ``token_manager``: Authentication & OTP
|
|
27
|
+
- ``trading``: Place/cancel/modify orders & condition orders
|
|
28
|
+
- ``account``: Account info, positions, portfolio, order history
|
|
29
|
+
- ``market_data``: OHLC, index, security info, FA, TA, download
|
|
30
|
+
- ``streaming``: Real-time data & portfolio via single WebSocket
|
|
31
|
+
|
|
32
|
+
Example::
|
|
33
|
+
|
|
34
|
+
from ssi_sdk import AsyncSSIClient, Config
|
|
35
|
+
|
|
36
|
+
config = Config(
|
|
37
|
+
api_key="your_id",
|
|
38
|
+
api_secret="your_secret",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
async with AsyncSSIClient(config) as client:
|
|
42
|
+
ohlc = await client.market_data.get_ohlc("VNM")
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, config: Config | None = None, **kwargs: object) -> None:
|
|
46
|
+
if config is None:
|
|
47
|
+
config = Config(**kwargs) # type: ignore[arg-type]
|
|
48
|
+
elif kwargs:
|
|
49
|
+
for key, value in kwargs.items():
|
|
50
|
+
if hasattr(config, key):
|
|
51
|
+
setattr(config, key, value)
|
|
52
|
+
self._config = config
|
|
53
|
+
|
|
54
|
+
get_logger(level=config.log_level)
|
|
55
|
+
|
|
56
|
+
# Transport layer
|
|
57
|
+
self._rest_client = AsyncRestClient(config)
|
|
58
|
+
self._ws_client = AsyncWebSocketClient(config)
|
|
59
|
+
|
|
60
|
+
# Business services
|
|
61
|
+
self.token_manager = AsyncTokenManager(self._rest_client, config)
|
|
62
|
+
self.account = AsyncAccountService(self._rest_client)
|
|
63
|
+
self.portfolio = AsyncPortfolioService(self._rest_client, config)
|
|
64
|
+
self.market_data = AsyncMarketDataService(self._rest_client)
|
|
65
|
+
self.trading = AsyncTradingService(self._rest_client)
|
|
66
|
+
self.streaming = AsyncStreamingService(self._ws_client)
|
|
67
|
+
|
|
68
|
+
async def authenticate(self, otp: str | None = None) -> None:
|
|
69
|
+
"""Authenticate with OTP and distribute token to all transports."""
|
|
70
|
+
token = await self.token_manager.authenticate(otp)
|
|
71
|
+
self._ws_client.set_token(token.access_token)
|
|
72
|
+
logger.info("AsyncSSIClient authenticated")
|
|
73
|
+
|
|
74
|
+
async def connect(self) -> None:
|
|
75
|
+
"""Connect WebSocket (must call authenticate first)."""
|
|
76
|
+
await self._ws_client.connect()
|
|
77
|
+
logger.info("AsyncSSIClient WebSocket connected")
|
|
78
|
+
|
|
79
|
+
async def connect_rest_only(self) -> None:
|
|
80
|
+
"""Authenticate via REST without connecting WebSocket.
|
|
81
|
+
|
|
82
|
+
.. deprecated:: Use authenticate(otp) instead.
|
|
83
|
+
"""
|
|
84
|
+
logger.warning("connect_rest_only is deprecated, use authenticate(otp) instead")
|
|
85
|
+
|
|
86
|
+
async def disconnect(self) -> None:
|
|
87
|
+
"""Close all connections and release resources."""
|
|
88
|
+
await self._ws_client.disconnect()
|
|
89
|
+
await self._rest_client.close()
|
|
90
|
+
logger.info("AsyncSSIClient disconnected")
|
|
91
|
+
|
|
92
|
+
async def __aenter__(self) -> AsyncSSIClient:
|
|
93
|
+
"""Enter async context manager."""
|
|
94
|
+
return self
|
|
95
|
+
|
|
96
|
+
async def __aexit__(
|
|
97
|
+
self,
|
|
98
|
+
exc_type: type[BaseException] | None,
|
|
99
|
+
exc_val: BaseException | None,
|
|
100
|
+
exc_tb: object,
|
|
101
|
+
) -> None:
|
|
102
|
+
"""Exit async context manager; disconnects automatically."""
|
|
103
|
+
await self.disconnect()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class SSIClient:
|
|
107
|
+
"""Synchronous unified client for the SSI trading platform.
|
|
108
|
+
|
|
109
|
+
Provides access to REST-based SDK services:
|
|
110
|
+
|
|
111
|
+
- ``token_manager``: Authentication & OTP
|
|
112
|
+
- ``trading``: Place/cancel/modify orders & condition orders
|
|
113
|
+
- ``account``: Account info, positions, portfolio, order history
|
|
114
|
+
- ``market_data``: OHLC, index, security info, FA, TA, download
|
|
115
|
+
- ``streaming``: Real-time data & portfolio via single WebSocket
|
|
116
|
+
|
|
117
|
+
Example::
|
|
118
|
+
|
|
119
|
+
from ssi_sdk import SSIClient, Config
|
|
120
|
+
|
|
121
|
+
config = Config(
|
|
122
|
+
api_key="your_id",
|
|
123
|
+
api_secret="your_secret",
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
with SSIClient(config) as client:
|
|
127
|
+
ohlc = client.market_data.get_ohlc("VNM")
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
def __init__(self, config: Config | None = None, **kwargs: object) -> None:
|
|
131
|
+
if config is None:
|
|
132
|
+
config = Config(**kwargs) # type: ignore[arg-type]
|
|
133
|
+
elif kwargs:
|
|
134
|
+
for key, value in kwargs.items():
|
|
135
|
+
if hasattr(config, key):
|
|
136
|
+
setattr(config, key, value)
|
|
137
|
+
self._config = config
|
|
138
|
+
|
|
139
|
+
get_logger(level=config.log_level)
|
|
140
|
+
|
|
141
|
+
# Transport layer
|
|
142
|
+
self._rest_client = RestClient(config)
|
|
143
|
+
self._ws_client = WebSocketClient(config)
|
|
144
|
+
|
|
145
|
+
# Business services
|
|
146
|
+
self.token_manager = TokenManager(self._rest_client, config)
|
|
147
|
+
self.account = AccountService(self._rest_client)
|
|
148
|
+
self.portfolio = PortfolioService(self._rest_client, config)
|
|
149
|
+
self.market_data = MarketDataService(self._rest_client)
|
|
150
|
+
self.trading = TradingService(self._rest_client)
|
|
151
|
+
self.streaming = StreamingService(self._ws_client)
|
|
152
|
+
|
|
153
|
+
def authenticate(self, otp: str | None = None) -> None:
|
|
154
|
+
"""Authenticate with OTP and distribute token to all transports."""
|
|
155
|
+
token = self.token_manager.authenticate(otp)
|
|
156
|
+
self._ws_client.set_token(token.access_token)
|
|
157
|
+
logger.info("SSIClient authenticated")
|
|
158
|
+
|
|
159
|
+
def connect(self) -> None:
|
|
160
|
+
"""Connect WebSocket (must call authenticate first)."""
|
|
161
|
+
self._ws_client.connect()
|
|
162
|
+
logger.info("SSIClient WebSocket connected")
|
|
163
|
+
|
|
164
|
+
def connect_rest_only(self) -> None:
|
|
165
|
+
"""Authenticate via REST without connecting WebSocket.
|
|
166
|
+
|
|
167
|
+
.. deprecated:: Use authenticate(otp) instead.
|
|
168
|
+
"""
|
|
169
|
+
logger.warning("connect_rest_only is deprecated, use authenticate(otp) instead")
|
|
170
|
+
|
|
171
|
+
def disconnect(self) -> None:
|
|
172
|
+
"""Close all connections and release resources."""
|
|
173
|
+
self._ws_client.disconnect()
|
|
174
|
+
self._rest_client.close()
|
|
175
|
+
logger.info("SSIClient disconnected")
|
|
176
|
+
|
|
177
|
+
def __enter__(self) -> SSIClient:
|
|
178
|
+
"""Enter context manager."""
|
|
179
|
+
return self
|
|
180
|
+
|
|
181
|
+
def __exit__(
|
|
182
|
+
self,
|
|
183
|
+
exc_type: type[BaseException] | None,
|
|
184
|
+
exc_val: BaseException | None,
|
|
185
|
+
exc_tb: object,
|
|
186
|
+
) -> None:
|
|
187
|
+
"""Exit context manager; disconnects automatically."""
|
|
188
|
+
self.disconnect()
|
ssi_sdk/config.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Configuration for SSI SDK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
|
|
7
|
+
from ssi_sdk.constant import (
|
|
8
|
+
DEFAULT_API_URL,
|
|
9
|
+
DEFAULT_STREAMING_URL,
|
|
10
|
+
DEFAULT_TIMEOUT,
|
|
11
|
+
DEFAULT_RETRY_DELAY,
|
|
12
|
+
DEFAULT_MAX_RETRIES,
|
|
13
|
+
DEFAULT_RATE_LIMIT_PER_SECOND,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class Config:
|
|
19
|
+
"""SDK configuration.
|
|
20
|
+
|
|
21
|
+
Attributes:
|
|
22
|
+
client_id: Client ID for API authentication (optional, can be set via env var).
|
|
23
|
+
api_url: Base URL for the SSI REST API.
|
|
24
|
+
streaming_url: URL for the SSI WebSocket streaming endpoint.
|
|
25
|
+
api_key: API key for API authentication.
|
|
26
|
+
api_secret: API secret for API authentication.
|
|
27
|
+
private_key: Private key for API authentication.
|
|
28
|
+
timeout: Request timeout in seconds.
|
|
29
|
+
max_retries: Maximum number of retry attempts for failed requests.
|
|
30
|
+
retry_delay: Base delay in seconds between retries (exponential backoff).
|
|
31
|
+
rate_limit_per_second: Maximum requests per second (0 = unlimited).
|
|
32
|
+
cache_ttl: Default cache TTL in seconds (0 = no cache).
|
|
33
|
+
log_level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL).
|
|
34
|
+
proxy: Proxy URL (e.g. 'http://user:pass@host:port' or 'socks5://host:port').
|
|
35
|
+
"""
|
|
36
|
+
client_id: str
|
|
37
|
+
api_url: str = DEFAULT_API_URL
|
|
38
|
+
streaming_url: str = DEFAULT_STREAMING_URL
|
|
39
|
+
api_key: str = ""
|
|
40
|
+
api_secret: str = ""
|
|
41
|
+
private_key: str = ""
|
|
42
|
+
timeout: int = DEFAULT_TIMEOUT
|
|
43
|
+
max_retries: int = DEFAULT_MAX_RETRIES
|
|
44
|
+
retry_delay: float = DEFAULT_RETRY_DELAY
|
|
45
|
+
rate_limit_per_second: int = DEFAULT_RATE_LIMIT_PER_SECOND
|
|
46
|
+
cache_ttl: int = 0
|
|
47
|
+
log_level: str = "INFO"
|
|
48
|
+
proxy: str | None = None
|
ssi_sdk/constant.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Protocol constants for SSI SDK (fixed by server/spec, not user-configurable)."""
|
|
2
|
+
|
|
3
|
+
# ---------------------------------------------------------------------------
|
|
4
|
+
# Pagination
|
|
5
|
+
# ---------------------------------------------------------------------------
|
|
6
|
+
DEFAULT_SIZE = 1000
|
|
7
|
+
DEFAULT_PAGE = 1
|
|
8
|
+
# ---------------------------------------------------------------------------
|
|
9
|
+
# Rate Limiting & Retries
|
|
10
|
+
# ---------------------------------------------------------------------------
|
|
11
|
+
DEFAULT_TIMEOUT = 60 # seconds
|
|
12
|
+
DEFAULT_MAX_RETRIES = 5
|
|
13
|
+
DEFAULT_RETRY_DELAY = 2 # seconds (base for exponential backoff)
|
|
14
|
+
DEFAULT_RATE_LIMIT_PER_SECOND = 10
|
|
15
|
+
RATE_LIMITER_SLEEP_INTERVAL = 2 # seconds
|
|
16
|
+
WS_THREAD_JOIN_TIMEOUT = 5 # seconds
|
|
17
|
+
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
# HTTP Headers & Auth
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
HEADER_CONTENT_TYPE = "Content-Type"
|
|
22
|
+
HEADER_ACCEPT = "Accept"
|
|
23
|
+
HEADER_AUTHORIZATION = "Authorization"
|
|
24
|
+
HEADER_RETRY_AFTER = "Retry-After"
|
|
25
|
+
HEADER_SIGNATURE = "X-Signature"
|
|
26
|
+
CONTENT_TYPE_JSON = "application/json"
|
|
27
|
+
AUTH_SCHEME_BEARER = "Bearer "
|
|
28
|
+
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
# HTTP Status Codes
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
HTTP_STATUS_UNAUTHORIZED = 401
|
|
33
|
+
HTTP_STATUS_FORBIDDEN = 403
|
|
34
|
+
HTTP_STATUS_RATE_LIMIT = 429
|
|
35
|
+
HTTP_STATUS_BAD_REQUEST = 400
|
|
36
|
+
HTTP_STATUS_NO_CONTENT = 204
|
|
37
|
+
HTTP_STATUS_ERROR_THRESHOLD = 400
|
|
38
|
+
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
# API Endpoints — Authentication
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
DEFAULT_API_URL = "https://api.ssi.com.vn"
|
|
43
|
+
DEFAULT_STREAMING_URL = "wss://api.ssi.com.vn/ws/v3"
|
|
44
|
+
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
# API Endpoints — Authentication
|
|
47
|
+
# ---------------------------------------------------------------------------
|
|
48
|
+
EP_ACCESS_TOKEN = "/api/v3/auth/token"
|
|
49
|
+
EP_REFRESH_TOKEN = "/api/v3/auth/refresh"
|
|
50
|
+
EP_REQUEST_OTP = "/api/v3/auth/requestOtp"
|
|
51
|
+
|
|
52
|
+
# ---------------------------------------------------------------------------
|
|
53
|
+
# API Endpoints — Market Data
|
|
54
|
+
# ---------------------------------------------------------------------------
|
|
55
|
+
EP_DATA_OHLC = "/api/v3/data/ohlc"
|
|
56
|
+
EP_DATA_OHLC_DOWNLOAD = "/api/v3/data/ohlc/download"
|
|
57
|
+
EP_DATA_INDEX_LIST = "/api/v3/data/indexList"
|
|
58
|
+
EP_DATA_INDEX_SUMMARY = "/api/v3/data/indexSummary"
|
|
59
|
+
EP_DATA_SECURITIES_BY_BOARD = "/api/v3/data/securitiesByBoard"
|
|
60
|
+
EP_DATA_SECURITIES_SUMMARY = "/api/v3/data/securitiesSummary"
|
|
61
|
+
|
|
62
|
+
# ---------------------------------------------------------------------------
|
|
63
|
+
# API Endpoints — Trading
|
|
64
|
+
# ---------------------------------------------------------------------------
|
|
65
|
+
EP_TRADING_ORDER = "/api/v3/trading/order"
|
|
66
|
+
EP_TRADING_MAX_BUY_SELL = "/api/v3/trading/maxBuySell"
|
|
67
|
+
|
|
68
|
+
# ---------------------------------------------------------------------------
|
|
69
|
+
# API Endpoints — Portfolio & Account
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
EP_ACCOUNT_INFO = "/api/v3/account/info"
|
|
72
|
+
EP_ACCOUNT_BALANCE = "/api/v3/trading/accountBalance"
|
|
73
|
+
EP_ACCOUNT_PPMMR = "/api/v3/trading/ppmmrAccount"
|
|
74
|
+
EP_POSITIONS = "/api/v3/trading/position"
|
|
75
|
+
EP_ORDER_HISTORY = "/api/v3/trading/orderBook"
|
|
76
|
+
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
# WebSocket Constants
|
|
79
|
+
# ---------------------------------------------------------------------------
|
|
80
|
+
WS_THREAD_NAME = "SSIWebSocketThread"
|
|
81
|
+
WS_KEY_CHANNEL = "channel"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
""" Enums for SSI. """
|
|
2
|
+
from ssi_sdk.enums.account import AccountType
|
|
3
|
+
from ssi_sdk.enums.streaming import (
|
|
4
|
+
StreamingType,
|
|
5
|
+
StreamingChannel,
|
|
6
|
+
StreamingMethod,
|
|
7
|
+
DataTopic,
|
|
8
|
+
DataType,
|
|
9
|
+
)
|
|
10
|
+
from ssi_sdk.enums.market_data import Board
|
|
11
|
+
from ssi_sdk.enums.timeframe import Timeframe
|
|
12
|
+
from ssi_sdk.enums.trading import OrderSide, OrderType, OrderStatus
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"AccountType",
|
|
16
|
+
"Board",
|
|
17
|
+
"OrderSide",
|
|
18
|
+
"OrderType",
|
|
19
|
+
"OrderStatus",
|
|
20
|
+
"Timeframe",
|
|
21
|
+
"StreamingType",
|
|
22
|
+
"StreamingChannel",
|
|
23
|
+
"StreamingMethod",
|
|
24
|
+
"DataTopic",
|
|
25
|
+
"DataType",
|
|
26
|
+
]
|
ssi_sdk/enums/account.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
""" Streaming enum for SSI."""
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class StreamingMethod(Enum):
|
|
6
|
+
"""Enum representing different streaming methods for SSI."""
|
|
7
|
+
SUBSCRIBE = "subscribe"
|
|
8
|
+
UNSUBSCRIBE = "unsubscribe"
|
|
9
|
+
PING_PONG = "ping_pong"
|
|
10
|
+
LIST_SUBSCRIPTION = "list_subscription"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class StreamingChannel(Enum):
|
|
14
|
+
"""Enum representing different streaming channels for SSI."""
|
|
15
|
+
DATA = "DATA"
|
|
16
|
+
HEARTBEAT = "HEARTBEAT"
|
|
17
|
+
TRADING = "TRADING"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class StreamingType(Enum):
|
|
21
|
+
"""Enum representing different streaming types for SSI."""
|
|
22
|
+
ORDER = "orderEvent"
|
|
23
|
+
ORDER_MATCH = "orderMatchEvent"
|
|
24
|
+
PORTFOLIO = "clientPortfolioEvent"
|
|
25
|
+
|
|
26
|
+
class DataTopic(Enum):
|
|
27
|
+
"""Enum representing different data topics for SSI."""
|
|
28
|
+
QUOTE = "quote."
|
|
29
|
+
TRADE = "trade."
|
|
30
|
+
ODD_LOT = "oddlot."
|
|
31
|
+
MARKET = "market."
|
|
32
|
+
ROOM = "room."
|
|
33
|
+
PUT = "put."
|
|
34
|
+
|
|
35
|
+
class DataType(Enum):
|
|
36
|
+
"""Enum representing different data types for SSI."""
|
|
37
|
+
QUOTE = "quote"
|
|
38
|
+
TRADE = "trade"
|
|
39
|
+
ODD_LOT = "oddlot"
|
|
40
|
+
MARKET = "market"
|
|
41
|
+
ROOM = "room"
|
|
42
|
+
PUT = "put"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
""" Timeframe enum for SSI. """
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Timeframe(Enum):
|
|
6
|
+
"""Enum representing different timeframes for SSI."""
|
|
7
|
+
MINUTE_1 = "1m"
|
|
8
|
+
MINUTE_3 = "3m"
|
|
9
|
+
MINUTE_5 = "5m"
|
|
10
|
+
MINUTE_15 = "15m"
|
|
11
|
+
HOUR_1 = "1h"
|
|
12
|
+
DAY_1 = "1d"
|
|
13
|
+
WEEK_1 = "1w"
|
|
14
|
+
MONTH_1 = "1M"
|
ssi_sdk/enums/trading.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
""" Trading enum for SSI. """
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class OrderSide(Enum):
|
|
6
|
+
"""Enum representing order sides for SSI."""
|
|
7
|
+
BUY = "B"
|
|
8
|
+
SELL = "S"
|
|
9
|
+
|
|
10
|
+
class OrderType(Enum):
|
|
11
|
+
"""Enum representing different order types for SSI."""
|
|
12
|
+
ATO = "ATO"
|
|
13
|
+
ATC = "ATC"
|
|
14
|
+
LO = "LO"
|
|
15
|
+
MTL = "MTL"
|
|
16
|
+
MP = "MP"
|
|
17
|
+
MOK = "MOK"
|
|
18
|
+
MAK = "MAK"
|
|
19
|
+
PLO = "PLO"
|
|
20
|
+
|
|
21
|
+
class OrderStatus(Enum):
|
|
22
|
+
"""Enum representing order statuses for SSI."""
|
|
23
|
+
PENDING_APPROVAL = "WA"
|
|
24
|
+
READY = "RS"
|
|
25
|
+
SENT = "SD"
|
|
26
|
+
QUEUED = "QU"
|
|
27
|
+
FILLED = "FF"
|
|
28
|
+
PARTIAL_FILLED = "PF"
|
|
29
|
+
PARTIAL_CANCELLED = "FFPC"
|
|
30
|
+
PENDING_MODIFY = "WM"
|
|
31
|
+
PENDING_CANCEL = "WC"
|
|
32
|
+
CANCELLED = "CL"
|
|
33
|
+
REJECTED = "RJ"
|
|
34
|
+
EXPIRED = "EX"
|
|
35
|
+
PRE_SESSION = "IAV"
|
ssi_sdk/exceptions.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Custom exceptions for SSI SDK."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SSIError(Exception):
|
|
5
|
+
"""Base exception for all SSI SDK errors."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, message: str = "", code: str | None = None):
|
|
8
|
+
self.message = message
|
|
9
|
+
self.code = code
|
|
10
|
+
super().__init__(self.message)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AuthenticationError(SSIError):
|
|
14
|
+
"""Raised when authentication fails (invalid credentials, expired token, etc.)."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class APIError(SSIError):
|
|
18
|
+
"""Raised when the SSI API returns an error response."""
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
message: str = "",
|
|
23
|
+
code: str | None = None,
|
|
24
|
+
status_code: int | None = None,
|
|
25
|
+
response_body: dict | None = None,
|
|
26
|
+
):
|
|
27
|
+
self.status_code = status_code
|
|
28
|
+
self.response_body = response_body
|
|
29
|
+
super().__init__(message, code)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class WebSocketError(SSIError):
|
|
33
|
+
"""Raised when a WebSocket connection or communication error occurs."""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ValidationError(SSIError):
|
|
37
|
+
"""Raised when input validation fails."""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class RateLimitError(SSIError):
|
|
41
|
+
"""Raised when API rate limit is exceeded."""
|
|
42
|
+
|
|
43
|
+
def __init__(self, message: str = "", retry_after: float | None = None):
|
|
44
|
+
self.retry_after = retry_after
|
|
45
|
+
super().__init__(message, code="RATE_LIMITED")
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Data models for SSI SDK."""
|
|
2
|
+
|
|
3
|
+
from ssi_sdk.models.account import Account
|
|
4
|
+
from ssi_sdk.models.portfolio import (
|
|
5
|
+
AccountBalanceRequest,
|
|
6
|
+
AccountBalance,
|
|
7
|
+
EquityAccountBalance,
|
|
8
|
+
DerivativeAccountBalance,
|
|
9
|
+
PositionsRequest,
|
|
10
|
+
Position,
|
|
11
|
+
DerivativePosition,
|
|
12
|
+
AllDerivativePosition,
|
|
13
|
+
EquityPosition,
|
|
14
|
+
PPMMRRequest,
|
|
15
|
+
PPMMR,
|
|
16
|
+
EquityPPMMR,
|
|
17
|
+
DerivativePPMMR,
|
|
18
|
+
|
|
19
|
+
OrderBookRequest,
|
|
20
|
+
OrderBook,
|
|
21
|
+
)
|
|
22
|
+
from ssi_sdk.models.auth import Token, TokenRequest, OTPRequest, RefreshTokenRequest
|
|
23
|
+
from ssi_sdk.models.market_data import (
|
|
24
|
+
DownloadData,
|
|
25
|
+
DownloadDataRequest,
|
|
26
|
+
|
|
27
|
+
OHLCRequest,
|
|
28
|
+
OHLCData,
|
|
29
|
+
|
|
30
|
+
MarketIndexes,
|
|
31
|
+
MarketIndexesRequest,
|
|
32
|
+
|
|
33
|
+
MarketIndexSummaryRequest,
|
|
34
|
+
MarketIndexSummary,
|
|
35
|
+
|
|
36
|
+
SecuritiesInfoRequest,
|
|
37
|
+
SecuritiesInfo,
|
|
38
|
+
)
|
|
39
|
+
from ssi_sdk.models.trading import (
|
|
40
|
+
PlaceOrderRequest,
|
|
41
|
+
ModifyOrderRequest,
|
|
42
|
+
CancelOrderRequest,
|
|
43
|
+
MaxBuySellRequest,
|
|
44
|
+
MaxBuySellResponse,
|
|
45
|
+
)
|
|
46
|
+
from ssi_sdk.models.streaming import (
|
|
47
|
+
RequestMessage,
|
|
48
|
+
|
|
49
|
+
HeartbeatMessage,
|
|
50
|
+
|
|
51
|
+
ForeignRoomMessage,
|
|
52
|
+
MarketStatusMessage,
|
|
53
|
+
OddLotMessage,
|
|
54
|
+
PutMessage,
|
|
55
|
+
QuoteMessage,
|
|
56
|
+
TradeMessage,
|
|
57
|
+
|
|
58
|
+
OrderStatusMessage,
|
|
59
|
+
PortfolioMessage,
|
|
60
|
+
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
__all__ = [
|
|
64
|
+
# -------------------------------------------------------------------------
|
|
65
|
+
# Authentication models
|
|
66
|
+
# -------------------------------------------------------------------------
|
|
67
|
+
"Token",
|
|
68
|
+
"TokenRequest",
|
|
69
|
+
"OTPRequest",
|
|
70
|
+
"RefreshTokenRequest",
|
|
71
|
+
# -------------------------------------------------------------------------
|
|
72
|
+
# Account models
|
|
73
|
+
# -------------------------------------------------------------------------
|
|
74
|
+
"Account",
|
|
75
|
+
# -------------------------------------------------------------------------
|
|
76
|
+
# Market data models
|
|
77
|
+
# -------------------------------------------------------------------------
|
|
78
|
+
"DownloadData",
|
|
79
|
+
"DownloadDataRequest",
|
|
80
|
+
"OHLCRequest",
|
|
81
|
+
"OHLCData",
|
|
82
|
+
"MarketIndexes",
|
|
83
|
+
"MarketIndexesRequest",
|
|
84
|
+
"MarketIndexSummary",
|
|
85
|
+
"MarketIndexSummaryRequest",
|
|
86
|
+
"SecuritiesInfo",
|
|
87
|
+
"SecuritiesInfoRequest",
|
|
88
|
+
# -------------------------------------------------------------------------
|
|
89
|
+
# Portfolio models
|
|
90
|
+
# -------------------------------------------------------------------------
|
|
91
|
+
"AccountBalanceRequest",
|
|
92
|
+
"AccountBalance",
|
|
93
|
+
"EquityAccountBalance",
|
|
94
|
+
"DerivativeAccountBalance",
|
|
95
|
+
"PositionsRequest",
|
|
96
|
+
"Position",
|
|
97
|
+
"DerivativePosition",
|
|
98
|
+
"AllDerivativePosition",
|
|
99
|
+
"EquityPosition",
|
|
100
|
+
# -------------------------------------------------------------------------
|
|
101
|
+
# PPMMR models
|
|
102
|
+
# -------------------------------------------------------------------------
|
|
103
|
+
"PPMMRRequest",
|
|
104
|
+
"PPMMR",
|
|
105
|
+
"EquityPPMMR",
|
|
106
|
+
"DerivativePPMMR",
|
|
107
|
+
# -------------------------------------------------------------------------
|
|
108
|
+
# Trading models
|
|
109
|
+
# -------------------------------------------------------------------------
|
|
110
|
+
"PlaceOrderRequest",
|
|
111
|
+
"ModifyOrderRequest",
|
|
112
|
+
"CancelOrderRequest",
|
|
113
|
+
"MaxBuySellRequest",
|
|
114
|
+
"MaxBuySellResponse",
|
|
115
|
+
# -------------------------------------------------------------------------
|
|
116
|
+
# Streaming models
|
|
117
|
+
# -------------------------------------------------------------------------
|
|
118
|
+
"RequestMessage",
|
|
119
|
+
"HeartbeatMessage",
|
|
120
|
+
"TradeMessage",
|
|
121
|
+
"QuoteMessage",
|
|
122
|
+
"MarketStatusMessage",
|
|
123
|
+
"ForeignRoomMessage",
|
|
124
|
+
"PutMessage",
|
|
125
|
+
"OddLotMessage",
|
|
126
|
+
"OrderStatusMessage",
|
|
127
|
+
"PortfolioMessage",
|
|
128
|
+
# -------------------------------------------------------------------------
|
|
129
|
+
# Order book models (not implemented yet)
|
|
130
|
+
# -------------------------------------------------------------------------
|
|
131
|
+
"OrderBookRequest",
|
|
132
|
+
"OrderBook",
|
|
133
|
+
]
|