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
@@ -0,0 +1,323 @@
1
+ from datetime import datetime
2
+ from typing import Dict, List
3
+
4
+ from ksxt.models.balance import BalanceData, BalanceInfo
5
+ from ksxt.models.cash import CashInfo
6
+ from ksxt.models.historical import HistoricalDataInfo, OHLCVData
7
+ from ksxt.models.market import HolidayHistory, HolidayInfo, MarketInfo, SecurityData, TradeFeeInfo
8
+ from ksxt.models.order import CancelOrderResponse, CreateOrderResponse, ModifyOrderResponse
9
+ from ksxt.models.orderbook import OrderBookData, OrderBookInfo
10
+ from ksxt.models.ticker import MultiSymbolTickerInfo, TickerInfo
11
+ from ksxt.models.token import TokenInfo
12
+ from ksxt.models.transaction import (
13
+ ClosedOrderHistory,
14
+ ClosedOrderInfo,
15
+ DepositHistory,
16
+ OpenedOrderHistory,
17
+ OpenedOrderInfo,
18
+ )
19
+ from ksxt.parser.parser import BaseParser
20
+ from ksxt.utils import safer, sorter, timer
21
+
22
+
23
+ class KoreaInvestParser(BaseParser):
24
+ def safe_symbol(self, base_market: str, symbol: str) -> str:
25
+ return symbol
26
+
27
+ def parse_token(self, response: dict) -> TokenInfo:
28
+ date_string = safer.safe_string(response, "access_token_token_expired")
29
+ date_format = "%Y-%m-%d %H:%M:%S"
30
+ expired_date = datetime.strptime(date_string, date_format)
31
+
32
+ return TokenInfo(
33
+ access_token=safer.safe_string(response, "access_token"),
34
+ token_type=safer.safe_string(response, "token_type"),
35
+ remain_time_second=safer.safe_number(response, "expires_in"),
36
+ expired_datetime=expired_date,
37
+ )
38
+
39
+ def parse_markets(self, response: dict, base_market: str = "KRW") -> MarketInfo:
40
+ securities = {}
41
+ for market in response:
42
+ security_data = self._parse_market(market, base_market)
43
+ securities[security_data.symbol] = security_data
44
+
45
+ return MarketInfo(market_id=base_market, securities=securities)
46
+
47
+ def _parse_market(self, market: Dict, base_market: str = "KRW") -> SecurityData:
48
+ return SecurityData(
49
+ symbol=safer.safe_string(market, "market"),
50
+ name=safer.safe_string(market, "korean_name"),
51
+ type="stock",
52
+ warning_code=safer.safe_boolean(market, " market_warning"),
53
+ )
54
+
55
+ def parse_historical_data(self, response: Dict, symbol: str, base_market: str = "KRW") -> HistoricalDataInfo:
56
+ safe_response = safer.safe_value(response, "output2")
57
+ ohlcv = [self._parse_ohlcva(_, base_market) for _ in safe_response]
58
+ sorted_ohlcv = sorter.sort_by(ohlcv, key="datetime")
59
+
60
+ security_name = symbol
61
+
62
+ result = HistoricalDataInfo(
63
+ symbol=self.safe_symbol(base_market, security_name), security_name=security_name, history=sorted_ohlcv
64
+ )
65
+
66
+ return result
67
+
68
+ def _parse_ohlcva(self, ohlcva: Dict, base_market: str = "KRW") -> OHLCVData:
69
+ fmt = "%Y%m%d"
70
+ return OHLCVData(
71
+ datetime=datetime.strptime(safer.safe_string(ohlcva, "stck_bsop_date"), fmt),
72
+ open_price=safer.safe_number(ohlcva, "stck_oprc"),
73
+ high_price=safer.safe_number(ohlcva, "stck_hgpr"),
74
+ low_price=safer.safe_number(ohlcva, "stck_lwpr"),
75
+ close_price=safer.safe_number(ohlcva, "stck_clpr"),
76
+ acml_volume=safer.safe_number(ohlcva, "acml_vol"),
77
+ acml_amount=safer.safe_number(ohlcva, "acml_tr_pbmn"),
78
+ )
79
+
80
+ def parse_historical_index_data(self, response: Dict, symbol: str, base_market: str = "KRW") -> HistoricalDataInfo:
81
+ safe_response = safer.safe_value(response, "output2")
82
+ ohlcv = [self._parse_index_ohlcva(_, base_market) for _ in safe_response]
83
+ sorted_ohlcv = sorter.sort_by(ohlcv, key="datetime")
84
+
85
+ security_name = symbol
86
+
87
+ result = HistoricalDataInfo(
88
+ symbol=self.safe_symbol(base_market, security_name), security_name=security_name, history=sorted_ohlcv
89
+ )
90
+
91
+ return result
92
+
93
+ def _parse_index_ohlcva(self, ohlcva: Dict, base_market: str = "KRW") -> OHLCVData:
94
+ fmt = "%Y%m%d"
95
+ return OHLCVData(
96
+ datetime=datetime.strptime(safer.safe_string(ohlcva, "stck_bsop_date"), fmt),
97
+ open_price=safer.safe_number(ohlcva, "bstp_nmix_oprc"),
98
+ high_price=safer.safe_number(ohlcva, "bstp_nmix_hgpr"),
99
+ low_price=safer.safe_number(ohlcva, "bstp_nmix_lwpr"),
100
+ close_price=safer.safe_number(ohlcva, "bstp_nmix_prpr"),
101
+ acml_volume=safer.safe_number(ohlcva, "acml_vol"),
102
+ acml_amount=safer.safe_number(ohlcva, "acml_tr_pbmn"),
103
+ )
104
+
105
+ def parse_ticker(self, response: List[Dict], base_market: str = "KRW") -> TickerInfo:
106
+ safe_response = safer.safe_value(response, "output")
107
+ ticker_info = self._parse_ticker(safe_response, base_market)
108
+ return ticker_info
109
+
110
+ def _parse_ticker(self, response: dict, base_market: str) -> TickerInfo:
111
+ return TickerInfo(
112
+ symbol=safer.safe_string(response, "stck_shrn_iscd"),
113
+ price=safer.safe_number(response, "stck_prpr"),
114
+ ts=datetime.now(),
115
+ )
116
+
117
+ def parse_orderbook(self, response: List[Dict], base_market: str = "KRW") -> OrderBookInfo:
118
+ ask_list = []
119
+ bid_list = []
120
+
121
+ level = 10
122
+
123
+ safe_response = safer.safe_value(response, "output1")
124
+
125
+ for index in range(1, level + 1):
126
+ ask_data, bid_data = self._parse_orderbook(safe_response, index, base_market)
127
+ ask_list.append(ask_data)
128
+ bid_list.append(bid_data)
129
+
130
+ return OrderBookInfo(
131
+ total_asks_qty=safer.safe_number(safe_response, "total_askp_rsqn"),
132
+ total_bids_qty=safer.safe_number(safe_response, "total_askp_rsqn"),
133
+ asks=ask_list,
134
+ bids=bid_list,
135
+ )
136
+
137
+ def _parse_orderbook(
138
+ self, orderbook: Dict, index: int, base_market: str = "KRW"
139
+ ) -> tuple[OrderBookData, OrderBookData]:
140
+ ask_data = OrderBookData(
141
+ side="ask",
142
+ ob_num=index,
143
+ ob_price=safer.safe_number(orderbook, f"askp{index}"),
144
+ ob_qty=safer.safe_number(orderbook, f"askp_rsqn{index}"),
145
+ )
146
+
147
+ bid_data = OrderBookData(
148
+ side="bid",
149
+ ob_num=index,
150
+ ob_price=safer.safe_number(orderbook, f"bidp{index}"),
151
+ ob_qty=safer.safe_number(orderbook, f"bidp_rsqn{index}"),
152
+ )
153
+
154
+ return ask_data, bid_data
155
+
156
+ def parse_balance(self, response: List[Dict], base_market: str = "KRW") -> BalanceInfo:
157
+ safe_response = safer.safe_value(response, "output1")
158
+
159
+ # Initialize balance list
160
+ balances = [self._parse_balance(_, base_market) for _ in safe_response]
161
+
162
+ # 총 매입금액
163
+ total_amount = sum(bal.price * bal.qty for bal in balances)
164
+ total_evaluation_amount = sum(bal.evaluation_price * bal.qty for bal in balances)
165
+ total_pnl_amount = total_evaluation_amount - total_amount
166
+ total_pnl_ratio = (total_pnl_amount / total_amount * 100) if total_amount != 0 else 0
167
+
168
+ return BalanceInfo(
169
+ currency=base_market,
170
+ total_amount=total_amount,
171
+ total_evaluation_amount=total_evaluation_amount,
172
+ total_pnl_amount=total_pnl_amount,
173
+ total_pnl_ratio=total_pnl_ratio,
174
+ balances=balances,
175
+ )
176
+
177
+ def _parse_balance(self, data: Dict, base_market: str = "KRW") -> BalanceData:
178
+ return BalanceData(
179
+ symbol=safer.safe_string(data, "pdno"),
180
+ name=safer.safe_string(data, "prdt_name"),
181
+ evaluation_price=safer.safe_number(data, "evlu_amt"),
182
+ price=safer.safe_number(data, "pchs_avg_pric"),
183
+ pnl_amount=safer.safe_number(data, "evlu_pfls_amt"),
184
+ pnl_ratio=safer.safe_number(data, "evlu_pfls_rt"),
185
+ qty=safer.safe_number(data, "hldg_qty"),
186
+ free_qty=safer.safe_number(data, "ord_psbl_qty"),
187
+ used_qty=safer.safe_number(data, "hldg_qty") - safer.safe_number(data, "ord_psbl_qty"),
188
+ )
189
+
190
+ def parse_cash(self, response: List[Dict], base_market: str = "KRW") -> CashInfo:
191
+ safe_response = safer.safe_value(response, "output2")
192
+
193
+ return CashInfo(
194
+ currency=base_market,
195
+ cash=safer.safe_number(safe_response, "dnca_tot_amt"),
196
+ cash_d1=safer.safe_number(safe_response, "nxdy_excc_amt"),
197
+ cash_d2=safer.safe_number(safe_response, "prvs_rcdl_excc_amt"),
198
+ )
199
+
200
+ def parse_security(self, response: Dict, base_market: str = "KRW") -> SecurityData:
201
+ safe_response = safer.safe_value(response, "output")
202
+
203
+ return SecurityData(
204
+ symbol=safer.safe_string(safe_response, "pdno")[-6:],
205
+ name=safer.safe_string(safe_response, "prdt_name"),
206
+ type=safer.safe_string(safe_response, "mket_id_cd"),
207
+ bid_qty_unit=0, # TODO : where is information?
208
+ ask_qty_unit=0, # TODO : where is information?
209
+ bid_min_qty=0, # TODO : where is information?
210
+ ask_min_qty=0, # TODO : where is information?
211
+ bid_max_qty=0, # TODO : where is information?
212
+ ask_max_qty=0, # TODO : where is information?
213
+ bid_min_amount=0, # TODO : where is information?
214
+ ask_min_amount=0, # TODO : where is information?
215
+ bid_max_amount=0, # TODO : where is information?
216
+ ask_max_amount=0, # TODO : where is information?
217
+ # 거래정지여부 or 관리종목여부
218
+ warning_code=safer.safe_string(safe_response, "tr_stop_yn") == "Y"
219
+ or safer.safe_string(safe_response, "admn_item_yn") == "Y",
220
+ )
221
+
222
+ def parse_trade_fee(self, response: Dict, base_market: str = "KRW") -> TradeFeeInfo:
223
+ # API에서 조회되는 내역을 찾지 못해 홈페이지 안내 문을 작성함. (단위 : %)
224
+ # https://securities.koreainvestment.com/main/customer/guide/_static/TF04ae010000.jsp
225
+
226
+ # 거래수수료
227
+ trading_fee = 0.18
228
+
229
+ # 매매수수료
230
+ return TradeFeeInfo(
231
+ limit_bid_fee=0.0140527 + trading_fee,
232
+ limit_ask_fee=0.0140527 + trading_fee,
233
+ market_bid_fee=0.0140527 + trading_fee,
234
+ market_ask_fee=0.0140527 + trading_fee,
235
+ )
236
+
237
+ def parse_closed_order_history(self, response: Dict, base_market: str = "KRW") -> ClosedOrderHistory:
238
+ safe_response = safer.safe_value(response, "output1")
239
+ history = [self._parse_closed_order_info(_, base_market) for _ in safe_response]
240
+ return ClosedOrderHistory(history=history)
241
+
242
+ def _parse_closed_order_info(self, order: Dict, base_market: str = "KRW") -> ClosedOrderInfo:
243
+ if safer.safe_string(order, "sll_buy_dvsn_cd") == "01":
244
+ _type = "ask"
245
+ else:
246
+ _type = "bid"
247
+
248
+ return ClosedOrderInfo(
249
+ uuid=safer.safe_string(order, "odno"),
250
+ type=_type,
251
+ symbol=safer.safe_string(order, "pdno"),
252
+ price=safer.safe_number(order, "avg_prvs"),
253
+ qty=safer.safe_number(order, "tot_ccld_qty"),
254
+ amount=safer.safe_number(order, "tot_ccld_amt"),
255
+ # 단일 주문 이력에는 수수료 관련 정보가 없고, 조회한 전체 목록에 대해 수수료 정보만 존재한다.
256
+ fee=0,
257
+ created_at=datetime.fromisoformat(safer.safe_string(order, "ord_dt")),
258
+ order_type=safer.safe_string(order, "avg_prvs"),
259
+ )
260
+
261
+ def parse_open_order_history(self, response: Dict, base_market: str = "KRW") -> OpenedOrderHistory:
262
+ safe_response = safer.safe_value(response, "output1")
263
+ history = [self._parse_open_order_info(_, base_market) for _ in safe_response]
264
+ return OpenedOrderHistory(history=history)
265
+
266
+ def _parse_open_order_info(self, order: Dict, base_market: str = "KRW") -> OpenedOrderInfo:
267
+ if safer.safe_string(order, "sll_buy_dvsn_cd") == "01":
268
+ _type = "ask"
269
+ else:
270
+ _type = "bid"
271
+
272
+ return OpenedOrderInfo(
273
+ uuid=safer.safe_string(order, "odno"),
274
+ type=_type,
275
+ symbol=safer.safe_string(order, "pdno"),
276
+ price=safer.safe_number(order, "avg_prvs"),
277
+ qty=safer.safe_number(order, "tot_ccld_qty"),
278
+ amount=safer.safe_number(order, "tot_ccld_amt"),
279
+ # 단일 주문 이력에는 수수료 관련 정보가 없고, 조회한 전체 목록에 대해 수수료 정보만 존재한다.
280
+ fee=0,
281
+ created_at=datetime.fromisoformat(safer.safe_string(order, "ord_dt")),
282
+ order_type=safer.safe_string(order, "avg_prvs"),
283
+ )
284
+
285
+ def parse_create_order(self, response: Dict, base_market: str = "KRW") -> CreateOrderResponse:
286
+ safe_response = safer.safe_value(response, "output")
287
+ fmt = "%Y%m%d"
288
+
289
+ order_hhmmss = datetime.strptime(safer.safe_string(safe_response, "ORD_TMD"), fmt)
290
+ order_datetime = timer.create_datetime_with_today(order_hhmmss)
291
+
292
+ return CreateOrderResponse(order_datetime=order_datetime, order_id=safer.safe_string(safe_response, "ODNO"))
293
+
294
+ def parse_cancel_order(self, response: Dict, base_market: str = "KRW") -> CancelOrderResponse:
295
+ safe_response = safer.safe_value(response, "output")
296
+ fmt = "%Y%m%d"
297
+
298
+ order_hhmmss = datetime.strptime(safer.safe_string(safe_response, "ORD_TMD"), fmt)
299
+ order_datetime = timer.create_datetime_with_today(order_hhmmss)
300
+
301
+ return CancelOrderResponse(order_datetime=order_datetime, order_id=safer.safe_string(safe_response, "ODNO"))
302
+
303
+ def parse_modify_order(self, response: Dict, base_market: str = "KRW") -> ModifyOrderResponse:
304
+ safe_response = safer.safe_value(response, "output")
305
+ fmt = "%Y%m%d"
306
+
307
+ order_hhmmss = datetime.strptime(safer.safe_string(safe_response, "ORD_TMD"), fmt)
308
+ order_datetime = timer.create_datetime_with_today(order_hhmmss)
309
+
310
+ return ModifyOrderResponse(order_datetime=order_datetime, order_id=safer.safe_string(safe_response, "ODNO"))
311
+
312
+ def parse_is_holiday(self, response: List[Dict], base_market: str = "KRW") -> HolidayHistory:
313
+ safe_response = safer.safe_value(response, "output")
314
+ history = [self._parse_is_holiday(_, base_market) for _ in safe_response]
315
+
316
+ return HolidayHistory(market=["kospi", "kosdaq"], country="KR", history=history)
317
+
318
+ def _parse_is_holiday(self, item: Dict, base_market: str = "KRW") -> HolidayInfo:
319
+ return HolidayInfo(
320
+ date=datetime.strptime(safer.safe_string(item, "bass_dt"), "%Y%m%d"),
321
+ weekend=safer.safe_string(item, "wday_dvsn_cd"),
322
+ is_holiday=not safer.safe_boolean(item, "opnd_yn"),
323
+ )
ksxt/parser/parser.py ADDED
@@ -0,0 +1,114 @@
1
+ from typing import Dict, List
2
+ import inspect
3
+
4
+ from ksxt.models.balance import BalanceData, BalanceInfo
5
+ from ksxt.models.cash import CashInfo
6
+ from ksxt.models.historical import HistoricalDataInfo, OHLCVData
7
+ from ksxt.models.market import HolidayHistory, HolidayInfo, MarketInfo, SecurityData, TradeFeeInfo
8
+ from ksxt.models.order import CancelOrderResponse, CreateOrderResponse, ModifyOrderResponse
9
+ from ksxt.models.orderbook import MultiSymbolOrderBookInfos, OrderBookData, OrderBookInfo
10
+ from ksxt.models.ticker import MultiSymbolTickerInfo, TickerInfo
11
+ from ksxt.models.transaction import (
12
+ ClosedOrderHistory,
13
+ ClosedOrderInfo,
14
+ DepositHistory,
15
+ OpenedOrderHistory,
16
+ OpenedOrderInfo,
17
+ TransactionInfo,
18
+ WithdrawalHistory,
19
+ )
20
+
21
+
22
+ class BaseParser:
23
+ def _raise_not_implemented_error(self):
24
+ caller = inspect.stack()[1].function
25
+ raise NotImplementedError(f"{self.__class__.__name__} called {caller}")
26
+
27
+ def parse_markets(self, response: List[Dict], base_market: str = "KRW") -> MarketInfo:
28
+ self._raise_not_implemented_error()
29
+
30
+ def _parse_market(self, market: Dict) -> SecurityData:
31
+ self._raise_not_implemented_error()
32
+
33
+ def parse_historical_data(self, response: dict, symbol: str, base_market: str = "KRW") -> HistoricalDataInfo:
34
+ self._raise_not_implemented_error()
35
+
36
+ def _parse_ohlcva(self, ohlcva: dict, base_market: str = "KRW") -> OHLCVData:
37
+ self._raise_not_implemented_error()
38
+
39
+ def parse_historical_index_data(self, response: dict, symbol: str, base_market: str = "KRW") -> HistoricalDataInfo:
40
+ self._raise_not_implemented_error()
41
+
42
+ def _parse_index_ohlcva(self, ohlcva: dict, base_market: str = "KRW") -> OHLCVData:
43
+ self._raise_not_implemented_error()
44
+
45
+ def parse_ticker(self, response: List[Dict], base_market: str = "KRW") -> TickerInfo:
46
+ self._raise_not_implemented_error()
47
+
48
+ def parse_tickers(self, response: List[Dict], base_market: str = "KRW") -> MultiSymbolTickerInfo:
49
+ self._raise_not_implemented_error()
50
+
51
+ def _parse_ticker(self, response: dict, base_market: str) -> TickerInfo:
52
+ self._raise_not_implemented_error()
53
+
54
+ def parse_orderbook(self, response: List[Dict], base_market: str = "KRW") -> OrderBookInfo:
55
+ self._raise_not_implemented_error()
56
+
57
+ def parse_orderbooks(self, response: dict, base_market: str) -> MultiSymbolOrderBookInfos:
58
+ self._raise_not_implemented_error()
59
+
60
+ def _parse_orderbook(
61
+ self, orderbook: dict, index: int, base_market: str = "KRW"
62
+ ) -> tuple[OrderBookData, OrderBookData]:
63
+ self._raise_not_implemented_error()
64
+
65
+ def parse_balance(self, response: List[Dict], base_market: str = "KRW") -> BalanceInfo:
66
+ self._raise_not_implemented_error()
67
+
68
+ def _parse_balance(self, data: Dict, base_market: str = "KRW") -> BalanceData:
69
+ self._raise_not_implemented_error()
70
+
71
+ def parse_cash(self, response: List[Dict], base_market: str = "KRW") -> CashInfo:
72
+ self._raise_not_implemented_error()
73
+
74
+ def parse_security(self, response: dict, base_market: str = "KRW") -> SecurityData:
75
+ self._raise_not_implemented_error()
76
+
77
+ def parse_trade_fee(self, response: dict, base_market: str = "KRW") -> TradeFeeInfo:
78
+ self._raise_not_implemented_error()
79
+
80
+ def parse_open_order_history(self, response: dict, base_market: str = "KRW") -> OpenedOrderHistory:
81
+ self._raise_not_implemented_error()
82
+
83
+ def _parse_open_order_info(self, order: dict, base_market: str = "KRW") -> OpenedOrderInfo:
84
+ self._raise_not_implemented_error()
85
+
86
+ def parse_closed_order_history(self, response: dict, base_market: str = "KRW") -> ClosedOrderHistory:
87
+ self._raise_not_implemented_error()
88
+
89
+ def _parse_closed_order_info(self, order: dict, base_market: str = "KRW") -> ClosedOrderInfo:
90
+ self._raise_not_implemented_error()
91
+
92
+ def parse_modify_order(self, response: dict, base_market: str = "KRW") -> ModifyOrderResponse:
93
+ self._raise_not_implemented_error()
94
+
95
+ def parse_cancel_order(self, response: dict, base_market: str = "KRW") -> CancelOrderResponse:
96
+ self._raise_not_implemented_error()
97
+
98
+ def parse_create_order(self, response: dict, base_market: str = "KRW") -> CreateOrderResponse:
99
+ self._raise_not_implemented_error()
100
+
101
+ def parse_withdrawal_history(self, response: List[Dict], base_market: str = "KRW") -> WithdrawalHistory:
102
+ self._raise_not_implemented_error()
103
+
104
+ def parse_deposit_history(self, response: List[Dict], base_market: str = "KRW") -> DepositHistory:
105
+ self._raise_not_implemented_error()
106
+
107
+ def _parse_transaction_history_item(self, item: Dict, base_market: str = "KRW") -> TransactionInfo:
108
+ self._raise_not_implemented_error()
109
+
110
+ def parse_is_holiday(self, response: List[Dict], base_market: str = "KRW") -> HolidayHistory:
111
+ self._raise_not_implemented_error()
112
+
113
+ def _parse_is_holiday(self, item: Dict, base_market: str = "KRW") -> HolidayInfo:
114
+ self._raise_not_implemented_error()