dnse-sdk-openapi 0.0.1__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.
Files changed (50) hide show
  1. broker-api/get_list_care_by.py +22 -0
  2. dnse/__init__.py +4 -0
  3. dnse/client.py +408 -0
  4. dnse/common.py +103 -0
  5. dnse_sdk_openapi-0.0.1.dist-info/METADATA +120 -0
  6. dnse_sdk_openapi-0.0.1.dist-info/RECORD +50 -0
  7. dnse_sdk_openapi-0.0.1.dist-info/WHEEL +5 -0
  8. dnse_sdk_openapi-0.0.1.dist-info/top_level.txt +5 -0
  9. marketdata-api/get_instruments.py +22 -0
  10. marketdata-api/get_latest_trade.py +22 -0
  11. marketdata-api/get_ohlc.py +31 -0
  12. marketdata-api/get_security_definition.py +22 -0
  13. marketdata-api/get_trades.py +22 -0
  14. marketdata-api/get_working_dates.py +22 -0
  15. trading-api/cancel_order.py +29 -0
  16. trading-api/close_position.py +27 -0
  17. trading-api/create_trading_token.py +26 -0
  18. trading-api/get_accounts.py +22 -0
  19. trading-api/get_balances.py +22 -0
  20. trading-api/get_close_price.py +22 -0
  21. trading-api/get_execution_detail.py +28 -0
  22. trading-api/get_loan_packages.py +27 -0
  23. trading-api/get_order_detail.py +28 -0
  24. trading-api/get_order_history.py +30 -0
  25. trading-api/get_orders.py +27 -0
  26. trading-api/get_position_by_id.py +26 -0
  27. trading-api/get_positions.py +26 -0
  28. trading-api/get_ppse.py +29 -0
  29. trading-api/post_order.py +38 -0
  30. trading-api/put_order.py +35 -0
  31. trading-api/send_email_otp.py +22 -0
  32. websocket-marketdata/expected_price.py +53 -0
  33. websocket-marketdata/foreign_investor.py +51 -0
  34. websocket-marketdata/market_index.py +52 -0
  35. websocket-marketdata/ohlc.py +55 -0
  36. websocket-marketdata/ohlc_closed.py +55 -0
  37. websocket-marketdata/order.py +51 -0
  38. websocket-marketdata/quote.py +50 -0
  39. websocket-marketdata/sec_def.py +52 -0
  40. websocket-marketdata/trade.py +52 -0
  41. websocket-marketdata/trade_extra.py +51 -0
  42. websocket-marketdata/trading_websocket/__init__.py +33 -0
  43. websocket-marketdata/trading_websocket/_version.py +3 -0
  44. websocket-marketdata/trading_websocket/auth.py +59 -0
  45. websocket-marketdata/trading_websocket/client.py +790 -0
  46. websocket-marketdata/trading_websocket/connection.py +151 -0
  47. websocket-marketdata/trading_websocket/encoding.py +78 -0
  48. websocket-marketdata/trading_websocket/exceptions.py +38 -0
  49. websocket-marketdata/trading_websocket/models.py +525 -0
  50. websocket-marketdata/trading_websocket/py.typed +0 -0
@@ -0,0 +1,151 @@
1
+ import asyncio
2
+ import logging
3
+ import ssl
4
+ from typing import Optional, AsyncIterator
5
+
6
+ import certifi
7
+ import websockets
8
+ from websockets import ClientConnection
9
+ from .exceptions import ConnectionError, ConnectionClosed
10
+
11
+ logger = logging.getLogger(__name__)
12
+ logger.setLevel(logging.INFO)
13
+ if not logger.handlers:
14
+ handler = logging.StreamHandler()
15
+ handler.setLevel(logging.INFO)
16
+ formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
17
+ handler.setFormatter(formatter)
18
+ logger.addHandler(handler)
19
+
20
+ class WebSocketConnection:
21
+ """
22
+ WebSocket connection manager with automatic reconnection.
23
+
24
+ Features:
25
+ - Exponential backoff reconnection
26
+ - Connection health monitoring
27
+ - Graceful shutdown
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ url: str,
33
+ timeout: float = 60.0,
34
+ heartbeat_interval: float = 25.0,
35
+ auto_reconnect: bool = True,
36
+ max_retries: int = 10,
37
+ ):
38
+ """
39
+ Initialize connection manager.
40
+
41
+ Args:
42
+ url: WebSocket URL
43
+ timeout: Connection timeout (seconds)
44
+ heartbeat_interval: Heartbeat interval (seconds)
45
+ auto_reconnect: Enable automatic reconnection
46
+ max_retries: Maximum reconnection attempts
47
+ """
48
+ self.url = url
49
+ self.timeout = timeout
50
+ self.heartbeat_interval = heartbeat_interval
51
+ self.auto_reconnect = auto_reconnect
52
+ self.max_retries = max_retries
53
+
54
+ self._ws: Optional[ClientConnection] = None
55
+ self._retry_count = 0
56
+ self._is_connected = False
57
+
58
+ async def connect(self) -> None:
59
+ """
60
+ Establish WebSocket connection.
61
+
62
+ Raises:
63
+ ConnectionError: Failed to connect after max retries
64
+ asyncio.TimeoutError: Connection timeout
65
+ """
66
+ while self._retry_count < self.max_retries:
67
+ try:
68
+ logger.info(f"Connecting to {self.url} (attempt {self._retry_count + 1}/{self.max_retries})")
69
+ ssl_context = ssl.create_default_context(cafile=certifi.where())
70
+ # ssl_context.check_hostname = False
71
+ # ssl_context.verify_mode = ssl.CERT_NONE
72
+ self._ws = await asyncio.wait_for(
73
+ websockets.connect(self.url,
74
+ ssl=ssl_context,
75
+ ping_interval=30,
76
+ ping_timeout=30,
77
+ close_timeout=10,
78
+ max_queue=512), timeout=self.timeout)
79
+
80
+ self._is_connected = True
81
+ self._retry_count = 0
82
+ logger.info("Connected successfully")
83
+ return
84
+
85
+ except (websockets.exceptions.WebSocketException, OSError) as e:
86
+ self._retry_count += 1
87
+
88
+ if self._retry_count >= self.max_retries:
89
+ raise ConnectionError(f"Failed to connect after {self.max_retries} attempts: {e}")
90
+
91
+ # Exponential backoff: 1s, 2s, 4s, 8s, ... up to 60s
92
+ delay = min(2 ** (self._retry_count - 1), 60)
93
+ logger.warning(f"Connection failed: {e}. Retrying in {delay}s...")
94
+ await asyncio.sleep(delay)
95
+
96
+ async def send(self, message: bytes) -> None:
97
+ if not self._ws or not self._is_connected:
98
+ raise ConnectionError("Not connected")
99
+
100
+ await self._ws.send(message)
101
+
102
+ async def receive(self) -> bytes:
103
+ if not self._ws or not self._is_connected:
104
+ raise ConnectionError("Not connected")
105
+
106
+ try:
107
+ message = await self._ws.recv()
108
+ return message if isinstance(message, bytes) else message.encode()
109
+ except websockets.exceptions.ConnectionClosed as e:
110
+ self._is_connected = False
111
+ code = e.rcvd.code if e.rcvd else 1006
112
+
113
+ if code in (1000, 1001): # Normal closure, going away
114
+ logger.info(f"Connection closed normally: {code}")
115
+ raise ConnectionClosed(f"Connection closed normally: {code}")
116
+ elif code in (1006, 1011, 1012): # Abnormal, server error, restart
117
+ logger.warning(f"Connection closed abnormally: {code}")
118
+ if self.auto_reconnect:
119
+ raise ConnectionClosed(f"Connection closed abnormally: {code}", recoverable=True)
120
+ else:
121
+ raise ConnectionClosed(f"Connection closed abnormally: {code}")
122
+ else:
123
+ logger.error(f"Connection closed with unexpected code: {code}")
124
+ raise ConnectionClosed(f"Connection closed: {code}", recoverable=True)
125
+
126
+ async def close(self) -> None:
127
+ """Close connection gracefully"""
128
+ if self._ws:
129
+ await self._ws.close()
130
+
131
+ self._is_connected = False
132
+ logger.info("Connection closed")
133
+
134
+ @property
135
+ def is_connected(self) -> bool:
136
+ """Check if connection is active"""
137
+ return self._is_connected and self._ws is not None
138
+
139
+ def __aiter__(self) -> AsyncIterator[bytes]:
140
+ """Allow async iteration over messages"""
141
+ return self
142
+
143
+ async def __anext__(self) -> bytes:
144
+ """Get next message"""
145
+ try:
146
+ return await self.receive()
147
+ except ConnectionClosed as e:
148
+ if e.recoverable:
149
+ # Re-raise so _message_handler can trigger reconnection
150
+ raise
151
+ raise StopAsyncIteration
@@ -0,0 +1,78 @@
1
+ import json
2
+ from typing import Dict, Any
3
+ import msgpack
4
+ from .exceptions import EncodingError
5
+
6
+
7
+ class MessageEncoder:
8
+ """Encode messages for WebSocket transmission"""
9
+
10
+ def __init__(self, encoding: str = "msgpack"):
11
+ """
12
+ Initialize encoder.
13
+
14
+ Args:
15
+ encoding: "json" or "msgpack"
16
+ """
17
+ if encoding not in ("json", "msgpack"):
18
+ raise ValueError(f"Invalid encoding: {encoding}. Must be 'json' or 'msgpack'")
19
+
20
+ self.encoding = encoding
21
+
22
+ def encode(self, data: Dict[str, Any]) -> bytes:
23
+ """
24
+ Encode message.
25
+
26
+ Args:
27
+ data: Message dict
28
+
29
+ Returns:
30
+ Encoded bytes
31
+
32
+ Raises:
33
+ EncodingError: Encoding failed
34
+ """
35
+ try:
36
+ if self.encoding == "json":
37
+ return json.dumps(data).encode('utf-8')
38
+ else: # msgpack
39
+ return msgpack.packb(data)
40
+ except Exception as e:
41
+ raise EncodingError(f"Failed to encode message: {e}")
42
+
43
+
44
+ class MessageDecoder:
45
+ """Decode messages from WebSocket"""
46
+
47
+ def __init__(self, encoding: str = "msgpack"):
48
+ """
49
+ Initialize decoder.
50
+
51
+ Args:
52
+ encoding: "json" or "msgpack"
53
+ """
54
+ if encoding not in ("json", "msgpack"):
55
+ raise ValueError(f"Invalid encoding: {encoding}. Must be 'json' or 'msgpack'")
56
+
57
+ self.encoding = encoding
58
+
59
+ def decode(self, data: bytes) -> Dict[str, Any]:
60
+ """
61
+ Decode message.
62
+
63
+ Args:
64
+ data: Encoded bytes
65
+
66
+ Returns:
67
+ Decoded dict
68
+
69
+ Raises:
70
+ EncodingError: Decoding failed
71
+ """
72
+ try:
73
+ if self.encoding == "json":
74
+ return json.loads(data.decode('utf-8'))
75
+ else: # msgpack
76
+ return msgpack.unpackb(data, raw=False)
77
+ except Exception as e:
78
+ raise EncodingError(f"Failed to decode message: {e}")
@@ -0,0 +1,38 @@
1
+ class TradingWebSocketError(Exception):
2
+ """Base exception for all SDK errors"""
3
+ pass
4
+
5
+
6
+ class ConnectionError(TradingWebSocketError):
7
+ """Failed to establish connection"""
8
+ pass
9
+
10
+
11
+ class ConnectionClosed(TradingWebSocketError):
12
+ """Connection was closed"""
13
+
14
+ def __init__(self, message: str, recoverable: bool = False):
15
+ """
16
+ Initialize ConnectionClosed exception.
17
+
18
+ Args:
19
+ message: Error message
20
+ recoverable: Whether this is a recoverable error (should retry)
21
+ """
22
+ super().__init__(message)
23
+ self.recoverable = recoverable
24
+
25
+
26
+ class AuthenticationError(TradingWebSocketError):
27
+ """Authentication failed"""
28
+ pass
29
+
30
+
31
+ class SubscriptionError(TradingWebSocketError):
32
+ """Subscription failed"""
33
+ pass
34
+
35
+
36
+ class EncodingError(TradingWebSocketError):
37
+ """Message encoding/decoding failed"""
38
+ pass