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/parser/upbit.py ADDED
@@ -0,0 +1,308 @@
1
+ from datetime import datetime
2
+ from operator import attrgetter
3
+ from typing import Dict, List
4
+
5
+ from ksxt.models.balance import BalanceData, BalanceInfo
6
+ from ksxt.models.cash import CashInfo
7
+ from ksxt.models.historical import HistoricalDataInfo, OHLCVData
8
+ from ksxt.models.market import MarketInfo, SecurityData, TradeFeeInfo
9
+ from ksxt.models.order import CancelOrderResponse, CreateOrderResponse
10
+ from ksxt.models.orderbook import MultiSymbolOrderBookInfos, OrderBookData, OrderBookInfo
11
+ from ksxt.models.ticker import MultiSymbolTickerInfo, TickerInfo
12
+ from ksxt.models.transaction import (
13
+ ClosedOrderHistory,
14
+ ClosedOrderInfo,
15
+ DepositHistory,
16
+ OpenedOrderHistory,
17
+ OpenedOrderInfo,
18
+ TransactionInfo,
19
+ WithdrawalHistory,
20
+ )
21
+ from ksxt.parser.parser import BaseParser
22
+ from ksxt.utils import safer, sorter
23
+
24
+
25
+ class UpbitParser(BaseParser):
26
+ def safe_symbol(self, base_market: str, security: str) -> str:
27
+ # If security already contains a hyphen, assume it's correctly formatted
28
+ if "-" in security:
29
+ return security
30
+
31
+ # Otherwise, assume security is just the symbol and format it with base_market
32
+ return f"{base_market}-{security}"
33
+
34
+ def safe_security(self, symbol: str) -> str:
35
+ if symbol.find("-") < 0:
36
+ return symbol
37
+ return symbol.split("-")[1]
38
+
39
+ def parse_markets(self, response: List[Dict], base_market: str = "KRW") -> MarketInfo:
40
+ securities = {}
41
+ for market in response:
42
+ market_base = market["market"].split("-")[0]
43
+ if market_base != base_market:
44
+ continue
45
+
46
+ security_data = self._parse_market(market, base_market)
47
+ securities[security_data.symbol] = security_data
48
+
49
+ return MarketInfo(market_id=base_market, securities=securities)
50
+
51
+ def _parse_market(self, market: Dict, base_market: str = "KRW") -> SecurityData:
52
+ return SecurityData(
53
+ symbol=safer.safe_string(market, "market"),
54
+ name=safer.safe_string(market, "korean_name"),
55
+ type="crypto",
56
+ warning_code=safer.safe_boolean(market, " market_warning"),
57
+ )
58
+
59
+ def parse_historical_data(self, response: List[Dict], symbol: str, base_market: str = "KRW") -> HistoricalDataInfo:
60
+ ohlcv = [self._parse_ohlcva(_, base_market) for _ in response]
61
+ sorted_ohlcv = sorter.sort_by(ohlcv, key="datetime")
62
+
63
+ security_name = symbol
64
+
65
+ result = HistoricalDataInfo(
66
+ symbol=self.safe_symbol(base_market, security_name), security_name=security_name, history=sorted_ohlcv
67
+ )
68
+
69
+ return result
70
+
71
+ def _parse_ohlcva(self, ohlcva: dict, base_market: str = "KRW") -> OHLCVData:
72
+ fmt = "%Y-%m-%dT%H:%M:%S"
73
+ return OHLCVData(
74
+ datetime=datetime.strptime(safer.safe_string(ohlcva, "candle_date_time_kst"), fmt),
75
+ open_price=safer.safe_number(ohlcva, "opening_price"),
76
+ high_price=safer.safe_number(ohlcva, "high_price"),
77
+ low_price=safer.safe_number(ohlcva, "low_price"),
78
+ close_price=safer.safe_number(ohlcva, "trade_price"),
79
+ acml_volume=safer.safe_number(ohlcva, "candle_acc_trade_volume"),
80
+ acml_amount=safer.safe_number(ohlcva, "candle_acc_trade_price"),
81
+ )
82
+
83
+ def parse_ticker(self, response: List[Dict], base_market: str = "KRW") -> TickerInfo:
84
+ ticker_info = self._parse_ticker(response[0], base_market)
85
+ return ticker_info
86
+
87
+ def parse_tickers(self, response: List[Dict], base_market: str = "KRW") -> MultiSymbolTickerInfo:
88
+ tickers_info = {}
89
+
90
+ for ticker_data in response:
91
+ symbol = safer.safe_string(ticker_data, "market")
92
+ ticker_info = self._parse_ticker(ticker_data, base_market)
93
+ tickers_info[symbol] = ticker_info
94
+
95
+ return MultiSymbolTickerInfo(tickers=tickers_info)
96
+
97
+ def _parse_ticker(self, response: dict, base_market: str) -> TickerInfo:
98
+ timestamp_ms = safer.safe_number(response, "timestamp")
99
+ timestamp_s = timestamp_ms / 1000.0
100
+
101
+ return TickerInfo(
102
+ symbol=safer.safe_string(response, "market"),
103
+ price=safer.safe_number(response, "trade_price"),
104
+ ts=datetime.fromtimestamp(timestamp_s),
105
+ )
106
+
107
+ def parse_orderbook(self, response: List[Dict], base_market: str = "KRW") -> OrderBookInfo:
108
+ ask_list = []
109
+ bid_list = []
110
+
111
+ for item in response:
112
+ for index, data in enumerate(item["orderbook_units"]):
113
+ ask_data, bid_data = self._parse_orderbook(data, index, base_market)
114
+ ask_list.append(ask_data)
115
+ bid_list.append(bid_data)
116
+
117
+ return OrderBookInfo(
118
+ total_asks_qty=safer.safe_number(response[0], "total_ask_size"),
119
+ total_bids_qty=safer.safe_number(response[0], "total_bid_size"),
120
+ asks=ask_list,
121
+ bids=bid_list,
122
+ )
123
+
124
+ def parse_orderbooks(self, response: List[Dict], base_market: str = "KRW") -> MultiSymbolOrderBookInfos:
125
+ order_books = {}
126
+
127
+ for symbol_info in response:
128
+ symbol = safer.safe_string(symbol_info, "market")
129
+ orderbook_units = symbol_info.get("orderbook_units", [])
130
+
131
+ ask_list = []
132
+ bid_list = []
133
+
134
+ for index, data in enumerate(orderbook_units):
135
+ ask_data, bid_data = self._parse_orderbook(data, index, base_market)
136
+ ask_list.append(ask_data)
137
+ bid_list.append(bid_data)
138
+
139
+ order_book_info = OrderBookInfo(
140
+ total_asks_qty=safer.safe_number(symbol_info, "total_ask_size"),
141
+ total_bids_qty=safer.safe_number(symbol_info, "total_bid_size"),
142
+ asks=ask_list,
143
+ bids=bid_list,
144
+ )
145
+
146
+ order_books[symbol] = order_book_info
147
+
148
+ return MultiSymbolOrderBookInfos(order_books=order_books)
149
+
150
+ def _parse_orderbook(
151
+ self, orderbook: dict, index: int, base_market: str = "KRW"
152
+ ) -> tuple[OrderBookData, OrderBookData]:
153
+ ask_data = OrderBookData(
154
+ side="ask",
155
+ ob_num=index,
156
+ ob_price=safer.safe_number(orderbook, "ask_price"),
157
+ ob_qty=safer.safe_number(orderbook, "ask_size"),
158
+ )
159
+
160
+ bid_data = OrderBookData(
161
+ side="bid",
162
+ ob_num=index,
163
+ ob_price=safer.safe_number(orderbook, "bid_price"),
164
+ ob_qty=safer.safe_number(orderbook, "bid_size"),
165
+ )
166
+
167
+ return ask_data, bid_data
168
+
169
+ def parse_balance(self, response: List[Dict], base_market: str = "KRW") -> BalanceInfo:
170
+ # currency가 'KRW'가 아닌 항목만 필터링
171
+ filtered_data = [data for data in response if data["currency"] != base_market]
172
+
173
+ # Initialize balance list
174
+ balances = [self._parse_balance(data, base_market) for data in filtered_data]
175
+
176
+ # 총 매입금액
177
+ total_amount = sum(bal.price * bal.qty for bal in balances)
178
+ total_evaluation_amount = sum(bal.evaluation_price * bal.qty for bal in balances)
179
+ total_pnl_amount = total_evaluation_amount - total_amount
180
+ total_pnl_ratio = (total_pnl_amount / total_amount * 100) if total_amount != 0 else 0
181
+
182
+ return BalanceInfo(
183
+ currency=base_market,
184
+ total_amount=total_amount,
185
+ total_evaluation_amount=total_evaluation_amount,
186
+ total_pnl_amount=total_pnl_amount,
187
+ total_pnl_ratio=total_pnl_ratio,
188
+ balances=balances,
189
+ )
190
+
191
+ def _parse_balance(self, data: Dict, base_market: str = "KRW") -> BalanceData:
192
+ return BalanceData(
193
+ symbol=self.safe_symbol(base_market, safer.safe_string(data, "currency")),
194
+ name=self.safe_symbol(base_market, safer.safe_string(data, "currency")),
195
+ evaluation_price=safer.safe_number(data, "avg_buy_price"),
196
+ price=safer.safe_number(data, "avg_buy_price"),
197
+ pnl_amount=0, # 필요에 따라 적절히 설정
198
+ pnl_ratio=0, # 필요에 따라 적절히 설정
199
+ qty=safer.safe_number(data, "balance"),
200
+ free_qty=safer.safe_number(data, "balance") - safer.safe_number(data, "locked"),
201
+ used_qty=safer.safe_number(data, "locked"),
202
+ )
203
+
204
+ def parse_cash(self, response: List[Dict], base_market: str = "KRW") -> CashInfo:
205
+ # currency가 'KRW'인 항목만 필터링
206
+ filtered_data = [data for data in response if data["currency"] == base_market]
207
+
208
+ # 필터링된 데이터가 없으면 기본값으로 CashInfo 반환
209
+ if not filtered_data:
210
+ return CashInfo(currency=base_market, cash=0.0, cash_d1=0.0, cash_d2=0.0)
211
+
212
+ # 필터링된 첫 번째 항목을 가져와서 CashInfo 생성
213
+ data = filtered_data[0]
214
+
215
+ return CashInfo(
216
+ currency=base_market,
217
+ cash=safer.safe_number(data, "balance"),
218
+ cash_d1=safer.safe_number(data, "balance"), # 데이터를 가정하여 같은 값 사용
219
+ cash_d2=safer.safe_number(data, "balance"), # 데이터를 가정하여 같은 값 사용
220
+ )
221
+
222
+ def parse_security(self, response: dict, base_market: str = "KRW") -> SecurityData:
223
+ market_info = response.get("market", {})
224
+
225
+ return SecurityData(
226
+ symbol=safer.safe_string(market_info, "id"),
227
+ name=safer.safe_string(market_info, "name"),
228
+ type="crypto",
229
+ bid_qty_unit=safer.safe_number(market_info.get("bid", {}), "price_unit"),
230
+ ask_qty_unit=safer.safe_number(market_info.get("ask", {}), "price_unit"),
231
+ bid_min_qty=safer.safe_number(market_info.get("bid", {}), "min_total"),
232
+ ask_min_qty=safer.safe_number(market_info.get("ask", {}), "min_total"),
233
+ bid_max_qty=safer.safe_number(market_info, "max_total"),
234
+ ask_max_qty=safer.safe_number(market_info, "max_total"),
235
+ bid_min_amount=safer.safe_number(market_info.get("bid", {}), "min_total"),
236
+ ask_min_amount=safer.safe_number(market_info.get("ask", {}), "min_total"),
237
+ bid_max_amount=safer.safe_number(market_info, "max_total"),
238
+ ask_max_amount=safer.safe_number(market_info, "max_total"),
239
+ warning_code=safer.safe_string(market_info, "state") != "active",
240
+ )
241
+
242
+ def parse_trade_fee(self, response: dict, base_market: str = "KRW") -> TradeFeeInfo:
243
+ return TradeFeeInfo(
244
+ limit_bid_fee=safer.safe_number(response, "bid_fee"),
245
+ limit_ask_fee=safer.safe_number(response, "ask_fee"),
246
+ market_bid_fee=safer.safe_number(response, "maker_bid_fee"),
247
+ market_ask_fee=safer.safe_number(response, "maker_ask_fee"),
248
+ )
249
+
250
+ def parse_closed_order_history(self, response: List[dict], base_market: str = "KRW") -> ClosedOrderHistory:
251
+ orders = [self._parse_closed_order_info(order, base_market) for order in response]
252
+ return ClosedOrderHistory(history=orders)
253
+
254
+ def _parse_closed_order_info(self, order: dict, base_market: str = "KRW") -> ClosedOrderInfo:
255
+ return ClosedOrderInfo(
256
+ uuid=safer.safe_string(order, "uuid"),
257
+ type=safer.safe_string(order, "side"),
258
+ symbol=self.safe_symbol(base_market, safer.safe_string(order, "market")),
259
+ price=safer.safe_number(order, "price"),
260
+ qty=safer.safe_number(order, "volume"),
261
+ amount=safer.safe_number(order, "price") * safer.safe_number(order, "volume"),
262
+ fee=safer.safe_number(order, "paid_fee"), # Adjusted to 'paid_fee' to match provided data
263
+ created_at=datetime.fromisoformat(safer.safe_string(order, "created_at")),
264
+ order_type=safer.safe_string(order, "ord_type"),
265
+ )
266
+
267
+ def parse_open_order_history(self, response: List[dict], base_market: str = "KRW") -> OpenedOrderHistory:
268
+ orders = [self._parse_open_order_info(order, base_market) for order in response]
269
+ return OpenedOrderHistory(history=orders)
270
+
271
+ def _parse_open_order_info(self, order: dict, base_market: str = "KRW") -> OpenedOrderInfo:
272
+ return OpenedOrderInfo(
273
+ uuid=safer.safe_string(order, "uuid"),
274
+ type=safer.safe_string(order, "side"),
275
+ symbol=self.safe_symbol(base_market, safer.safe_string(order, "market")),
276
+ price=safer.safe_number(order, "price"),
277
+ qty=safer.safe_number(order, "volume"),
278
+ amount=safer.safe_number(order, "price") * safer.safe_number(order, "volume"),
279
+ fee=safer.safe_number(order, "reserved_fee"),
280
+ created_at=datetime.fromisoformat(safer.safe_string(order, "created_at")),
281
+ order_type=safer.safe_string(order, "ord_type"),
282
+ )
283
+
284
+ def parse_withdrawal_history(self, response: List[Dict], base_market: str = "KRW") -> WithdrawalHistory:
285
+ parsed_items = [self._parse_transaction_history_item(item, base_market) for item in response]
286
+ return WithdrawalHistory(history=parsed_items)
287
+
288
+ def parse_deposit_history(self, response: List[Dict], base_market: str = "KRW") -> DepositHistory:
289
+ parsed_items = [self._parse_transaction_history_item(item, base_market) for item in response]
290
+ return DepositHistory(history=parsed_items)
291
+
292
+ def _parse_transaction_history_item(self, item: Dict, base_market: str = "KRW") -> TransactionInfo:
293
+ return TransactionInfo(
294
+ uuid=safer.safe_string(item, "uuid"),
295
+ type=safer.safe_string(item, "type"),
296
+ amount=safer.safe_number(item, "amount"),
297
+ fee=safer.safe_number(item, "fee"),
298
+ currency=safer.safe_string(item, "currency"),
299
+ created_at=datetime.fromisoformat(safer.safe_string(item, "done_at")),
300
+ )
301
+
302
+ def parse_create_order(self, response: dict, base_market: str = "KRW") -> CreateOrderResponse:
303
+ order_datetime = datetime.fromisoformat(safer.safe_string(response, "created_at"))
304
+ return CreateOrderResponse(order_datetime=order_datetime, order_id=safer.safe_string(response, "uuid"))
305
+
306
+ def parse_cancel_order(self, response: dict, base_market: str = "KRW") -> CancelOrderResponse:
307
+ order_datetime = datetime.fromisoformat(safer.safe_string(response, "created_at"))
308
+ return CancelOrderResponse(order_datetime=order_datetime, order_id=safer.safe_string(response, "uuid"))