ksxt 0.0.7__py3-none-any.whl → 0.0.9__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 (141) hide show
  1. ksxt/__init__.py +3 -1
  2. ksxt/__pycache__/__init__.cpython-312.pyc +0 -0
  3. ksxt/__pycache__/bithumb.cpython-312.pyc +0 -0
  4. ksxt/__pycache__/koreainvest.cpython-312.pyc +0 -0
  5. ksxt/__pycache__/upbit.cpython-312.pyc +0 -0
  6. ksxt/api/__init__.py +26 -0
  7. ksxt/api/__pycache__/__init__.cpython-312.pyc +0 -0
  8. ksxt/api/__pycache__/bithumb.cpython-312.pyc +0 -0
  9. ksxt/api/__pycache__/koreainvest.cpython-312.pyc +0 -0
  10. ksxt/api/__pycache__/upbit.cpython-312.pyc +0 -0
  11. ksxt/api/auto/api_generator.py +54 -0
  12. ksxt/api/auto/bithumb.py +35 -0
  13. ksxt/api/auto/koreainvest.py +49 -0
  14. ksxt/api/auto/upbit.py +39 -0
  15. ksxt/api/bithumb.py +42 -0
  16. ksxt/api/koreainvest.py +40 -0
  17. ksxt/api/upbit.py +54 -0
  18. ksxt/async_/__init__.py +4 -0
  19. ksxt/async_/__pycache__/__init__.cpython-312.pyc +0 -0
  20. ksxt/async_/__pycache__/bithumb.cpython-312.pyc +0 -0
  21. ksxt/async_/__pycache__/koreainvest.cpython-312.pyc +0 -0
  22. ksxt/async_/__pycache__/upbit.cpython-312.pyc +0 -0
  23. ksxt/async_/base/__init__.py +0 -0
  24. ksxt/async_/base/__pycache__/__init__.cpython-312.pyc +0 -0
  25. ksxt/async_/base/__pycache__/async_exchange.cpython-312.pyc +0 -0
  26. ksxt/async_/base/__pycache__/throttler.cpython-312.pyc +0 -0
  27. ksxt/async_/base/async_exchange.py +232 -0
  28. ksxt/async_/base/throttler.py +63 -0
  29. ksxt/async_/bithumb.py +455 -0
  30. ksxt/async_/koreainvest.py +849 -0
  31. ksxt/async_/upbit.py +488 -0
  32. ksxt/base/__pycache__/__init__.cpython-312.pyc +0 -0
  33. ksxt/base/__pycache__/errors.cpython-312.pyc +0 -0
  34. ksxt/base/__pycache__/exchange.cpython-312.pyc +0 -0
  35. ksxt/base/__pycache__/rest_exchange.cpython-312.pyc +0 -0
  36. ksxt/base/__pycache__/types.cpython-312.pyc +0 -0
  37. ksxt/base/com_exchange.py +2 -2
  38. ksxt/base/errors.py +10 -0
  39. ksxt/base/exchange.py +188 -497
  40. ksxt/base/rest_exchange.py +297 -113
  41. ksxt/base/types.py +1 -36
  42. ksxt/bithumb.py +504 -0
  43. ksxt/config/__init__.py +2 -1
  44. ksxt/config/__pycache__/__init__.cpython-312.pyc +0 -0
  45. ksxt/config/bithumb.toml +380 -0
  46. ksxt/config/koreainvest.toml +312 -0
  47. ksxt/config/token.toml +7 -0
  48. ksxt/config/upbit.toml +428 -0
  49. ksxt/koreainvest.py +409 -1055
  50. ksxt/market/__pycache__/base.cpython-312.pyc +0 -0
  51. ksxt/market/__pycache__/db.cpython-312.pyc +0 -0
  52. ksxt/market/__pycache__/logging.cpython-312.pyc +0 -0
  53. ksxt/market/__pycache__/manager.cpython-312.pyc +0 -0
  54. ksxt/market/__pycache__/markets.cpython-312.pyc +0 -0
  55. ksxt/market/base.py +50 -50
  56. ksxt/market/db.py +5 -4
  57. ksxt/market/krx/__pycache__/kosdaq.cpython-312.pyc +0 -0
  58. ksxt/market/krx/__pycache__/kospi.cpython-312.pyc +0 -0
  59. ksxt/market/krx/__pycache__/stock.cpython-312.pyc +0 -0
  60. ksxt/market/krx/kosdaq.py +150 -147
  61. ksxt/market/krx/kospi.py +179 -175
  62. ksxt/market/krx/stock.py +136 -134
  63. ksxt/market/logging.py +4 -4
  64. ksxt/market/manager.py +11 -13
  65. ksxt/market/markets.py +1 -1
  66. ksxt/market/us/__pycache__/amex.cpython-312.pyc +0 -0
  67. ksxt/market/us/__pycache__/nasdaq.cpython-312.pyc +0 -0
  68. ksxt/market/us/__pycache__/nyse.cpython-312.pyc +0 -0
  69. ksxt/market/us/__pycache__/stock.cpython-312.pyc +0 -0
  70. ksxt/market/us/amex.py +31 -31
  71. ksxt/market/us/nasdaq.py +31 -31
  72. ksxt/market/us/nyse.py +31 -31
  73. ksxt/market/us/stock.py +20 -28
  74. ksxt/models/__init__.py +16 -0
  75. ksxt/models/__pycache__/__init__.cpython-312.pyc +0 -0
  76. ksxt/models/__pycache__/balance.cpython-312.pyc +0 -0
  77. ksxt/models/__pycache__/cash.cpython-312.pyc +0 -0
  78. ksxt/models/__pycache__/common.cpython-312.pyc +0 -0
  79. ksxt/models/__pycache__/error.cpython-312.pyc +0 -0
  80. ksxt/models/__pycache__/historical.cpython-312.pyc +0 -0
  81. ksxt/models/__pycache__/market.cpython-312.pyc +0 -0
  82. ksxt/models/__pycache__/order.cpython-312.pyc +0 -0
  83. ksxt/models/__pycache__/orderbook.cpython-312.pyc +0 -0
  84. ksxt/models/__pycache__/ticker.cpython-312.pyc +0 -0
  85. ksxt/models/__pycache__/token.cpython-312.pyc +0 -0
  86. ksxt/models/__pycache__/transaction.cpython-312.pyc +0 -0
  87. ksxt/models/balance.py +30 -0
  88. ksxt/models/cash.py +15 -0
  89. ksxt/models/common.py +31 -0
  90. ksxt/models/error.py +13 -0
  91. ksxt/models/historical.py +26 -0
  92. ksxt/models/market.py +81 -0
  93. ksxt/models/order.py +42 -0
  94. ksxt/models/orderbook.py +32 -0
  95. ksxt/models/ticker.py +25 -0
  96. ksxt/models/token.py +14 -0
  97. ksxt/models/transaction.py +79 -0
  98. ksxt/parser/__pycache__/bithumb.cpython-312.pyc +0 -0
  99. ksxt/parser/__pycache__/koreainvest.cpython-312.pyc +0 -0
  100. ksxt/parser/__pycache__/parser.cpython-312.pyc +0 -0
  101. ksxt/parser/__pycache__/upbit.cpython-312.pyc +0 -0
  102. ksxt/parser/bithumb.py +300 -0
  103. ksxt/parser/koreainvest.py +323 -0
  104. ksxt/parser/parser.py +114 -0
  105. ksxt/parser/upbit.py +308 -0
  106. ksxt/upbit.py +499 -0
  107. ksxt/utils/__pycache__/safer.cpython-312.pyc +0 -0
  108. ksxt/utils/__pycache__/sorter.cpython-312.pyc +0 -0
  109. ksxt/utils/__pycache__/timer.cpython-312.pyc +0 -0
  110. ksxt/utils/safer.py +48 -0
  111. ksxt/utils/sorter.py +8 -0
  112. ksxt/utils/timer.py +47 -0
  113. {ksxt-0.0.7.dist-info → ksxt-0.0.9.dist-info}/METADATA +11 -1
  114. ksxt-0.0.9.dist-info/RECORD +119 -0
  115. {ksxt-0.0.7.dist-info → ksxt-0.0.9.dist-info}/WHEEL +1 -1
  116. ksxt/__pycache__/__init__.cpython-39.pyc +0 -0
  117. ksxt/__pycache__/koreainvest.cpython-39.pyc +0 -0
  118. ksxt/base/__pycache__/__init__.cpython-39.pyc +0 -0
  119. ksxt/base/__pycache__/exchange.cpython-39.pyc +0 -0
  120. ksxt/base/__pycache__/rest_exchange.cpython-39.pyc +0 -0
  121. ksxt/base/__pycache__/restexchange.cpython-39.pyc +0 -0
  122. ksxt/base/__pycache__/types.cpython-39.pyc +0 -0
  123. ksxt/base/api_response.py +0 -68
  124. ksxt/config/__pycache__/__init__.cpython-39.pyc +0 -0
  125. ksxt/config/tr_app.json +0 -381
  126. ksxt/config/tr_dev.json +0 -446
  127. ksxt/market/__pycache__/base.cpython-39.pyc +0 -0
  128. ksxt/market/__pycache__/db.cpython-39.pyc +0 -0
  129. ksxt/market/__pycache__/logging.cpython-39.pyc +0 -0
  130. ksxt/market/__pycache__/manager.cpython-39.pyc +0 -0
  131. ksxt/market/__pycache__/markets.cpython-39.pyc +0 -0
  132. ksxt/market/krx/__pycache__/kosdaq.cpython-39.pyc +0 -0
  133. ksxt/market/krx/__pycache__/kospi.cpython-39.pyc +0 -0
  134. ksxt/market/krx/__pycache__/stock.cpython-39.pyc +0 -0
  135. ksxt/market/us/__pycache__/amex.cpython-39.pyc +0 -0
  136. ksxt/market/us/__pycache__/nasdaq.cpython-39.pyc +0 -0
  137. ksxt/market/us/__pycache__/nyse.cpython-39.pyc +0 -0
  138. ksxt/market/us/__pycache__/stock.cpython-39.pyc +0 -0
  139. ksxt-0.0.7.dist-info/RECORD +0 -49
  140. {ksxt-0.0.7.dist-info → ksxt-0.0.9.dist-info}/LICENSE.txt +0 -0
  141. {ksxt-0.0.7.dist-info → ksxt-0.0.9.dist-info}/top_level.txt +0 -0
ksxt/async_/upbit.py ADDED
@@ -0,0 +1,488 @@
1
+ import hashlib
2
+ import json
3
+ import time
4
+ import uuid
5
+ from datetime import datetime
6
+ from typing import Any, Dict, List, Literal, Optional
7
+ from urllib.parse import urlencode
8
+
9
+ import jwt
10
+
11
+ from ksxt.api.upbit import ImplicitAPI
12
+ from ksxt.async_.base.async_exchange import AsyncExchange
13
+ from ksxt.parser.upbit import UpbitParser
14
+ import ksxt.models
15
+
16
+
17
+ class Upbit(AsyncExchange, ImplicitAPI):
18
+ def __init__(self, config: Dict = None):
19
+ super().__init__(config, "upbit.toml")
20
+ self.parser = UpbitParser()
21
+
22
+ def safe_symbol(self, base_market: str, security: str) -> str:
23
+ # If security already contains a hyphen, assume it's correctly formatted
24
+ if "-" in security:
25
+ return security
26
+
27
+ # Otherwise, assume security is just the symbol and format it with base_market
28
+ return f"{base_market}-{security}"
29
+
30
+ def safe_security(self, symbol: str) -> str:
31
+ if symbol.find("-") < 0:
32
+ return symbol
33
+ return symbol.split("-")[1]
34
+
35
+ def sign(
36
+ self,
37
+ path,
38
+ security_type,
39
+ method_type,
40
+ api_type: Any = "public",
41
+ headers: Optional[Any] = None,
42
+ body: Optional[Any] = None,
43
+ params: Optional[Any] = None,
44
+ config={},
45
+ ):
46
+ host_url = self.apis[self.type]["hostname"]
47
+ destination = self.apis[self.type][security_type][path]["url"]
48
+ version = self.apis[self.type]["version"]
49
+ destination = self.implode_params(destination, params)
50
+
51
+ url = host_url + "/" + version + "/" + destination
52
+
53
+ if api_type == "private":
54
+ payload = {
55
+ "access_key": self.open_key,
56
+ "nonce": str(uuid.uuid4()),
57
+ }
58
+
59
+ if params is not None and len(params) > 0:
60
+ m = hashlib.sha512()
61
+ m.update(urlencode(params).encode())
62
+ payload.update({"query_hash": m.hexdigest(), "query_hash_alg": "SHA512"})
63
+
64
+ jwt_token = jwt.encode(payload=payload, key=self.secret_key)
65
+ authorization_token = f"Bearer {jwt_token}"
66
+
67
+ if headers is None:
68
+ headers = {}
69
+ headers.update({"content-type": "application/json", "Authorization": authorization_token})
70
+
71
+ if method_type.upper() == "POST":
72
+ body = json.dumps(params)
73
+ params = {}
74
+
75
+ return {"url": url, "method": method_type, "headers": headers, "body": body, "params": params}
76
+
77
+ def get_common_response(self, response):
78
+ if "error" in response:
79
+ return self.create_common_response(
80
+ success="1",
81
+ msg_code=self.safe_string(response["error"], "name"),
82
+ msg=self.safe_string(response["error"], "message"),
83
+ info=response,
84
+ )
85
+
86
+ return self.create_common_response(
87
+ success="0",
88
+ msg_code=self.safe_string(response, "name"),
89
+ msg=self.safe_string(response, "message"),
90
+ info=response,
91
+ )
92
+
93
+ async def fetch_markets(self, base_market: str = "KRW") -> ksxt.models.KsxtMarketResponse:
94
+ params = {"isDetails": "True"}
95
+
96
+ common_header = self.create_common_header(request_params=params)
97
+
98
+ response = await self.public_get_fetch_markets(self.extend(params))
99
+
100
+ common_response = self.get_common_response(response=response)
101
+ if common_response.success != "0":
102
+ return ksxt.models.KsxtMarketResponse(header=common_header, response=common_response, info=None)
103
+
104
+ parsed_info = self.parser.parse_markets(response=response, base_market=base_market)
105
+
106
+ return ksxt.models.KsxtMarketResponse(header=common_header, response=common_response, info=parsed_info)
107
+
108
+ async def fetch_historical_data(
109
+ self, symbol: str, time_frame: str, start: str | None = None, end: str | None = None, base_market: str = "KRW"
110
+ ) -> ksxt.models.KsxtHistoricalDataResponse:
111
+ params = {"market": self.safe_symbol(base_market, symbol), "count": 200}
112
+
113
+ # TODO : time_frame 을 어떻게 고정시킬까? 우리는 분봉, 일봉, 주봉, 월봉 만 지원한다고 가정하면?
114
+ if time_frame.endswith("m"):
115
+ # TODO : parse number
116
+ period = 1
117
+ params.update({"unit": period})
118
+
119
+ common_header = self.create_common_header(request_params=params)
120
+ response = await self.public_get_fetch_security_ohlcv_minute(self.extend(params))
121
+
122
+ elif time_frame.endswith("D"):
123
+ common_header = self.create_common_header(request_params=params)
124
+ response = await self.public_get_fetch_security_ohlcv_day(self.extend(params))
125
+
126
+ elif time_frame.endswith("W"):
127
+ common_header = self.create_common_header(request_params=params)
128
+ response = await self.public_get_fetch_security_ohlcv_week(self.extend(params))
129
+
130
+ elif time_frame.endswith("M"):
131
+ common_header = self.create_common_header(request_params=params)
132
+ response = await self.public_get_fetch_security_ohlcv_month(self.extend(params))
133
+
134
+ common_response = self.get_common_response(response=response)
135
+ if common_response.success != "0":
136
+ return ksxt.models.KsxtHistoricalDataResponse(header=common_header, response=common_response, info=None)
137
+
138
+ parsed_info = self.parser.parse_historical_data(response=response, symbol=symbol, base_market=base_market)
139
+
140
+ return ksxt.models.KsxtHistoricalDataResponse(header=common_header, response=common_response, info=parsed_info)
141
+
142
+ async def fetch_ticker(self, symbol: str, base_market: str = "KRW") -> ksxt.models.KsxtTickerResponse:
143
+ params = {"markets": self.safe_symbol(base_market, symbol)} # 다중 조회 시, 콤마로 구분 ex. 'KRW-BTC, BTC-ETH'
144
+
145
+ common_header = self.create_common_header(request_params=params)
146
+
147
+ response = await self.public_get_fetch_ticker_price(self.extend(params))
148
+
149
+ common_response = self.get_common_response(response=response)
150
+ if common_response.success != "0":
151
+ return ksxt.models.KsxtTickerResponse(header=common_header, response=common_response, info=None)
152
+
153
+ parsed_info = self.parser.parse_ticker(response=response, base_market=base_market)
154
+
155
+ return ksxt.models.KsxtTickerResponse(header=common_header, response=common_response, info=parsed_info)
156
+
157
+ async def fetch_tickers(self, symbols: List[str], base_market: str = "KRW") -> ksxt.models.KsxtTickersResponse:
158
+ symbol = ",".join([self.safe_symbol(base_market, symbol) for symbol in symbols])
159
+ params = {"markets": symbol}
160
+
161
+ common_header = self.create_common_header(request_params=params)
162
+
163
+ response = await self.public_get_fetch_tickers_price(self.extend(params))
164
+
165
+ common_response = self.get_common_response(response=response)
166
+
167
+ # 실패 시 오류 응답 반환
168
+ if common_response.success != "0":
169
+ return ksxt.models.KsxtTickersResponse(header=common_header, response=common_response, info=[])
170
+
171
+ # 데이터 파싱
172
+ parsed_info = self.parser.parse_tickers(response, base_market)
173
+
174
+ return ksxt.models.KsxtTickersResponse(header=common_header, response=common_response, info=parsed_info)
175
+
176
+ async def fetch_orderbook(self, symbol: str, base_market: str = "KRW") -> ksxt.models.KsxtSingleOrderBookResponse:
177
+ params = {"markets": self.safe_symbol(base_market, symbol), "level": 0}
178
+
179
+ common_header = self.create_common_header(request_params=params)
180
+
181
+ response = await self.public_get_fetch_orderbook(self.extend(params))
182
+
183
+ common_response = self.get_common_response(response=response)
184
+ if common_response.success != "0":
185
+ return ksxt.models.KsxtSingleOrderBookResponse(header=common_header, response=common_response, info=None)
186
+
187
+ parsed_info = self.parser.parse_orderbook(response=response, base_market=base_market)
188
+
189
+ return ksxt.models.KsxtSingleOrderBookResponse(header=common_header, response=common_response, info=parsed_info)
190
+
191
+ async def fetch_orderbooks(
192
+ self, symbols: List[str], base_market: str = "KRW"
193
+ ) -> ksxt.models.KsxtMultiOrderBookResponse:
194
+ symbol = ",".join([self.safe_symbol(base_market, symbol) for symbol in symbols])
195
+ params = {"markets": symbol}
196
+
197
+ common_header = self.create_common_header(request_params=params)
198
+
199
+ response = await self.public_get_fetch_orderbooks(self.extend(params))
200
+
201
+ common_response = self.get_common_response(response=response)
202
+
203
+ # 실패 시 오류 응답 반환
204
+ if common_response.success != "0":
205
+ return ksxt.models.KsxtMultiOrderBookResponse(header=common_header, response=common_response, info=None)
206
+
207
+ # 데이터 파싱
208
+ parsed_info = self.parser.parse_orderbooks(response, base_market)
209
+
210
+ return ksxt.models.KsxtMultiOrderBookResponse(header=common_header, response=common_response, info=parsed_info)
211
+
212
+ async def fetch_balance(self, acc_num: str, base_market: str = "KRW") -> ksxt.models.KsxtBalanceResponse:
213
+ params = {}
214
+
215
+ common_header = self.create_common_header(request_params=params)
216
+
217
+ response = await self.private_get_fetch_balance(self.extend(params))
218
+
219
+ common_response = self.get_common_response(response=response)
220
+ if common_response.success != "0":
221
+ return ksxt.models.KsxtBalanceResponse(header=common_header, response=common_response, info=None)
222
+
223
+ parsed_info = self.parser.parse_balance(response=response, base_market=base_market)
224
+
225
+ return ksxt.models.KsxtBalanceResponse(header=common_header, response=common_response, info=parsed_info)
226
+
227
+ async def fetch_cash(self, acc_num: str, base_market: str = "KRW") -> ksxt.models.KsxtCashResponse:
228
+ params = {}
229
+
230
+ common_header = self.create_common_header(request_params=params)
231
+
232
+ response = await self.private_get_fetch_cash(self.extend(params))
233
+
234
+ common_response = self.get_common_response(response=response)
235
+ if common_response.success != "0":
236
+ return ksxt.models.KsxtCashResponse(header=common_header, response=common_response, info=None)
237
+
238
+ parsed_info = self.parser.parse_cash(response=response, base_market=base_market)
239
+
240
+ return ksxt.models.KsxtCashResponse(header=common_header, response=common_response, info=parsed_info)
241
+
242
+ async def fetch_security(self, symbol: str, base_market: str = "KRW") -> ksxt.models.KsxtSecurityResponse:
243
+ params = {"market": self.safe_symbol(base_market, symbol)}
244
+
245
+ common_header = self.create_common_header(request_params=params)
246
+
247
+ response = await self.private_get_fetch_security_info(self.extend(params))
248
+
249
+ common_response = self.get_common_response(response=response)
250
+ if common_response.success != "0":
251
+ return ksxt.models.KsxtSecurityResponse(header=common_header, response=common_response, info=None)
252
+
253
+ parsed_info = self.parser.parse_security(response=response, base_market=base_market)
254
+
255
+ return ksxt.models.KsxtSecurityResponse(header=common_header, response=common_response, info=parsed_info)
256
+
257
+ async def fetch_trade_fee(
258
+ self, symbol: Optional[str] = "", base_market: str = "KRW"
259
+ ) -> ksxt.models.KsxtTradeFeeResponse:
260
+ params = {"market": self.safe_symbol(base_market, symbol)}
261
+
262
+ common_header = self.create_common_header(request_params=params)
263
+
264
+ response = await self.private_get_fetch_trade_fee(self.extend(params))
265
+
266
+ common_response = self.get_common_response(response=response)
267
+ if common_response.success != "0":
268
+ return ksxt.models.KsxtTradeFeeResponse(header=common_header, response=common_response, info=None)
269
+
270
+ parsed_info = self.parser.parse_trade_fee(response=response, base_market=base_market)
271
+ return ksxt.models.KsxtTradeFeeResponse(header=common_header, response=common_response, info=parsed_info)
272
+
273
+ async def fetch_closed_order_detail(
274
+ self, acc_num: str, order_ids: List[str], base_market: str = "KRW"
275
+ ) -> ksxt.models.KsxtOpenOrderResponse:
276
+ params = {
277
+ "uuids": order_ids,
278
+ }
279
+
280
+ common_header = self.create_common_header(request_params=params)
281
+
282
+ response = await self.private_get_fetch_closed_order_detail(self.extend(params))
283
+
284
+ common_response = self.get_common_response(response=response)
285
+
286
+ # 실패 시 오류 응답 반환
287
+ if common_response.success != "0":
288
+ return ksxt.models.KsxtOpenOrderResponse(header=common_header, response=common_response, info=None)
289
+
290
+ # 데이터 파싱
291
+ parsed_info = self.parser.parse_closed_order_history(response, base_market)
292
+
293
+ return ksxt.models.KsxtOpenOrderResponse(header=common_header, response=common_response, info=parsed_info)
294
+
295
+ async def fetch_open_order_detail(
296
+ self, acc_num: str, order_ids: List[str], base_market: str = "KRW"
297
+ ) -> ksxt.models.KsxtOpenOrderResponse:
298
+ params = {
299
+ "uuids": order_ids,
300
+ }
301
+
302
+ common_header = self.create_common_header(request_params=params)
303
+
304
+ response = await self.private_get_fetch_opened_order_detail(self.extend(params))
305
+
306
+ common_response = self.get_common_response(response=response)
307
+
308
+ # 실패 시 오류 응답 반환
309
+ if common_response.success != "0":
310
+ return ksxt.models.KsxtOpenOrderResponse(header=common_header, response=common_response, info=None)
311
+
312
+ # 데이터 파싱
313
+ parsed_info = self.parser.parse_open_order_history(response, base_market)
314
+
315
+ return ksxt.models.KsxtOpenOrderResponse(header=common_header, response=common_response, info=parsed_info)
316
+
317
+ async def fetch_closed_order(
318
+ self,
319
+ acc_num: str,
320
+ symbol: Optional[str] = "",
321
+ start: Optional[str] = None,
322
+ end: Optional[str] = None,
323
+ base_market: str = "KRW",
324
+ ) -> ksxt.models.KsxtClosedOrderResponse:
325
+ params = {"state": "done"}
326
+
327
+ if bool(symbol):
328
+ params.update({"market": self.safe_symbol(base_market, symbol)})
329
+
330
+ common_header = self.create_common_header(request_params=params)
331
+
332
+ response = await self.private_get_fetch_closed_order(self.extend(params))
333
+
334
+ common_response = self.get_common_response(response=response)
335
+ if common_response.success != "0":
336
+ return ksxt.models.KsxtClosedOrderResponse(header=common_header, response=common_response, info=None)
337
+
338
+ parsed_info = self.parser.parse_closed_order_history(response=response, base_market=base_market)
339
+
340
+ return ksxt.models.KsxtClosedOrderResponse(header=common_header, response=common_response, info=parsed_info)
341
+
342
+ async def fetch_open_order(
343
+ self,
344
+ acc_num: str,
345
+ symbol: str | None = "",
346
+ start: str | None = None,
347
+ end: str | None = None,
348
+ base_market: str = "KRW",
349
+ ) -> ksxt.models.KsxtOpenOrderResponse:
350
+ params = {"state": "wait"}
351
+
352
+ if bool(symbol):
353
+ params.update({"market": self.safe_symbol(base_market, symbol)})
354
+
355
+ common_header = self.create_common_header(request_params=params)
356
+
357
+ response = await self.private_get_fetch_opened_order(self.extend(params))
358
+
359
+ common_response = self.get_common_response(response=response)
360
+ if common_response.success != "0":
361
+ return ksxt.models.KsxtOpenOrderResponse(header=common_header, response=common_response, info=None)
362
+
363
+ parsed_info = self.parser.parse_open_order_history(response=response, base_market=base_market)
364
+
365
+ return ksxt.models.KsxtOpenOrderResponse(header=common_header, response=common_response, info=parsed_info)
366
+
367
+ async def fetch_withdrawal_history(
368
+ self, acc_num: str, base_market: str = "KRW"
369
+ ) -> ksxt.models.KsxtWithdrawalHistoryResponse:
370
+ params = {"currency": base_market, "state": "DONE"}
371
+
372
+ common_header = self.create_common_header(request_params=params)
373
+
374
+ response = await self.private_get_fetch_withdrawal_history(self.extend(params))
375
+
376
+ common_response = self.get_common_response(response=response)
377
+ if common_response.success != "0":
378
+ return ksxt.models.KsxtWithdrawalHistoryResponse(header=common_header, response=common_response, info=None)
379
+
380
+ parsed_info = self.parser.parse_withdrawal_history(response=response, base_market=base_market)
381
+
382
+ return ksxt.models.KsxtWithdrawalHistoryResponse(
383
+ header=common_header, response=common_response, info=parsed_info
384
+ )
385
+
386
+ async def fetch_deposit_history(
387
+ self, acc_num: str, base_market: str = "KRW"
388
+ ) -> ksxt.models.KsxtDepositHistoryResponse:
389
+ params = {"currency": base_market, "state": "ACCEPTED"}
390
+
391
+ common_header = self.create_common_header(request_params=params)
392
+
393
+ response = await self.private_get_fetch_deposit_history(self.extend(params))
394
+
395
+ common_response = self.get_common_response(response=response)
396
+ if common_response.success != "0":
397
+ return ksxt.models.KsxtDepositHistoryResponse(header=common_header, response=common_response, info=None)
398
+
399
+ parsed_info = self.parser.parse_deposit_history(response=response, base_market=base_market)
400
+
401
+ return ksxt.models.KsxtDepositHistoryResponse(header=common_header, response=common_response, info=parsed_info)
402
+
403
+ async def create_order(
404
+ self,
405
+ acc_num: str,
406
+ symbol: str,
407
+ ticket_type: Literal["EntryLong", "EntryShort", "ExitLong", "ExitShort"],
408
+ otype: Literal["limit", "market"],
409
+ price: Optional[float] = 0,
410
+ qty: Optional[float] = 0,
411
+ amount: Optional[float] = 0,
412
+ base_market: str = "KRW",
413
+ ) -> ksxt.models.KsxtCreateOrderResponse:
414
+
415
+ params = {
416
+ "market": self.safe_symbol(base_market, symbol),
417
+ }
418
+
419
+ if ticket_type == "EntryLong":
420
+ order_side = "bid"
421
+
422
+ if otype == "limit":
423
+ params.update({"side": order_side, "price": str(price), "volume": str(qty), "ord_type": "limit"})
424
+
425
+ response = await self.private_post_send_order_entry(self.extend(params))
426
+ elif otype == "market":
427
+ params.update(
428
+ {
429
+ "side": order_side,
430
+ "price": amount,
431
+ "ord_type": "price",
432
+ }
433
+ )
434
+
435
+ response = await self.private_post_send_order_entry_market(self.extend(params))
436
+ elif ticket_type == "ExitLong":
437
+ order_side = "ask"
438
+ params = {
439
+ "market": self.safe_symbol(base_market, symbol),
440
+ "side": order_side,
441
+ }
442
+
443
+ if otype == "limit":
444
+ params.update({"price": str(price), "volume": str(qty), "ord_type": "limit"})
445
+
446
+ response = await self.private_post_send_order_exit(self.extend(params))
447
+
448
+ elif otype == "market":
449
+ params.update({"ord_type": "market", "volume": str(qty)})
450
+
451
+ response = await self.private_post_send_order_exit_market(self.extend(params))
452
+ else:
453
+ raise ValueError(
454
+ f"Unsupported ticket_type '{ticket_type}'. {__class__.__name__} supports only 'EntryLong' and 'ExitLong'."
455
+ )
456
+
457
+ common_header = self.create_common_header(request_params=params)
458
+ common_response = self.get_common_response(response=response)
459
+ if common_response.success != "0":
460
+ return ksxt.models.KsxtCreateOrderResponse(header=common_header, response=common_response, info=None)
461
+
462
+ parsed_info = self.parser.parse_create_order(response=response, base_market=base_market)
463
+
464
+ return ksxt.models.KsxtCreateOrderResponse(header=common_header, response=common_response, info=parsed_info)
465
+
466
+ async def cancel_order(
467
+ self,
468
+ acc_num: str,
469
+ order_id: str,
470
+ symbol: str | None = "",
471
+ qty: float = 0,
472
+ *args,
473
+ base_market: str = "KRW",
474
+ **kwargs,
475
+ ) -> ksxt.models.KsxtCancelOrderResponse:
476
+ params = {"uuid": order_id}
477
+
478
+ common_header = self.create_common_header(request_params=params)
479
+
480
+ response = await self.private_delete_send_cancel_order(self.extend(params))
481
+
482
+ common_response = self.get_common_response(response=response)
483
+ if common_response.success != "0":
484
+ return ksxt.models.KsxtCancelOrderResponse(header=common_header, response=common_response, info=None)
485
+
486
+ parsed_info = self.parser.parse_cancel_order(response=response, base_market=base_market)
487
+
488
+ return ksxt.models.KsxtCancelOrderResponse(header=common_header, response=common_response, info=parsed_info)
ksxt/base/com_exchange.py CHANGED
@@ -2,7 +2,7 @@ from ksxt.base.exchange import Exchange
2
2
 
3
3
 
4
4
  class ComExchange(Exchange):
5
- def __init__(self, id: str, name: str, zone: str = 'Asia/Seoul', is_dev: bool = False) -> None:
5
+ def __init__(self, id: str, name: str, zone: str = "Asia/Seoul", is_dev: bool = False) -> None:
6
6
  super().__init__(id=id, name=name, zone=zone, is_dev=is_dev)
7
7
 
8
- self.is_session_alive = False
8
+ self.is_session_alive = False
ksxt/base/errors.py ADDED
@@ -0,0 +1,10 @@
1
+ class BaseError(Exception):
2
+ pass
3
+
4
+
5
+ class ExchangeError(BaseError):
6
+ pass
7
+
8
+
9
+ class NotSupportedError(ExchangeError):
10
+ pass